缺氧游戏中的电力系统

2024.05.05

由于缺氧中的地图非常大,导致有时候连电线非常长,所以我想实现能够不用电线连接所有电器的模组。

这篇文章仅是我对这方面代码阅读之后的记录。

电量管理组件

观察源码中所有消耗电力的建筑,发现在它们的 Def 中都会有 RequiresPowerInput​、PowerInputOffset​、EnergyConsumptionWhenActive​ 这三条属性,对这三条属性进行一个搜索,发现前两条主要是用在创建电力输入接口,在对EnergyConsumptionWhenActive​进行搜索时,最终是定位到了 EnergyConsumer​ 这个组件,通过阅读这个组件的源码,我们提取出一些比较关键的方法:

  1. 实例化时的方法:

    我们可以看到在实例化的时候首先将这个组件实例添加到 EnergyConsumers​ 这个组件库中,接着设定这个组件对应建筑的输入端口,然后再在游戏实例中的 circuitManager​ 和 energySim​ 添加该组件实例。相应地,在这个组件实例清理的时候在前面提到的三个列表中删除本实例。

    csharp
    protected override void OnSpawn() {
      base.OnSpawn();
      Components.EnergyConsumers.Add(this);
      Building component = GetComponent<Building>();
      PowerCell = component.GetPowerInputCell();
      Game.Instance.circuitManager.Connect(this);
      Game.Instance.energySim.AddEnergyConsumer(this);
    }
  2. 设置连接状态的方法:

    csharp
    public virtual void SetConnectionStatus(CircuitManager.ConnectionStatus connection_status) {
      switch (connection_status) {
        case CircuitManager.ConnectionStatus.NotConnected:
          IsPowered = false;
          break;
        case CircuitManager.ConnectionStatus.Unpowered:
          if (IsPowered && GetComponent<Battery>() == null) {
            IsPowered = false;
            circuitOverloadTime = 6f;
            PlayCircuitSound("overdraw");
          }
          break;
        case CircuitManager.ConnectionStatus.Powered:
          if (!IsPowered && circuitOverloadTime <= 0f) {
            IsPowered = true;
            PlayCircuitSound("powered");
          }
          break;
      }
    }
  3. 每200ms会执行一次的方法:

    csharp
    public virtual void EnergySim200ms(float dt) {
      CircuitID = Game.Instance.circuitManager.GetCircuitID(this);
      if (!IsConnected) {
        IsPowered = false;
      }
      circuitOverloadTime = Mathf.Max(0f, circuitOverloadTime - dt);
    }

通过以上的阅读,我们没有发现 EnergyConsumptionWhenActive​ 被谁使用了,只是将这个数据传递到组件的属性中,但是我们可以发现这个组件应当受到 circuitManager​ 和 energySim​ 这两个实例化后的组件控制。所以可以往下继续阅读,就是 CircuitManager​ 和 EnergySim

EnergySim​ 执行的方法比较简单,就是每帧都调用一次所有记录的用、存、发电设备的每帧需要调用的函数,这里暂时没有看到电力转移相关代码,基本是更新设备状态的相关操作。

从名字来看,CircuitManager​ 是电力控制组件,通过阅读这个组件的源码,可以发现 Sim200msLast​ 是电网电力转移的关键,Sim200msFirst​ 是会对整个电路进行更新。

电路更新

在缺氧里电线建造的时候会向一个网格系统中添加当前格子的位置,互相连接的电线都会添加到一个电网中,CircuitManager​ 中的 CircuitInfo​ 会同步电网的更新,并将与电网相连的电力设备与电线添加到一个 CircuitInfo​ 中。

电力转移

Sim200msLast​ 中一共有 5 个 for 循环,我们来看一下这几个循环都干了什么

第一个循环

大致浏览一下,可以看到第一个循环中主要是计算当前电路中是否有可用电力以及电力数量,如果足够用电器消耗则会将电力转移给用电器。

第二个循环

为发电机、变压器、电池做优先级排序,并先给变压器充电后再给电池充电

第三个循环

将变压器中的电力转移到电池内

第四个循环

更新电池连接状态以及向统计数据报告当前发电机浪费的电力

第五个循环

更新电路过载状态

而所有电力设备的电力产生、存储、消耗状态都由 EnergySim 来更新,所以想要实现开篇中的功能,只需要自己写相应的组件并实现设备接入功能就行。

本文最后更新于 2024 年 5 月 5 日