The Motor Control Units Development (MCU Dev) package was created to familiarize myself with the Motor Control Unit functionality for the physics based interaction system called Forcibles which the VR framework PuppetJump uses for object interactions. This package was also created to explore some of the capabilities which could become possible by using the Motor Control Units functionality.
This post will cover the following sections:
- Intro
- Basic Flyer Navigation
- Basic Bait
- Basic Landing
- Target Obstruction Detection
- Collision Avoidance
- Basic Prefab Spawner
Intro
As a developer, I find that the development process gets streamlines when working with content and functionality that I am familiar with. The more acclimated I am to the core functions and capabilities of the framework or platforms I am working with, the easier and more intuitive it is to build, create, and brainstorm various functionality and features for any project.
With that in mind I started the MCU Dev package. The MCU Dev package is a bundle of assets and code I created while exploring the Motor Control Unit (MCU) feature included in the Forcibles package used to create and control physics based interactions in the PuppetJump framework.
The goal of the MCU Dev package was to familiarize myself with the basics of Motor Control Units, while also exploring what capabilities I could create by leveraging their functionality.
The following blog post covers each step I took during my exploration of the Motor Control Units functionality.
Basic Flyer Navigation
The first step in getting myself acclimated with the Forcibles system was to work with a basic flyer and target set. This set contains a moving object (flyer) and an object that it tracks and tried to move toward (target). Each of the flyers is equipped with a set of motors, one that moves the flyer forward and one that controls the direction the flyer is facing by adding rotational force. By giving the flyer a target to always attempt to look at and throttling the power of its forward motor based on distance from that target, the flyers would automatically move to a target object. Once the flyer reaches its target, the target gets randomly placed at a new location within pre-defined boundary parameters. This allows the flyer to constantly create dynamically randomized flight patterns within a specified space.
Basic Bait
Now that the I understood how the flyers navigated their environment, it was time to add some variation in the form of basic bait. Basic bait objects are small spheres which have a specified area of effect around them. Anytime a flyer moves within a specified proximity of a bait object, that bait object gets set as the flyers primary target. If a flyer moves out of the specified proximity of a bait object, it returns to tracking its original target object.
Basic Landing
Once I had the basic bait functionality up and running correctly, I quickly started noticing a behavioral quirk they created in the flyers. When a flyer was actively tracking a bait object, they would fly directly toward it until they ran directly into it. At this point they would constantly continue to fly directly into the bait, essentially becoming trapped in a single location. I realized at this point that I needed to create a basic landing functionality.
The basic landing functionality I created allows each flyer to land on a bait object for a short time before continuing on to a new target. This works by slowing the flyers forward movement to a stop as it gets within a certain distance of the bait. Then the landing functionality starts a short timer to track the landed duration for the flyer. Once the timer ends, the flyer moves the bait object to an “ignore” list before assigning another target object to be its active tracking target. When the flyer exits the effect proximity of the bait object, the bait is removed from the ignore list so that the flyer may be drawn to the bait object again in future encounters.
Target Obstruction Detection
The next behavioral quirk I addressed with the flyers was a tendency to get stuck. This most frequently happened due to their target object being placed in a location that they could not directly access. This would happen when a target object was placed behind another stationary object in the scene. To remedy this quirk, I created the target obstruction detection functionality.
This functionality works by using a raycast to check for any obstacles between the flyer and its current target. If the raycast hits any object between the flyer and its target, then the flyer reacts in one of two ways based on the type of target. If the flyer is tracking its regular default target when the raycast finds a path obstruction, the default target is randomly placed at a new location within the placement boundaries. If the flyer is tracking a bait object when an obstacle is found, then the flyer moves the bait object to the ignore list and starts tracking its next target. This dual functionality allows the flyers to more naturally and continuously navigate their environment without making the bait objects jump around the scene anytime they are blocked from a flyers path.
Collision Avoidance
The next big obstacle to creating natural and continuous navigation was Collision Avoidance. When implemented properly, this would allow each flyer to intelligently identify approaching obstacles in their path and dynamically alter their course to avoid colliding with the obstacles. This task took quite a bit of trial and error to create.
When building the collision avoidance functionality, I started with the most basic situation of checking for objects directly in front of the flyer within a given distance. As the flyer moves, it casts a line (ray) directly forward and if this ray hits any objects that aren’t the flyers target, that means an obstacle has been found. To avoid running into this obstacle, the flyer initiates a dodge. During a dodge, the flyer is assigned a temporary target to move toward which is placed in a direction which has been determined as being clear of obstacles. Verifying which directions are viable directions to dodge, and more so making those dodge options dynamically randomized is where things got tricky.
I knew I could give the code a set of predefined directions to check in order, but I wanted to avoid obvious repetitions in dodge functionality. I knew that if the flyers always tried to dodge to the left when encountering an obstacle, they would appear too mechanical and less organic in their behavior.
So, I created a set of directions whose order gets randomized each time a dodge direction needs to be found. Once a viable dodge direction is found, the flyer alters its course to move to the temporary dodge target’s location. When the flyer has reached the dodge target or has cleared the obstacle, it’s original target is restored and it continues on its way.
The video below exhibits this dodge functionality in action. The second portion of the video below displays colored debugging lines along the rays used to check for obstacles.
The green line displays the direct path to the target.
The red line is drawn between the flyer and the obstacle it is attempting to dodge.
The yellow lines display failed checks for viable dodge directions, or directions which have been checked and found to not be viable dodge directions.
The blue line is drawn between the flyer and its current dodge target location, or the location the flyer is actively trying to dodge to.
Basic Prefab Spawner
Now that I had flyers which intuitively navigate their environment and interact dynamically with bait around the scene, it was time to create a function to alter the number of flyers in the scene. This is when I created the Basic Prefab Spawner functionality which would generate a new flyer at set increments of time until reaching a specified flyer limit. This would gradually populate the scene with flyers, rather than starting the scene out with the user completely swarmed by them.
Leave a Reply