13
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

StateMachine 状態遷移マシンを作ってみたw

Posted at

状態遷移マシンとは?

  • 簡単に言うと・・・
    • 状態を管理して、状態同士の遷移を行うことです。
  • 各遷移する状態を表す状態は以下の要件があれば十分です
    • 各状態は、特定のタイミングで指定した関数を実行する
  • 状態遷移マシンでは、マシンがすべての状態を登録、抹消、切り替えだけをする。
  • ゲームに使える状態遷移マシンは、そこまで複雑なのは必要ではありません。

コード

StateMachine

//-------------------------------------------------
// 状態管理クラス
// 
// StateMachine : 状態の管理部分
// StateBase : 状態の基底クラス
// StateSwitch : 状態を切り替えるクラス
//--------------------------------------------------


#include <iostream>
#include <unordered_map>
#include <string>
#include <memory>


class StateBase
{
public:
	StateBase() :
		isNext(false)
	{

	};

	// アップデートの前に呼ばれる
	virtual void Start() = 0;

	// アップデート
	virtual void Update() = 0;

	// 状態が変わるときの処理
	virtual void OnChangeEvent() = 0;

	// 切り替える条件を取得
	bool IsNext()const{ return isNext; }

protected:
	// 次に行くための条件
	bool isNext;

};


// 状態の切り替えクラス
class StateSwitch
{
public:
	StateSwitch() = default;
	StateSwitch(const std::shared_ptr<StateBase> state, const std::string nextRegisterName) :
		state(state), nextRegisterName(nextRegisterName)
	{
	}

	// アップデートの前に呼ばれる
	void Start()
	{
		state->Start();
	}

	// アップデート
	void Update()
	{
		state->Update();
	}

	// 次の状態にいけるかどうか
	bool CanNextState()
	{
		if (state->IsNext())
		{
			state->OnChangeEvent();
			return true;
		}
		return false;
	}

	// 次に行く登録した名前
	const std::string nextRegisterName;

private:
	// 親クラス
	std::shared_ptr<StateBase> state;
};

// 状態管理
class StateMachine
{
public:
	StateMachine():
		nowState()
	{

	}

	void Update()
	{
		nowState->Update();
		if (nowState->CanNextState())
		{
			auto it = stateMap.find(nowState->nextRegisterName);
			if (it == stateMap.end()) return;

			nowState = it->second;
			nowState->Start();
		}
	}

	// 登録する
	void Register(const std::string& name, const std::shared_ptr<StateSwitch> state)
	{
		stateMap.insert(std::make_pair(name, state));
		std::cout << "StateMachine : " << name << "を登録" << std::endl;
	}

	// 最初から始める状態を設定
	void SetStartState(const std::string& registerName)
	{
		auto it = stateMap.find(registerName);
		if (it == stateMap.end()) return;

		std::cout << "StateMachine : " << it->first << "をスタート状態に設定" << std::endl;

		nowState = it->second;
		nowState->Start();

	}

	// 登録したものを削除する
	void Deregistration(const std::string& registerName)
	{
		auto it = stateMap.find(registerName);
		if (it == stateMap.end()) return;
		
		std::cout << "StateMachine : " << it->first << "を登録を削除する" << std::endl;

		stateMap.erase(it);
	}

	// すべての登録を削除する
	void AllDeregistration()
	{
		std::cout << "StateMachine : " << "すべての登録を削除する" << std::endl;

		stateMap.clear();
	}

private:
	std::unordered_map<std::string, std::shared_ptr<StateSwitch>> stateMap;
	std::shared_ptr<StateSwitch> nowState;
};

// 移動状態クラス
class PlayerMover:public StateBase
{
public:
	PlayerMover()
	{

	}

	void Start()
	{
		std::cout << "PlayerMover : " << "スタート" << std::endl;
	}

	void Update()
	{
		std::cout << "PlayerMover : " << "アップデート" << std::endl;
		isNext = true;
	}

	// 状態が変わるときの処理
	void OnChangeEvent()
	{
		isNext = false;
		std::cout << "PlayerMover : "<< "切り替わるイベントに入った" << std::endl;
	}

private:

};

// 攻撃状態クラス
class PlayerAttacker :public StateBase
{
public:
	PlayerAttacker()
	{

	}

	void Start()
	{
		std::cout << "PlayerAttacker : " << "スタート" << std::endl;
	}

	void Update()
	{
		std::cout << "PlayerAttacker : " << "アップデート" << std::endl;
		isNext = true;
	}

	void OnChangeEvent()
	{
		isNext = false;
		std::cout << "PlayerAttacker : " << "切り替わるイベントに入った" << std::endl;
	}

private:

};

// 止まる状態クラス
class PlayerStoper :public StateBase
{
public:
	PlayerStoper()
	{

	}


	void Start()
	{
		std::cout << "PlayerStoper : " << "スタート" << std::endl;
	}

	void Update()
	{
		std::cout << "PlayerStoper : " << "アップデート" << std::endl;
		//isNext = true;
	}

	void OnChangeEvent()
	{
		isNext = false;
		std::cout << "PlayerStoper : " << "切り替わるイベントに入った" << std::endl;
	}

private:

};

class Player
{
public:
	Player():
		stateMachine(std::make_unique<StateMachine>())
	{
		const std::string moveName = "PlayerMove";
		const std::string attackName = "PlayerAttack";
		const std::string stopName = "PlayerStop";

		auto moveState = std::make_shared<StateSwitch>(std::make_shared<PlayerMover>(), attackName);
		auto attackState = std::make_shared<StateSwitch>(std::make_shared<PlayerAttacker>(), stopName);
		auto stopState = std::make_shared<StateSwitch>(std::make_shared<PlayerStoper>(), moveName);

		stateMachine->Register(moveName, moveState);
		stateMachine->Register(attackName, attackState);
		stateMachine->Register(stopName, stopState);

		stateMachine->SetStartState(moveName);

	}

	void Update()
	{
		stateMachine->Update();
	}

private:
	std::unique_ptr<StateMachine> stateMachine;
};



int main()
{
	std::unique_ptr<Player> player;
	player = std::make_unique<Player>();

	player->Update();
	player->Update();
	player->Update();
	player->Update();

	return 0;
}



状態遷移マシンがあるといいこと

  • 細かく状態が分けることが可能です。
  • 他人とのコーディングがしやすくなると思います。
  • オブジェクトに依存しなくてよくなる
    • 例) プレイヤーに状態の処理を書かないようになる
13
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?