feat(animation): 添加动画状态回调支持

refactor(character): 重构角色动作处理逻辑

feat(swordman): 实现剑士基础攻击和技能1处理

refactor(state): 优化状态机与动作上下文管理

feat(input): 改进输入系统支持动作请求队列

refactor(movement): 重构移动系统支持行走/奔跑模式
This commit is contained in:
2026-04-04 14:45:41 +08:00
parent 1200cf0181
commit 5e80df040b
29 changed files with 1251 additions and 513 deletions

View File

@@ -8,7 +8,11 @@
namespace frostbite2D {
/// 管理角色动作定义。优先从 PVF 动作脚本读取,不再自动生成回退动作。
/// 管理角色显式注册的逻辑动作。
///
/// 动画配置只负责提供资源标签,不再自动推导哪些逻辑动作存在。
/// 基础状态动作和职业技能入口都在代码中显式注册,让技能实现类自己决定
/// 当前要播放哪个 ani。
class CharacterActionLibrary {
public:
bool LoadForConfig(const character::CharacterConfig& config);
@@ -17,7 +21,7 @@ public:
std::string DescribeActionIds() const;
private:
bool TryLoadPvfActionScripts(const character::CharacterConfig& config);
bool BuildExplicitActionRegistry(const character::CharacterConfig& config);
std::map<std::string, CharacterActionDefinition> actions_;
};

View File

@@ -2,6 +2,9 @@
#include <frostbite2D/types/type_math.h>
#include <string>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
namespace frostbite2D {
@@ -10,9 +13,13 @@ namespace frostbite2D {
enum class CharacterCommandType {
Move,
JumpPressed,
AttackPressed,
Skill1Pressed,
DashPressed,
ActionPressed,
};
enum class CharacterMoveMode {
None,
Walk,
Run,
};
/// 单条输入命令。移动命令携带轴值,按钮命令只关心触发时机。
@@ -20,16 +27,17 @@ struct CharacterCommand {
CharacterCommandType type = CharacterCommandType::Move;
Vec2 moveAxis = Vec2::Zero();
float timestamp = 0.0f;
CharacterMoveMode moveMode = CharacterMoveMode::None;
std::string actionId;
};
/// 角色在当前帧想要执行的意图,供状态机消费。
struct CharacterIntent {
float moveX = 0.0f;
float moveY = 0.0f;
CharacterMoveMode moveMode = CharacterMoveMode::None;
bool wantJump = false;
bool wantAttack = false;
bool wantSkill1 = false;
bool wantDash = false;
std::vector<std::string> requestedActions;
};
/// 主状态机的大状态,普攻和技能统一走 Action。
@@ -59,7 +67,9 @@ struct CharacterWorldPosition {
struct CharacterMotor {
CharacterWorldPosition position;
Vec2 groundVelocity = Vec2::Zero();
Vec2 forcedSlideVelocity = Vec2::Zero();
float verticalVelocity = 0.0f;
float forcedSlideRemainingTime = 0.0f;
bool grounded = true;
int facing = 1;
float moveSpeed = 220.0f;
@@ -85,6 +95,20 @@ struct CharacterMotor {
}
void StopGroundMovement() { groundVelocity = Vec2::Zero(); }
void ClearForcedSlide() {
forcedSlideVelocity = Vec2::Zero();
forcedSlideRemainingTime = 0.0f;
}
void StartForcedSlide(const Vec2& direction, float distance, float duration) {
if (distance <= 0.0f || duration <= 0.0f || direction.lengthSquared() < 0.0001f) {
ClearForcedSlide();
return;
}
forcedSlideVelocity = direction.normalized() * (distance / duration);
forcedSlideRemainingTime = duration;
}
void Jump() {
grounded = false;
@@ -97,6 +121,17 @@ struct CharacterMotor {
void Update(float deltaTime) {
position.x += groundVelocity.x * deltaTime;
position.y += groundVelocity.y * deltaTime;
if (forcedSlideRemainingTime > 0.0f && forcedSlideVelocity.lengthSquared() > 0.0f) {
float slideDeltaTime = deltaTime < forcedSlideRemainingTime
? deltaTime
: forcedSlideRemainingTime;
position.x += forcedSlideVelocity.x * slideDeltaTime;
position.y += forcedSlideVelocity.y * slideDeltaTime;
forcedSlideRemainingTime -= slideDeltaTime;
if (forcedSlideRemainingTime <= 0.0f) {
ClearForcedSlide();
}
}
if (!grounded || position.z > 0.0f || verticalVelocity > 0.0f) {
position.z += verticalVelocity * deltaTime;
@@ -116,51 +151,166 @@ struct CharacterMotor {
}
};
/// 帧事件是动作定义驱动逻辑的钩子,后续可扩展命中框与位移曲线
enum class CharacterFrameEventType {
OpenHitWindow,
CloseHitWindow,
OpenInputBuffer,
CloseInputBuffer,
SetVelocityXY,
SetVelocityZ,
AnimationEvent,
FinishAction,
};
/// 单个动作帧上的逻辑事件。
struct CharacterFrameEventDefinition {
int frame = 0;
CharacterFrameEventType type = CharacterFrameEventType::AnimationEvent;
Vec2 velocityXY = Vec2::Zero();
float velocityZ = 0.0f;
std::string stringValue;
};
/// 取消规则决定动作在什么窗口内可以接下一个动作。
struct CharacterCancelRuleDefinition {
std::string targetAction;
int beginFrame = 0;
int endFrame = 0;
bool requireGrounded = false;
bool requireAirborne = false;
};
/// 逻辑动作定义。actionId 是玩法动作名animationTag 是资源动作名。
/// 逻辑动作定义。这里只保留显式注册的逻辑入口名
///
/// 动画播放统一由状态类/技能类直接调用 `PlayAnimationTag()` 控制,
/// 不再通过动作定义反查 animationTag。
struct CharacterActionDefinition {
std::string actionId;
std::string animationTag;
int totalFrames = 1;
bool loop = false;
bool canMove = false;
bool canTurn = true;
bool canBeInterrupted = true;
int inputBufferOpenFrame = 0;
float moveSpeedScale = 0.0f;
std::vector<CharacterCancelRuleDefinition> cancelRules;
std::vector<CharacterFrameEventDefinition> frameEvents;
};
using CharacterRuntimeValue = std::variant<bool, int, float, std::string, Vec2>;
class CharacterRuntimeBlackboard {
public:
void SetBool(const std::string& key, bool value);
void SetInt(const std::string& key, int value);
void SetFloat(const std::string& key, float value);
void SetString(const std::string& key, const std::string& value);
void SetVec2(const std::string& key, const Vec2& value);
bool TryGetBool(const std::string& key, bool& outValue) const;
bool TryGetInt(const std::string& key, int& outValue) const;
bool TryGetFloat(const std::string& key, float& outValue) const;
bool TryGetString(const std::string& key, std::string& outValue) const;
bool TryGetVec2(const std::string& key, Vec2& outValue) const;
bool Has(const std::string& key) const;
void Remove(const std::string& key);
void Clear();
private:
template <typename T>
void SetValue(const std::string& key, T&& value) {
values_[key] = std::forward<T>(value);
}
template <typename T>
bool TryGetValue(const std::string& key, T& outValue) const {
auto it = values_.find(key);
if (it == values_.end()) {
return false;
}
const T* value = std::get_if<T>(&it->second);
if (!value) {
return false;
}
outValue = *value;
return true;
}
std::unordered_map<std::string, CharacterRuntimeValue> values_;
};
struct CharacterActionContextData {
bool valid = false;
std::string requestedActionId;
std::string sourceActionId;
CharacterStateId sourceStateId = CharacterStateId::Idle;
bool hasSourceStateId = false;
CharacterRuntimeBlackboard values;
void Clear() {
valid = false;
requestedActionId.clear();
sourceActionId.clear();
sourceStateId = CharacterStateId::Idle;
hasSourceStateId = false;
values.Clear();
}
void SetSourceStateId(CharacterStateId stateId) {
sourceStateId = stateId;
hasSourceStateId = true;
}
void SetBool(const std::string& key, bool value) { values.SetBool(key, value); }
void SetInt(const std::string& key, int value) { values.SetInt(key, value); }
void SetFloat(const std::string& key, float value) { values.SetFloat(key, value); }
void SetString(const std::string& key, const std::string& value) {
values.SetString(key, value);
}
void SetVec2(const std::string& key, const Vec2& value) { values.SetVec2(key, value); }
bool TryGetBool(const std::string& key, bool& outValue) const {
return values.TryGetBool(key, outValue);
}
bool TryGetInt(const std::string& key, int& outValue) const {
return values.TryGetInt(key, outValue);
}
bool TryGetFloat(const std::string& key, float& outValue) const {
return values.TryGetFloat(key, outValue);
}
bool TryGetString(const std::string& key, std::string& outValue) const {
return values.TryGetString(key, outValue);
}
bool TryGetVec2(const std::string& key, Vec2& outValue) const {
return values.TryGetVec2(key, outValue);
}
bool Has(const std::string& key) const { return values.Has(key); }
void Remove(const std::string& key) { values.Remove(key); }
};
inline void CharacterRuntimeBlackboard::SetBool(const std::string& key, bool value) {
SetValue(key, value);
}
inline void CharacterRuntimeBlackboard::SetInt(const std::string& key, int value) {
SetValue(key, value);
}
inline void CharacterRuntimeBlackboard::SetFloat(const std::string& key, float value) {
SetValue(key, value);
}
inline void CharacterRuntimeBlackboard::SetString(const std::string& key,
const std::string& value) {
SetValue(key, value);
}
inline void CharacterRuntimeBlackboard::SetVec2(const std::string& key, const Vec2& value) {
SetValue(key, value);
}
inline bool CharacterRuntimeBlackboard::TryGetBool(const std::string& key,
bool& outValue) const {
return TryGetValue(key, outValue);
}
inline bool CharacterRuntimeBlackboard::TryGetInt(const std::string& key, int& outValue) const {
return TryGetValue(key, outValue);
}
inline bool CharacterRuntimeBlackboard::TryGetFloat(const std::string& key,
float& outValue) const {
return TryGetValue(key, outValue);
}
inline bool CharacterRuntimeBlackboard::TryGetString(const std::string& key,
std::string& outValue) const {
return TryGetValue(key, outValue);
}
inline bool CharacterRuntimeBlackboard::TryGetVec2(const std::string& key,
Vec2& outValue) const {
return TryGetValue(key, outValue);
}
inline bool CharacterRuntimeBlackboard::Has(const std::string& key) const {
return values_.find(key) != values_.end();
}
inline void CharacterRuntimeBlackboard::Remove(const std::string& key) {
values_.erase(key);
}
inline void CharacterRuntimeBlackboard::Clear() {
values_.clear();
}
/// 运行期状态标记,承载霸体、无敌等战斗属性。
struct CharacterStatus {
bool superArmor = false;
@@ -183,15 +333,20 @@ public:
CharacterIntent BuildIntent() const;
bool HasBuffered(CharacterCommandType type) const;
void Consume(CharacterCommandType type);
bool HasBufferedAction(const std::string& actionId) const;
void ConsumeAction(const std::string& actionId);
void ClearBufferedActions();
void Clear();
private:
struct BufferedButton {
CharacterCommandType type = CharacterCommandType::AttackPressed;
CharacterCommandType type = CharacterCommandType::ActionPressed;
std::string actionId;
float age = 0.0f;
};
Vec2 moveAxis_ = Vec2::Zero();
CharacterMoveMode moveMode_ = CharacterMoveMode::None;
std::vector<BufferedButton> buttons_;
float bufferWindowSeconds_ = 0.2f;
};

View File

@@ -5,6 +5,7 @@
#include <frostbite2D/2d/actor.h>
#include <frostbite2D/animation/animation.h>
#include <frostbite2D/base/RefPtr.h>
#include <functional>
#include <map>
#include <string>
#include <vector>
@@ -16,6 +17,8 @@ class CharacterObject;
class CharacterAnimation : public Actor {
public:
using ActionAnimationList = std::map<std::string, std::vector<RefPtr<Animation>>>;
using ActionFrameFlagCallback = std::function<void(int)>;
using ActionEndCallback = std::function<void()>;
bool Init(CharacterObject* parent,
const character::CharacterConfig& config,
@@ -28,9 +31,19 @@ public:
return actionAnimations_.count(actionName) > 0;
}
std::string DescribeAvailableActions() const;
bool IsCurrentActionFinished() const;
int GetCurrentFrameIndex() const;
int GetCurrentFrameCount() const;
float GetCurrentNormalizedProgress() const;
animation::AniFrame GetCurrentFrameInfo() const;
void SetActionFrameFlagCallback(ActionFrameFlagCallback callback);
void SetActionEndCallback(ActionEndCallback callback);
private:
static std::string FormatImgPath(std::string path, Animation::ReplaceData data);
Animation* GetPrimaryAnimation(const std::string& actionName) const;
Animation* GetCurrentPrimaryAnimation() const;
void RefreshRuntimeCallbacks();
void CreateAnimationBySlot(const std::string& actionName,
const std::string& slotName,
@@ -42,6 +55,8 @@ private:
ActionAnimationList actionAnimations_;
std::string currentActionTag_;
int direction_ = 1;
ActionFrameFlagCallback actionFrameFlagCallback_;
ActionEndCallback actionEndCallback_;
};
} // namespace frostbite2D

View File

@@ -3,6 +3,9 @@
#include "character/CharacterActionTypes.h"
#include <frostbite2D/event/joystick_event.h>
#include <frostbite2D/event/key_event.h>
#include <cstdint>
#include <string>
#include <vector>
namespace frostbite2D {
@@ -19,14 +22,31 @@ public:
void ClearFrameState();
private:
enum class KeyboardMoveDirection {
None,
Left,
Right,
Up,
Down,
};
void QueueAction(const std::string& actionId);
bool IsDirectionPressed(KeyboardMoveDirection direction) const;
bool HasDirectionalInput() const;
void RefreshKeyboardMoveMode();
bool left_ = false;
bool right_ = false;
bool up_ = false;
bool down_ = false;
bool jumpPressed_ = false;
bool attackPressed_ = false;
bool skill1Pressed_ = false;
bool dashPressed_ = false;
std::vector<std::string> actionRequests_;
CharacterMoveMode keyboardMoveMode_ = CharacterMoveMode::None;
KeyboardMoveDirection runDirection_ = KeyboardMoveDirection::None;
std::uint32_t leftLastTapTimestamp_ = 0;
std::uint32_t rightLastTapTimestamp_ = 0;
std::uint32_t upLastTapTimestamp_ = 0;
std::uint32_t downLastTapTimestamp_ = 0;
float leftStickX_ = 0.0f;
float leftStickY_ = 0.0f;
float joystickDeadZone_ = 0.2f;

View File

@@ -97,6 +97,26 @@ public:
CharacterMotor& GetMotorMutable() { return motor_; }
CharacterCommandBuffer& GetCommandBufferMutable() { return commandBuffer_; }
float GetLastDeltaTime() const { return lastDeltaTime_; }
const CharacterRuntimeBlackboard& GetRuntimeBlackboard() const { return runtimeBlackboard_; }
CharacterRuntimeBlackboard& GetRuntimeBlackboardMutable() { return runtimeBlackboard_; }
/// @brief Prepare one-shot data for the next action transition.
///
/// The returned context is consumed by the next successful `EnterAction()`.
CharacterActionContextData& PreparePendingActionContext(
const std::string& requestedActionId,
const std::string& sourceActionId = std::string(),
CharacterStateId sourceStateId = CharacterStateId::Idle);
void ClearPendingActionContext();
bool HasPendingActionContext() const { return pendingActionContext_.valid; }
const CharacterActionContextData* GetPendingActionContext() const;
CharacterActionContextData* GetPendingActionContextMutable();
/// @brief Read transition metadata for the currently running action.
const CharacterActionContextData* GetCurrentActionContext() const;
CharacterActionContextData* GetCurrentActionContextMutable();
bool HasCurrentActionContext() const { return currentActionContext_.valid; }
void ClearCurrentActionContext();
/// @brief 按逻辑动作 id 查询动作定义。
const CharacterActionDefinition* FindAction(const std::string& actionId) const;
@@ -105,13 +125,24 @@ public:
const CharacterActionDefinition* RequireAction(const std::string& actionId,
const char* phase) const;
/// @brief 按动作定义播放动画;缺少 animationTag 或资源不存在时会输出详细错误并退出。
bool PlayActionDefinition(const CharacterActionDefinition& action, const char* phase);
/// @brief 播放某个动画标签。
///
/// 当前只是对 `SetAction()` 的语义包装,方便状态机表达“播放动作标签”。
void PlayAnimationTag(const std::string& actionTag);
bool HasAnimationTag(const std::string& actionTag) const;
/// @brief 查询当前动作主动画是否已经播放完成。
bool IsCurrentAnimationFinished() const;
/// @brief 读取当前动作主动画的帧信息,方便状态类/脚本做运行时判断。
int GetCurrentAnimationFrameIndex() const;
int GetCurrentAnimationFrameCount() const;
float GetCurrentAnimationProgressNormalized() const;
animation::AniFrame GetCurrentAnimationFrameInfo() const;
/// @brief 绑定当前动作主动画的运行时回调,供后续脚本系统接入。
void SetAnimationFrameFlagCallback(CharacterAnimation::ActionFrameFlagCallback callback);
void SetAnimationEndCallback(CharacterAnimation::ActionEndCallback callback);
/// @brief 输出角色动作/动画的致命错误,并请求应用退出。
void ReportFatalCharacterError(const char* phase,
@@ -136,6 +167,9 @@ public:
const CharacterEquipmentManager& GetEquipmentManager() const { return equipmentManager_; }
private:
void CommitPendingActionContext(const std::string& defaultRequestedActionId,
const std::string& defaultSourceActionId,
CharacterStateId defaultSourceStateId);
bool SetActionStrict(const std::string& actionName,
const char* phase,
const std::string& requestedActionId);
@@ -159,7 +193,7 @@ private:
/// 负责拼装装备部件,决定分层 Avatar 的表现资源。
CharacterEquipmentManager equipmentManager_;
/// 动作定义表:把逻辑 actionId 映射到帧数据、取消规则、动画标签等
/// 动作定义表:保存显式注册的逻辑动作入口
CharacterActionLibrary actionLibrary_;
/// 输入适配层,把键盘/手柄事件收敛成统一命令。
@@ -171,7 +205,16 @@ private:
/// 当前帧从命令缓冲整理出的“角色意图”。
CharacterIntent currentIntent_;
/// 状态调度器,负责状态切换、动作进入和帧事件推进。
/// Character-wide shared runtime data for skills, buffs, and future scripts.
CharacterRuntimeBlackboard runtimeBlackboard_;
/// One-shot payload prepared before switching into the next action.
CharacterActionContextData pendingActionContext_;
/// Metadata attached to the action currently running inside Action state.
CharacterActionContextData currentActionContext_;
/// 状态调度器,负责状态切换与动作进入。
CharacterStateMachine stateMachine_;
/// 纯运动数据:位置、速度、落地状态和朝向都在这里维护。
@@ -188,6 +231,8 @@ private:
/// 是否允许从事件系统接收输入AI/剧情接管时可临时关闭。
bool inputEnabled_ = true;
friend class CharacterStateMachine;
};
} // namespace frostbite2D

View File

@@ -47,18 +47,17 @@ private:
const char* phase) const;
bool TryStartAction(CharacterObject& owner,
CharacterCommandBuffer& commandBuffer,
CharacterCommandType commandType,
const std::string& requestedActionId,
const std::string& actionId);
bool StartAction(CharacterObject& owner,
const std::string& requestedActionId,
const std::string& actionId);
bool TryStartRegisteredAction(CharacterStateContext& context);
void ApplyFrameEvents(CharacterObject& owner, int previousFrame, int currentFrame);
CharacterStateId currentState_ = CharacterStateId::Idle;
std::string currentActionId_;
const CharacterActionDefinition* currentAction_ = nullptr;
float stateTime_ = 0.0f;
float actionFrameProgress_ = 0.0f;
int actionFrame_ = 0;
float landingTimer_ = 0.0f;
StateRegistry stateRegistry_;
friend class CharacterStateBase;

View File

@@ -57,21 +57,26 @@ protected:
void ChangeState(CharacterStateMachine& machine,
CharacterStateContext& context,
CharacterStateId nextState) const;
void PlayActionById(CharacterObject& owner, const std::string& actionId) 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 TryStartAction(CharacterStateMachine& machine,
CharacterStateContext& context,
const std::string& requestedActionId,
const std::string& actionId) const;
bool StartAction(CharacterStateMachine& machine,
CharacterStateContext& context,
const std::string& actionId) const;
bool StartAction(CharacterStateMachine& machine,
CharacterStateContext& context,
const std::string& requestedActionId,
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;
@@ -81,9 +86,6 @@ protected:
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;

View File

@@ -0,0 +1,66 @@
#pragma once
#include "character/states/CharacterStateBase.h"
#include <string>
namespace frostbite2D {
struct SwordmanActionUpdateResult {
enum class Type {
Running,
Finished,
Transition,
};
static SwordmanActionUpdateResult Running() {
return {};
}
static SwordmanActionUpdateResult Finished() {
SwordmanActionUpdateResult result;
result.type = Type::Finished;
return result;
}
static SwordmanActionUpdateResult TransitionTo(const std::string& requestedActionId,
bool requireBufferedInput = true) {
SwordmanActionUpdateResult result;
result.type = Type::Transition;
result.requestedActionId = requestedActionId;
result.requireBufferedInput = requireBufferedInput;
return result;
}
Type type = Type::Running;
std::string requestedActionId;
bool requireBufferedInput = true;
};
class ISwordmanActionHandler {
public:
virtual ~ISwordmanActionHandler() = default;
virtual const std::string& GetActionId() const = 0;
virtual void OnEnter(CharacterStateContext& context,
const CharacterActionDefinition& action) = 0;
virtual SwordmanActionUpdateResult OnUpdate(CharacterStateContext& context,
const CharacterActionDefinition& action) = 0;
virtual void OnExit(CharacterStateContext& context,
const CharacterActionDefinition& action) {
(void)context;
(void)action;
}
};
class SwordmanActionHandlerBase : public ISwordmanActionHandler {
public:
explicit SwordmanActionHandlerBase(std::string actionId);
~SwordmanActionHandlerBase() override = default;
const std::string& GetActionId() const override { return actionId_; }
protected:
std::string actionId_;
};
} // namespace frostbite2D

View File

@@ -1,6 +1,10 @@
#pragma once
#include "character/states/CharacterStateBase.h"
#include "character/states/jobs/swordman/SwordmanActionHandler.h"
#include <memory>
#include <string>
#include <unordered_map>
namespace frostbite2D {
@@ -18,6 +22,11 @@ public:
void OnExit(CharacterStateMachine& machine,
CharacterStateContext& context,
CharacterStateId nextState) override;
private:
ISwordmanActionHandler* FindHandler(const std::string& actionId) const;
std::unordered_map<std::string, std::unique_ptr<ISwordmanActionHandler>> handlers_;
};
} // namespace frostbite2D

View File

@@ -0,0 +1,24 @@
#pragma once
#include "character/states/jobs/swordman/SwordmanActionHandler.h"
namespace frostbite2D {
class SwordmanBasicAttackHandler : public SwordmanActionHandlerBase {
public:
SwordmanBasicAttackHandler();
void OnEnter(CharacterStateContext& context,
const CharacterActionDefinition& action) override;
SwordmanActionUpdateResult OnUpdate(CharacterStateContext& context,
const CharacterActionDefinition& action) override;
void OnExit(CharacterStateContext& context,
const CharacterActionDefinition& action) override;
private:
void BeginComboStep(CharacterStateContext& context, int comboStep);
int comboStep_ = 0;
};
} // namespace frostbite2D

View File

@@ -0,0 +1,17 @@
#pragma once
#include "character/states/jobs/swordman/SwordmanActionHandler.h"
namespace frostbite2D {
class SwordmanSkill1Handler : public SwordmanActionHandlerBase {
public:
SwordmanSkill1Handler();
void OnEnter(CharacterStateContext& context,
const CharacterActionDefinition& action) override;
SwordmanActionUpdateResult OnUpdate(CharacterStateContext& context,
const CharacterActionDefinition& action) override;
};
} // namespace frostbite2D