求有限状态机的资料

antonyhome 2006-02-24 10:55:09
有些设计(比如游戏)是用有限状态机(FSM)实现的,
这个我只在数字电路的课程中学过,
不知道软件设计中如何实现状态迁移,
一般的软件设计的书上没有讲,只找到GOF的State模式,
谁有这方面的较详细的资料。先谢谢了!


...全文
980 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
jasonhc 2006-04-27
  • 打赏
  • 举报
回复
To guokai1217:
非常感谢,你提供的这个模型非常好,可以用宏很方便的扩展状态和描述状态迁移表,而且也描述出了父类型和子类型之间的复杂的树型关系。代码很干净漂亮。

赞一个!!
guokai1217 2006-04-24
  • 打赏
  • 举报
回复
推荐这个:[程序员杂志2004.8月刊_state模式和composite模式实现的状态机引擎]
http://www.contextfree.net/wangyw/source/oofsm.html

个人感觉状态机的几个不同实现阶段:
1、switch/case 最原始的实现方式,是很多的c程序员习惯采用的方式。

2、查找表[状态、事件、动作],稍微做了一点改进。有点类似MFC的雏形。

3、在以上基础上做的一些改进或者变体。
[比如用一个栈结构,激活的状态位于栈顶,自动的映射事件和动作的对应,再或者通过一些巧妙的宏等手段进行包装。但是线性结构在实际中使用比较受限、过于技巧性的宏比较难于理解...]

4、面向对象的设计下、灵活运用模式。如上面给出的链接。重用性和灵活性方面都有不错的表现。沿袭类似的设计思路、根据实际开发内容进行改造后利用。
逸学堂 2006-04-17
  • 打赏
  • 举报
回复
lz看你编译原理,对你有帮助
yuanchuang 2006-04-17
  • 打赏
  • 举报
回复
不懂帮顶……
rickerliang 2006-04-17
  • 打赏
  • 举报
回复
ox_thedarkness大哥,你的代码有问题啊,派生类函数hitTest少了个const
嘻嘻
rickerliang 2006-04-17
  • 打赏
  • 举报
回复
对状态机非常有兴趣,而且也写过一些游戏,学习了
alucardpj 2006-04-07
  • 打赏
  • 举报
回复
http://blog.csdn.net/alucardpj/archive/2006/03/31/645876.aspx
我自己写的对有限状态自动机的归纳,献丑了。
jixingzhong 2006-04-07
  • 打赏
  • 举报
回复
离散数学[ 繁体 ]有限状态自动机(finite state automata)或有限状态机(finite state machine),是一个 简单的数学模式,具有离散式输入的有限 ... 如表二所示的列表方式能够很方便地描述有限 状态机。对于表二所示的有限状态机而言,其集合为{S0,S1,S2,S3,S4,S5,S6},其输入 ...
www.cis.nctu.edu.tw/~is83039/discret/discrete83.html - 19k - 网页快照 - 类似网页



有限状态机理论联系实际要想深入学习有限状态机,请学习《形式语言与自动机理论》,该课程理论性很强,是计算机 软件与理论的基础。 ... 钱学森的《控制论》里面提到了有限状态机的问题,1978年出版 的,不过,他还论到,并非所有逻辑问题都可以用此方法解决。 ...
blog.joycode.com/sam1111/archive/2004/01/12/11302.aspx - 28k - 网页快照 - 类似网页


楼主 Google 一下 有限状态机 就是了 ...
jasonhc 2006-03-24
  • 打赏
  • 举报
回复

Robert C. Martin有一片文章,讲用面向对象的方法构造状态机:
http://www.objectmentor.com/resources/articles/umlfsm.pdf
北极猩猩 2006-03-24
  • 打赏
  • 举报
回复
有限状态自动机可以应用于许多问题的解决。
比如:正则表达式,UI状态控制等
yuanchuang 2006-03-23
  • 打赏
  • 举报
回复
up
zbl101 2006-03-07
  • 打赏
  • 举报
回复
mark
minus273 2006-02-26
  • 打赏
  • 举报
