feat(character): 实现角色状态机与输入路由系统

新增角色状态机框架,包含空闲、移动、跳跃、攻击等状态
添加输入路由组件,统一处理键盘和手柄输入
引入动作库管理角色动作定义,支持PVF脚本配置
重构角色对象,整合运动器、状态机和输入处理
修复Actor子节点排序时的迭代安全问题
This commit is contained in:
2026-04-03 08:08:23 +08:00
parent b5c432e77a
commit bb75a57afb
14 changed files with 1463 additions and 6 deletions

View File

@@ -0,0 +1,142 @@
#include "character/CharacterInputRouter.h"
#include <SDL2/SDL_gamecontroller.h>
#include <cmath>
namespace frostbite2D {
namespace {
float applyDeadZone(float value, float deadZone) {
return std::fabs(value) >= deadZone ? value : 0.0f;
}
} // namespace
bool CharacterInputRouter::OnKeyDown(const KeyEvent& event) {
switch (event.getKeyCode()) {
case KeyCode::a:
case KeyCode::Left:
left_ = true;
return true;
case KeyCode::d:
case KeyCode::Right:
right_ = true;
return true;
case KeyCode::w:
case KeyCode::Up:
up_ = true;
return true;
case KeyCode::s:
case KeyCode::Down:
down_ = true;
return true;
case KeyCode::j:
attackPressed_ = true;
return true;
case KeyCode::k:
jumpPressed_ = true;
return true;
case KeyCode::l:
skill1Pressed_ = true;
return true;
case KeyCode::LShift:
dashPressed_ = true;
return true;
default:
return false;
}
}
bool CharacterInputRouter::OnKeyUp(KeyCode keyCode) {
switch (keyCode) {
case KeyCode::a:
case KeyCode::Left:
left_ = false;
return true;
case KeyCode::d:
case KeyCode::Right:
right_ = false;
return true;
case KeyCode::w:
case KeyCode::Up:
up_ = false;
return true;
case KeyCode::s:
case KeyCode::Down:
down_ = false;
return true;
default:
return false;
}
}
bool CharacterInputRouter::OnJoystickAxis(const JoystickAxisEvent& event) {
if (event.getAxis() == SDL_CONTROLLER_AXIS_LEFTX) {
leftStickX_ = applyDeadZone(event.getNormalizedValue(), joystickDeadZone_);
return true;
}
if (event.getAxis() == SDL_CONTROLLER_AXIS_LEFTY) {
leftStickY_ = applyDeadZone(event.getNormalizedValue(), joystickDeadZone_);
return true;
}
return false;
}
bool CharacterInputRouter::OnJoystickButtonDown(
const JoystickButtonDownEvent& event) {
switch (event.getButton()) {
case SDL_CONTROLLER_BUTTON_A:
jumpPressed_ = true;
return true;
case SDL_CONTROLLER_BUTTON_X:
attackPressed_ = true;
return true;
case SDL_CONTROLLER_BUTTON_B:
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
skill1Pressed_ = true;
return true;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
dashPressed_ = true;
return true;
default:
return false;
}
}
bool CharacterInputRouter::OnJoystickButtonUp(
const JoystickButtonUpEvent& event) {
(void)event;
return false;
}
void CharacterInputRouter::EmitCommands(CharacterCommandBuffer& commandBuffer) {
Vec2 moveAxis(leftStickX_, leftStickY_);
if (moveAxis.lengthSquared() < 0.0001f) {
moveAxis.x = (right_ ? 1.0f : 0.0f) - (left_ ? 1.0f : 0.0f);
moveAxis.y = (down_ ? 1.0f : 0.0f) - (up_ ? 1.0f : 0.0f);
}
commandBuffer.Submit({CharacterCommandType::Move, moveAxis, 0.0f});
if (jumpPressed_) {
commandBuffer.Submit({CharacterCommandType::JumpPressed, Vec2::Zero(), 0.0f});
}
if (attackPressed_) {
commandBuffer.Submit({CharacterCommandType::AttackPressed, Vec2::Zero(), 0.0f});
}
if (skill1Pressed_) {
commandBuffer.Submit({CharacterCommandType::Skill1Pressed, Vec2::Zero(), 0.0f});
}
if (dashPressed_) {
commandBuffer.Submit({CharacterCommandType::DashPressed, Vec2::Zero(), 0.0f});
}
ClearFrameState();
}
void CharacterInputRouter::ClearFrameState() {
jumpPressed_ = false;
attackPressed_ = false;
skill1Pressed_ = false;
dashPressed_ = false;
}
} // namespace frostbite2D