游戏技能编辑器之状态机的设计与实现

状态机系统概述

在游戏开发中,状态机是控制角色行为逻辑的核心系统。本文基于提供的代码,分析技能编辑器中状态机系统的设计与实现,特别是如何通过状态机管理复杂的技能逻辑。

状态机核心组件

1. 状态基类设计 (FSMStateBase)

状态基类是所有具体状态的父类,定义了状态的基本行为:

复制代码
class FSMStateBase {
public:
    virtual void enter();    // 进入状态
    virtual void update(uint32 dwElapsed); // 更新状态
    virtual void exit();     // 退出状态
    virtual void refreshState() {}; // 刷新状态
    
protected:
    CUnit *m_pUnit; // 关联的游戏单位
    std::string m_strPresentationName; // 当前状态的表现名称
};

2. 状态机模板 (Fsm)

状态机模板类负责管理状态注册和状态切换:

复制代码
template<typename classname, typename statetype, typename EVTID, typename PARAM>
class Fsm {
public:
    void SetState(statetype state) {
        // 退出当前状态
        if (m_stateFunc.find(m_state) != m_stateFunc.end()) {
            m_stateFunc[m_state].pBase->exit();
        }
        
        // 进入新状态
        m_last_state = m_state;
        m_state = state;
        
        if (m_stateFunc.find(m_state) != m_stateFunc.end()) {
            m_stateFunc[m_state].pBase->enter();
        }
    }
    
    void RegisterStateFunc(statetype state, FSMStateBase* cls) {
        m_stateFunc[state].pBase = cls;
    }
    
    void Update(float diff) {
        if (m_stateFunc.find(m_state) != m_stateFunc.end()) {
            m_stateFunc[m_state].pBase->update(diff);
        }
    }

private:
    statetype m_state;        // 当前状态
    statetype m_last_state;   // 上一个状态
    MapStateFunc m_stateFunc; // 状态映射表
};

具体状态实现

1. 待机状态 (FSMStateStandBy)

复制代码
void FSMStateStandBy::enter() {
    Base::enter();
    ref_ptr<CPresentationAction> p = m_pUnit->playStandbyPresentation();
    if (p) {
        Base::setPresentation(p->GetName());
    }
}

void FSMStateStandBy::update(uint32 dwElapsed) {
    Base::update(dwElapsed);
    this->updateStandby(dwElapsed);
}

2. 移动状态 (FSMStateMove)

复制代码
void FSMStateMove::enter() {
    Base::enter();
    ref_ptr<CPresentationAction> p = m_pUnit->playMovePresentation();
    if (p) {
        Base::setPresentation(p->GetName());
    }
}

void FSMStateMove::update(uint32 dwElapsed) {
    Base::update(dwElapsed);
    this->updateMove(dwElapsed);
}

3. 受击状态 (FSMStateHitted)

复制代码
void FSMStateHitted::enter() {
    Base::enter();
    ref_ptr<CPresentationAction> p = m_pUnit->playHittedPresentation();
    if (p) {
        Base::setPresentation(p->GetName());
    }
    
    // 特殊受击类型处理
    switch (mHittedType) {
    case zhuge_chuifeng:
        zhugeChuifengAction.readyToMove();
        break;
    }
}

void FSMStateHitted::update(uint32 dwElapsed) {
    Base::update(dwElapsed);
    
    // 处理特殊受击类型的位移
    switch (mHittedType) {
    case zhuge_chuifeng:
        zhugeChuifengAction.move(dwElapsed);
        break;
    }
}

4. 技能状态 (FSMStateSkill)

复制代码
void FSMStateSkill::enter() {
    Base::enter();
    
    // 获取技能数据
    S_SKILL *skillData = getData();
    
    // 播放技能表现
    ref_ptr<CPresentationAction> pa = gSPConditonMgr->getPresentationBySkillData(skillData);
    if (pa) {
        Base::setPresentation(pa->GetName());
        
        // 注册回调
        pa->OnStopSignal.notify(this, &FSMStateBase::OnStop);
        pa->OnFrameEventSignal.notify(this, &FSMStateBase::OnFrame);
        
        // 设置技能数据
        pa->SetUserData("SKILL_DATA", Any(skillData));
    }
}

