Friday, September 27, 2013

State Based AI

I want to explain a little bit about FSM based AI. The concept is simple: an AI has a set of behaviors, and acts on a behavior depending on certain conditions. The enemies in Cranky Rampage all use this method.

The Sentry is your basic cannon fodder enemy. It can be in any of three states:

private int state;
private final int MOVING_TO_PLAYER = 0;
private final int SHOOTING = 1;
private final int COOLDOWN = 2;

When in the MOVING_TO_PLAYER state, the Sentry will simply try to get closer to the player. Once it reaches a certain proximity, it will move on to the SHOOTING state. Here, it will stop moving and fire a bullet. Then it will move into the COOLDOWN state where it will wait for some time. I have to find the edges in this FSM, meaning I have to find which states can transition to which other states.

MOVING_TO_PLAYER transitions to SHOOTING, SHOOTING transitions to COOLDOWN, and COOLDOWN can either go back to MOVING_TO_PLAYER or SHOOTING. So here's our list of edges for the graph:

MOVING_TO_PLAYER --> SHOOTING : if within range
SHOOTING --> COOLDOWN
COOLDOWN --> MOVING_TO_PLAYER : if out of range
COOLDOWN --> SHOOTING : if within range

Now coding the Sentry's AI is simple.

if(state == MOVING_TO_PLAYER) {
    if(player.getx() < x) {
        left = true;
        right = false;
    }
    else if(player.getx() > x) {
        left = false;
        right = true;
    }
    if(Math.abs(player.getx() - x) < range) {
        state == SHOOTING;
    }
}
else if(state == SHOOTING) {
    left = right = false;
    GameObjectFactory.createBullet(...);
    state = COOLDOWN;
}
else if(state == COOLDOWN) {
    timer++;
    if(timer > time) {
        timer = 0;
        if(Math.abs(player.getx() - x) < range) {
            state = SHOOTING;
        }
        else {
            state = MOVING_TO_PLAYER;
        }
    }
}

This is an easy way to give your NPCs some behavior. State based AI is simple to implement and can be effective.


9 comments:

  1. Looks cool, hopefully be a demo on youtube soon ?

    ReplyDelete
  2. I'll bring out a demo once I finish the demo level. When I said soon, it usually means I'm not really sure lol. Anyway I'll try to keep posting if there's any relevant tutorial value in it.

    ReplyDelete
  3. Have you got any android / iOS game or are u thinking about launching one?? it would be great.

    ReplyDelete
    Replies
    1. Nope. Never tried either Android or iOS SDK.

      Delete
    2. You should. Your games are great. I am sure it would be a huge sucess!

      Delete
  4. Hello mike im working with this code:


    "You need to check each asteroid with all other asteroids, but not check asteroid with itself.

    for(int i = 0 ; i < asteroids.length; i++) {
    for(int j = 0; j < asteroids.length; j++) {
    if(i == j) continue;
    // if hit, reflect
    }
    }

    ==============================
    =

    I have the reflection code in com.neet.gamestates.PlayState#update().

    // check if asteroid hits asteroid
    for(int i = 0; i < asteroids.size(); i++) {
    Asteroid ai = asteroids.get(i);
    for(int j = 0; j < asteroids.size(); j++) {
    if(i == j) continue;
    Asteroid aj = asteroids.get(j);
    if(ai.intersects(aj)) {
    double aidx = ai.getdx();
    double aidy = ai.getdy();
    double ajdx = aj.getdx();
    double ajdy = aj.getdy();
    double dx = aj.getx() - ai.getx();
    double dy = aj.gety() - ai.gety();
    double nn = dx * dx + dy * dy;
    if(nn == 0) continue;
    aidx -= (2.0 * (ai.getImpulse() / nn)) * dx;
    aidy -= (2.0 * (ai.getImpulse() / nn)) * dy;
    double dist = Math.sqrt(aidx * aidx + aidy * aidy);
    aidx /= dist;
    aidy /= dist;
    aidx *= ai.getSpeed();
    aidy *= ai.getSpeed();
    ai.setVector(aidx, aidy);
    dx = -dx;
    dy = -dy;
    ajdx -= (2.0 * (128 / nn)) * dx;
    ajdy -= (2.0 * (128 / nn)) * dy;
    ajdx /= dist;
    ajdy /= dist;
    ajdx *= aj.getSpeed();
    ajdy *= aj.getSpeed();
    aj.setVector(ajdx, ajdy);
    }
    }
    }"


    im almost to make it work but cant see the method ai.getImpuse() on asteroid class. which is the difference between ai.getSpeed() and ai.getImpulse()? thanks!

    ReplyDelete
    Replies
    1. Sorry I didn't use any impulse for the asteroids anymore. Use 128 for the impulse.

      Same as ajdx, and ajdy.

      Delete
  5. Any update on this? Would love to play it and see what you did with the code

    ReplyDelete
    Replies
    1. Unfortunately, not enough inspiration. I haven't worked on it in a while.

      Delete