概要
状態
ゲーム世界には色々な人物が存在しますが、今回はプログラムを単純化するために主人公(プレイヤー)がただひとりだけ世界に存在すると考えます。
その主人公は、
の地点に移動することが出来ます。
これらの地点を"状態"、別の地点に移動することを"状態の遷移"と捉えて、Stateパターンを記述してみましょう。
遷移条件
| 現在の状態(場所) |
遷移する条件(移動するための条件) |
遷移先(移動先) |
| ダンジョン |
敵と戦う体力が無い(HPが0になった) |
宿屋 |
| 宿屋 |
体力が全快した |
ダンジョン |
プログラム
package
{
public interface State
{
function enter(player:Player):void;
function update(player:Player):void;
function exit(player:Player):void;
}
}
状態を表すクラスは全てこのStateインタフェースを実装させます。
| メソッド名 |
状態としての意味 |
状態の役割としての意味 |
| enter |
状態開始時の処理 |
地点に到着した時の行動 |
| update |
この状態のときに行動する処理 |
地点に居る時の行動 |
| exit |
状態終了時(他の状態に移る直前)の処理 |
別地点に移動する直前の行動 |
状態を持つ対象のクラスがPlayerで、各メソッドに必ずPlayerオブジェクトを渡すようにします。
Player.as
|
+ | | ... |
package
{
public class Player
{
private var _state:State;
private var _hp:int;
private var _maxHP:int;
private var _name:String;
public function Player()
{
_hp = 30;
_maxHP = 30;
_name = "主人公";
_state = StateINN.instance;
}
public function getHPText():String
{
return "HP" + hp + "/" + maxHP;
}
public function get state():State
{
return _state;
}
public function set state(value:State):void
{
_state.exit(this);
_state = value;
_state.enter(this);
}
public function update():void
{
_state.update(this);
}
public function get hp():int
{
return _hp;
}
public function set hp(value:int):void
{
_hp = value;
if (maxHP < _hp) _hp = maxHP;
if (_hp < 0) _hp = 0;
}
public function get maxHP():int
{
return _maxHP;
}
public function set maxHP(value:int):void
{
_maxHP = value;
}
public function get name():String
{
return _name;
}
public function set name(value:String):void
{
_name = value;
}
}
}
|
主人公を表すPlayerクラスには、
- 名前を表すname
- 体力を表すHP/最大HP
- 主人公が今どこに居るかを表すstate
StateDungeon.as
|
+ | | ... |
package
{
public class StateDungeon implements State
{
private static var _instance:StateDungeon;
public function StateDungeon(lock:SingletonLock) {}
public static function get instance():StateDungeon
{
if (_instance == null) _instance = new StateDungeon(new SingletonLock());
return _instance;
}
public function enter(player:Player):void
{
trace(player.name + "はダンジョンに進入した");
}
public function update(player:Player):void
{
player.hp -= Math.random() * 5 + 1;
trace(player.name + "はモンスターを倒した!! " + player.getHPText());
if (player.hp == 0)
{
player.state = StateINN.instance;
}
}
public function exit(player:Player):void
{
trace(player.name + "はダンジョンから脱出した");
}
}
}
class SingletonLock {}
|
StateINN.as
|
+ | | ... |
package
{
public class StateINN implements State
{
private static var _instance:StateINN;
public function StateINN(lock:SingletonLock) {}
public static function get instance():StateINN
{
if (_instance == null) _instance = new StateINN(new SingletonLock());
return _instance;
}
public function enter(player:Player):void
{
trace(player.name + "は宿屋に入った");
}
public function update(player:Player):void
{
player.hp += 5;
trace("ZZZ... : " + player.getHPText());
if (player.hp == player.maxHP)
{
player.state = StateDungeon.instance;
}
}
public function exit(player:Player):void
{
trace(player.name + "は宿屋から出た");
}
}
}
class SingletonLock {}
|
Stateを継承した各状態を表すクラスです。
インスタンスは複数作る必要が無いので、
シングルトンにしておきます。
Main.as
package
{
import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.utils.Timer;
public class Main extends Sprite
{
private var player:Player;
public function Main()
{
player = new Player();
var timer:Timer = new Timer(1500, 1000);
timer.addEventListener(TimerEvent.TIMER, onTimer);
timer.start();
}
private function onTimer(event:TimerEvent):void
{
player.update();
}
}
}
Player#update()を呼ぶごとに時間が経過します。
実行結果
ZZZ... : HP30/30
主人公は宿屋から出た
主人公はダンジョンに進入した
主人公はモンスターを倒した!! HP27/30
主人公はモンスターを倒した!! HP25/30
主人公はモンスターを倒した!! HP21/30
主人公はモンスターを倒した!! HP17/30
主人公はモンスターを倒した!! HP12/30
主人公はモンスターを倒した!! HP10/30
主人公はモンスターを倒した!! HP7/30
主人公はモンスターを倒した!! HP3/30
主人公はモンスターを倒した!! HP0/30
主人公はダンジョンから脱出した
主人公は宿屋に入った
ZZZ... : HP5/30
ZZZ... : HP10/30
ZZZ... : HP15/30
ZZZ... : HP20/30
ZZZ... : HP25/30
ZZZ... : HP30/30
主人公は宿屋から出た
検証用コード