Oh no. I accidentally created
a Newton’s Cradle of Skellies. Howdy! And welcome to Game Endeavor.
Where I make devlogs with an emphasis on game design and technique.
I’m developing an open world RPG where you’ll meet lovable characters, explore
dark and treacherous dungeons, and build a base to stash your precious cheese. One of the more
difficult tasks that I’ve ever come across as a developer is how to handle enemy movement in a
way that’s convincing and engaging for the player. This is especially difficult because there are a
lot of factors to consider such as what direction they should move towards to reach their goal.
What entities are nearby so they don’t clump up together into a hyperdense singuskellety.
The nearby environment and everything else around them, and even once they have figured
out how they should move, they need to do so smoothly so that they don’t jitter around like
me after a long night of binge coding and coffee. Even though combat isn’t the primary focus
of my game, not nearly as much as exploration and the roleplaying. I still want it to
be enjoyable, because it is what builds tension and keeps the player engaged as they
explore the world and uncovers its secrets. If you look into AI movement, then one of
the first things you may encounter is what’s known as boids and steering behaviors.
This is not what I’m using to be clear, but it is where I started and is what helped me to
develop the technique that I am currently using. Steering behaviors are where you combine
simple actions such as move to this point, keep away from these points, and move in
the same direction as these other points. On their own these behaviors are very simple and
unimpressive. But when combined and tweaked in specific ways, they create something that I was
quite stunned by when I first encountered them. But after implementing this some time ago in a
prototype for another game, I quickly noticed some major flaws with the system. The biggest
glaring one being how opposing vectors can cancel each other out to cause pretty silly situations
where the AI needs to run away from the player, but there’s a wall immediately opposite to
them. So they just stand there completely dumbfounded by the existence of this wall as
they wait for their slowly impending doom. Ideally you would want them to realize that
they have other options available to them, such as stepping ever so slightly to the side
and continuing along that path for a bit. But since there’s only one vector that
has complete control over how they move, they just sit there barely moving if at all. I searched for a while about how I could
improve this, or alternatives to AI movement and found a context based steering behavior
that inspired what I am currently using. This is very similar to the behavior that I
mentioned before, except it keeps a record of how desirable different directions are and chooses the
preferred direction that it can actually perform. For example, say I set down a carrot for
Truffles to snack upon, odds are he’s going to move directly towards the carrot. That would be
the obvious choice. But hold on there Truffles, not so fast. He could, in theory, step off to the
side for a bit, make a big half circle around the carrot, and then go in for the snack. It would
be rather silly, yes. But it’s still an option. So to help truffles evaluate the different
directions that he could take, I’m storing his options as weights that he can check and
pick the most desirable path in the event that some of his options may be limited. To help me
visualize these weights as I’m developing the movement systems, and help you visualize them
while binging these devlogs and subliminally clicking little blue thumbs, I have created a
gizmo that can draw what the AI is thinking. Each line represents a direction that
they are considering. Green lines showing a desire to move towards that direction
with longer lines being most desirable, and red lines meaning that they have
absolutely no desire to move in that direction. So as you can see, when one direction
becomes obstructed then the AI will pick the next best option and move in that direction
instead. In order for the AI to determine this, I need to tell it how likely a direction is to
produce a desired result. I’m using a dot product here between the direction being considered and a
vector that the entity would like to move towards. For those unaware, dot product is a very useful
thing that will return the cosine value of the angle between two normalized vectors. This
value will be 1 in the direction of the normal, -1 in the opposite direction,
and 0 perpendicular to it. When normalized between 0 and 1, you get a
scale of how desirable that direction is. The AI is using this to decide
which direction it should move. This is great for moving towards a specific
location. I can simply take the highest unobstructed weight and move in that direction.
But the true power of this system comes when you start shaping the weights and combining them
to perform even more interesting behaviors. Skellies making a beeline for you is one way to
prototype a simple combat system. It is what I have used so far and it served its purpose as a
placeholder, but it’s not nearly as engaging as it could be. A much more interesting behavior would
be for the skellies to move in and out of combat, strafe around their target, all
while keeping a safe distance. I have one set of weights that will move
towards their target as normal, but as they get closer to their target, the value of these
weights are lessened so that other behaviors can take over. So I’m weighting the weights.
Once they are within range to start strafing around the character, I apply a shaping function
to the dot product that favors sideways movement instead of forward and backwards. This causes them
to start circling around their target. Except, oh no… I accidentally created a Newton’s
cradle of skellies. I had to offset their following distance a bit otherwise they
start making perfect circles around their target and bouncing off of each other.
A big issue that I’ve had with this system though is tweaking the movement to eliminate a
lot of jitteriness that I’ve been noticing due to entities rapidly changing their direction
back and forth. This is because I was having them move directly away from any nearby
entities and so they would get caught up in this tug-o-war of pushing each other in
an attempt to get to their desired target. I fixed this by also applying a shaping
function to their separation logic so that instead of trying to move
directly away from the nearest enemy, they would prefer to do so at a bit of an angle.
When you combine this smarter form of movement with the overall tweaks to the skelly’s combat
state machine, you get a very fun enemy to fight that even I spend my free time spawning a few
in for a quick session. And when I had a tester play this, they spent over an hour just fighting
a handful of skellies and sprouts over and over. Combat wasn’t the only enemy behavior to receive a
massive improvement though. One of the main design pillars for my game is immersion, having things
feel natural and convincing. And something I’ve been wanting to improve for a while now is
the wandering logic for my enemies. Before now it has been a placeholder just so they’re not
standing in one place when I record these devlogs. The logic was that they would pick a random point
somewhere around their spawnpoint and move towards it. If they were near the point or it became
obstructed within a short distance then they would pick another point and start moving towards it.
This is decent enough but it feels very erratic and unnatural. The AI changes direction
sporadically and to me it feels a little off. Rather than picking a random point to
move towards, I would rather have them meander about. Gradually changing their
direction, but remaining near their spawn point so that they don’t wander off too far.
To have them wander about, I’m having them move in a direction, but over time this direction
will gradually sway left and right which causes the entity to start changing direction. I’m using
OpenSimplex noise here to adjust the direction. There are a few benefits to using it here. Namely
that the randomization it produces is smooth which gives the wandering a more natural feel to it.
But more importantly the values of OpenSimplex noise tend to be more heavily weighted towards 0,
which means that if done right the entity isn’t going to spend too much time changing direction.
Instead it will move straight for periods of time, and then every so often it’ll start changing its
direction but then start pulling back towards 0, which causes it to again move mostly straight.
To keep them near their spawn point, I’m weighing their wandering direction back
towards their spawn point whenever they start getting too far away from it. This causes them
to gradually start turning around until they’re no longer moving away from the spawn point.
To keep this looking smooth and natural, this isn’t a hard transition though. The weighting
starts to take effect at a pretty short distance from their spawn point, currently at about
a 2 tile radius. But it’s weighted based on how far they are from the spawn point, so
it’s much less effective until they start getting further and further away. But since
it’s smoothed out over that distance, it can be difficult to tell where that point is, so to
me it feels much more natural and convincing. After I added these movement tweaks and ai
improvements, I started experimenting with a faction system for NPC targeting, I ended up
making it so that enemies could target each other, which surprisingly enough led to this interesting
battle royale behavior with the enemies. I didn’t expect this to look nearly as amazing
as it did with no additional tweaking, but this was an aha moment for me. If I can
flip a switch and make NPCs fight each other, and have it look this amazing. Then I think I’m
going to lean a whole lot more into the pet taming and raising system that I have planned next.
If you want to join me as I work on this, then I’ve been streaming development nearly
every day on my Discord, there’s a link in the description to join our community. It has been
a fantastic way to interact with everyone as we do everything from adding in new mechanics,
drawing art, or just playing games together. We also got our first fanart of the spirit
skelly which is absolutely adorable, as well as the two new NPCs that we designed during
the Tuesday streams. It’s always a treat when sco_otr draws along with me during these. Thanks
for watching, and I’ll see you in the next one!