参考
什么是状态机?
设计模式-状态机模式
什么是状态机(有限状态自动机)
可以把状态机比作方程式, 你输入当前信息, 就能得到下一个信息
举个例子, 按钮门有两个状态, 关闭状态和打开状态, 如果按下开门按钮, 门的状态就从关闭到打开
状态机就是接受门当前状态和发生的事件, 输出门的下一个状态
状态机四大概念
1. 状态(State):
一个状态机至少包含两个状态, 门, 就有两个状态, 一个打开状态, 一个关闭状态
2. 事件(Event):
一个行为, 一个操作的触发条件, 门, 按下开门按钮, 这是一个事件, 一个事件对应一个动作
3. 动作(Action):
事件发生后要执行的动作, 门被按下开门按钮, 这个事件发生后, 门会执行动作, 门打开了
4. 变换(Transition):
表示一个状态变成另一个状态的过程, 在执行完action后, 门的状态也就从关闭状态到打开
类图
一个事件绑定一个动作
一个状态绑定多个事件
Machine 需要初始化添加所有状态, 事件所对应的动作, 状态所对应事件
Machine优化
实战
模拟玩家进入大厅, 选择房间进行游戏, 再退出房间, 退出游戏的操作
看玩家空闲状态->在游戏大厅那条连线, 初始状态是 空闲, 事件是 按下进入游戏按钮, 行动是 玩家进入游戏, 变换是 状态从空闲变为在游戏大厅
玩家状态分析
玩家状态: 空闲, 在游戏大厅, 在房间201, 在房间202
玩家事件分析
玩家Action
一个事件绑定一个Action
UserMichine中事件绑定的Action
事件 | 动作 |
---|---|
PressEnterGameBtn | EnterGameAction |
PressLeaveGameBtn | LeaveGameAction |
PressEnterGameRoom201Btn | EnterGameRoom201Action |
PressEnterGameRoom202Btn | EnterGameRoom202Action |
PressLeaveGameRoom201Btn | LeaveGameRoom201Action |
PressLeaveGameRoom202Btn | LeaveGameRoom202Action |
代码
Action.h
#ifndef _ACTION_H_
#define _ACTION_H_
#include <memory>
#include <iostream>
#include "Event.h"
#include "State.h"
class Action{
public:
Action(int curState, int nextState): m_curState(curState), m_nextState(nextState)
{
}
virtual std::shared_ptr<State> Execute(std::shared_ptr<State> state, std::shared_ptr<Event> event)
{
std::shared_ptr<State> nextState = std::make_shared<UserState>(m_nextState);
std::cout << event->GetEventActionName() << ", 玩家状态从 " << state->GetString() << " 到 " << nextState->GetString() << std::endl;
return nextState;
}
public:
int m_curState;
int m_nextState;
};
// 对应 PressEnterGameBtn事件
class EnterGameAction: public Action{
public:
EnterGameAction(int curState, int nextState): Action(curState, nextState){}
virtual std::shared_ptr<State> Execute(std::shared_ptr<State> state, std::shared_ptr<Event> event)
{
// EnterGameAction 特别操作 TODO
std::shared_ptr<State> nextState = std::make_shared<UserState>(m_nextState);
auto userEvent = std::static_pointer_cast<UserEvent>(event);
std::cout << userEvent->m_userName << " " << event->GetEventActionName() << ", 玩家状态从 " << state->GetString() << " 到 " << nextState->GetString() << std::endl;
return nextState;
}
};
// 对应 PressLeaveGameBtn事件
class LeaveGameAction: public Action{
public:
LeaveGameAction(int curState, int nextState): Action(curState, nextState){}
virtual std::shared_ptr<State> Execute(std::shared_ptr<State> state, std::shared_ptr<Event> event)
{
// LeaveGameAction 特别操作 TODO
std::shared_ptr<State> nextState = std::make_shared<UserState>(m_nextState);
auto userEvent = std::static_pointer_cast<UserEvent>(event);
std::cout << userEvent->m_userName << " " << event->GetEventActionName() << ", 玩家状态从 " << state->GetString() << " 到 " << nextState->GetString() << std::endl;
return nextState;
}
};
// 对应 PressEnterGameRoom201Btn事件
class EnterGameRoom201Action: public Action{
public:
EnterGameRoom201Action(int curState, int nextState): Action(curState, nextState){}
virtual std::shared_ptr<State> Execute(std::shared_ptr<State> state, std::shared_ptr<Event> event)
{
// EnterGameRoom201Action 特别操作 TODO
std::shared_ptr<State> nextState = std::make_shared<UserState>(m_nextState);
auto userEvent = std::static_pointer_cast<UserEvent>(event);
std::cout << userEvent->m_userName << " " << event->GetEventActionName() << ", 玩家状态从 " << state->GetString() << " 到 " << nextState->GetString() << std::endl;
return nextState;
}
};
// 对应 PressEnterGameRoom202Btn事件
class EnterGameRoom202Action: public Action{ //
public:
EnterGameRoom202Action(int curState, int nextState): Action(curState, nextState){}
virtual std::shared_ptr<State> Execute(std::shared_ptr<State> state, std::shared_ptr<Event> event)
{
// EnterGameRoom202Action 特别操作 TODO
std::shared_ptr<State> nextState = std::make_shared<UserState>(m_nextState);
auto userEvent = std::static_pointer_cast<UserEvent>(event);
std::cout << userEvent->m_userName << " " << event->GetEventActionName() << ", 玩家状态从 " << state->GetString() << " 到 " << nextState->GetString() << std::endl;
return nextState;
}
};
// 对应 PressLeaveGameRoom201Btn事件
class LeaveGameRoom201Action: public Action{
public:
LeaveGameRoom201Action(int curState, int nextState): Action(curState, nextState){}
virtual std::shared_ptr<State> Execute(std::shared_ptr<State> state, std::shared_ptr<Event> event)
{
// LeaveGameRoom201Action 特别操作 TODO
std::shared_ptr<State> nextState = std::make_shared<UserState>(m_nextState);
auto userEvent = std::static_pointer_cast<UserEvent>(event);
std::cout << userEvent->m_userName << " " << event->GetEventActionName() << ", 玩家状态从 " << state->GetString() << " 到 " << nextState->GetString() << std::endl;
return nextState;
}
};
// 对应 PressLeaveGameRoom202Btn事件
class LeaveGameRoom202Action: public Action{
public:
LeaveGameRoom202Action(int curState, int nextState): Action(curState, nextState){}
virtual std::shared_ptr<State> Execute(std::shared_ptr<State> state, std::shared_ptr<Event> event)
{
// LeaveGameRoom202Action 特别操作 TODO
std::shared_ptr<State> nextState = std::make_shared<UserState>(m_nextState);
auto userEvent = std::static_pointer_cast<UserEvent>(event);
std::cout << userEvent->m_userName << " " << event->GetEventActionName() << ", 玩家状态从 " << state->GetString() << " 到 " << nextState->GetString() << std::endl;
return nextState;
}
};
#endif
Event.h
#ifndef _EVENT_H_
#define _EVENT_H_
#include <string>
#include "SharedDef.h"
class Event{
public:
Event(int eventId):m_eventId(eventId){}
virtual std::string GetEventActionName(){
return "";
}
public:
int m_eventId;
};
class UserEvent: public Event{
public:
UserEvent(int eventId, std::string userName="玩家A"):Event(eventId),m_userName(userName){}
virtual std::string GetEventActionName(){
switch(m_eventId)
{
case PressEnterGameBtn:
return "进入游戏";
case PressLeaveGameBtn:
return "离开游戏";
case PressEnterGameRoom201Btn:
return "进入201房间";
case PressEnterGameRoom202Btn:
return "进入202房间";
case PressLeaveGameRoom201Btn:
return "退出201房间";
case PressLeaveGameRoom202Btn:
return "退出202房间";
}
}
public:
std::string m_userName;
};
#endif
SharedDef.h
#ifndef _SHARED_DEF_H
#define _SHARED_DEF_H
enum UserStateEnum {
Idle, // 空闲
InGameHall, // 在游戏大厅
InRoom201, // 在房间201
InRoom202 // 在房间202
};
enum EventEnum {
PressEnterGameBtn, // 按下进入游戏按钮事件
PressLeaveGameBtn, // 按下离开游戏按钮事件
PressEnterGameRoom201Btn, // 按下进入201房间按钮事件
PressEnterGameRoom202Btn, // 按下进入202房间按钮事件
PressLeaveGameRoom201Btn, // 按下退出201房间按钮事件
PressLeaveGameRoom202Btn // 按下退出202房间按钮事件
};
#endif
State.h
#ifndef _STATE_H_
#define _STATE_H_
#include <string>
#include "SharedDef.h"
class State{
public:
State(int stateId):m_stateId(stateId){}
virtual std::string GetString(){
return "";
}
public:
int m_stateId;
};
class UserState:public State{
public:
UserState(int stateId):State(stateId){}
virtual std::string GetString()
{
switch(m_stateId)
{
case Idle:
return "空闲";
case InGameHall:
return "在游戏大厅";
case InRoom201:
return "在房间201";
case InRoom202:
return "在房间202";
}
return "无状态";
}
};
#endif
UserMachine.h
#ifndef _USER_MACHINE_H_
#define _USER_MACHINE_H_
#include <unordered_map>
#include <memory>
#include "Event.h"
#include "State.h"
#include "Action.h"
typedef std::unordered_map<int, std::shared_ptr<Action>> EventAction;
class UserMachine
{
public:
void Init();
std::shared_ptr<State> Execute(int state, std::shared_ptr<Event> event);
std::shared_ptr<State> Execute(std::shared_ptr<State> state, std::shared_ptr<Event> event);
public:
std::unordered_map<int, std::unordered_map<int, std::shared_ptr<Action>>> m_stateEventsMap; // <状态id,<事件id, Action>>
};
#endif
UserMachine.cpp
#include <vector>
#include "UserMachine.h"
// 初始化, 事件绑定动作, 状态保定事件
void UserMachine::Init()
{
m_stateEventsMap.clear();
std::vector<std::shared_ptr<State>> states; states.clear();
std::shared_ptr<State> idleState = std::make_shared<UserState>(Idle);
std::shared_ptr<State> inGameHallState = std::make_shared<UserState>(InGameHall);
std::shared_ptr<State> inRoom201State = std::make_shared<UserState>(InRoom201);
std::shared_ptr<State> inRoom202State = std::make_shared<UserState>(InRoom202);
states.push_back(idleState);
states.push_back(inGameHallState);
states.push_back(inRoom201State);
states.push_back(inRoom202State);
for(int i = 0; i < states.size(); i++)
{
EventAction eventActionMap; eventActionMap.clear();
m_stateEventsMap.insert(std::make_pair(states[i]->m_stateId, eventActionMap));
}
// 空闲 -----按下进入游戏按钮-------> 游戏大厅
std::shared_ptr<Action> idle_InGameHall_Action = std::make_shared<EnterGameAction>(Idle, InGameHall);
m_stateEventsMap[idleState->m_stateId].insert(std::make_pair(PressEnterGameBtn, idle_InGameHall_Action));
// 游戏大厅 -----按下退出游戏按钮-------> 休闲
std::shared_ptr<Action> inGamehall_Idle_Action = std::make_shared<LeaveGameAction>(InGameHall, Idle);
m_stateEventsMap[inGameHallState->m_stateId].insert(std::make_pair(PressLeaveGameBtn, inGamehall_Idle_Action));
// 游戏大厅 -----按下进入201游戏按钮-------> 在房间201
std::shared_ptr<Action> inGamehall_InRoom201_Action = std::make_shared<EnterGameRoom201Action>(InGameHall, InRoom201);
m_stateEventsMap[inGameHallState->m_stateId].insert(std::make_pair(PressEnterGameRoom201Btn, inGamehall_InRoom201_Action));
// 在房间201 -----按下退出201游戏按钮-------> 游戏大厅
std::shared_ptr<Action> inRoom201_InGamehall_Action = std::make_shared<LeaveGameRoom201Action>(InRoom201, InGameHall);
m_stateEventsMap[inRoom201State->m_stateId].insert(std::make_pair(PressLeaveGameRoom201Btn, inRoom201_InGamehall_Action));
// 游戏大厅 -----按下进入202游戏按钮-------> 在房间202
std::shared_ptr<Action> inGamehall_InRoom202_Action = std::make_shared<EnterGameRoom202Action>(InGameHall, InRoom202);
m_stateEventsMap[inGameHallState->m_stateId].insert(std::make_pair(PressEnterGameRoom202Btn, inGamehall_InRoom202_Action));
// 在房间202 -----按下退出202游戏按钮-------> 游戏大厅
std::shared_ptr<Action> inRoom202_InGamehall_Action = std::make_shared<LeaveGameRoom202Action>(InRoom202, InGameHall);
m_stateEventsMap[inRoom202State->m_stateId].insert(std::make_pair(PressLeaveGameRoom202Btn, inRoom202_InGamehall_Action));
}
std::shared_ptr<State> UserMachine::Execute(int state, std::shared_ptr<Event> event)
{
if(m_stateEventsMap.count(state) == 0)
{
std::cout << "状态未注册" << std::endl;
return NULL;
}
EventAction ea = m_stateEventsMap[state];
if(ea.count(event->m_eventId) == 0)
{
std::cout << "未注册事件" << std::endl;
return NULL;
}
std::shared_ptr<State> curState = std::make_shared<UserState>(state);
return ea[event->m_eventId]->Execute(curState, event);
}
std::shared_ptr<State> UserMachine::Execute(std::shared_ptr<State> state, std::shared_ptr<Event> event)
{
if(m_stateEventsMap.count(state->m_stateId) == 0)
{
std::cout << "状态未注册" << std::endl;
return NULL;
}
EventAction ea = m_stateEventsMap[state->m_stateId];
if(ea.count(event->m_eventId) == 0)
{
std::cout << "未注册事件" << std::endl;
return NULL;
}
return ea[event->m_eventId]->Execute(state, event);
}
CMakeList.txt
cmake_minimum_required(VERSION 3.15)
project(main)
include_directories(./)
aux_source_directory(./ main_source)
aux_source_directory(./States states_source)
add_executable(main ${main_source} ${states_source})
main.cpp
#include <iostream>
#include <memory>
#include <string>
#include "States/SharedDef.h"
#include "States/Event.h"
#include "States/UserMachine.h"
int main()
{
std::cout << "Hello World" << std::endl;
std::shared_ptr<UserMachine> userMachine = std::make_shared<UserMachine>();
userMachine->Init();
// 预想 玩家空闲 + 按下进入游戏按钮 -> 玩家在游戏大厅
auto nextState = userMachine->Execute(Idle, std::make_shared<UserEvent>(PressEnterGameBtn));
// 预想 玩家空闲 + 按下进入房间201按钮 -> 玩家在房间201
nextState = userMachine->Execute(nextState->m_stateId, std::make_shared<UserEvent>(PressEnterGameRoom201Btn));
// 预想 玩家在房间 + 按下退出游戏按钮 -> X
nextState = userMachine->Execute(nextState->m_stateId, std::make_shared<UserEvent>(PressLeaveGameBtn));
getchar();
return 0;
}
增加难度 添加房间状态
房间在有人的时候会打印有人, 没人的时候打印没人
要注意, 这里有两个房间, 也就是分别要保存这两个房间的状态, 但只需要一个Machine, Machine只是个数学公式, 两个房间的状态变化都是一样的
相比玩家的单纯的状态转移, 房间的状态转移增加了 转移条件 condistion
类图的修改
在之前的基础上把Transition从Action中提取出来
再增加一个Context类, 给转移条件传输必要的数据, 玩家人数是当前条件的必要项
房间状态分析
房间两种状态变化: 有人, 没人
注意不要和玩家状态的值重复了, 状态都是唯一的
房间事件分析
房间的人数变化, 状态变化, 跟玩家进出房间密切相关, 可以复用这些事件
房间Action
代码
抽象Machine
RoomMachine.h
重点: CheckUserCountM0 和 CheckUserCountL0 是Context自定义限制条件函数
#include <vector>
#include <functional>
#include "RoomMachine.h"
#include "../Context.h"
#include "../Action.h"
// 初始化, 事件绑定动作, 状态保定事件
void RoomMachine::Init()
{
m_stateEventsMap.clear();
std::vector<std::shared_ptr<State>> states; states.clear();
std::shared_ptr<State> emptyState = std::make_shared<UserState>(Empty);
std::shared_ptr<State> hasUserState = std::make_shared<UserState>(HasUser);
states.push_back(emptyState);
states.push_back(hasUserState);
for(int i = 0; i < states.size(); i++)
{
EventAction eventActionMap; eventActionMap.clear();
m_stateEventsMap.insert(std::make_pair(states[i]->m_stateId, eventActionMap));
}
std::function<bool(std::shared_ptr<Context>)> checkUserCountM0Func = std::bind(&RoomMachine::CheckUserCountM0, this, std::placeholders::_1);
std::function<bool(std::shared_ptr<Context>)> checkUserCountL0Func = std::bind(&RoomMachine::CheckUserCountL0, this, std::placeholders::_1);
std::shared_ptr<Action> hasUser_Empty_Action = std::make_shared<Room_LeaveGameRoomAction>(HasUser, Empty, checkUserCountL0Func);
std::shared_ptr<Action> empty_HasUser_Action = std::make_shared<Room_EnterGameRoomAction>(Empty, HasUser, checkUserCountM0Func);
// 没人状态 -----按下进入游戏房间按钮 + 判断人数 -------> 有人状态
m_stateEventsMap[emptyState->m_stateId].insert(std::make_pair(PressEnterGameRoom201Btn, empty_HasUser_Action));
m_stateEventsMap[emptyState->m_stateId].insert(std::make_pair(PressEnterGameRoom202Btn, empty_HasUser_Action));
// 没人状态 -----按下退出游戏房间按钮 + 判断人数 -------> 无人状态
m_stateEventsMap[emptyState->m_stateId].insert(std::make_pair(PressLeaveGameRoom201Btn, empty_HasUser_Action));
m_stateEventsMap[emptyState->m_stateId].insert(std::make_pair(PressLeaveGameRoom202Btn, empty_HasUser_Action));
// 有人状态 -----按下进入游戏房间按钮 + 判断人数 -------> 有人状态
m_stateEventsMap[hasUserState->m_stateId].insert(std::make_pair(PressEnterGameRoom201Btn, hasUser_Empty_Action));
m_stateEventsMap[hasUserState->m_stateId].insert(std::make_pair(PressEnterGameRoom202Btn, hasUser_Empty_Action));
// 有人状态 -----按下退出游戏房间按钮 + 判断人数 -------> 无人状态
m_stateEventsMap[hasUserState->m_stateId].insert(std::make_pair(PressLeaveGameRoom201Btn, hasUser_Empty_Action));
m_stateEventsMap[hasUserState->m_stateId].insert(std::make_pair(PressLeaveGameRoom202Btn, hasUser_Empty_Action));
}
bool RoomMachine::CheckUserCountM0(std::shared_ptr<Context> context)
{
if(context->m_userCount >= 0){
return true;
}
return false;
}
bool RoomMachine::CheckUserCountL0(std::shared_ptr<Context> context)
{
if(context->m_userCount <= 0){
return true;
}
return false;
}
Action.h
原本简写的动作变化, int curState和int nextState, 集成到Transition, Transition同时保存了自定义限制函数
这是自定义限制函数在action 使用, 如果不满足条件, 返回自己本身的状态, 否则就是下一个状态
main.cpp 运行后, 符合预期
#include <iostream>
#include <memory>
#include <string>
#include "States/SharedDef.h"
#include "States/Event.h"
#include "States/Machine/UserMachine.h"
#include "States/Machine/RoomMachine.h"
int main()
{
std::cout << "Hello World" << std::endl;
std::shared_ptr<UserMachine> userMachine = std::make_shared<UserMachine>();
std::shared_ptr<RoomMachine> roomMachine = std::make_shared<RoomMachine>();
// 0 为 201, 1 为 202
// 初始化玩家数量为0
std::shared_ptr<Context> context[2];
// 两间房间的状态
std::shared_ptr<State> roomState[2];
for(int i = 0; i < 2; i++)
{
context[i] = std::make_shared<Context>(0);
roomState[i] = std::make_shared<RoomState>(Empty);
}
userMachine->Init();
roomMachine->Init();
// 预想 玩家A 空闲 + 按下进入游戏按钮 -> 玩家B 在游戏大厅
auto nextState = userMachine->Execute(Idle, std::make_shared<UserEvent>(PressEnterGameBtn));
// 预想 玩家B 空闲 + 按下进入游戏按钮 -> 玩家B 在游戏大厅
auto nextState2 = userMachine->Execute(Idle, std::make_shared<UserEvent>(PressEnterGameBtn, "玩家B"));
// 预想 玩家A 空闲 + 按下进入房间201按钮 -> 玩家A 在房间201
nextState = userMachine->Execute(nextState->m_stateId, std::make_shared<UserEvent>(PressEnterGameRoom201Btn));
if(nextState != NULL)
{
// 预想 房间201 空闲 + 按下进入房间201按钮 + context判断数据 -> 房间201 有人
context[0]->m_userCount ++;
roomState[0] = roomMachine->Execute(roomState[0], std::make_shared<RoomEvent>(PressEnterGameRoom201Btn), context[0]);
}
// 预想 玩家B空闲 + 按下进入房间201按钮 -> 玩家B 在房间201
nextState2 = userMachine->Execute(nextState2->m_stateId, std::make_shared<UserEvent>(PressEnterGameRoom201Btn, "玩家B"));
if(nextState2 != NULL)
{
// 预想 房间201 有人 + 按下进入房间201按钮 + context判断数据 -> 房间201 有人
context[0]->m_userCount ++;
roomState[0] = roomMachine->Execute(roomState[0], std::make_shared<RoomEvent>(PressEnterGameRoom201Btn), context[0]);
}
// 预想 玩家B 在房间201 + 按下离开房间201按钮 -> 玩家B 空闲
nextState2 = userMachine->Execute(nextState2->m_stateId, std::make_shared<UserEvent>(PressLeaveGameRoom201Btn, "玩家B"));
if(nextState2 != NULL)
{
// 预想 房间201 有人 + 按下推出房间201按钮 + context判断数据 -> 房间201 有人
context[0]->m_userCount --;
roomState[0] = roomMachine->Execute(roomState[0], std::make_shared<RoomEvent>(PressLeaveGameRoom201Btn), context[0]);
}
// 预想 玩家A 在房间201 + 按下离开房间201按钮 -> 玩家A 空闲
nextState = userMachine->Execute(nextState->m_stateId, std::make_shared<UserEvent>(PressLeaveGameRoom201Btn));
if(nextState != NULL)
{
// 预想 房间201 有人 + 按下离开房间201按钮 + context判断数据 -> 房间201 没人
context[0]->m_userCount --;
roomState[0] = roomMachine->Execute(roomState[0], std::make_shared<RoomEvent>(PressLeaveGameRoom201Btn), context[0]);
}
// 预想 玩家A 在房间201 + 按下退出游戏按钮 -> 未注册
nextState = userMachine->Execute(InRoom201, std::make_shared<UserEvent>(PressLeaveGameBtn));
getchar();
return 0;
}
完整代码