Skip to content

新增生物

新增生物是内容扩展里最复杂的一类。它不只是一个会动的实体,还涉及导航、AI、大脑、年龄、繁殖、捕捉、掉落物、动画状态机和牧场系统。

如果这是你第一次写生物,建议不要从零造一个完整生物。更稳的路线是:复制一个原版生物的配置,再逐步改外观、数值和掉落物。这样可以先复用原版已经跑通的 AI 和状态机。

推荐路线

新增生物可以分三步做:

  1. 先做一个“克隆生物”,能生成、能移动、能保存读取。
  2. 再改温度、食物、掉落物、繁殖周期等数值。
  3. 最后再写自定义行为,比如特殊攻击、特殊产物、范围效果。

不要一开始就同时改 AI、动画、繁殖和掉落。生物系统报错时,日志通常很长,拆小一点会好查很多。

文件结构

text
MyFirstCreature/
├── MyFirstCreatureConfig.cs
├── MyFirstCreaturePatches.cs
├── MyFirstCreature.cs
└── STRINGS.cs

Config 负责实体,Patches 负责把它挂进世界生成或调试生成,MyFirstCreature 是你自己的行为组件。

从克隆开始

很多时候不需要手写完整生物模板。可以先在游戏加载后拿到原版预制体,复制一份出来,再改 ID、名称和少量组件。

csharp
using HarmonyLib;
using UnityEngine;

namespace MyFirstCreature
{
    [HarmonyPatch(typeof(Assets), "OnPrefabInit")]
    public static class CreatureClonePatch
    {
        public static void Postfix()
        {
            GameObject source = Assets.GetPrefab("Drecko");
            if (source == null)
            {
                Debug.LogError("[MyFirstCreature] Drecko prefab not found");
                return;
            }

            GameObject clone = Object.Instantiate(source);
            clone.name = MyFirstCreatureConfig.ID;

            KPrefabID prefabId = clone.GetComponent<KPrefabID>();
            prefabId.PrefabTag = TagManager.Create(MyFirstCreatureConfig.ID);

            clone.AddOrGet<MyFirstCreature>();
            Assets.AddPrefab(clone);
        }
    }
}

这种写法适合做原版生物的变体。比如“新的毛鳞壁虎”“新的喷浮飞鱼”,大部分行为沿用原版,只改少量数值。

自定义组件

先把自己的逻辑放进组件里,不要直接塞在 Patch 里:

csharp
using UnityEngine;

namespace MyFirstCreature
{
    public class MyFirstCreature : KMonoBehaviour
    {
        protected override void OnSpawn()
        {
            base.OnSpawn();
            Debug.Log("[MyFirstCreature] spawned");
        }
    }
}

等生物能正常生成后,再在这里订阅事件、检查状态、产出物品。

改基础数值

克隆出来以后,可以按需拿组件改数值。比如改选择面板显示、质量或标签:

csharp
KSelectable selectable = clone.GetComponent<KSelectable>();
if (selectable != null)
{
    selectable.SetName(STRINGS.CREATURES.SPECIES.MYFIRSTCREATURE.NAME);
}

PrimaryElement primary = clone.GetComponent<PrimaryElement>();
if (primary != null)
{
    primary.Mass = 100f;
    primary.Temperature = 293.15f;
}

KPrefabID prefabId = clone.GetComponent<KPrefabID>();
prefabId.AddTag(GameTags.Creatures.Species, false);

这类改动比较安全。涉及 AI 行为树、导航类型、繁殖器时,要先确认原版组件的依赖关系,不要看到组件就直接删。

掉落物和产物

生物掉落通常依赖死亡掉落、排泄、产蛋或特殊状态机。入门时建议先用已有掉落逻辑,不要马上写完整繁殖链。

如果只是周期性产出一个物品,可以写在自己的组件里:

csharp
public class MyFirstCreature : KMonoBehaviour, ISim200ms
{
    private float timer;

    public void Sim200ms(float dt)
    {
        timer += dt;
        if (timer < 600f) return;

        timer = 0f;
        GameObject prefab = Assets.GetPrefab(MyFirstDropConfig.ID);
        if (prefab == null) return;

        GameUtil.KInstantiate(prefab, transform.position, Grid.SceneLayer.Ore).SetActive(true);
    }
}

这不是牧场系统的完整产物链,但很适合先验证“生物能触发自己的逻辑”。

名称和描述

csharp
namespace MyFirstCreature
{
    public static class STRINGS
    {
        public static class CREATURES
        {
            public static class SPECIES
            {
                public static class MYFIRSTCREATURE
                {
                    public static LocString NAME = "我的第一个生物";
                    public static LocString DESC = "一个基于原版生物改出来的测试生物。";
                }
            }
        }
    }
}

生物文本路径比建筑和物品更容易乱。建议先统一放到 CREATURES.SPECIES 下,后面需要蛋、幼体、掉落物时再补子项。

常见问题

生物生成后不动,通常是 NavigatorBrainChoreProvider 或状态机依赖坏了。先回退到只改 ID 和名字,确认克隆体能动。

保存读取崩溃,多半是新增组件里有不能序列化的字段,或者 prefab ID/tag 改得不完整。

动画错位或空白,先用原版动画跑通。完全自制生物动画要配好每个状态机需要的动画状态,不然生物会在某个状态突然消失。

完整从零写生物当然可以,但不适合作为第一篇教程。先用克隆路线把链路跑通,再逐步替换系统,成功率会高很多。