refactor(character): 重构角色动作与动画系统
- 移除自动回退动作生成逻辑,改为严格检查动作定义 - 增加动作资源缺失时的详细错误报告机制 - 统一输入事件处理接口,优化角色对象生命周期管理 - 改进动画标签管理,移除隐式回退逻辑 - 增强状态机对无效动作的处理能力
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#include <frostbite2D/resource/script_parser.h>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace frostbite2D {
|
||||
@@ -227,9 +228,6 @@ void CharacterCommandBuffer::Clear() {
|
||||
|
||||
bool CharacterActionLibrary::LoadForConfig(const character::CharacterConfig& config) {
|
||||
actions_.clear();
|
||||
// 先填一套最小可运行的默认动作,保证资源或 PVF 缺失时角色仍能工作。
|
||||
BuildFallbackActions(config);
|
||||
// 再尝试用 PVF 动作逻辑覆盖默认值。
|
||||
TryLoadPvfActionScripts(config);
|
||||
return !actions_.empty();
|
||||
}
|
||||
@@ -247,95 +245,22 @@ const CharacterActionDefinition* CharacterActionLibrary::GetDefaultAction() cons
|
||||
return FindAction("idle");
|
||||
}
|
||||
|
||||
void CharacterActionLibrary::BuildFallbackActions(
|
||||
const character::CharacterConfig& config) {
|
||||
auto animationTagOrDefault = [&config](const std::string& actionId,
|
||||
const std::string& fallback) {
|
||||
if (config.animationPath.count(actionId) > 0) {
|
||||
return actionId;
|
||||
std::string CharacterActionLibrary::DescribeActionIds() const {
|
||||
if (actions_.empty()) {
|
||||
return "<none>";
|
||||
}
|
||||
|
||||
std::ostringstream stream;
|
||||
bool first = true;
|
||||
for (const auto& [actionId, definition] : actions_) {
|
||||
(void)definition;
|
||||
if (!first) {
|
||||
stream << ", ";
|
||||
}
|
||||
if (config.animationPath.count(fallback) > 0) {
|
||||
return fallback;
|
||||
}
|
||||
if (!config.animationPath.empty()) {
|
||||
return config.animationPath.begin()->first;
|
||||
}
|
||||
return fallback;
|
||||
};
|
||||
|
||||
CharacterActionDefinition idle;
|
||||
idle.actionId = "idle";
|
||||
idle.animationTag = animationTagOrDefault("rest", "rest");
|
||||
idle.totalFrames = 1;
|
||||
idle.loop = true;
|
||||
idle.canMove = true;
|
||||
addOrReplaceAction(actions_, idle);
|
||||
|
||||
CharacterActionDefinition move;
|
||||
move.actionId = "move";
|
||||
move.animationTag = animationTagOrDefault("run", animationTagOrDefault("walk", "rest"));
|
||||
move.totalFrames = 1;
|
||||
move.loop = true;
|
||||
move.canMove = true;
|
||||
addOrReplaceAction(actions_, move);
|
||||
|
||||
CharacterActionDefinition jump;
|
||||
jump.actionId = "jump";
|
||||
jump.animationTag = animationTagOrDefault("jump", "rest");
|
||||
jump.totalFrames = 18;
|
||||
jump.canTurn = true;
|
||||
addOrReplaceAction(actions_, jump);
|
||||
|
||||
CharacterActionDefinition fall;
|
||||
fall.actionId = "fall";
|
||||
fall.animationTag = animationTagOrDefault("jump", "rest");
|
||||
fall.totalFrames = 1;
|
||||
fall.loop = true;
|
||||
fall.canTurn = true;
|
||||
addOrReplaceAction(actions_, fall);
|
||||
|
||||
CharacterActionDefinition landing;
|
||||
landing.actionId = "landing";
|
||||
landing.animationTag = animationTagOrDefault("rest", "rest");
|
||||
landing.totalFrames = 6;
|
||||
addOrReplaceAction(actions_, landing);
|
||||
|
||||
CharacterActionDefinition attack1;
|
||||
attack1.actionId = "attack_1";
|
||||
attack1.animationTag = animationTagOrDefault("attack1", animationTagOrDefault("attack", "rest"));
|
||||
attack1.totalFrames = 24;
|
||||
attack1.canMove = false;
|
||||
attack1.canTurn = false;
|
||||
attack1.canBeInterrupted = true;
|
||||
attack1.cancelRules.push_back({"attack_2", 12, 22, true, false});
|
||||
addOrReplaceAction(actions_, attack1);
|
||||
|
||||
CharacterActionDefinition attack2;
|
||||
attack2.actionId = "attack_2";
|
||||
attack2.animationTag = animationTagOrDefault("attack2", attack1.animationTag);
|
||||
attack2.totalFrames = 28;
|
||||
attack2.canMove = false;
|
||||
attack2.canTurn = false;
|
||||
attack2.canBeInterrupted = true;
|
||||
addOrReplaceAction(actions_, attack2);
|
||||
|
||||
CharacterActionDefinition skill1;
|
||||
skill1.actionId = "skill_1";
|
||||
skill1.animationTag = animationTagOrDefault("skill1", attack1.animationTag);
|
||||
skill1.totalFrames = 36;
|
||||
skill1.canMove = false;
|
||||
skill1.canTurn = false;
|
||||
skill1.canBeInterrupted = false;
|
||||
addOrReplaceAction(actions_, skill1);
|
||||
|
||||
CharacterActionDefinition hurt;
|
||||
hurt.actionId = "hurt_light";
|
||||
hurt.animationTag = animationTagOrDefault("damage", animationTagOrDefault("hurt", "rest"));
|
||||
hurt.totalFrames = 18;
|
||||
hurt.canMove = false;
|
||||
hurt.canTurn = false;
|
||||
hurt.canBeInterrupted = true;
|
||||
addOrReplaceAction(actions_, hurt);
|
||||
stream << actionId;
|
||||
first = false;
|
||||
}
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
bool CharacterActionLibrary::TryLoadPvfActionScripts(
|
||||
@@ -413,12 +338,6 @@ bool CharacterActionLibrary::TryLoadPvfActionScripts(
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.animationTag.empty()) {
|
||||
// 如果动作脚本没有显式指定动画标签,则尝试用同名动作映射。
|
||||
if (config.animationPath.count(actionId) > 0) {
|
||||
definition.animationTag = actionId;
|
||||
}
|
||||
}
|
||||
addOrReplaceAction(actions_, std::move(definition));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user