void FSMStateSkill::exit() {
    // 清理技能相关状态
    if (m_pUnit->isStatusById(IgnoreRTSRelation)) {
        m_pUnit->delStatusById(IgnoreRTSRelation);
    }
    
    Base::exit();
}

状态转换机制

1. 状态切换流程

  1. 事件驱动的状态转换

    // 注册事件处理
    void RegisterEvt(statetype state, EVTID evt, classname* cls,
    statetype(classname::*f)(statetype, EVTID, PARAM))
    {
    EvtProc& proc = m_mapTrans[state][evt];
    proc.notifyFunc.cls = cls;
    proc.notifyFunc.imp = f;
    }

    // 事件分发
    void DispatchEvent(EVTID evt, PARAM pms) {
    // 查找当前状态的事件处理
    auto itS = m_mapTrans.find(m_state);
    if (itS != m_mapTrans.end()) {
    auto itE = itS->second.find(evt);
    if (itE != itS->second.end()) {
    // 执行事件处理函数并切换状态
    SetState((itE->second.notifyFunc.cls->*itE->second.notifyFunc.imp)
    (m_state, evt, pms));
    return;
    }
    }
    }

技能编辑器集成

1. 状态机在编辑器中的可视化

在技能编辑器中,状态机可以直观地展示:

状态机视图

| 当前状态: 技能状态 (STATE_SKILL_1) |

|------------------------------------|

| 可转换状态: |

| - 待机状态 (STATE_STANDBY) |

| - 移动状态 (STATE_MOVE) |

| - 受击状态 (STATE_HITTED) |

| - 死亡状态 (STATE_DEAD) |

| |

| 事件列表: |

| [动画事件] 帧事件(100): 触发伤害 |

| [动画事件] 结束事件: 返回待机 |

2. 状态与技能时间轴的关联

在技能编辑器中,状态切换可以与时间轴上的关键帧关联:

复制代码
{
  "tracks": [
    {
      "type": "State",
      "keyframes": [
        {"time": 0.0, "state": "STATE_SKILL_1"},
        {"time": 2.5, "state": "STATE_STANDBY"}
      ]
    }
  ]
}

3. 状态机调试功能

技能编辑器提供状态机调试工具:

  • 状态切换历史记录

  • 当前状态变量查看

  • 事件触发模拟

  • 断点调试功能

高级特性实现

1. 状态过渡机制

复制代码
void FSMStateMove::enter() {
    // 检查是否可以进入过渡状态
    if (m_pUnit->canEnterTransitionToMove(m_pUnit->getLastFsmPrt())) {
        m_pUnit->setFsmState(STATE_TRANSITION_MOVE);
        return;
    }
    
    // 否则进入普通移动状态
    Base::enter();
}

2. 动画事件驱动状态切换

复制代码
void RegisterAniEvt(statetype state, EVENT_ANI_E aniEvent, 
                   statetype retState) 
{
    AniEvtProc& proc = m_mapAniTrans[state][aniEvent];
    proc.retState = retState;
}

void DispatchAniEvent(EVENT_ANI_E evt, CPresentationAction* anim, int param) {
    auto itS = m_mapAniTrans.find(m_state);
    if (itS != m_mapAniTrans.end()) {
        auto itE = itS->second.find(evt);
        if (itE != itS->second.end()) {
            SetState(itE->second.retState); // 切换到新状态
        }
    }
}

3. 状态特定的资源管理

