GdxAI是libgdx的一個AI庫,裏面有各種AI系統和算法,包括有限狀態機,狀態樹,行爲算法(追逐,躲避...羣聚),路徑算法。基於Message和Scheduler兩種事件驅動機制。這絕對是個好東西,但要講這些知識的話,就不是侷限於libgdx這個平臺了,AI算法是遊戲開發通用的,所以以後隨着我的學習可能會有這個專題。這裏就先貼上狀態機的demo。
狀態機邏輯:
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.ai.fsm.State;
import com.badlogic.gdx.ai.msg.MessageManager;
import com.badlogic.gdx.ai.msg.Telegram;
/** @author davebaol */
public enum BobState implements State<Bob> {
ENTER_MINE_AND_DIG_FOR_NUGGET() {
@Override
public void enter (Bob bob) {
// if the miner is not already located at the goldmine, he must
// change location to the gold mine
if (bob.getLocation() != Location.GOLD_MINE) {
talk(bob, "Walkin' to the goldmine");
bob.setLocation(Location.GOLD_MINE);
}
}
@Override
public void update (Bob bob) {
// Now bob is at the goldmine he digs for gold until he
// is carrying in excess of MAX_NUGGETS. If he gets thirsty during
// his digging he packs up work for a while and changes state to
// go to the saloon for a whiskey.
bob.addToGoldCarried(1);
bob.increaseFatigue();
talk(bob, "Pickin' up a nugget");
// if enough gold mined, go and put it in the bank
if (bob.isPocketsFull()) {
bob.getStateMachine().changeState(VISIT_BANK_AND_DEPOSIT_GOLD);
}
if (bob.isThirsty()) {
bob.getStateMachine().changeState(QUENCH_THIRST);
}
}
@Override
public void exit (Bob bob) {
talk(bob, "Ah'm leavin' the goldmine with mah pockets full o' sweet gold");
}
},
GO_HOME_AND_SLEEP_TILL_RESTED() {
@Override
public void enter (Bob bob) {
if (bob.getLocation() != Location.SHACK) {
talk(bob, "Walkin' home");
bob.setLocation(Location.SHACK);
// Let Elsa know I'm home
MessageManager.getInstance().dispatchMessage( //
0.0f, // time delay
bob, // ID of sender
bob.elsa, // ID of recipient
MessageType.HI_HONEY_I_M_HOME, // the message
null);
}
}
@Override
public void update (Bob bob) {
// if miner is not fatigued start to dig for nuggets again.
if (!bob.isFatigued()) {
talk(bob, "All mah fatigue has drained away. Time to find more gold!");
bob.getStateMachine().changeState(ENTER_MINE_AND_DIG_FOR_NUGGET);
} else {
// sleep
bob.decreaseFatigue();
talk(bob, "ZZZZ... ");
}
}
@Override
public void exit (Bob bob) {
}
@Override
public boolean onMessage (Bob bob, Telegram telegram) {
if (telegram.message == MessageType.STEW_READY) {
System.out.println("Message handled by " + bob.getClass().getSimpleName() + " at time: "
+ MessageManager.getInstance().getCurrentTime());
talk(bob, "Okay Hun, ahm a comin'!");
bob.getStateMachine().changeState(EAT_STEW);
return true;
}
return false; // send message to global message handler
}
},
QUENCH_THIRST() {
@Override
public void enter (Bob bob) {
if (bob.getLocation() != Location.SALOON) {
bob.setLocation(Location.SALOON);
talk(bob, "Boy, ah sure is thusty! Walking to the saloon");
}
}
@Override
public void update (Bob bob) {
bob.buyAndDrinkAWhiskey();
talk(bob, "That's mighty fine sippin liquer");
bob.getStateMachine().changeState(ENTER_MINE_AND_DIG_FOR_NUGGET);
}
@Override
public void exit (Bob bob) {
talk(bob, "Leaving the saloon, feelin' good");
}
},
VISIT_BANK_AND_DEPOSIT_GOLD() {
@Override
public void enter (Bob bob) {
// On entry bob makes sure he is located at the bank
if (bob.getLocation() != Location.BANK) {
talk(bob, "Goin' to the bank. Yes siree");
bob.setLocation(Location.BANK);
}
}
@Override
public void update (Bob bob) {
// Deposit the gold
bob.addToWealth(bob.getGoldCarried());
bob.setGoldCarried(0);
talk(bob, "Depositing gold. Total savings now: " + bob.getWealth());
// Wealthy enough to have a well earned rest?
if (bob.getWealth() >= Bob.COMFORT_LEVEL) {
talk(bob, "WooHoo! Rich enough for now. Back home to mah li'lle lady");
bob.getStateMachine().changeState(GO_HOME_AND_SLEEP_TILL_RESTED);
} else { // otherwise get more gold
bob.getStateMachine().changeState(ENTER_MINE_AND_DIG_FOR_NUGGET);
}
}
@Override
public void exit (Bob bob) {
talk(bob, "Leavin' the bank");
}
},
EAT_STEW() {
@Override
public void enter (Bob bob) {
talk(bob, "Smells Reaaal goood Elsa!");
}
@Override
public void update (Bob bob) {
talk(bob, "Tastes real good too!");
bob.getStateMachine().revertToPreviousState();
}
@Override
public void exit (Bob bob) {
talk(bob, "Thankya li'lle lady. Ah better get back to whatever ah wuz doin'");
}
};
@Override
public boolean onMessage (Bob bob, Telegram telegram) {
return false;
}
protected void talk (Bob bob, String msg) {
Gdx.app.log(bob.getClass().getSimpleName(), msg);
}
}
模型:
import com.badlogic.gdx.ai.fsm.DefaultStateMachine;
import com.badlogic.gdx.ai.fsm.StateMachine;
import com.badlogic.gdx.ai.msg.Telegraph;
import com.badlogic.gdx.ai.msg.Telegram;
/** @author davebaol */
public class Bob implements Telegraph {
// the amount of gold a miner must have before he feels comfortable
final public static int COMFORT_LEVEL = 5;
// the amount of nuggets a miner can carry
final public static int MAX_NUGGETS = 3;
// above this value a miner is thirsty
final public static int THIRST_LEVEL = 5;
// above this value a miner is sleepy
final public static int TIREDNESS_THRESHOLD = 5;
private StateMachine<Bob> stateMachine;
private Location location;
// how many nuggets the miner has in his pockets
private int goldCarried;
private int moneyInBank;
// the higher the value, the thirstier the miner
private int thirst;
// the higher the value, the more tired the miner
private int fatigue;
Elsa elsa;
public Bob () {
this(null);
}
public Bob (Elsa elsa) {
this.elsa = elsa;
location = Location.SHACK;
goldCarried = 0;
moneyInBank = 0;
thirst = 0;
fatigue = 0;
stateMachine = new DefaultStateMachine<Bob>(this, BobState.GO_HOME_AND_SLEEP_TILL_RESTED);
}
@Override
public boolean handleMessage (Telegram msg) {
return stateMachine.handleMessage(msg);
}
public void update (float delta) {
thirst += 1;
stateMachine.update();
}
public StateMachine<Bob> getStateMachine () {
return stateMachine;
}
public Elsa getElsa () {
return elsa;
}
public void setElsa (Elsa elsa) {
this.elsa = elsa;
}
public void addToGoldCarried (int val) {
goldCarried += val;
if (goldCarried < 0) {
goldCarried = 0;
}
}
public void addToWealth (int val) {
moneyInBank += val;
if (moneyInBank < 0) {
moneyInBank = 0;
}
}
public boolean isThirsty () {
return thirst >= THIRST_LEVEL;
}
public boolean isFatigued () {
return fatigue > TIREDNESS_THRESHOLD;
}
public Location getLocation () {
return location;
}
public void setLocation (Location location) {
this.location = location;
}
public int getGoldCarried () {
return goldCarried;
}
public void setGoldCarried (int val) {
goldCarried = val;
}
public boolean isPocketsFull () {
return goldCarried >= MAX_NUGGETS;
}
public void decreaseFatigue () {
fatigue--;
}
public void increaseFatigue () {
fatigue++;
}
public int getWealth () {
return moneyInBank;
}
public void setWealth (int val) {
moneyInBank = val;
}
public void buyAndDrinkAWhiskey () {
thirst = 0;
moneyInBank -= 2;
}
}
使用:
public class StateMachineTest extends GdxAiTest {
public static void main (String[] argv) {
launch(new StateMachineTest());
}
Bob bob;
Elsa elsa;
float elapsedTime;
@Override
public void create () {
elapsedTime = 0;
// Create Bob and his wife
bob = new Bob();
elsa = new Elsa(bob);
bob.setElsa(elsa);
}
@Override
public void render () {
elapsedTime += Gdx.graphics.getRawDeltaTime();
if (elapsedTime > 0.8f) {
// Update Bob and his wife
bob.update(elapsedTime);
elsa.update(elapsedTime);
// Dispatch any delayed messages
MessageManager.getInstance().update(elapsedTime);
elapsedTime = 0;
}
}
}