回复
我用的状态机是用一个数组实现 eg:
SMtable table[]
{
{状态1,事件1,动作1(回调函数)},
{状态2,事件2,动作2(回调函数)},
.....
{状态n,事件n,动作n(回调函数)}
}
xianshiqi 2006-02-25
  • 打赏
  • 举报
回复
有限状态机好像在编译原理中讲的多啊
ox_thedarkness 2006-02-25
  • 打赏
  • 举报
回复
下面是我习惯的做法:
基类Unit包含下面这些重要的模块:

1 状态描述。状态和帧回调函数统一,这可以令派生类工作最简化。
用 setState( pState )设置回调函数,用 bool isState( pState ) 检查当前状态。
有一个问题是 this 错位... 也就是涉及多继承的情况:如果Unit类不是最左基类就会出现这个问题。 所以有一个 pthis( void* ) 设置状态实际定义类的this位置。

2 包含帧数计数器 _fCount,这个计数器为 protected,派生类可见。
每次状态变化初始化为0。

因为游戏往往包含和状态相关的计时:比如Boss攻击前摆120帧的pose,这样玩家就有时间躲开;或者某敌人连续移动 300帧 触发一次“索敌AI”等等; 而且、如果动画模块没有单独制作的话,动画的计帧很适合用他完成。

3 包含普通函数 reflush() 刷新。 这个函数让 _fCount 加一,然后调用 Unit对应的状态回调函数(也就是当前状态)。 所以,如果我们有一个基类容器,比如 vector< Unit* >, 每帧的时候我们只需要遍历这个容器,然后调用 *iter.reflush() 就可以了。

注意,这个不是虚函数。

4 包含接口,纯虚函数。这里列举了游戏两个必不可少的接口:

bool hitTest( UnitHit& ) const
判断 Unit 是否碰到某个判定物体。无论攻击检测还是吃宝物都需要这个;有些时候地形约束也靠这个。
他只是返回几何判断,而不进行操作。

bool takeDmg( Weapon& )
Unit已经和某个Weapon 碰撞了,这个函数完成Unit这方的行动。
和 hitTest 相反,他不进行几何判断,而是完成操作。
什么行动呢?比如,如果 Weapon 是敌方的武器的话,那么计算伤害、转换本Unit 到 “被击中”、“被击飞”等就是他了。

返回是否成功 —— 为什么要返回是否成功呢?这也是经验...
因为有些东西逻辑上(或者游戏规则上)和Unit碰撞无效果。如果游戏规则说:自己人发射的子弹能穿过友军而不消失,那么这个返回值可以很漂亮的完成这个要求。


5 实际游戏如何运行呢?

一般来说游戏有两个模块:渲染和内核运算刷新。内核运算以内核帧记,游戏中几乎所有“活动”也是以内核帧为时间单位。
比如规定:内核帧 60 fps,
主角移动速度: 4 每帧(即一秒240单位);
主角移动动画循环: 20帧。
挥刀1: 20帧,起手4帧,带判定的攻击10帧,硬直6帧。
等等...


每当系统运行到 reflush刷新模块时,比较总时间,计算出当前绝对帧数和已经刷新的帧数。如果判断需要刷新n帧,那么就连续 reflush 刷新N次;否则,就 sleep或者其他啥等待时间流逝。

每次 reflush 刷新包括:遍历 Unit表逐个调用 reflush、以及其他的诸如关卡对象的 reflush。 如上面所说的,每个 Unit的 reflush 调用自己的当前状态回调函数。 一般分为下面几类:

没有判定的移动或者站立: 只考虑计算重力、环境约束(比如墙)对自己位置的影响。

带判定的行为: 除上面之外,一般向游戏系统对象要求 hitTest 测试。 一旦 hitTest 成功,则调用对方的 takeDmg()。 这样,对方就会进入合适的状态 ( 比如挨打/或者加分... 虽说叫 takeDmg, 但是宝物等等完全可以用这个完成)

特殊行为: 一般是播放一段特定动画(比如挨打、挂掉...),动画结束(确切地说,比较_fCount == 当前动画的结束帧编号)则进入下一个状态,有时候还要调用系统接口:比如死掉之后往往要告诉系统删除自己、在原地产生其他对象(如:我挂了,在我这里添加一串编号为002的喷血效果...) 等等....

