feat(角色状态机): 重构角色状态系统为基于类的设计
新增通用状态类和职业专属状态类,将状态逻辑从状态机中解耦 添加状态注册机制,支持按职业配置状态 实现基础状态如待机、移动、跳跃、受击等 为剑士职业实现专属攻击状态 补充状态机开发说明文档
This commit is contained in:
@@ -44,11 +44,15 @@ public:
|
||||
const CharacterWorldPosition& GetWorldPosition() const { return motor_.position; }
|
||||
const CharacterMotor& GetMotor() const { return motor_; }
|
||||
CharacterMotor& GetMotorMutable() { return motor_; }
|
||||
CharacterCommandBuffer& GetCommandBufferMutable() { return commandBuffer_; }
|
||||
float GetLastDeltaTime() const { return lastDeltaTime_; }
|
||||
const CharacterActionDefinition* FindAction(const std::string& actionId) const;
|
||||
std::string ResolveAnimationTag(const std::string& preferred,
|
||||
const std::string& fallback) const;
|
||||
void PlayAnimationTag(const std::string& actionTag);
|
||||
void RunStateHook(const std::string& phase,
|
||||
CharacterStateId stateId,
|
||||
const CharacterActionDefinition* action);
|
||||
void SetFacing(int direction);
|
||||
|
||||
const character::CharacterConfig* GetConfig() const {
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "character/CharacterActionLibrary.h"
|
||||
#include "character/CharacterDataLoader.h"
|
||||
#include "character/states/CharacterStateBase.h"
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
class CharacterObject;
|
||||
|
||||
/// 角色主状态机。输入先变成意图,再由状态机决定当前大状态和动作推进。
|
||||
class CharacterStateMachine {
|
||||
public:
|
||||
using StateRegistry = std::unordered_map<CharacterStateId, std::unique_ptr<ICharacterStateNode>>;
|
||||
|
||||
CharacterStateMachine() = default;
|
||||
|
||||
void Configure(const character::CharacterConfig& config);
|
||||
void Reset();
|
||||
void Update(CharacterObject& owner,
|
||||
CharacterCommandBuffer& commandBuffer,
|
||||
const CharacterIntent& intent,
|
||||
float deltaTime);
|
||||
void ForceHurt(const CharacterActionDefinition* hurtAction);
|
||||
void ForceHurt(CharacterObject& owner, const CharacterActionDefinition* hurtAction);
|
||||
|
||||
CharacterStateId GetState() const { return currentState_; }
|
||||
const std::string& GetCurrentActionId() const { return currentActionId_; }
|
||||
@@ -27,24 +35,21 @@ public:
|
||||
bool IsMovementLocked() const;
|
||||
bool CanTurn() const;
|
||||
|
||||
void RegisterState(std::unique_ptr<ICharacterStateNode> stateNode);
|
||||
void ClearRegisteredStates();
|
||||
ICharacterStateNode* FindStateNode(CharacterStateId stateId) const;
|
||||
|
||||
private:
|
||||
void ChangeState(CharacterObject& owner, CharacterStateId nextState);
|
||||
void EnterAction(CharacterObject& owner, const CharacterActionDefinition* action);
|
||||
void UpdateGroundState(CharacterObject& owner,
|
||||
CharacterCommandBuffer& commandBuffer,
|
||||
const CharacterIntent& intent);
|
||||
void UpdateAirState(CharacterObject& owner,
|
||||
CharacterCommandBuffer& commandBuffer,
|
||||
const CharacterIntent& intent);
|
||||
void UpdateActionState(CharacterObject& owner,
|
||||
CharacterCommandBuffer& commandBuffer,
|
||||
const CharacterIntent& intent,
|
||||
float deltaTime);
|
||||
void UpdateHurtState(CharacterObject& owner, float deltaTime);
|
||||
void InvokeStateHook(CharacterObject& owner,
|
||||
const CharacterActionDefinition* action,
|
||||
const char* phase) const;
|
||||
bool TryStartAction(CharacterObject& owner,
|
||||
CharacterCommandBuffer& commandBuffer,
|
||||
CharacterCommandType commandType,
|
||||
const std::string& actionId);
|
||||
bool TryStartRegisteredAction(CharacterStateContext& context);
|
||||
void ApplyFrameEvents(CharacterObject& owner, int previousFrame, int currentFrame);
|
||||
|
||||
CharacterStateId currentState_ = CharacterStateId::Idle;
|
||||
@@ -54,6 +59,9 @@ private:
|
||||
float actionFrameProgress_ = 0.0f;
|
||||
int actionFrame_ = 0;
|
||||
float landingTimer_ = 0.0f;
|
||||
StateRegistry stateRegistry_;
|
||||
|
||||
friend class CharacterStateBase;
|
||||
};
|
||||
|
||||
} // namespace frostbite2D
|
||||
|
||||
94
Game/include/character/states/CharacterStateBase.h
Normal file
94
Game/include/character/states/CharacterStateBase.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include "character/CharacterActionTypes.h"
|
||||
#include <string>
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
class CharacterObject;
|
||||
class CharacterStateMachine;
|
||||
struct CharacterActionDefinition;
|
||||
|
||||
struct CharacterStateContext {
|
||||
CharacterObject& owner;
|
||||
CharacterCommandBuffer& commandBuffer;
|
||||
const CharacterIntent& intent;
|
||||
float deltaTime = 0.0f;
|
||||
};
|
||||
|
||||
class ICharacterStateNode {
|
||||
public:
|
||||
virtual ~ICharacterStateNode() = default;
|
||||
|
||||
virtual CharacterStateId GetId() const = 0;
|
||||
virtual void OnEnter(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context,
|
||||
CharacterStateId previousState) {
|
||||
(void)machine;
|
||||
(void)context;
|
||||
(void)previousState;
|
||||
}
|
||||
virtual void OnUpdate(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context) = 0;
|
||||
virtual void OnExit(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context,
|
||||
CharacterStateId nextState) {
|
||||
(void)machine;
|
||||
(void)context;
|
||||
(void)nextState;
|
||||
}
|
||||
};
|
||||
|
||||
class ICharacterActionStateNode {
|
||||
public:
|
||||
virtual ~ICharacterActionStateNode() = default;
|
||||
virtual bool TryEnter(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context) = 0;
|
||||
};
|
||||
|
||||
class CharacterStateBase : public ICharacterStateNode {
|
||||
public:
|
||||
explicit CharacterStateBase(CharacterStateId stateId);
|
||||
~CharacterStateBase() override = default;
|
||||
|
||||
CharacterStateId GetId() const override;
|
||||
|
||||
protected:
|
||||
void ChangeState(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context,
|
||||
CharacterStateId nextState) const;
|
||||
void PlayActionById(CharacterObject& owner,
|
||||
const std::string& actionId,
|
||||
const std::string& fallbackTag = "rest") const;
|
||||
void InvokeHook(CharacterStateMachine& machine,
|
||||
CharacterObject& owner,
|
||||
const CharacterActionDefinition* action,
|
||||
const char* phase) const;
|
||||
bool TryStartAction(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context,
|
||||
CharacterCommandType commandType,
|
||||
const std::string& actionId) const;
|
||||
bool TryStartRegisteredAction(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context) const;
|
||||
void ApplyFrameEvents(CharacterStateMachine& machine,
|
||||
CharacterObject& owner,
|
||||
int previousFrame,
|
||||
int currentFrame) const;
|
||||
|
||||
const CharacterActionDefinition* GetCurrentAction(const CharacterStateMachine& machine) const;
|
||||
const std::string& GetCurrentActionId(const CharacterStateMachine& machine) const;
|
||||
void SetCurrentAction(CharacterStateMachine& machine,
|
||||
const CharacterActionDefinition* action,
|
||||
const std::string& actionId) const;
|
||||
void ClearCurrentAction(CharacterStateMachine& machine) const;
|
||||
|
||||
float& StateTime(CharacterStateMachine& machine) const;
|
||||
float& ActionFrameProgress(CharacterStateMachine& machine) const;
|
||||
int& ActionFrame(CharacterStateMachine& machine) const;
|
||||
float& LandingTimer(CharacterStateMachine& machine) const;
|
||||
|
||||
private:
|
||||
CharacterStateId stateId_ = CharacterStateId::Idle;
|
||||
};
|
||||
|
||||
} // namespace frostbite2D
|
||||
13
Game/include/character/states/CharacterStateRegistry.h
Normal file
13
Game/include/character/states/CharacterStateRegistry.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "character/CharacterDataLoader.h"
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
class CharacterStateMachine;
|
||||
|
||||
void RegisterCommonCharacterStates(CharacterStateMachine& machine);
|
||||
void RegisterJobCharacterStates(CharacterStateMachine& machine,
|
||||
const character::CharacterConfig& config);
|
||||
|
||||
} // namespace frostbite2D
|
||||
15
Game/include/character/states/common/DeadState.h
Normal file
15
Game/include/character/states/common/DeadState.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "character/states/CharacterStateBase.h"
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
class DeadState : public CharacterStateBase {
|
||||
public:
|
||||
DeadState();
|
||||
|
||||
void OnUpdate(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context) override;
|
||||
};
|
||||
|
||||
} // namespace frostbite2D
|
||||
18
Game/include/character/states/common/FallState.h
Normal file
18
Game/include/character/states/common/FallState.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "character/states/CharacterStateBase.h"
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
class FallState : public CharacterStateBase {
|
||||
public:
|
||||
FallState();
|
||||
|
||||
void OnEnter(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context,
|
||||
CharacterStateId previousState) override;
|
||||
void OnUpdate(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context) override;
|
||||
};
|
||||
|
||||
} // namespace frostbite2D
|
||||
21
Game/include/character/states/common/HurtState.h
Normal file
21
Game/include/character/states/common/HurtState.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "character/states/CharacterStateBase.h"
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
class HurtState : public CharacterStateBase {
|
||||
public:
|
||||
HurtState();
|
||||
|
||||
void OnEnter(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context,
|
||||
CharacterStateId previousState) override;
|
||||
void OnUpdate(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context) override;
|
||||
void OnExit(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context,
|
||||
CharacterStateId nextState) override;
|
||||
};
|
||||
|
||||
} // namespace frostbite2D
|
||||
18
Game/include/character/states/common/IdleState.h
Normal file
18
Game/include/character/states/common/IdleState.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "character/states/CharacterStateBase.h"
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
class IdleState : public CharacterStateBase {
|
||||
public:
|
||||
IdleState();
|
||||
|
||||
void OnEnter(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context,
|
||||
CharacterStateId previousState) override;
|
||||
void OnUpdate(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context) override;
|
||||
};
|
||||
|
||||
} // namespace frostbite2D
|
||||
18
Game/include/character/states/common/JumpState.h
Normal file
18
Game/include/character/states/common/JumpState.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "character/states/CharacterStateBase.h"
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
class JumpState : public CharacterStateBase {
|
||||
public:
|
||||
JumpState();
|
||||
|
||||
void OnEnter(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context,
|
||||
CharacterStateId previousState) override;
|
||||
void OnUpdate(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context) override;
|
||||
};
|
||||
|
||||
} // namespace frostbite2D
|
||||
18
Game/include/character/states/common/LandingState.h
Normal file
18
Game/include/character/states/common/LandingState.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "character/states/CharacterStateBase.h"
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
class LandingState : public CharacterStateBase {
|
||||
public:
|
||||
LandingState();
|
||||
|
||||
void OnEnter(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context,
|
||||
CharacterStateId previousState) override;
|
||||
void OnUpdate(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context) override;
|
||||
};
|
||||
|
||||
} // namespace frostbite2D
|
||||
18
Game/include/character/states/common/MoveState.h
Normal file
18
Game/include/character/states/common/MoveState.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "character/states/CharacterStateBase.h"
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
class MoveState : public CharacterStateBase {
|
||||
public:
|
||||
MoveState();
|
||||
|
||||
void OnEnter(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context,
|
||||
CharacterStateId previousState) override;
|
||||
void OnUpdate(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context) override;
|
||||
};
|
||||
|
||||
} // namespace frostbite2D
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "character/states/CharacterStateBase.h"
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
class SwordmanAttackState : public CharacterStateBase, public ICharacterActionStateNode {
|
||||
public:
|
||||
SwordmanAttackState();
|
||||
|
||||
bool TryEnter(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context) override;
|
||||
void OnEnter(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context,
|
||||
CharacterStateId previousState) override;
|
||||
void OnUpdate(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context) override;
|
||||
void OnExit(CharacterStateMachine& machine,
|
||||
CharacterStateContext& context,
|
||||
CharacterStateId nextState) override;
|
||||
};
|
||||
|
||||
} // namespace frostbite2D
|
||||
Reference in New Issue
Block a user