复制代码
void FSMStateDead::enter() {
    // 根据角色类型加载不同的死亡表现
    if (m_pUnit->isStatusById(Ghost)) {
        m_pUnit->doPressentation("ghost_dead");
    } else {
        m_pUnit->doPressentation("enter_dead");
        m_pUnit->doPressentation("enter_dead_effect");
    }
}

性能优化策略

1. 状态对象池

复制代码
class StatePool {
public:
    FSMStateBase* Acquire(StateType type) {
        if (pool[type].empty()) {
            return CreateState(type);
        }
        auto state = pool[type].back();
        pool[type].pop_back();
        return state->Reset();
    }
    
    void Release(FSMStateBase* state) {
        pool[state->GetType()].push_back(state);
    }

private:
    std::map<StateType, std::vector<FSMStateBase*>> pool;
};

2. 状态切换优化

复制代码
// 避免不必要的状态切换
void SetState(statetype newState) {
    if (m_state == newState) {
        // 仅刷新状态,不执行完整切换
        if (m_stateFunc.find(m_state) != m_stateFunc.end()) {
            m_stateFunc[m_state].pBase->refreshState();
        }
        return;
    }
    // 执行完整状态切换...
}

3. 基于状态的LOD优化

复制代码
void FSMStateBase::update(uint32 dwElapsed) {
    // 根据距离相机的距离调整更新频率
    float distance = Camera::Main()->DistanceTo(m_pUnit->Position);
    if (distance > LOD_DISTANCE) {
        // 低细节更新,每3帧更新一次
        if (frameCount % 3 == 0) {
            UpdateLOD(dwElapsed * 3);
        }
    } else {
        // 全细节更新
        UpdateFull(dwElapsed);
    }
    frameCount++;
}

总结

本文详细分析了技能编辑器中状态机系统的设计与实现,核心要点包括:

  1. 分层状态架构

    • 基础状态机模板提供核心状态管理功能

    • 具体状态实现游戏逻辑(待机、移动、技能等)

    • 状态过渡机制实现平滑切换

  2. 事件驱动设计

    • 支持普通事件和动画事件驱动状态转换

    • 灵活的事件注册和分发机制

    • 时间轴关键帧与状态绑定

  3. 编辑器集成

    • 可视化状态机编辑

    • 状态与技能表现关联

    • 实时调试和模拟功能

  4. 性能优化

    • 状态对象池减少内存分配

    • LOD策略按需更新

    • 避免冗余状态切换

这种状态机设计为技能编辑器提供了强大的逻辑控制能力,使设计师能够创建复杂的技能行为,同时保持代码的可维护性和运行时性能。

最佳实践建议:在技能编辑器中实现状态机时,重点考虑:

  1. 状态划分的粒度 - 不宜过细也不宜过粗

  2. 状态转换的可视化调试支持

  3. 与时间轴编辑器的深度集成

  4. 状态机的版本兼容性和序列化方案

相关推荐
时空自由民.6 小时前
vscode clangd插件 编译失败会跳转失败,有时候也会经常出现跳转失败的情况
ide·vscode·编辑器
誰能久伴不乏13 小时前
深入了解 Vim 编辑器:从入门到精通
linux·编辑器·vim
GDAL16 小时前
vscode 插件开发activityba
vscode·编辑器
learn_coder16 小时前
在vscode中和obsidian中使用Mermaid
ide·vscode·编辑器
前端小超人rui1 天前
UEditor 对接 秀米 手机编辑器流程与问题
编辑器
l1x1n01 天前
Vim 编辑器常用操作详解(新手快速上手指南)
linux·编辑器·vim
Shan12052 天前
编辑器Vim的快速入门
linux·编辑器·vim
通信小小昕2 天前
ubuntu18.04.1无法安装vscode(安装依赖无效)
ide·vscode·编辑器
ykjhr_3d2 天前
华锐云空间展销编辑器:开启数字化展示新时代
编辑器
许白掰2 天前
Linux入门篇学习——Linux 工具之 make 工具和 makefile 文件
linux·运维·服务器·前端·学习·编辑器