6 渲染和内核是分开的。
与内核不同,渲染的fps往往不是固定的(尤其是PC平台)。需要渲染的时候,一般渲染上一个已经完成的内核状态; 3D游戏中更加精密一些的是,根据当前精确时间,在上一个内核帧和下一内核帧之间进行插值( 反正是骨骼嘛... 假定下一帧和当前是同一个动作 ) 这样,即使在内核帧只有30的情况下,也能提供真正120 fps的流畅动画了~~

---------------------------------------------------

下面是一个包含状态机的Unit类,以及他的派生类的代码。(当然只是简化的例子)运行于VC7之下。

//===============================================================
#include <iostream>
using std::cout;
using std::endl;

class Unit;
class UnitHit;
class Weapon;
// 通知系统从单位表中删除当前单位
void RemoveUnit( Unit& ){};


// 基类
class Unit{
typedef void( Unit::* P_CALL )();
P_CALL _stateNow; // 当前状态(同时也是回调函数)
void* _pthis; // 假如基类this和派生类不同,请调用 pthis()修改这个值

protected:
int _fCount; // 帧数计

public:
virtual bool hitTest( UnitHit& ) const = 0; // 碰撞检测
virtual bool takeDmg( Weapon& ) = 0; // 被Weapon命中,如果有判定则返回true

public:
// 参数为初始状态(函数指针)
template < typename T >
Unit( void (T::* initState)() ):
_stateNow( (P_CALL) initState ), _pthis(0),_fCount(0) {}

virtual ~Unit(){}

// 设置_pthis为p,影响之后的 reflush()
void pthis( void* p ){ _pthis = p; }

// 设置下一个状态(函数指针),_fCount清0
template < typename T >
void setState( void (T::* nextState )() ){
_stateNow = ( P_CALL ) nextState;
_fCount = 0;
}

// 比较当前状态(函数指针)
template < typename T >
bool isState( void (T::* rhs )() ){
return _stateNow == ( P_CALL ) rhs ?true:false;
}

// _fCount加1
// 调用当前状态回调函数,默认以this调用
// 若_pthis非0,则以_pthis为 this调用
// 若是状态回调函数指针为 0,则调用 RemoveUnit( Unit* )
void reflush(){
if( _stateNow ) {
if( _pthis ) ( (Unit*) _pthis ->*_stateNow )();
else ( this->*_stateNow )();
}
else RemoveUnit( *this );
++_fCount;
}
};


//某个敌人的类
class RedBall: public Unit{
typedef void( STATE_CALL )();
// 下面都是成员函数声明,例如第一个等价于 void stand()
STATE_CALL stand;
STATE_CALL move;
STATE_CALL attack;
STATE_CALL hitted;
STATE_CALL fall;
private:
int _x, _y;
int frameCount;
public:
bool hitTest( UnitHit& ){ return false; }
bool takeDmg( Weapon& ){ return false; }
public:
RedBall():Unit( stand ){
_x = _y = 0;
frameCount = 0;
}
};

void RedBall::stand(){
cout<<"RedBall ("<< _x <<','<< _y <<") stand(), f:"<<_fCount<<endl;
if( _fCount == 3 ){
cout<<"> Change to Move"<<endl;
setState( move );
}
}

void RedBall::move(){
static int speed = 5;

cout<<"RedBall ("<< _x <<','<< _y <<") move (), f:"<<_fCount<<endl;
_x += speed;
if( _x > 20 ) {
cout<<"> Change to Attack"<<endl;
setState( attack );
}
}

void RedBall::attack(){
cout<<"RedBall ("<< _x <<','<< _y <<") attack(), f:"<<_fCount<<endl;
if( _fCount == 5 ){
cout<<"> Attack Finished! Change to Stand"<<endl;
setState( stand );
}
}

int main(){
Unit *p = new RedBall();

// 我们让他跑20帧。
// 实际的工程中,一般是比较计时器的值并且间歇sleep
for( int i = 0; i < 20; ++i )
p->reflush();
}
//===============================================================
ox_thedarkness 2006-02-25
  • 打赏
  • 举报
回复
楼主是要做游戏么?

5,530

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 模式及实现
社区管理员
  • 模式及实现社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