feat(animation): 添加动画状态回调支持
refactor(character): 重构角色动作处理逻辑 feat(swordman): 实现剑士基础攻击和技能1处理 refactor(state): 优化状态机与动作上下文管理 feat(input): 改进输入系统支持动作请求队列 refactor(movement): 重构移动系统支持行走/奔跑模式
This commit is contained in:
@@ -1,45 +1,115 @@
|
||||
#include "character/CharacterInputRouter.h"
|
||||
#include <SDL2/SDL_gamecontroller.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace frostbite2D {
|
||||
namespace {
|
||||
|
||||
constexpr std::uint32_t kKeyboardRunDoubleTapWindowMs = 250;
|
||||
constexpr float kAnalogRunThreshold = 0.75f;
|
||||
|
||||
float applyDeadZone(float value, float deadZone) {
|
||||
return std::fabs(value) >= deadZone ? value : 0.0f;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void CharacterInputRouter::QueueAction(const std::string& actionId) {
|
||||
if (actionId.empty()) {
|
||||
return;
|
||||
}
|
||||
if (std::find(actionRequests_.begin(), actionRequests_.end(), actionId)
|
||||
== actionRequests_.end()) {
|
||||
actionRequests_.push_back(actionId);
|
||||
}
|
||||
}
|
||||
|
||||
bool CharacterInputRouter::IsDirectionPressed(KeyboardMoveDirection direction) const {
|
||||
switch (direction) {
|
||||
case KeyboardMoveDirection::Left:
|
||||
return left_;
|
||||
case KeyboardMoveDirection::Right:
|
||||
return right_;
|
||||
case KeyboardMoveDirection::Up:
|
||||
return up_;
|
||||
case KeyboardMoveDirection::Down:
|
||||
return down_;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CharacterInputRouter::HasDirectionalInput() const {
|
||||
return left_ || right_ || up_ || down_;
|
||||
}
|
||||
|
||||
void CharacterInputRouter::RefreshKeyboardMoveMode() {
|
||||
if (!HasDirectionalInput()) {
|
||||
keyboardMoveMode_ = CharacterMoveMode::None;
|
||||
runDirection_ = KeyboardMoveDirection::None;
|
||||
return;
|
||||
}
|
||||
|
||||
if (runDirection_ != KeyboardMoveDirection::None && IsDirectionPressed(runDirection_)) {
|
||||
keyboardMoveMode_ = CharacterMoveMode::Run;
|
||||
return;
|
||||
}
|
||||
|
||||
runDirection_ = KeyboardMoveDirection::None;
|
||||
keyboardMoveMode_ = CharacterMoveMode::Walk;
|
||||
}
|
||||
|
||||
bool CharacterInputRouter::OnKeyDown(const KeyEvent& event) {
|
||||
auto handleDirectionPress = [&](KeyboardMoveDirection direction,
|
||||
bool& pressedFlag,
|
||||
std::uint32_t& lastTapTimestamp) {
|
||||
pressedFlag = true;
|
||||
std::uint32_t now = event.getTimestamp();
|
||||
bool isDoubleTap = lastTapTimestamp > 0
|
||||
&& now >= lastTapTimestamp
|
||||
&& now - lastTapTimestamp <= kKeyboardRunDoubleTapWindowMs;
|
||||
lastTapTimestamp = now;
|
||||
|
||||
if (isDoubleTap) {
|
||||
keyboardMoveMode_ = CharacterMoveMode::Run;
|
||||
runDirection_ = direction;
|
||||
} else if (keyboardMoveMode_ != CharacterMoveMode::Run
|
||||
|| !IsDirectionPressed(runDirection_)) {
|
||||
keyboardMoveMode_ = CharacterMoveMode::Walk;
|
||||
runDirection_ = KeyboardMoveDirection::None;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
switch (event.getKeyCode()) {
|
||||
case KeyCode::a:
|
||||
case KeyCode::Left:
|
||||
left_ = true;
|
||||
return true;
|
||||
return handleDirectionPress(
|
||||
KeyboardMoveDirection::Left, left_, leftLastTapTimestamp_);
|
||||
case KeyCode::d:
|
||||
case KeyCode::Right:
|
||||
right_ = true;
|
||||
return true;
|
||||
return handleDirectionPress(
|
||||
KeyboardMoveDirection::Right, right_, rightLastTapTimestamp_);
|
||||
case KeyCode::w:
|
||||
case KeyCode::Up:
|
||||
up_ = true;
|
||||
return true;
|
||||
return handleDirectionPress(
|
||||
KeyboardMoveDirection::Up, up_, upLastTapTimestamp_);
|
||||
case KeyCode::s:
|
||||
case KeyCode::Down:
|
||||
down_ = true;
|
||||
return handleDirectionPress(
|
||||
KeyboardMoveDirection::Down, down_, downLastTapTimestamp_);
|
||||
case KeyCode::x:
|
||||
QueueAction("attack");
|
||||
return true;
|
||||
case KeyCode::j:
|
||||
attackPressed_ = true;
|
||||
return true;
|
||||
case KeyCode::k:
|
||||
case KeyCode::c:
|
||||
jumpPressed_ = true;
|
||||
return true;
|
||||
case KeyCode::l:
|
||||
skill1Pressed_ = true;
|
||||
case KeyCode::z:
|
||||
QueueAction("skill_1");
|
||||
return true;
|
||||
case KeyCode::LShift:
|
||||
dashPressed_ = true;
|
||||
QueueAction("dash");
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -47,23 +117,25 @@ bool CharacterInputRouter::OnKeyDown(const KeyEvent& event) {
|
||||
}
|
||||
|
||||
bool CharacterInputRouter::OnKeyUp(const KeyUpEvent& event) {
|
||||
auto handleDirectionRelease = [&](bool& pressedFlag) {
|
||||
pressedFlag = false;
|
||||
RefreshKeyboardMoveMode();
|
||||
return true;
|
||||
};
|
||||
|
||||
switch (event.getKeyCode()) {
|
||||
case KeyCode::a:
|
||||
case KeyCode::Left:
|
||||
left_ = false;
|
||||
return true;
|
||||
return handleDirectionRelease(left_);
|
||||
case KeyCode::d:
|
||||
case KeyCode::Right:
|
||||
right_ = false;
|
||||
return true;
|
||||
return handleDirectionRelease(right_);
|
||||
case KeyCode::w:
|
||||
case KeyCode::Up:
|
||||
up_ = false;
|
||||
return true;
|
||||
return handleDirectionRelease(up_);
|
||||
case KeyCode::s:
|
||||
case KeyCode::Down:
|
||||
down_ = false;
|
||||
return true;
|
||||
return handleDirectionRelease(down_);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -88,14 +160,14 @@ bool CharacterInputRouter::OnJoystickButtonDown(
|
||||
jumpPressed_ = true;
|
||||
return true;
|
||||
case SDL_CONTROLLER_BUTTON_X:
|
||||
attackPressed_ = true;
|
||||
QueueAction("attack");
|
||||
return true;
|
||||
case SDL_CONTROLLER_BUTTON_B:
|
||||
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
|
||||
skill1Pressed_ = true;
|
||||
QueueAction("skill_1");
|
||||
return true;
|
||||
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
|
||||
dashPressed_ = true;
|
||||
QueueAction("dash");
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -110,23 +182,41 @@ bool CharacterInputRouter::OnJoystickButtonUp(
|
||||
|
||||
void CharacterInputRouter::EmitCommands(CharacterCommandBuffer& commandBuffer) {
|
||||
Vec2 moveAxis(leftStickX_, leftStickY_);
|
||||
if (moveAxis.lengthSquared() < 0.0001f) {
|
||||
CharacterMoveMode moveMode = CharacterMoveMode::None;
|
||||
float stickMagnitudeSquared = moveAxis.lengthSquared();
|
||||
if (stickMagnitudeSquared >= joystickDeadZone_ * joystickDeadZone_) {
|
||||
float stickMagnitude = std::sqrt(stickMagnitudeSquared);
|
||||
if (stickMagnitude > 1.0f) {
|
||||
moveAxis = moveAxis.normalized();
|
||||
stickMagnitude = 1.0f;
|
||||
}
|
||||
moveMode = stickMagnitude >= kAnalogRunThreshold
|
||||
? CharacterMoveMode::Run
|
||||
: CharacterMoveMode::Walk;
|
||||
} else {
|
||||
moveAxis.x = (right_ ? 1.0f : 0.0f) - (left_ ? 1.0f : 0.0f);
|
||||
moveAxis.y = (down_ ? 1.0f : 0.0f) - (up_ ? 1.0f : 0.0f);
|
||||
if (moveAxis.lengthSquared() > 1.0f) {
|
||||
moveAxis = moveAxis.normalized();
|
||||
}
|
||||
if (moveAxis.lengthSquared() >= 0.0001f) {
|
||||
moveMode = keyboardMoveMode_ == CharacterMoveMode::Run
|
||||
? CharacterMoveMode::Run
|
||||
: CharacterMoveMode::Walk;
|
||||
}
|
||||
}
|
||||
commandBuffer.Submit({CharacterCommandType::Move, moveAxis, 0.0f});
|
||||
commandBuffer.Submit(
|
||||
{CharacterCommandType::Move, moveAxis, 0.0f, moveMode, std::string()});
|
||||
|
||||
if (jumpPressed_) {
|
||||
commandBuffer.Submit({CharacterCommandType::JumpPressed, Vec2::Zero(), 0.0f});
|
||||
commandBuffer.Submit(
|
||||
{CharacterCommandType::JumpPressed, Vec2::Zero(), 0.0f,
|
||||
CharacterMoveMode::None, std::string()});
|
||||
}
|
||||
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});
|
||||
for (const auto& actionId : actionRequests_) {
|
||||
commandBuffer.Submit(
|
||||
{CharacterCommandType::ActionPressed, Vec2::Zero(), 0.0f,
|
||||
CharacterMoveMode::None, actionId});
|
||||
}
|
||||
|
||||
ClearFrameState();
|
||||
@@ -134,9 +224,7 @@ void CharacterInputRouter::EmitCommands(CharacterCommandBuffer& commandBuffer) {
|
||||
|
||||
void CharacterInputRouter::ClearFrameState() {
|
||||
jumpPressed_ = false;
|
||||
attackPressed_ = false;
|
||||
skill1Pressed_ = false;
|
||||
dashPressed_ = false;
|
||||
actionRequests_.clear();
|
||||
}
|
||||
|
||||
} // namespace frostbite2D
|
||||
|
||||
Reference in New Issue
Block a user