libgdx API之AI:AI讓遊戲對象思考

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;
		}
	}
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章