这是将会是你的第一个实用性脚本,你也不希望自己飞船的门打开后还得自己关上吧(
这游戏最常见的应该就是各种门了,我们就写一个可以控制门的脚本,将从 简单 -> 进阶 两部分来讲解
本脚本目标是在门被打开后一段延迟内自动将其关闭,减少玩家手动关门的麻烦。实现思路很直接:
自动控制门 )Runtime.UpdateFrequency = Update10 优化性能减少高频占用)CloseDoor() 关闭门并重置计时。CustomData 设置自动关闭的时间(秒)。这种实现简单易懂,适合小型船坞或室内门的自动化。
还是老样子,只不过这次我们多放几个门方块(因为是编组,所以我们模拟多个门的情况)

这样我们就有3个门方块了
根据刚刚的思路,现在我们需要给门编组为 自动控制门 。不过在此之前,我们先来讲讲如何给方块编组,主要分为以下两种操作方式:
我们先来看第一种每次选择单个方块,具体操作为:

当然也可以用这种操作取消选择的方块

这种方式适用于选择连续多个方块的情况,具体操作为:

经过以上操作之后,我们就成功的将所有门编组为了 自动控制门
在完成了上述步骤之后呢,先来看一下我们根据我们的思路实现的代码:
using Sandbox.ModAPI.Ingame;
using System;
using System.Collections.Generic;
using XFEExtension.SpaceEngineers.ScriptingHelper;
namespace MyProject
{
internal class Program : MyGridProgram, IProgramBase
{
// 注意,复制到游戏内编程块的时候应当从下面开始复制
//------------------从此处开始复制------------------//
// 定义成员变量
List<IMyDoor> doorList = new List<IMyDoor>(); // 定义一个门列表,用于存储需要自动关闭的门
double time; // 定义一个时间变量,用于记录门打开的持续时间
double closetime = 1.2d; // 定义一个自动关闭的时间阈值,默认为1.2秒
public Program()
{
// 这里是 构造函数
Runtime.UpdateFrequency = UpdateFrequency.Update1; // 设置 Main函数 的触发频率为 Update1 —— 每帧更新一次
var doors = GridTerminalSystem.GetBlockGroupWithName("自动控制门"); // 获取一个叫 自动控制门 的方块组
doors.GetBlocksOfType(doorList); // 将刚刚获取的方块组中的所有门方块添加到 doorList 中
}
public void Main(string argument, UpdateType updateSource)
{
// 这里是 主函数
if (Me.CustomData != "") // 如果自定义数据不为空,则尝试将其解析为自动关闭的时间
{
closetime = double.Parse(Me.CustomData); // 将自定义数据解析为 double 类型,并赋值给 closetime 变量
}
Echo("脚本使用说明:\n将需要自动关闭的门编组,设置名称为:自动控制门\n在自定义数据里面输入自动关门的时间\n目前自动关门时间:" + closetime.ToString() + "秒\nby XFEstudio"); // 输出脚本使用说明和当前自动关门时间
Me.CustomName = "自动关门-XFEstudio"; // 设置编程块的名称为 自动关门-XFEstudio
foreach (var door in doorList) // 遍历门列表中的每个门
{
if (door.Status == DoorStatus.Open) // 如果门的状态是打开的
{
time += Runtime.TimeSinceLastRun.TotalSeconds; // 将每帧的时间增量累加到 time 变量中
if (time > closetime) // 如果累计的时间超过了自动关闭的时间阈值
{
door.CloseDoor(); // 尝试关闭门
time = 0.0d; // 重置 time 变量,以便下次门打开时重新计时
}
}
}
}
public void Save()
{
// 这里是 保存函数
}
//------------------从此处结束复制------------------//
}
}
说明:将上面的代码复制到可编程方块并编译运行,确保你所有的门已经加入名为
自动控制门的方块组。

是不是更神奇了,这些门居然在打开后的一段时间后自己关上了(666这个门又开桂)
其实根据我们之前的思路,这段时间发生的事件大致是
CustomData 中设定的秒数(默认 1.2s)时,脚本调用 CloseDoor() 关闭门实际效果是门会在短时间后自动合上,常用于防止门长时间敞开导致气压流失或视觉不美观。
List<IMyDoor> doorList:用于存储需要自动关门的门引用。double time:当前实现使用单一计时器记录“检测到打开到关闭”的累计时间。double closetime:自动关闭阈值(秒),可通过可编程方块的 CustomData 修改。Program():设置 Runtime.UpdateFrequency = Update10,并通过 GridTerminalSystem.GetBlockGroupWithName("自动控制门") 获取组内门并填充 doorList。Main:每帧先读取 CustomData 更新 closetime,然后遍历 doorList,若任一门为 Open 状态则对 time 做累加,当超过 closetime 则调用 CloseDoor() 并重置 time。问:脚本没有关闭门?
自动控制门),确认门在同一网格或可被脚本访问;Echo 输出 door.Status 与 time 辅助调试;问:多个门同时使用时行为不正确?
time 变量来为所有门计时,这会导致当多个门独立打开时计时逻辑混淆(例如门A短时间关闭,门B刚打开但 time 已部分被消耗)。见“进阶改进”。其实这个改进并不是那么重要,因为检测门状态几乎是实时的,而且在整体上也大致是每个门单独计时(因为基本上不会出现两扇门同时打开的情况)
不过呢,如果硬是要解决多个门同时打开时计时冲突的问题,可以为每扇门维护独立的计时器。可以使用 Dictionary<long, double> 以门的 EntityId 作为键,记录每扇门的累计打开时间:
示例改进:
Dictionary<long, double> doorTimers = new Dictionary<long, double>();
// 在构造函数或初始化时填充 doorTimers 中的键,初始值为 0
foreach (var door in doorList)
{
long id = door.EntityId;
if (!doorTimers.ContainsKey(id)) doorTimers[id] = 0.0;
}
// 在 Main 中对每扇门分别计时
foreach (var door in doorList)
{
long id = door.EntityId;
if (door.Status == DoorStatus.Open)
{
doorTimers[id] += Runtime.TimeSinceLastRun.TotalSeconds;
if (doorTimers[id] >= closetime)
{
door.CloseDoor();
doorTimers[id] = 0.0;
}
}
else
{
doorTimers[id] = 0.0; // 门关着时重置该门计时
}
}
这种改进能保证每扇门的自动关闭行为互不干扰,适用于多个门的场景(如多个机舱或走廊门)。
本篇介绍了一个简单的自动关门脚本:如何获取门组、设置执行频率、通过 CustomData 控制延迟时间并在检测到门打开后延迟关闭。对于更复杂的场景,建议为每扇门维护独立计时器以避免互相影响。
练习:尝试将改进后的独立计时版本实现到你的 Program.cs 中,并测试多个门同时开启时的行为是否正常。