Skip to content

验证环境

本页示例已使用 Build 740622 对应的本机 Managed DLL 完成编译检查;尚未在游戏内完成运行验证。

新增建筑

这一章做一个真正会出现在建造菜单里的建筑。建筑和普通物品不一样,它要处理占格、材料、建造时间、电力、端口、储存、逻辑信号等内容,所以代码通常会拆成两部分:

  • IBuildingConfig:告诉游戏这个建筑长什么样、占几格、需要什么材料。
  • Harmony Patch:把建筑放进建造菜单,并解锁到某个科技里。

本章用一个小型功能建筑作为示例,只保留新增建筑最常用的骨架。

文件结构

text
Building/
├── Building.csproj
├── Mod.cs
├── BuildingConfig.cs
├── STRINGS.cs
├── mod.yaml
└── mod_info.yaml

BuildingConfig.cs 管建筑本体,Mod.cs 负责注册入口和科技解锁,STRINGS.cs 放名称和描述。

动手顺序

第一次做建筑时,按这个顺序来:

  1. 先用原版动画和一个 1 × 1 建筑确认配置能编译。
  2. 注册建筑并放进建造菜单。
  3. 加入 OperationalStorageEnergyConsumer 等组件。
  4. 最后再换自己的动画、端口和自定义逻辑。

每次只加一种功能。建筑不出现时查注册,建筑能出现但不能工作时查组件,动画空白时查资源目录,不要同时改三处。

建筑配置

新增建筑需要继承当前 DLL 中的 IBuildingConfig 基类。最少要实现三个阶段:

  • CreateBuildingDef():定义尺寸、动画、材料、建造规则。
  • ConfigureBuildingTemplate():给建筑预制体加组件。
  • DoPostConfigureComplete():在建筑完成配置后补运行组件。

当前版本的可编译配置位于 examples/Building/BuildingConfig.cs

csharp
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

csharp
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

csharp
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_METALSMATERIALS.RAW_MINERALS 测试。

建筑建出来后状态很怪,检查 OperationalEnergyConsumerStorage 这些组件是不是放在了正确阶段。

动画不显示,先确认 .anim.build.png 都打进 anim/assets,并且 Assets.GetAnim() 使用的是 xxx_kanim