refactor(animation): 重构动画方向处理逻辑
- 将翻转逻辑集中到Animation类中处理 - 添加spriteFrameOffsets_存储帧偏移量 - 改进角色动画方向切换时的表现 - 移除CharacterAnimation中的ApplyFlipRecursive方法 - 优化动画帧位置和旋转的计算方式
This commit is contained in:
@@ -50,14 +50,6 @@ std::string trim(const std::string& value) {
|
||||
return value.substr(begin, end - begin);
|
||||
}
|
||||
|
||||
std::string trimBackticks(const std::string& value) {
|
||||
std::string result = trim(value);
|
||||
if (result.size() >= 2 && result.front() == '`' && result.back() == '`') {
|
||||
return result.substr(1, result.size() - 2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string fileStem(const std::string& path) {
|
||||
size_t slashPos = path.find_last_of("/\\");
|
||||
size_t begin = slashPos == std::string::npos ? 0 : slashPos + 1;
|
||||
@@ -108,10 +100,18 @@ private:
|
||||
// action_list.lst 同时兼容两种格式:
|
||||
// 1. 标准 PVF #PVF_File + 索引 + `path`
|
||||
// 2. 早期临时版的 actionId + path 成对写法
|
||||
bool isNewActionListPath(const std::string& path) {
|
||||
return path.find("action_logic") != std::string::npos
|
||||
&& path.size() >= 4
|
||||
&& path.substr(path.size() - 4) == ".act";
|
||||
}
|
||||
|
||||
// action_list.lst only accepts #PVF_File entries like `swordman/action_logic/idle.act`.
|
||||
std::vector<std::pair<std::string, std::string>> parseActionListEntries(
|
||||
ScriptTokenStream& listStream) {
|
||||
std::vector<std::pair<std::string, std::string>> entries;
|
||||
std::vector<std::string> tokens;
|
||||
auto& pvf = PvfArchive::get();
|
||||
while (!listStream.IsEnd()) {
|
||||
std::string token = trim(listStream.Next());
|
||||
if (!token.empty()) {
|
||||
@@ -119,23 +119,13 @@ std::vector<std::pair<std::string, std::string>> parseActionListEntries(
|
||||
}
|
||||
}
|
||||
|
||||
if (tokens.empty()) {
|
||||
return entries;
|
||||
}
|
||||
|
||||
if (tokens.front() == "#PVF_File") {
|
||||
for (size_t i = 1; i + 1 < tokens.size(); i += 2) {
|
||||
std::string actionPath = trimBackticks(tokens[i + 1]);
|
||||
if (actionPath.empty()) {
|
||||
continue;
|
||||
}
|
||||
entries.push_back({toLower(fileStem(actionPath)), actionPath});
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i + 1 < tokens.size(); i += 2) {
|
||||
entries.push_back({toLower(tokens[i]), trimBackticks(tokens[i + 1])});
|
||||
std::string rawActionPath = trim(tokens[i + 1]);
|
||||
std::string actionPath = pvf.normalizePath(rawActionPath);
|
||||
if (actionPath.empty() || !isNewActionListPath(actionPath)) {
|
||||
continue;
|
||||
}
|
||||
entries.push_back({toLower(fileStem(actionPath)), actionPath});
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
@@ -357,13 +347,20 @@ bool CharacterActionLibrary::TryLoadPvfActionScripts(
|
||||
}
|
||||
|
||||
auto entries = parseActionListEntries(listStream);
|
||||
if (entries.empty()) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"CharacterActionLibrary: action_list parsed 0 entries from %s",
|
||||
listPath.c_str());
|
||||
}
|
||||
for (const auto& [actionId, relativePath] : entries) {
|
||||
if (actionId.empty() || relativePath.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ScriptTokenStream actionStream(
|
||||
"character/" + config.jobTag + "/action_logic/" + relativePath);
|
||||
std::string fullActPath = relativePath.rfind("character/", 0) == 0
|
||||
? relativePath
|
||||
: "character/" + relativePath;
|
||||
ScriptTokenStream actionStream(fullActPath);
|
||||
if (!actionStream.IsValid()) {
|
||||
continue;
|
||||
}
|
||||
@@ -377,9 +374,9 @@ bool CharacterActionLibrary::TryLoadPvfActionScripts(
|
||||
}
|
||||
|
||||
if (token == "[action id]") {
|
||||
definition.actionId = toLower(actionStream.Next());
|
||||
definition.actionId = toLower(trim(actionStream.Next()));
|
||||
} else if (token == "[animation tag]") {
|
||||
definition.animationTag = actionStream.Next();
|
||||
definition.animationTag = trim(actionStream.Next());
|
||||
} else if (token == "[total frames]") {
|
||||
definition.totalFrames = toInt(actionStream.Next(), definition.totalFrames);
|
||||
} else if (token == "[loop]") {
|
||||
@@ -394,7 +391,7 @@ bool CharacterActionLibrary::TryLoadPvfActionScripts(
|
||||
definition.moveSpeedScale = toFloat(actionStream.Next(), definition.moveSpeedScale);
|
||||
} else if (token == "[cancel rule]") {
|
||||
CharacterCancelRuleDefinition rule;
|
||||
rule.targetAction = toLower(actionStream.Next());
|
||||
rule.targetAction = toLower(trim(actionStream.Next()));
|
||||
rule.beginFrame = toInt(actionStream.Next(), 0);
|
||||
rule.endFrame = toInt(actionStream.Next(), rule.beginFrame);
|
||||
rule.requireGrounded = isTrueToken(actionStream.Next());
|
||||
@@ -403,14 +400,14 @@ bool CharacterActionLibrary::TryLoadPvfActionScripts(
|
||||
} else if (token == "[frame event]") {
|
||||
CharacterFrameEventDefinition frameEvent;
|
||||
frameEvent.frame = toInt(actionStream.Next(), 0);
|
||||
frameEvent.type = parseFrameEventType(actionStream.Next());
|
||||
frameEvent.type = parseFrameEventType(trim(actionStream.Next()));
|
||||
if (frameEvent.type == CharacterFrameEventType::SetVelocityXY) {
|
||||
frameEvent.velocityXY.x = toFloat(actionStream.Next(), 0.0f);
|
||||
frameEvent.velocityXY.y = toFloat(actionStream.Next(), 0.0f);
|
||||
} else if (frameEvent.type == CharacterFrameEventType::SetVelocityZ) {
|
||||
frameEvent.velocityZ = toFloat(actionStream.Next(), 0.0f);
|
||||
} else {
|
||||
frameEvent.stringValue = actionStream.Next();
|
||||
frameEvent.stringValue = trim(actionStream.Next());
|
||||
}
|
||||
definition.frameEvents.push_back(frameEvent);
|
||||
}
|
||||
@@ -425,8 +422,6 @@ bool CharacterActionLibrary::TryLoadPvfActionScripts(
|
||||
addOrReplaceAction(actions_, std::move(definition));
|
||||
}
|
||||
|
||||
SDL_Log("CharacterActionLibrary: loaded %d actions for job %d",
|
||||
static_cast<int>(actions_.size()), config.jobId);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user