状态模式

当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。状态模式主要解决的是控制一个对象状态转换条件表达式过于复杂时的情况。 把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化

UML类图


Alt text

应用及代码示例


传统MMO游戏中基于状态机实现的AI系统大部分使用的是状态模式。

状态模式的优点:

  1. 消除庞大的条件分支语句。庞大的逻辑分支,复杂的逻辑判断很多时候是导致BUG的原因,同时对于查找原因也很难
  2. 状态模式通过把各种状态转移逻辑分布到State的子类之间,减少了相互间的依赖,应用了职责单一原则

状态模式的缺点:

  1. 使用状态模式会增加系统类和对象的个数
  2. 一个对象的状态是在不同状态间转换。在实现前最好画一个状态图,这样逻辑清晰,不然,随着业务需求的增加导致状态难于追踪,很容易状态死循环

下面是简单的MMO游戏中基于状态机实现的AI系统:

enum class EState
{
	E_Idle = 0,
	E_Patrol,
	E_Follow,
	E_Attack,
	E_Go_Home,
};

class StateABC
{
public:
	virtual void run(Monster* pMonster) = 0;
};

class StateIdle : public StateABC
{
public:
	void run(Monster* pMonster) override
	{
		if (nullptr == pMonster)
		{
			return;
		}

		cout << "my state:" << static_cast<int>(EState::E_Idle) << endl;

		/*if (发现敌人)
		{
			if (在攻击范围)
			{
				pMonster->SetState(std::make_shared<StateAttack>());
			}
			else
			{
				pMonster->SetState(std::make_shared<StateFollow>());
			}
			return;
		}*/
		

		// 持续一段时间开始巡逻
		pMonster->SetState(std::make_shared<StatePatrol>());
		
	}
};

class StatePatrol : public StateABC
{
public:
	void run(Monster* pMonster) override
	{
		if (nullptr == pMonster)
		{
			return;
		}

		cout << "my state:" << static_cast<int>(EState::E_Patrol) << endl;

		/*if (发现敌人)
		{
			if (在攻击范围)
			{
				pMonster->SetState(std::make_shared<StateAttack>());
			}
			else
			{
				pMonster->SetState(std::make_shared<StateFollow>());
			}
			return;
		}*/

		/*if (在目标点)
		{
			pMonster->SetState(std::make_shared<StateGoHome>());
			return;
		}*/
	}
};

class StateFollow : public StateABC
{
public:
	void run(Monster* pMonster) override
	{
		if (nullptr == pMonster)
		{
			return;
		}

		cout << "my state:" << static_cast<int>(EState::E_Follow) << endl;

		/*if (目标敌人存在)
		{
			if (在攻击范围)
			{
				pMonster->SetState(std::make_shared<StateAttack>());
				return;
			}
		}
		else
		{
			pMonster->SetState(std::make_shared<StateGoHome>());
		}
		*/
	}
};
class StateAttack : public StateABC
{
public:
	void run(Monster* pMonster) override
	{
		if (nullptr == pMonster)
		{
			return;
		}

		cout << "my state:" << static_cast<int>(EState::E_Attack) << endl;
		/*if (目标敌人存在)
		{
			if (不在攻击范围)
			{
				pMonster->SetState(std::make_shared<StateFollow>());
				return;
			}
		}
		else
		{
			pMonster->SetState(std::make_shared<StateGoHome>());
		}
		*/
	}
};
class StateGoHome : public StateABC
{
public:
	void run(Monster* pMonster) override
	{
		if (nullptr == pMonster)
		{
			return;
		}

		cout << "my state:" << static_cast<int>(EState::E_Go_Home) << endl;

		/*if (发现敌人)
		{
			if (在攻击范围)
			{
				pMonster->SetState(std::make_shared<StateAttack>());
			}
			else
			{
				pMonster->SetState(std::make_shared<StateFollow>());
			}
			return;
		}*/

		/*if (在home点)
		{
			pMonster->SetState(std::make_shared<StateIdle>());
			return;
		}*/
	}
};

class Monster
{
public:
	Monster()
	{
		m_ptrState = std::make_shared<StateIdle>();
	}
	void SetState(std::shared_ptr<StateABC> ptrState)
	{
		m_ptrState = ptrState;
	}
	void run()
	{
		m_ptrState->run(this);
	}
private:
	std::shared_ptr<StateABC> m_ptrState;
};
int main()
{
	{
		Monster objMonster;
		//每个tick都要run
		objMonster.run();
	}

	system("pause");
	return 0;
}

对于简单的逻辑分支判断没必要使用状态模式,否则状态模式很自然的显现出来上面所提到的缺点,代码有种杀鸡用牛刀的味道