Wall or corner avoidance
Let’s start with a demo of the new wall avoidance behaviour, alright? Let’s.
- the first scene shows what would happen with no wall avoidance
- 2nd and 3rd scenes show the additive and the cross product method with a constant acceleration
- the 4th and 5th show the additive and the cross product method with increasing acceleration
- you can switch between the scenes with the keys 1 – 5 on your keyboard
- more details below
I have already written about how it is difficult to find the right balance between natural looking movement and control. The issues become the most obvious when the agent has to go around corners. With a sloppy path following behaviour (by this I mean one where the agent is allowed to wander off path quite freely) the agent can easily get caught on corners.
To avoid this we would use some avoidance behaviour that corrects the agent’s movement when it gets dangerously off path near obstacles. The one I have read about introduces more issues so I decided to not use, but here is how it works:
- we use some sort of raycasting to check for obstacles
- in the picture I show one with 2 whiskers
- when the ray hits some collider we take the hit normal and multiply it by some value if necessary
- then we add it to the hit point
- we use a seek behaviour to accelerate the agent towards this point
And here is the issue it introduces:
- the agent is trying to follow the path around the corner, but the whisker catches the wrong side of the wall
- the avoidance behaviour accelerates the agent in the wrong direction
- agent is likely to end up in a corner trap where one of the whiskers always hits the obstacle accelerating the agent back and forth in opposite directions
So what can we do about it? Well I came up with two solutions which are pretty similar. I haven’t read about these yet, but I’m sure they have been used before. Both involve some basic vector math. I call the first one the additive method, because it uses vector addition. The second one uses cross product calculation so I call it the cross product method. They both use raytracing but calculate the acceleration differently.
The additive method:
- we check for obstacles with raytracing
- when there is an obstacle near the agent we use the hit coordinates of the ray to calculate the relative position
- we add the relative position vector to the agent’s velocity vector
- we normalize the resulting vector and multiply it by a certain amount to get the acceleration we are going to apply
The cross product method:
- starts with raytracing too
- here we calculate the direction of the acceleration by taking the cross product of the velocity and the global up vector if the obstacle is on the right side of the agent, and the same vector multiplied by -1 if it’s on the left
- this way we get an acceleration that is perpendicular to the velocity vector
- then we normalize and multiply as always
We can multiply by a constant amount or use an inverse square law calculation to give an increasing strength to the force pushing the agent away from the obstacle as it gets closer. We would still want to put a cap on the maximum acceleration we want to use.
It would look something like this:
strength = Min(decayCoeficient / distanceSquare, maxAcceleration);
The conditions in the demo are quite harsh. The capsule’s radius is 0.5 units, while doors in the upper half of the map are 1.5 units wide, and doors in the bottom half are 2 units wide. The thickness of the walls varies between 0.1 and 0.4 units. To get better results you have to put restrictions on the level design. Doors that are a bit wider would present much less of a problem. Although I find that a bit problematic since single doors in our world are rarely wider than twice the shoulder width of a person. However wide walls are much less of an immersion breaking elements than an NPC not being able to go through a door.