Skip to content

动画资源制作

ONI 的动画资源最后会变成 xxx_kanim,代码里用 Assets.GetAnim("xxx_kanim") 读取。建筑、物品、植物、生物、特效都吃这一套,只是需要的动画状态不一样。

这一章先讲 Mod 里最常用的资源组织和代码调用,不展开讲每个动画工具的按钮。

文件放哪

最常见的做法是把动画工具导出的资源放进 Mod 的 anim/assets 目录。目录通常长这样:

text
MyMod/
├── anim/
│   └── assets/
│       └── MyMachine/
│           ├── MyMachine_anim.bytes
│           ├── MyMachine_build.bytes
│           └── MyMachine.png
└── mod.yaml

一个建筑 Mod 的输出结构可以整理成这样:

text
MyBuildingMod/
├── anim/
│   └── assets/
│       └── MyBuilding/
│           ├── MyBuilding_anim.bytes
│           ├── MyBuilding_build.bytes
│           └── MyBuilding.png
├── MyBuildingMod.dll
├── mod_info.yaml
└── mod.yaml

游戏加载后,这组资源对应的动画名通常是 MyBuilding_kanim,所以代码里这样写:

csharp
Assets.GetAnim("MyBuilding_kanim")

植物、种子、果实也是同样思路,只是每个实体一套资源目录。示例:

text
MyPlantMod/
└── anim/
    └── assets/
        ├── MyPlant/
        │   ├── MyPlant_anim.bytes
        │   ├── MyPlant_build.bytes
        │   └── MyPlant.png
        ├── MyPlantFruit/
        │   ├── MyPlantFruit_anim.bytes
        │   ├── MyPlantFruit_build.bytes
        │   └── MyPlantFruit.png
        └── MyPlantSeed/
            ├── MyPlantSeed_anim.bytes
            ├── MyPlantSeed_build.bytes
            └── MyPlantSeed.png

代码里写的是动画资源名,不是文件路径,也不要写 .png.bytes 这类扩展名:

csharp
Assets.GetAnim("my_machine_kanim")

如果 Assets.GetAnim("my_machine_kanim") 返回 null,说明资源没有被游戏加载到。先检查导出文件是否真的进了 Mod 输出目录,再检查资源名是不是和代码里的字符串一致。

元素材质用的动画资源有时会放在 anim/elements,例如:

text
MyElementMod/
└── anim/
    ├── assets/
    │   └── MyElement/
    │       ├── MyElement_anim.bytes
    │       ├── MyElement_build.bytes
    │       └── MyElement.png
    └── elements/
        └── MyElement/
            ├── MyElementSubstance_anim.bytes
            ├── MyElementSubstance_build.bytes
            └── MyElementSubstance.png

普通建筑、物品、植物先放 anim/assets 就够了;只有在做元素 Substance 显示时,再考虑 anim/elements

建筑动画状态

建筑最常用的状态:

  • off:关闭。
  • on:待机或开启。
  • working_pre:开始工作。
  • working_loop:工作循环。
  • working_pst:工作结束。

建筑配置里要填默认状态:

csharp
BuildingDef def = BuildingTemplates.CreateBuildingDef(
    ID,
    1,
    1,
    "my_machine_kanim",
    30,
    60f,
    BUILDINGS.CONSTRUCTION_MASS_KG.TIER2,
    MATERIALS.REFINED_METALS,
    1600f,
    BuildLocationRule.OnFloor,
    DECOR.NONE,
    NOISE_POLLUTION.NONE);

def.DefaultAnimState = "off";

如果接了状态机,就在状态里播放:

csharp
off.PlayAnim("off");
working.PlayAnim("working_loop", KAnim.PlayMode.Loop);
finish.PlayAnim("working_pst").OnAnimQueueComplete(idle);

物品动画状态

掉落物通常只要一个 object

csharp
EntityTemplates.CreateLooseEntity(
    ID,
    name,
    desc,
    1f,
    false,
    Assets.GetAnim("my_item_kanim"),
    "object",
    Grid.SceneLayer.Front,
    EntityTemplates.CollisionShape.RECTANGLE,
    0.6f,
    0.6f,
    true,
    0,
    SimHashes.Creature);

如果临时借用植物或建筑动画测试,状态名可能不是 object。这时先用资源查看工具确认动画里到底有哪些状态。

植物动画状态

植物常见状态更多:

  • idle_empty:没结果。
  • idle_full:成熟。
  • grow:生长过程。
  • grow_pst:进入成熟。
  • harvest:收获。
  • wilting 或分级枯萎动画:枯萎。
  • place:种植预览。

注册预览时会用到 place

csharp
EntityTemplates.CreateAndRegisterPreviewForPlant(
    seed,
    "MyPlant_preview",
    Assets.GetAnim("my_plant_kanim"),
    "place",
    1,
    2);

植物动画名写错时,最常见表现是种植预览空白,或生长到某个阶段突然消失。

特效动画

特效可以用 FXHelpers.CreateEffect()

csharp
KBatchedAnimController fx = FXHelpers.CreateEffect(
    "upgrade_fx_kanim",
    Grid.CellToPosCCC(cell, Grid.SceneLayer.FXFront),
    null,
    false,
    Grid.SceneLayer.FXFront,
    false);

fx.destroyOnAnimComplete = true;
fx.Play("idle", KAnim.PlayMode.Once);

如果是循环特效,记得自己安排销毁:

csharp
fx.destroyOnAnimComplete = false;
fx.Play("loop", KAnim.PlayMode.Loop);
GameScheduler.Instance.Schedule("DestroyFx", 3f, _ => Object.Destroy(fx.gameObject));

符号和挂点

复杂建筑经常会把某个部件拆成单独对象,比如机械臂、炮口、转盘。可以通过符号找位置:

csharp
KBatchedAnimController controller = GetComponent<KBatchedAnimController>();
Vector3 pos = controller.GetSymbolTransform(new HashedString("gun_target"), out _).GetColumn(3);

额外的机械臂、炮口或指示灯,都可以用这种方式挂到建筑符号上。

符号名错了,位置会非常怪。做这种动画前,先把关键符号命名记下来。

调试方式

最直接的调试代码:

csharp
KAnimFile anim = Assets.GetAnim("my_machine_kanim");
if (anim == null)
{
    Debug.LogError("missing anim: my_machine_kanim");
}

对象生成后也可以临时强制播放:

csharp
GetComponent<KBatchedAnimController>()?.Play("off");

如果 Assets.GetAnim() 是 null,先看资源有没有打包。不是 null 但画面空白,再看动画状态名、图层和缩放。

命名建议

  • 建筑:my_machine_kanim,状态用 off/on/working_*
  • 物品:my_item_kanim,状态用 object
  • 植物:my_plant_kanim,状态用 idle_empty/idle_full/place
  • 特效:my_fx_kanim,状态用 idle/loop/pst

名字朴素一点反而好维护。资源名、代码 ID、文件名最好能一眼对应。