Files
Frostbite2D/Game/src/character/CharacterObject.cpp
Lenheart de522a1e64 refactor(animation): 重构动画方向处理逻辑
- 将翻转逻辑集中到Animation类中处理
- 添加spriteFrameOffsets_存储帧偏移量
- 改进角色动画方向切换时的表现
- 移除CharacterAnimation中的ApplyFlipRecursive方法
- 优化动画帧位置和旋转的计算方式
2026-04-03 09:13:50 +08:00

181 lines
5.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "character/CharacterObject.h"
#include <SDL2/SDL.h>
namespace frostbite2D {
bool CharacterObject::Construction(int jobId) {
EnableEventReceive();
RemoveAllChildren();
animationManager_ = nullptr;
config_.reset();
currentAction_.clear();
actionLibrary_ = CharacterActionLibrary();
commandBuffer_.Clear();
currentIntent_ = CharacterIntent();
stateMachine_.Reset();
motor_ = CharacterMotor();
status_ = CharacterStatus();
lastDeltaTime_ = 0.0f;
auto config = character::loadCharacterConfig(jobId);
if (!config) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"CharacterObject: failed to load job %d config", jobId);
return false;
}
jobId_ = jobId;
growType_ = -1;
direction_ = 1;
config_ = *config;
equipmentManager_.Init(config_->baseJobConfig);
if (auto actionLibrary = loadCharacterActionLibrary(*config_)) {
actionLibrary_ = *actionLibrary;
}
animationManager_ = MakePtr<CharacterAnimation>();
if (!animationManager_->Init(this, *config_, equipmentManager_)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"CharacterObject: no layered avatar animations available, character will stay empty");
}
AddChild(animationManager_);
if (const auto* idleAction = actionLibrary_.GetDefaultAction()) {
PlayAnimationTag(idleAction->animationTag);
} else if (config_->animationPath.count("rest") > 0) {
SetAction("rest");
} else if (!config_->animationPath.empty()) {
SetAction(config_->animationPath.begin()->first);
}
SetWorldPosition({});
SetFacing(1);
return true;
}
void CharacterObject::SetAction(const std::string& actionName) {
currentAction_ = actionName;
if (animationManager_) {
animationManager_->SetAction(actionName);
}
}
void CharacterObject::SetDirection(int direction) {
direction_ = direction >= 0 ? 1 : -1;
if (animationManager_) {
animationManager_->SetDirection(direction_);
}
}
void CharacterObject::SetCharacterPosition(const Vec2& pos) {
motor_.SetGroundPosition(pos);
SetWorldPosition(motor_.position);
}
void CharacterObject::SetWorldPosition(const CharacterWorldPosition& pos) {
motor_.position = pos;
// 逻辑世界坐标是 x/y/z真正渲染时再投影到屏幕坐标。
SetPosition(pos.ToScreenPosition());
// 地面 y 继续决定同层对象前后遮挡顺序。
SetZOrder(static_cast<int>(pos.y));
}
void CharacterObject::PushCommand(const CharacterCommand& command) {
commandBuffer_.Submit(command);
}
void CharacterObject::ApplyHit(const HitContext& hit) {
if (status_.invincible) {
return;
}
if (status_.superArmor && !hit.ignoreInterrupt) {
return;
}
if (!hit.ignoreInterrupt && !stateMachine_.CanBeInterrupted()) {
return;
}
const CharacterActionDefinition* hurtAction = FindAction(hit.hurtAction);
if (!hurtAction) {
hurtAction = FindAction("hurt_light");
}
motor_.verticalVelocity = hit.launchZVelocity;
if (hit.launchZVelocity > 0.0f) {
motor_.grounded = false;
}
stateMachine_.ForceHurt(hurtAction);
PlayAnimationTag(hurtAction ? hurtAction->animationTag
: ResolveAnimationTag("hurt_light", "rest"));
}
void CharacterObject::OnUpdate(float deltaTime) {
// 角色主循环顺序固定为:
// 输入采样 -> 缓冲推进 -> 意图生成 -> 状态机决策 -> motor 推进 -> 投影到屏幕。
lastDeltaTime_ = deltaTime;
commandBuffer_.Advance(deltaTime);
inputRouter_.EmitCommands(commandBuffer_);
currentIntent_ = commandBuffer_.BuildIntent();
stateMachine_.Update(*this, commandBuffer_, currentIntent_, deltaTime);
motor_.Update(deltaTime);
SetWorldPosition(motor_.position);
SetFacing(motor_.facing);
}
bool CharacterObject::OnKeyDown(const KeyEvent& event) {
if (event.isRepeat()) {
return false;
}
return inputRouter_.OnKeyDown(event);
}
bool CharacterObject::OnKeyUp(const KeyEvent& event) {
return inputRouter_.OnKeyUp(event.getKeyCode());
}
bool CharacterObject::OnJoystickAxis(const JoystickEvent& event) {
return inputRouter_.OnJoystickAxis(static_cast<const JoystickAxisEvent&>(event));
}
bool CharacterObject::OnJoystickButtonDown(const JoystickEvent& event) {
return inputRouter_.OnJoystickButtonDown(
static_cast<const JoystickButtonDownEvent&>(event));
}
bool CharacterObject::OnJoystickButtonUp(const JoystickEvent& event) {
return inputRouter_.OnJoystickButtonUp(
static_cast<const JoystickButtonUpEvent&>(event));
}
const CharacterActionDefinition* CharacterObject::FindAction(
const std::string& actionId) const {
return actionLibrary_.FindAction(actionId);
}
std::string CharacterObject::ResolveAnimationTag(
const std::string& preferred,
const std::string& fallback) const {
// 逻辑动作与资源动作是两层名字:优先选动作定义里的标签,不存在再回退。
if (animationManager_ && animationManager_->HasAction(preferred)) {
return preferred;
}
if (animationManager_ && animationManager_->HasAction(fallback)) {
return fallback;
}
if (config_ && !config_->animationPath.empty()) {
return config_->animationPath.begin()->first;
}
return preferred;
}
void CharacterObject::PlayAnimationTag(const std::string& actionTag) {
SetAction(actionTag);
}
void CharacterObject::SetFacing(int direction) {
motor_.facing = direction >= 0 ? 1 : -1;
SetDirection(motor_.facing);
}
} // namespace frostbite2D