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

状态机系统概述

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

状态机核心组件

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. 状态机的版本兼容性和序列化方案

相关推荐
向宇it13 小时前
【unity游戏开发——热更新】什么是Unity热更新
游戏·unity·编辑器·游戏引擎
神码编程17 小时前
【Unity】MiniGame编辑器小游戏(三)马赛克【Mosaic】
游戏·unity·编辑器
come1123418 小时前
VS Code 项目中的 .vscode 目录详解
ide·vscode·编辑器
像素之间18 小时前
设置vscode使用eslint
ide·vscode·编辑器
阿幸软件杂货间21 小时前
VSCode1.101.1Win多语言语言编辑器便携版安装教程
vscode·编辑器
小天源1 天前
Visual Studio Code 1.101下载
ide·vscode·编辑器
晨曦backend1 天前
Vim-vimrc 脚本文件表头设置
编辑器·vim·vimrc
蚕与禅1 天前
从零学起VIM
linux·编辑器·vim
edward_zcl2 天前
vscode代码块快捷操作
ide·vscode·编辑器