验证环境
本页示例已使用 Build 740622 对应的本机 Managed DLL 完成编译检查;尚未在游戏内完成运行验证。
新增建筑
这一章做一个真正会出现在建造菜单里的建筑。建筑和普通物品不一样,它要处理占格、材料、建造时间、电力、端口、储存、逻辑信号等内容,所以代码通常会拆成两部分:
IBuildingConfig:告诉游戏这个建筑长什么样、占几格、需要什么材料。- Harmony Patch:把建筑放进建造菜单,并解锁到某个科技里。
本章用一个小型功能建筑作为示例,只保留新增建筑最常用的骨架。
文件结构
Building/
├── Building.csproj
├── Mod.cs
├── BuildingConfig.cs
├── STRINGS.cs
├── mod.yaml
└── mod_info.yamlBuildingConfig.cs 管建筑本体,Mod.cs 负责注册入口和科技解锁,STRINGS.cs 放名称和描述。
动手顺序
第一次做建筑时,按这个顺序来:
- 先用原版动画和一个
1 × 1建筑确认配置能编译。 - 注册建筑并放进建造菜单。
- 加入
Operational、Storage或EnergyConsumer等组件。 - 最后再换自己的动画、端口和自定义逻辑。
每次只加一种功能。建筑不出现时查注册,建筑能出现但不能工作时查组件,动画空白时查资源目录,不要同时改三处。
建筑配置
新增建筑需要继承当前 DLL 中的 IBuildingConfig 基类。最少要实现三个阶段:
CreateBuildingDef():定义尺寸、动画、材料、建造规则。ConfigureBuildingTemplate():给建筑预制体加组件。DoPostConfigureComplete():在建筑完成配置后补运行组件。
当前版本的可编译配置位于 examples/Building/BuildingConfig.cs:
using TUNING;
using UnityEngine;
namespace ONITutorial.Building
{
public sealed class BuildingConfig : IBuildingConfig
{
public const string ID = "ONITutorialBuilding";
public override BuildingDef CreateBuildingDef()
{
BuildingDef def = BuildingTemplates.CreateBuildingDef(
ID,
1,
1,
"battery_kanim",
30,
60f,
BUILDINGS.CONSTRUCTION_MASS_KG.TIER1,
MATERIALS.REFINED_METALS,
1600f,
BuildLocationRule.OnFloor,
DECOR.BONUS.TIER0,
NOISE_POLLUTION.NONE,
0.8f);
def.Floodable = false;
def.Entombable = true;
def.Overheatable = false;
def.AudioCategory = "Metal";
def.DefaultAnimState = "off";
def.ObjectLayer = ObjectLayer.Building;
def.RequiresPowerInput = true;
def.EnergyConsumptionWhenActive = 10f;
def.SelfHeatKilowattsWhenActive = 0.1f;
return def;
}
public override void ConfigureBuildingTemplate(GameObject go, Tag prefabTag)
{
go.AddOrGet<Operational>();
Storage storage = go.AddOrGet<Storage>();
storage.capacityKg = 100f;
storage.storageFilters = STORAGEFILTERS.NOT_EDIBLE_SOLIDS;
storage.showCapacityStatusItem = true;
storage.showCapacityAsMainStatus = true;
go.AddOrGet<EnergyConsumer>();
}
public override void DoPostConfigureComplete(GameObject go)
{
}
public override void DoPostConfigurePreview(BuildingDef def, GameObject go)
{
}
public override void DoPostConfigureUnderConstruction(GameObject go)
{
}
public override void ConfigurePost(BuildingDef def)
{
}
public override string[] GetRequiredDlcIds() => new string[0];
public override string[] GetForbiddenDlcIds() => new string[0];
public override bool ForbidFromLoading() => false;
}
}先不要急着往里面塞功能。建筑能被注册、能建出来、能正常保存读取以后,再加自己的组件会稳很多。
加储存
示例在 ConfigureBuildingTemplate() 中加入 Storage,并使用当前 DLL 中存在的 TUNING.STORAGEFILTERS.NOT_EDIBLE_SOLIDS 作为过滤列表。完整代码见上方导入的 examples/Building/BuildingConfig.cs。
capacityKg 是容量,storageFilters 是允许放入的标签。比如只收种子可以用 GameTags.Seed,只收食物可以用 GameTags.Edible。
加电力
示例在 CreateBuildingDef() 中设置电力参数,并在 ConfigureBuildingTemplate() 中加入 EnergyConsumer。完整代码见 examples/Building/BuildingConfig.cs。
如果忘了 EnergyConsumer,建筑可能显示有电力口,但运行状态不对。
放进建造菜单
建筑配置写好以后,还需要告诉游戏:把它放到哪个分类里。
可编译的入口和两个补丁位于 examples/Building/Mod.cs:
using HarmonyLib;
using KMod;
using UnityEngine;
namespace ONITutorial.Building
{
public sealed class Mod : UserMod2
{
public override void OnLoad(Harmony harmony)
{
base.OnLoad(harmony);
Debug.Log("[ONITutorial.Building] Loaded");
}
[HarmonyPatch(typeof(GeneratedBuildings), nameof(GeneratedBuildings.LoadGeneratedBuildings))]
private static class GeneratedBuildingsPatch
{
private static void Prefix()
{
BuildingConfigManager.Instance.RegisterBuilding(new BuildingConfig());
ModUtil.AddBuildingToPlanScreen("Base", BuildingConfig.ID);
}
}
[HarmonyPatch(typeof(Db), nameof(Db.Initialize))]
private static class DbInitializePatch
{
private static void Postfix()
{
Db.Get().Techs.Get("BasicRefinement").unlockedItemIDs.Add(BuildingConfig.ID);
}
}
}
}"Base" 是建造菜单分类,"BasicRefinement" 是科技 ID。想放到别的分类或科技里,可以先找一个原版建筑,看看它所在的分类和解锁科技,再照着填。
如果建筑只需要在已有科技中解锁,直接修改 unlockedItemIDs 就够了。不要为了放一个建筑,先去创建新的科技节点;当前科技树 API 的旧示例和新 DLL 并不一致。
本地化文本
建筑菜单需要名称、描述和效果:
完整本地化声明位于 examples/Building/STRINGS.cs:
namespace ONITutorial.Building
{
public static class STRINGS
{
public static class BUILDINGS
{
public static class PREFABS
{
public static class ONITUTORIALBUILDING
{
public static LocString NAME = "ONI Tutorial Building";
public static LocString DESC = "A building from the tutorial example.";
public static LocString EFFECT = "Stores solids and consumes a small amount of power.";
}
}
}
}
}注意类名通常使用大写 ID。示例中的 BuildingConfig.ID = "ONITutorialBuilding" 对应 STRINGS.BUILDINGS.PREFABS.ONITUTORIALBUILDING。
常见问题
建筑出现在菜单但不能建,多半是材料分类或科技解锁写错了。先把材料改成 MATERIALS.ALL_METALS 或 MATERIALS.RAW_MINERALS 测试。
建筑建出来后状态很怪,检查 Operational、EnergyConsumer、Storage 这些组件是不是放在了正确阶段。
动画不显示,先确认 .anim、.build、.png 都打进 anim/assets,并且 Assets.GetAnim() 使用的是 xxx_kanim。