Skip to content

生物体视觉与动画

本文介绍一组 mod 可以接入的运行时 Godot 工厂接口(替换原版 CreateVisuals / GenerateAnimator),以及后端无关的动画状态机 ModAnimStateMachine。后者让非 Spine 的战斗视觉通过和 Spine 生物相同的触发协议驱动动画。

概览

原版将 MonsterModel / CharacterModel 绑定到战斗视觉的三个入口:

  • Model.CreateVisuals() — 返回一个 NCreatureVisuals
  • Model.GenerateAnimator(MegaSprite controller) — 返回一个 CreatureAnimator
  • NCreature.SetAnimationTrigger(trigger) — 在运行时把触发器派发给 animator

RitsuLib 为这三种需求暴露了三个彼此正交的工厂接口,以及一个针对非 Spine 场景的状态机抽象。

接口用途对应原版入口
IModCreatureVisualsFactory从代码构造 NCreatureVisualsCharacterModel.CreateVisualsMonsterModel.CreateVisuals
IModCreatureAnimatorFactory从代码构造 Spine CreatureAnimatorCharacterModel.GenerateAnimatorMonsterModel.GenerateAnimator
IModNonSpineAnimationStateMachineFactory为非 Spine 视觉构造 ModAnimStateMachineNCreature.SetAnimationTrigger(路由补丁)
IModCharacterMerchantAnimationStateMachineFactory为商人 / 休息站中的角色视觉构造 ModAnimStateMachine商人场景初始化流程

生物视觉工厂

IModCreatureVisualsFactory 在返回非 null 的 NCreatureVisuals 时,会替换 (Character|Monster)Model.CreateVisuals 的原有行为;返回 null 则退回到 CustomVisualsPath 等原版解析链路。

csharp
public class MyCharacter : ModCharacterTemplate<...>
{
    protected override NCreatureVisuals? TryCreateCreatureVisuals()
    {
        var scene = GD.Load<PackedScene>(
            "res://MyMod/scenes/my_character/my_character_visuals.tscn");
        return scene.Instantiate<NCreatureVisuals>();
    }
}

Spine Animator 工厂

IModCreatureAnimatorFactory 用来替换 GenerateAnimator,适用于 Spine 视觉。推荐使用 ModAnimStateMachines.Standard 以复用原版状态图的形状:

csharp
public class MySpineCharacter : ModCharacterTemplate<...>
{
    protected override CreatureAnimator? SetupCustomCreatureAnimator(MegaSprite controller) =>
        ModAnimStateMachines.Standard(
            controller,
            idleName: "idle_loop",
            deadName: "die",
            hitName: "hit",
            attackName: "attack",
            castName: "cast",
            relaxedName: "relaxed");
}

非 Spine 状态机

如果生物的战斗视觉不是 Spine(没有 MegaSprite 控制器),实现 IModNonSpineAnimationStateMachineFactory 并返回绑定到视觉根节点的 ModAnimStateMachine

csharp
public class MyWolf : ModMonsterTemplate
{
    protected override ModAnimStateMachine? SetupCustomNonSpineAnimationStateMachine(
        Node visualsRoot, MonsterModel monster)
    {
        if (visualsRoot is not MyWolfVisuals wolfVisuals)
            return null;

        var backend = new AnimatedSprite2DBackend(wolfVisuals.GetAnimatedSprite());

        return ModAnimStateMachineBuilder.Create()
            .AddState("idle", loop: true).AsInitial().Done()
            .AddState("attack").WithNext("idle").Done()
            .AddState("hurt").WithNext("idle").Done()
            .AddState("die").Done()
            .AddAnyState("Idle",   "idle")
            .AddAnyState("Attack", "attack")
            .AddAnyState("Hit",    "hurt")
            .AddAnyState("Dead",   "die")
            .Build(backend);
    }
}

动画后端

IAnimationBackendModAnimStateMachine 消费的统一驱动层。

后端驱动对象适用场景
AnimatedSprite2DBackendAnimatedSprite2D基于帧的 sprite 动画
GodotAnimationPlayerBackendAnimationPlayerGodot .tres 动画库
CueAnimationBackendVisualCueSet(cue 帧序列 / cue 贴图)单帧贴图或帧序列播放
SpineAnimationBackendMegaSpriteSpine 骨骼动画
CompositeAnimationBackend任意组合多后端派发

生命周期触发补丁

原版 NCreature.StartDeathAnimNCreature.StartReviveAnim 只在 _spineAnimator != null 时派发 Dead / Revive 触发器。RitsuLib 通过两个 Postfix 补丁修正这一缺陷:

  • NCreatureNonSpineDeathAnimationTriggerPatch — 在 StartDeathAnim 之后派发 Dead
  • NCreatureNonSpineReviveAnimationTriggerPatch — 在 StartReviveAnim 之后派发 Revive

速查表

目标                                        实现的接口
---------------------------------------------------------------------------
替换 CreateVisuals(玩家或怪物)             IModCreatureVisualsFactory
替换 Spine GenerateAnimator                  IModCreatureAnimatorFactory
驱动非 Spine 状态机                          IModNonSpineAnimationStateMachineFactory
驱动商人 / 休息站状态机                       IModCharacterMerchantAnimationStateMachineFactory