aigc实现的分层状态机

分层状态机
再充实一下英雄的行为,她可能会有更多相似的状态。 举个例子,她也许有站立,行走,
奔跑,和滑铲状态。在这些状态中,按B跳,按下蹲。
如果使用简单的状态机实现,我们在每个状态中的都重复了代码。 如果我们能够实现一次
,在多个状态间重用就好了。
如果这是面向对象的代码而不是状态机的,在状态间分享代码的方式是通过继承。 我们可
以为“在地面上”定义一个类处理跳跃和速降。 站立,行走,奔跑和滑铲都从它继承,然后
增加各自的附加行为。
它的影响有好有坏。 继承是一种有力的代码重用工具,但也在两块代码间建立了
非常强的耦合。 这是重锤,所以请小心使用。
你会发现,这是个被称为分层状态机的通用结构。 状态可以有父状态(这让它变为子状态
)。 当一个事件进来,如果子状态没有处理,它就会交给链上的父状态。 换言之,它像重
载的继承方法那样运作。
事实上,如果我们使用状态模式实现FSM,我们可以使用继承来实现层次。 定义一个基类
作为父状态:
class OnGroundState : public HeroineState
{
public:
virtual void handleInput(Heroine& heroine, Input input)
{
if (input == PRESS_B)
{
// 跳跃……
}
else if (input == PRESS_DOWN)
{
// 俯卧……
}
}
};
每个子状态继承它:
class DuckingState : public OnGroundState
{
public:
virtual void handleInput(Heroine& heroine, Input input)
{
if (input == RELEASE_DOWN)
{
// 站起……
}
else
{
// 没有处理输入,返回上一层
OnGroundState::handleInput(heroine, input);
}
}
};
这当然不是唯一实现层次的方法。 如果你没有使用GoF的状态模式,这可能不会有用。 相
反,你可以显式的使用状态栈而不是单一状态来表示当前状态的父状态链。
栈顶的状态是当前状态,在他下面是它的直接父状态, 然后是那个父状态的父状态,以此
类推。 当你需要状态的特定行为,你从栈的顶端开始, 然后向下寻找,直到某一个状态处
理了它。(如果到底也没找到,就无视它。)

···
// 定义角色状态
enum CharacterState {
Idle,
Moving,
Attacking,
Ducking,
Jumping,
}
// 定义角色类
class Character extends cc.Component {
private _stateStack: CharacterState[] = [CharacterState.Idle]; // 状态栈
private _isFiring: boolean = false; // 是否正在开火
private _moveSpeed: number = 100; // 移动速度
private _fireInterval: number = 0.5; // 开火间隔时间
private _fireTimer: number = 0; // 开火计时器
constructor() {
super();
}
update(dt: number) {
this._fireTimer += dt;
}
// 切换状态方法
private changeState(newState: CharacterState) {
if (this._stateStack[this._stateStack.length - 1] === newState) {
return;
}
this._stateStack.push(newState);
switch (newState) {
case CharacterState.Idle:
this.stopMoving();
break;
case CharacterState.Moving:
this.startMoving();
break;
case CharacterState.Attacking:
this.startAttacking();
break;
case CharacterState.Ducking:
this.startDucking();
break;
case CharacterState.Jumping:
this.startJumping();
break;
}
}
// 结束当前状态方法
private endCurrentState() {
const currentState = this._stateStack.pop()!;
switch (currentState) {
case CharacterState.Idle:
break;
case CharacterState.Moving:
this.stopMoving();
break;
case CharacterState.Attacking:
this.stopAttacking();
break;
case CharacterState.Ducking:
this.stopDucking();
break;
case CharacterState.Jumping:
this.stopJumping();
break;
}
}
// 开始移动方法
private startMoving() {
// TODO:播放移动动画等操作
}
// 停止移动方法
private stopMoving() {
// TODO:播放停止动画等操作
}
// 开始攻击方法
private startAttacking() {
if (!this._isFiring && this._fireTimer >= this._fireInterval) {
this._isFiring = true;
this._fireTimer = 0;
// TODO:播放攻击动画等操作,并在攻击结束后调用 stopAttacking 方法停止攻击
}
}
// 停止攻击方法
private stopAttacking() {
this._isFiring = false;
// TODO:停止播放攻击动画等操作
}
// 开始俯卧方法
private startDucking() {
// TODO:播放俯卧动画等操作,并在俯卧结束后调用 stopDucking 方法停止俯卧
}
// 停止俯卧方法
private stopDucking() {
// TODO:停止播放俯卧动画等操作
}
// 开始跳跃方法
private startJumping() {
// TODO:播放跳跃动画等操作,并在跳跃结束后调用 stopJumping 方法停止跳跃
}
// 停止跳跃方法
private stopJumping() {
// TODO:停止播放跳跃动画等操作
}
// 移动方法
public move(direction: cc.Vec3) {
if (direction.mag() > 0) {
if (this._stateStack[this._stateStack.length - 1] !== CharacterState.Moving) {
this.changeState(CharacterState.Moving);
}
this.node.position = this.node.position.add(direction.normalize().mul(this._moveSpeed));
} else {
if (this._stateStack[this._stateStack.length - 1] === CharacterState.Moving) {
this.endCurrentState();
}
}
}
// 开火方法
public fire() {
if (this._stateStack[this._stateStack.length - 1] !== CharacterState.Attacking) {
this.changeState(CharacterState.Attacking);
}
}
// 跳跃方法
public jump() {
if (this._stateStack[this._stateStack.length - 1] !== CharacterState.Jumping) {
this.changeState(CharacterState.Jumping);
}
}
// 俯卧方法
public duck() {
if (this._stateStack[this._stateStack.length - 1] !== CharacterState.Ducking) {
this.changeState(CharacterState.Ducking);
}
}
// 站起方法
public standUp() {
if (this._stateStack[this._stateStack.length - 1] === CharacterState.Ducking) {
this.endCurrentState();
}
}
}
···

在上面的代码中,我们定义了一个名为 Character 的角色类,实现了分层状态机。我们使用状态栈 _stateStack 来表示角色的状态,每个状态都可以有一个或多个父状态。在 update 方法中,我们更新开火计时器 _fireTimer 的值。在 changeState 方法中,我们根据新状态切换角色的行为,并在需要时播放相应的动画。在 endCurrentState 方法中,我们结束当前状态,并根据需要停止相应的动画。在 move 方法中,我们根据移动方向更新角色的位置,并根据需要切换角色的状态。在 fire、jump、duck 和 standUp 方法中,我们切换角色的状态,并在需要时播放相应的动画。

需要注意的是,在实际开发中,我们还需要根据具体需求进行修改和优化,并添加相应的条件和转换条件。同时,在使用分层状态机时,我们需要小心使用继承和耦合问题,并尽可能地保持代码简洁和易于维护。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容