好好的一个游戏,玩着玩着就写起了代码
游戏里的编辑器没有代码提示没有代码高亮,甚至连括号对齐都没有,于是我们首先需要搭一个VS下的开发环境,用于写代码
并不是所有C#的内置类或者游戏类均可用,文章的最后会说明
首先需要把游戏源码clone下来https://github.com/KeenSoftwareHouse/SpaceEngineers/
为了不要一不小心修改了游戏代码,可以在解决方案里新建一个Console项目,然后添加对游戏项目的引用,然后让生成出来的类继承MyGridProgram类,
并且修改Main方法,拿掉static,参数是(string args),
添加需要用到的引用,
最终代码应该长这样
using Sandbox.Common; using Sandbox.Common.Components; using Sandbox.Definitions; using Sandbox.Engine; using Sandbox.ModAPI.Ingame; using Sandbox.ModAPI.Interfaces; using Sandbox.Game; using Sandbox.Game.Entities.Blocks; using Sandbox.Game.Gui; using Sandbox.ModAPI; namespace Scripts { class Program : MyGridProgram { public void Main(string args) { } } }
顺着MyGridProgram类,不难找到GridTerminalSystem这个属性,它也是我们在可编程方块里唯一可用的和游戏对接的入口
它拥有六个方法
- void GetBlocks(List
blocks); 获取当前设施内的所有方块 void GetBlockGroups(List
blockGroups); 获取当前设施内的所有组 void GetBlocksOfType
(List blocks, Func<IMyTerminalBlock, bool> collect = null); 获取当前设施内的所有继承T类型的方块,继承关系参见http://www.spaceengineerswiki.com/Programming_Guide/Action_List void SearchBlocksOfName(string name,List
blocks, Func<IMyTerminalBlock, bool> collect = null); 按照名称搜索,大小写不敏感 IMyTerminalBlock GetBlockWithName(string name); 按照名称获取方块
IMyBlockGroup GetBlockGroupWithName(string name); 获取组
获取到特定方块后,可以用as方法将其转换为特定方块的Interface,比如 var block = GridTerminalSystem.GetBlockWithName("Assembler Main") as IMyAssembler;
类型转换后就能得到更多当前方块的特定属性了,比如IMyAssembler可以获取IsQueueEmpty来判断是否队列空
Action
控制门的开关、起落架的锁定、连接器的对接都是一个Action,每个方块可用的Action见http://www.spaceengineerswiki.com/Programming_Guide/Action_List
这是一个切换门开关状态的栗子
public void SwitchDoor(string name) { var block = GridTerminalSystem.GetBlockWithName(name) as IMyDoor; var action = block.GetActionWithName("Open"); action.Apply(block); }
如果想要切换一个分组的门开关,没有直接的方法,只能
public void SwitchDoors(string group) { GridTerminalSystem.GetBlockGroupWithName(group).Blocks.ForEach(block => SwitchDoor(block.CustomName)); }
Inventory
获取方块存储的方式也很简单
var mainInv = GridTerminalSystem.GetBlockWithName(main).GetInventory(0);
有些方块是不止一个存储空间的,比如Assembler,矿物区和成品区是两个Inventory,从上往下从0开始数,作为GetInventory传入的参数即可
在Inventory间传递物品
获取到的Inventory对象实现了IMyInventory接口,有
bool TransferItemTo(IMyInventory dst, int sourceItemIndex, int? targetItemIndex = null, bool? stackIfPossible = null, VRage.MyFixedPoint? amount = null);和
bool TransferItemFrom(IMyInventory sourceInventory, int sourceItemIndex, int? targetItemIndex = null, bool? stackIfPossible = null, VRage.MyFixedPoint? amount = null);
两个方法用于物品传输,前提是block之间有管道连接,若无管道连接或者目标存储已满,则返回false
判断物品种类
var inv = block.GetInventory(1); var items = inv.GetItems(); if (items.Count > 0) { foreach (var item in items) { if (item.Content.TypeId == typeof(MyObjectBuilder_Ingot)) { inv.TransferItemTo(target.GetInventory(0), 0); } } }
如此可以判断是否为矿物
代码限制
只能使用
Sandbox.ModAPI.Ingame
Sandbox.ModAPI.Interfaces
Sandbox.Common.ObjectBuilders
VRageMath
VRage
这五个命名空间内的API,不能使用反射,不能使用Console对象
调试
public void Main(string args) { Echo(args); }
类API
构造函数 Program(){},可以不存在
public void Main()或者public void Main(string arg)必须存在其中一个,有参数的优先