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.
Looks cool, hopefully be a demo on youtube soon ?
ReplyDeleteI'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.
ReplyDeleteHave you got any android / iOS game or are u thinking about launching one?? it would be great.
ReplyDeleteNope. Never tried either Android or iOS SDK.
DeleteYou should. Your games are great. I am sure it would be a huge sucess!
DeleteHello mike im working with this code:
ReplyDelete"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!
Sorry I didn't use any impulse for the asteroids anymore. Use 128 for the impulse.
DeleteSame as ajdx, and ajdy.
Any update on this? Would love to play it and see what you did with the code
ReplyDeleteUnfortunately, not enough inspiration. I haven't worked on it in a while.
Delete