Context steering is an improved steeringbehaviour in which you have a specified number of rays pointing outwards from your agent. (usually saved in an array or a vector) Based on the context, these rays will be assigned an interest value, or a danger value if needed. The latter could be applied when the ray hits an obstacle. This would mean the agent could possibly ignore this direction in it's final calculation, or instead, subtract this danger value from it's original interest value.
The biggest improvement context steering offers, is the fact that you don't need to make tons of different steeringbehaviours. Meaning, you're code will be much more limited, and possible more readable as well.
The Context-Based Steering algorithm is quite a simple one, but before we get started, let's have a quick overview! Imagine your agent has 8 arrows around him, each in a different direction.
These arrows will hold the interest value, or in other words, the more-likely the agent is to move in that direction. We can then visualize these arrows as follows:
But as you can imagine, if this were the case, the agent would be willing to move in all directions, meaning they will cancel each other out, and your agent will remain stationary. We want the agent to be moving forward, so we will say that the less forward the arrow points, the less interest this direction should get. This leads us to the following interest vector:
The last thing we want to cover in this algorithm, is avoiding obstacles. To handle this, we will create a second vector, let's call it the danger vector. For each arrow, we will check if the arrow is colliding with an obstacle. If so, we will simply ignore the interest value for this direction.
And there you have it! As I mentioned above, it's quite a simple algorithm!
We start off by creating the arrows, this is a very simple for-loop, calculating it's desired angle. (or direction) Obviously, the size and the amount of arrows can be edited, experimenting with these variables can lead to different behaviours. For example, if you want to have an enemy that's able to detect and avoid obstacles from afar, you might want to increase the size of each arrow. But on the other hand, you might also want to increase the number of arrows, since, if you are looking far ahead, the gaps in between each direction grows, leading to potentially unseen obstacles, until your close. (See Image)
On the other hand you also have to account the performance aspect. Since increasing the number of arrows will require more computations. This however, does not apply to the size of the arrow, since my object detection is simply a distance check to the obstacle.
For this part we will use an std::vector of floats, each value in range of [0,1]. The higher the value, the more likely it is the agent will go that direction. In our case, we want to move towards a position we can define by the mouse cursor. To handle this, we will take the dot product of the arrow's direction, and the direction to the target we're headed towards. If this dot product is below 0, we know that this direction is headed in the opposite direction of the target, so we can set it's interest to 0. For the other arrows, we'll just set the interest value to the dot product's result. Doing this should already make the agent move in the direction of the target.
(In my example, the length of the arrow represents it's importance)
Of course we also have to account the actual obstacles in the scene. To do this we'll create another std::vector of floats, again with a value in range [0,1]. To keep it simple, we'll start off by simply checking if the arrow is colliding with the obstacle, we'll put that specific arrow's danger value to 1.
If you want to, you can make it a little more complex, by actually interpolating this danger between 0 and 1, depending on the range of the obstacle. This will make the movement a little bit smoother than before. But depending on the use case, this may or may not be the result you want to achieve.
Once we're done calculating the danger values, we want to subtract these from the interest values. Meaning that, if we have an interest value of 1, and a danger value of 1 for that same direction. We'll end up with an interest value of 0. This will make sure that this direction is ignored in the final direction's calculation.
Finally, we have to merge all of these together, so we can calculate the desired direction of the agent. To do this, we basically sum up all the desired directions multiplied by their interest value and then divide these again by the number of arrows used in all previous calculations. If done correctly, this will ignore the directions headed to the obstacle(s). Or, if you're using the interpolating danger calculations, it will smoothly go around the object.
And that's basically it! It's a very simple and quick way to implement a basic, but sufficient steering behaviour in your games!
- Name: Sasha Vigneron
- Group: 2GD07
- Institute: Digital Arts & Entertainment @ Howest Kortrijk, Belgium