Finite State Machines

Hi again

It is time for bloging again. I have neglected the bloging for some time. I have many times prioritized actual studies instead of bloging.

It is only a few days left and I am not sure if we will make the first deadline and have everything to pass the course.

Here is a little recap of my last update and what’s been going on since then:

I was doing the AI and had just discovered a way to get one object to chase after another using the distance formula. I was also considering how to make an object move around in patterns such as a rectangle and how to make an object move away from another.

What I have done since then is, made a simple rectangular pattern movement thing using points which can hold an object inside a rectangular shape for speeds high enough indeed for the pourpouses of this game. They are placed manually as of now but I can easily make them a Little bit more dynamic using the object’s starting Point as a base to create the four corners of the rectangular shape.
can it be any more simple than this:?
if (m_position.m_x >= p1.x && m_position.m_y <= p1.y)

{

m_velocity.m_x = 0;

m_velocity.m_y = speed;

}

if (m_position.m_x >= p2.x && m_position.m_y >= p2.y)
{
m_velocity.m_x = -speed;
m_velocity.m_y = 0;
}
if (m_position.m_x <= p3.x && m_position.m_y >= p3.y)
{
m_velocity.m_x = 0;
m_velocity.m_y = -speed;
}
if (m_position.m_x <= p0.x && m_position.m_y <= p0.y)
{
m_velocity.m_x = speed;
m_velocity.m_y = 0;
}

The starting position is somwhere between p0 and p1. (pn are simple struct objects containing the floats x and y.)
This is plainly simple, but also very bulky to have in the update() function of the object, right? I will come to that later on and show you how that function looks after the FSM was implemented.

now, the chasing code is fairly simple as well. the enemy have a pointer to a player object as a parameter in it’s constructor. it was the easiest way for me to let the enemy check for the players position. Enemy checks for the distance to the player and then uses the unit vector to get the direction to the player. the normalize() function takes the vector (not std::vector array – vector, but a 2 dimensional directional vector object) member variables and calculates the direction.
chasing code:


float speed = 200.0f;
m_velocity = { 0.0f, 0.0f };
Vector2 distance_to_player;
float delta_e_p_x = m_player->GetPosition().m_x - m_position.m_x;
float delta_e_p_y = m_player->GetPosition().m_y - m_position.m_y;
distance_to_player.m_x = delta_e_p_x;
distance_to_player.m_y = delta_e_p_y;
distance_to_player.Normalize();
m_velocity.m_x += distance_to_player.m_x * speed;
m_velocity.m_y += distance_to_player.m_y * speed;

I Think there are overloaded operators in the platformer to reduce this code and if not I can overload some. this is how ever as it looks right now and he is indeed chasing from any direction.

I reuse this code to make him run away I just add the velocity by the negative direction.

Like I mentioned earlier, Imagine this code straight in the update()-function including the run away code and a lot of if-statements to get the enemy to behave accordingly. Sure it could work and would do so but it would look like crap. and what if this was a big Project with many different behaviours? Another set-back of having this straight in the update() is it would possibly not give us any points. the AI must use a Finite State Machine (FSM) according to the assignment.

A FSM is a mathematical thing from a few hundred years ago but its implementation in the real World is everywhere and often quite simple. two common examples are the subway automated gates with the three metal bars moving in a circular motion to let one person pass at a time, and the lightswitch. The subway gates are locked until one person inputs something for example a coin. It then opens and the bars can move until it comes to the closing position and is closed again. it has one closed state and one open state and they can only change from one of the states to the other. The lightswitch has two states: glow and not glow. I think you know how to operate a lightswitch.

My FSM for my AI works like this:
There is one class called state. this is an abstract class (it cannot be instaciated (become an object)) It holds the pure virtual method Execute(arg1, arg2).
Then there are specific states who are inheariting from the abstract class state. These are however instanciated and they execute the function Execute(arg1, arg2) with the parameters being pointers to enemy objects and player objects. in this function I can call enemy functions and change states according to how the enemy end the player interacts.
The following example shows what happens in the RectPattern state’s Execute(arg1, arg2) function, in plain English what this does is the enemy moves in a rectangular pattern and if a player is at a sertain distance from the enemy, the enemy starts chasing the player.


void RectPattern::Execute(Enemy* enemy, Player* player)
{
enemy->Movement();
float delta_x = player->GetPosition().m_x - enemy->GetPosition().m_x;
float delta_y = player->GetPosition().m_y - enemy->GetPosition().m_y;
//if delta pos is negative, enemy will chase no matter how far a distance away.
if (fabs(delta_x) < 200.0f
&& fabs(delta_y) < 200.0f) { enemy->ChangeState(Chase::ChaseObject());
}
}

If the enemy’s currentstate pointer points to this next state chase state, it will call the enemy’s chase function and contain the logic to change state again to the run away state and so on. The enemy functions movement and chase are the ones shown earlier in this blog-post.

Now finally I can show you how my enemy’s update function looks, remember the code earlier? add things like the if-statement above and similar thing that could have been in the update()-function without the FSM and look at how it looks now:

void Enemy::Update(float deltatime)
{

m_pCurrentState->Execute(this, m_player);

m_position += m_velocity * deltatime;
if (HasCollider())
m_collider->m_position = m_position;
}

That’s ALL it takes to Control multiple behaviours of an object. (m_pCurrentState pointing at a RectPattern state object to begin with).

AI, check!

I Think we have almost all required points for the assignment, but we need to merge Everything, add some more maps and some Grafical UI..

man, sigh, crunching time, will I ever lose you?

Leave a comment