新增植物
植物本质上也是实体,所以它使用 IEntityConfig。和普通掉落物不同的是,植物还要处理生长状态、种子、收获物、温度压力范围,以及是否需要灌溉。
这一章用“植物本体 + 果实物品”的组合,走一遍完整注册流程。
文件结构
MyFirstPlant/
├── MyFirstPlantConfig.cs
├── MyFirstFruitConfig.cs
├── Patches.cs
└── STRINGS.cs植物和果实最好分开写。植物负责长在地上,果实负责掉落、食用或加工。
果实配置
先写果实,因为植物注册时会引用它的 ID。
using UnityEngine;
namespace MyFirstPlant
{
public class MyFirstFruitConfig : IEntityConfig
{
public const string ID = "MyFirstFruit";
public GameObject CreatePrefab()
{
GameObject go = EntityTemplates.CreateLooseEntity(
ID,
STRINGS.ITEMS.FOOD.MYFIRSTFRUIT.NAME,
STRINGS.ITEMS.FOOD.MYFIRSTFRUIT.DESC,
1f,
false,
Assets.GetAnim("my_first_fruit_kanim"),
"object",
Grid.SceneLayer.Front,
EntityTemplates.CollisionShape.RECTANGLE,
0.8f,
0.5f,
true,
0,
SimHashes.Creature
);
EdiblesManager.FoodInfo foodInfo = new EdiblesManager.FoodInfo(
ID,
1000f * 800f,
1,
255.15f,
277.15f,
4800f,
true
);
return EntityTemplates.ExtendEntityToFood(go, foodInfo);
}
public void OnPrefabInit(GameObject inst) { }
public void OnSpawn(GameObject inst) { }
}
}1000f * 800f 表示 800 千卡。温度用开尔文,273.15f 才是 0 摄氏度。
植物本体
植物用 EntityTemplates.CreatePlacedEntity() 创建,因为它是放在世界格子里的实体,不是掉在地上的物品。
using System.Collections.Generic;
using TUNING;
using UnityEngine;
namespace MyFirstPlant
{
public class MyFirstPlantConfig : IEntityConfig
{
public const string ID = "MyFirstPlant";
public const string SEED_ID = "MyFirstPlantSeed";
public GameObject CreatePrefab()
{
GameObject go = EntityTemplates.CreatePlacedEntity(
ID,
STRINGS.CREATURES.SPECIES.MYFIRSTPLANT.NAME,
STRINGS.CREATURES.SPECIES.MYFIRSTPLANT.DESC,
1f,
Assets.GetAnim("my_first_plant_kanim"),
"idle_empty",
Grid.SceneLayer.BuildingFront,
1,
2,
DECOR.BONUS.TIER0,
NOISE_POLLUTION.NONE,
SimHashes.Creature,
new List<Tag> { GameTags.Plant },
293.15f
);
EntityTemplates.ExtendEntityToBasicPlant(
go,
temperature_lethal_low: 248.15f,
temperature_warning_low: 263.15f,
temperature_warning_high: 313.15f,
temperature_lethal_high: 343.15f,
safe_elements: new[] { SimHashes.Oxygen, SimHashes.CarbonDioxide },
pressure_sensitive: true,
pressure_lethal_low: 0.05f,
pressure_warning_low: 0.15f,
crop_id: MyFirstFruitConfig.ID,
max_age: 2400f,
baseTraitId: "BasicPlantTrait"
);
go.AddOrGet<Crop>();
go.AddOrGet<Growing>();
go.AddOrGet<StandardCropPlant>();
go.AddOrGet<BlightVulnerable>();
GameObject seed = EntityTemplates.CreateAndRegisterSeedForPlant(
go,
DlcRestrictionsUtil.GetTransientHelperObjectFromAllowList(null),
SeedProducer.ProductionType.Harvest,
SEED_ID,
STRINGS.CREATURES.SPECIES.SEEDS.MYFIRSTPLANT.NAME,
STRINGS.CREATURES.SPECIES.SEEDS.MYFIRSTPLANT.DESC,
Assets.GetAnim("seed_kanim"),
"object",
1,
new List<Tag> { GameTags.CropSeed },
SingleEntityReceptacle.ReceptacleDirection.Top,
default(Tag),
2,
STRINGS.CREATURES.SPECIES.MYFIRSTPLANT.DOMESTICATEDDESC
);
EntityTemplates.CreateAndRegisterPreviewForPlant(
seed,
ID + "_preview",
Assets.GetAnim("my_first_plant_kanim"),
"place",
1,
2
);
return go;
}
public void OnPrefabInit(GameObject inst)
{
inst.GetComponent<PrimaryElement>().Temperature = 293.15f;
}
public void OnSpawn(GameObject inst) { }
}
}crop_id 决定植物成熟后产出什么。max_age 是生长周期,单位是游戏秒。
CreateAndRegisterSeedForPlant() 当前推荐签名需要一个 IHasDlcRestrictions 参数。示例里的 DlcRestrictionsUtil.GetTransientHelperObjectFromAllowList(null) 表示不限制 DLC;如果你的配置类自己实现了 IHasDlcRestrictions,也可以传 this。
灌溉和施肥
如果植物需要消耗液体或气体,可以继续扩展:
EntityTemplates.ExtendPlantToIrrigated(
go,
new[]
{
new PlantElementAbsorber.ConsumeInfo
{
tag = GameTags.Water,
massConsumptionRate = 0.01f
}
}
);massConsumptionRate 是每秒消耗量。植物数值很容易写大,建议先用很小的值测试。
注册实体
植物和果实都要被 Assets 扫到。最常见做法是在 OnLoad 里调用:
using HarmonyLib;
using KMod;
namespace MyFirstPlant
{
public class Mod : UserMod2
{
public override void OnLoad(Harmony harmony)
{
base.OnLoad(harmony);
}
}
}只要 IEntityConfig 类在程序集里,游戏通常会自动发现。出问题时先检查类是否 public,以及 Mod 是否成功加载。
常见问题
植物不显示,多半是动画状态名错了。植物常用 idle_empty、idle_full、place,具体要以当前 kanim 里的状态为准。
种子能生成但不能种,检查 CreateAndRegisterSeedForPlant() 的植物 ID、种子 ID、GameTags.CropSeed 和种植方向。
植物一落地就死,优先看温度、压力、safe_elements。尤其温度要写开尔文,不要把 20 摄氏度写成 20f。