战斗动画不是播放一个 animation_name
Godot 做动作游戏时,最容易的做法是在角色脚本里 animation_player.play("attack")。原型阶段够用,但战斗动作一多,问题会马上出现:移动和攻击怎么混合,受击能不能打断,翻滚何时无敌,命中判定在哪一帧打开,连招输入窗口怎么处理,服务器结果怎么和动画对齐。
Godot 的 AnimationTree 和状态机能帮助组织动画,但动画状态机不应该承载全部战斗逻辑。动画负责表现和时间窗口,战斗逻辑负责规则和结果。两者通过明确事件协作,而不是互相硬调用。
flowchart TD
A[输入/AI 意图] --> B[战斗状态机]
B --> C{动作是否允许?}
C -->|允许| D[设置动画状态]
D --> E[AnimationTree 播放]
E --> F[动画事件: 前摇/命中/收招]
F --> G[战斗判定窗口]
G --> H[命中结果/表现反馈]
C -->|不允许| I[输入缓冲/忽略]
AnimationTree 管表现层状态
AnimationTree 适合处理待机、移动、攻击、受击、闪避、死亡等表现状态,也适合用 BlendSpace 混合移动方向或速度。角色脚本给 AnimationTree 设置参数,AnimationTree 输出平滑动画。这样比手动切 AnimationPlayer 更容易维护。
但不要把所有战斗规则塞进 AnimationTree。比如“体力不足不能翻滚”“沉默状态不能施法”“霸体不受击退”,这些属于战斗状态机。AnimationTree 只表现最终动作。否则策划改规则时,你会在动画图里找逻辑。
表现状态和逻辑状态可以对应,但不必完全相同。逻辑上角色处于 Attack,表现上可能分为 attack_start、attack_loop、attack_end。逻辑状态更粗,动画状态更细。两者通过事件同步。
命中窗口要数据化
动作游戏里,攻击不是整段动画都有判定。通常有前摇、命中窗口、收招。翻滚也有无敌窗口和移动曲线。如果这些窗口写在脚本的定时器里,很容易和动画改动脱节。动画师调整攻击速度后,判定仍在旧时间点。
可以把窗口数据绑定到动画事件或动作配置里:0.18 秒打开 hitbox,0.32 秒关闭,0.45 秒允许连招,0.6 秒进入收招。Godot 可以用动画轨道调用方法,也可以由动作配置按动画时间驱动。关键是数据和动画版本一起维护。
动画事件调用的方法要轻。事件可以通知“命中窗口打开”,但不要在动画里直接计算伤害和发奖励。伤害判定交给战斗系统,动画只是提供时间点。
输入缓冲让手感更自然
玩家按下一段连招时,输入可能发生在上一段收招之前。如果完全忽略,会觉得不跟手;如果任何时候都接受,会破坏动作节奏。输入缓冲可以记录短时间内的下一动作意图,在允许窗口到来时执行。
缓冲窗口应由动作配置控制。轻攻击可能允许较早缓冲,重攻击窗口更短,闪避可能优先级更高。战斗状态机决定缓冲动作是否能转移,AnimationTree 只负责播放。
调试时要显示当前动作、动画时间、可取消窗口、缓冲输入。手感问题往往来自这些窗口差几十毫秒。
受击和打断要有优先级
角色攻击中被击中,是否打断取决于霸体、技能类型、伤害等级、当前阶段。不要简单地任何受击都 play("hit")。战斗状态机应根据规则决定是否切到受击、只播放受击叠加、还是完全忽略。
AnimationTree 可以用叠加层表现轻微受击,比如上半身抖动,而不打断移动。重击再切完整受击状态。这样角色表现更细腻,也不会让控制感被频繁打断。
死亡是最高优先级,但死亡动画也要处理当前状态。投射物命中、击飞、倒地、掉落悬崖可能有不同死亡表现。规则先确定死亡事实,表现层选择合适动画。
网络和回放场景
如果是联网游戏,本地动画不能决定权威结果。客户端可以按输入预测播放攻击,但命中结果来自服务器或本地权威模拟。服务器拒绝时,表现要能校正。动画事件仍可用于本地特效和音效,但伤害数字要等待结果。
回放系统也依赖动画状态可重建。记录动作 ID、开始时间、关键事件,比记录每帧动画名更稳定。重放时用同一套动作配置驱动 AnimationTree。
小结
Godot 战斗动画状态机要把表现和规则拆开。AnimationTree 管混合和动画状态,战斗状态机管动作合法性和打断,窗口数据定义命中、无敌和连招,动画事件只通知时间点。这样动画师能调整表现,程序能维护规则,玩家得到的是既流畅又可信的战斗手感。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
我会为每个动作生成一张调试时间轴:前摇、命中、无敌、可取消、输入缓冲和特效点都画出来。Godot 编辑器里能预览这张时间轴,战斗调参会比在脚本里查秒数直观很多。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。