获取方块

注:前面的篇章基本上将基础都讲完了,那么现在我们开始真正步入主题——讲一讲如何实现 获取游戏内方块 的操作。
这是编写脚本最核心的能力之一,几乎所有自动化逻辑都离不开它。


大致分类

获取方块有多种方法,而且也分获取 单个方块 还是 多个方块,这里我们只讲最常用的几种(剩下的可以自行查询 API 文档)。

主要分为:

  • 根据方块名字获取 单个方块
  • 根据方块编组名字获取 方块组
  • 根据方块类型获取 方块组

根据方块名字获取 单个方块

这种方式就是根据游戏中显示的 方块名称 来获取对象。

⚠️ 注意:

  • 这里的名称是玩家自定义名称
  • 不是方块类型名

用法

GridTerminalSystem.GetBlockWithName("目标方块名字");

返回类型:

IMyTerminalBlock

通常需要 强制转换为具体类型1 才能使用。

因为程序只知道获取指定方块名的方块,但是不清楚这个方块的具体类型是什么。比如我获取的方块名字叫 但它可能实际上是一个转子(?)虽然这种情况一般不会发生(但愿吧)但是计算机是不知道的,所以它会给我们返回一个笼统的类型—— IMyTerminalBlock 接口,该接口是 所有有功能的方块 都具有的接口。

现在你可能意识到了一个问题 IMyTerminalBlock 接口既然是所有有功能方块都具有的接口,那么它们之间的功能怎么区分呢?诶!这你就问到点子上了,正因为它是一个笼统的接口所以它不具备具体方块的具体功能。

而现在我们需要门方块的具体功能,那么这个时候我们就需要使用 as 关键字来进行强制将 IMyTerminalBlock具体化为我们的需要的 IMyDoor 类型,也就是如下代码:

myDoor = GridTerminalSystem.GetBlockWithName("门") as IMyDoor;
// 使用 as 关键字强制让IMyTerminalBlock转为我们的IMyDoor类

示例:输出门的开启状态

我们以一个滑动门为例:

  1. 将门方块命名为:
  2. 获取它并输出开启状态

PS:此处代码省略类和命名空间等,可以直接复制进游戏内使用

IMyDoor myDoor;

public Program()
{
    Runtime.UpdateFrequency = UpdateFrequency.Update10;

    // 获取方块并转换类型
    myDoor = GridTerminalSystem.GetBlockWithName("门") as IMyDoor;
}

public void Main(string argument, UpdateType updateSource)
{
    Echo($"当前门的状态是:{myDoor.Status}");
}

空引用问题(非常重要)

如果名称写错:

myDoor = null;

调用时会直接报错。

建议养成检测习惯:

if (myDoor == null)
{
    Echo("没有找到门!");
    return;
}

根据方块编组名字获取 方块组

当需要一次控制 多个方块 时,最常用的方法就是使用编组。

例如:

  • 多个推进器
  • 多个灯
  • 多个炮塔

用法

GridTerminalSystem.GetBlockGroupWithName("编组名称");

返回类型:

IMyBlockGroup

不能直接使用,需要再取出其中的方块。

示例:控制一组灯

第一步:在游戏中创建编组

  1. 打开控制面板
  2. 选择多个灯
  3. 创建编组名称:照明系统

第二步:脚本实现

IMyBlockGroup lightGroup;
List<IMyLightingBlock> lights = new List<IMyLightingBlock>();

public Program()
{
    Runtime.UpdateFrequency = UpdateFrequency.Update10;

    // 获取编组
    lightGroup = GridTerminalSystem.GetBlockGroupWithName("照明系统");

    // 将编组内的灯加入列表
    lightGroup.GetBlocksOfType(lights);
}

public void Main(string arg, UpdateType source)
{
    foreach (var light in lights)
    {
        light.Enabled = !light.Enabled; // 切换开关状态
    }
}

核心流程总结

获取编组 → 获取其中的方块 → 操作方块

常见错误

忘记初始化 List

List<IMyLightingBlock> lights; // 会报错

必须写成:

new List<IMyLightingBlock>();

编组不存在

一定要判空:

if (lightGroup == null)
{
    Echo("未找到编组!");
    return;
}

根据方块类型获取 方块组

这是最常用的方式之一

  • 不需要重命名
  • 不需要编组
  • 自动获取全部同类方块

用法

GridTerminalSystem.GetBlocksOfType<T>(列表);

示例:获取所有电池

List<IMyBatteryBlock> batteries = new List<IMyBatteryBlock>();

public Program()
{
    Runtime.UpdateFrequency = UpdateFrequency.Update100;

    GridTerminalSystem.GetBlocksOfType(batteries);
}

public void Main(string arg, UpdateType source)
{
    float totalPower = 0;

    foreach (var battery in batteries)
    {
        totalPower += battery.CurrentStoredPower;
    }

    Echo($"总电量:{totalPower} MWh");
}

进阶用法:筛选条件

可以加入过滤器:

GridTerminalSystem.GetBlocksOfType(
    batteries,
    b => b.CustomName.Contains("主电池")
);

这样只获取 名称包含指定字符 的方块。


三种获取方式对比

方法 优点 缺点 使用场景
按名字获取 最简单 只能单个 只控制单一方块
按编组获取 灵活 需要手动编组 系统控制
按类型获取 自动化最强 需要筛选 自动化脚本

性能注意事项

不要每帧都获取方块

错误写法:

public void Main(...)
{
    GridTerminalSystem.GetBlocksOfType(batteries);
}

这会严重影响性能


正确写法

只在构造函数中获取一次:

public Program()
{
    GridTerminalSystem.GetBlocksOfType(batteries);
}

或定期刷新:

if (tick % 600 == 0)
{
    batteries.Clear();
    GridTerminalSystem.GetBlocksOfType(batteries);
}

  1. GetBlockWithName() 返回的是通用类型 IMyTerminalBlock,必须强制转换为具体接口(如 IMyDoor)后,才能访问该方块特有的属性和方法;若类型不匹配,转换结果会是 null