Simple AI Behaviors: Seeking
In Unity, one of the most basic movement functions we’re all fairly familiar with involves taking the float values from our WSAD inputs, applying those values to a direction vector, and then using that vector, combined with a speed variable, to affect the transform of an object that we want to control.
With minor changes to the direction vector, this simple function can also apply to autonomous objects, but unfortunately, the straightforward nature of this code is also its downfall. Mainly, there’s no account for rotations and the movement is rather unrealistic, among other things.
Fortunately, Craig Reynolds’ 1999 paper Steering Behaviors For Autonomous Characters proposed methods to allow autonomous objects to move and interact realistically through an artificial environment via their own local vectors and forces, or steering behaviors. There are numerous different behaviors, but like the code above that follows a specific direction towards a location, the seek behavior is calculated “to steer the character towards a specified position in global space” by adding forces (steering).
When implementing seek, there are three main velocity vectors to be concerned with: current, desired, and as referenced above, steering. Essentially, current velocity is the current force driving an object along a path, desired velocity is the force driving an object towards a target using the shortest possible path, and steering velocity is the resulting force of substracting the current from the desired velocity.
The steering velocity/force can then be added back to the object’s current velocity each frame in order to create a seek path. That seek path is what’s applied to the object’s transform position to smoothly adjust it’s movement to try and avoid unrealistic snap changes.
Like with the four pictures above, the seek behavior method can be broken into four simple sections, each relying on global variables.
These variables include three Vector3s for current, desired, and steering velocity, two floats for max velocity and force, and one Rigidbody for the rigidbody of the game object that the script is attached to. The max velocity and force can be modified in the inspector for easier testing and each velocity will initially start at Vector3.zero.
The desired velocity is found by simply subtracting the object’s transform position from the target’s position and then normalizing the value. Once normalized, it’s then multiplied by the max velocity to resemble a max speed.
Next, the steering velocity is set to the difference between the desired and current velocities, and it’s magnitude, or length, is then clamped by the max force. Max force represents the object’s turning power, and to calculate turning speed based on the object’s mass, the steering velocity can be divided by the mass of the object’s rigidbody.
After calculating steering velocity, add that vector back to current velocity which will then also have it’s magnitude clamped by max velocity.
Finally, to actually move the object, multiply the current velocity by Time.deltaTime to smooth out the movement even more and add it to the object’s transform.position. To get a nice looking behavior, the current velocity can also be added to the object’s transform.forward just like with it’s position, but other rotation alternatives like Quaternion.LookRotation() can be used.
If all goes well after mixing in a bit of your own targeting logic, then you should be left with a nice little autonomous steering behavior that not only looks like you’ve mastered all things vector-related in Unity, but also allows your objects to become quite the seeker!