## 太空工程师编程笔记1 - 搭建环境与第一个程序
### 太空工程师(SpaceEngineer)编程
太空工程师是一款以太空为背景的建造游戏,类似于Minecraft。
游戏中有一个组件叫做可编程块,可以控制其他方块的行为,因此可以通过编程在游戏中实现复杂的功能。比如导弹,比如自动追踪太阳的太阳能电池板,比如无人货船。
游戏中的编程语言为C#,用户的代码作为一个类去执行。
游戏中的编译器存在白名单,即用户只能使用特定的类与方法,白名单如下:
~~~
Sandbox.ModAPI.Ingame;
Sandbox.ModAPI.Interfaces;
Sandbox.Common.ObjectBuilders;
VRageMath;
VRage;
~~~
#### 环境搭建
- 太空工程师游戏本体
用于搭建飞船和基地,以及观察代码执行情况。
- VSCode
替代游戏内编辑器,用于编辑C#代码。
- .Net Reflector
用于获得编程API,官方提供的API可以从github上获得,但是官方API过于古老,需要我们自己去查看API。反编游戏路径下\Bin64下SandBox.Game.dll和Sandbox.Common.dll即可获得。
备注:使用Visual Studio可以搭建游戏外编译环境,不过有点麻烦,而且会因为白名单导致和游戏内编译环境不一致,更适合于mod开发而不是脚本。
#### 文档参考
可以从https://forums.keenswh.com/categories/modding-and-programming.423171/获得一些资料。
也可以从Wiki得到一些资料https://spaceengineerswiki.com/Programming_Guide
官方论坛也提供了一些教程https://forum.keenswh.com/threads/guide-programmable-block-c-101-for-space-engineers.7225150/
当然,这些内容有时十分老旧,应以逆向dll为准。
### 编程
#### HelloWorld
熟悉太空工程师编程环境并调通。
**代码**
~~~
public void Main(string argument, UpdateType updateSource)
{
// The main entry point of the script, invoked every time
// one of the programmable block's Run actions are invoked,
// or the script updates itself. The updateSource argument
// describes where the update came from.
//
// The method itself is required, but the arguments above
// can be removed if not needed.
Echo("HelloWorld!");
}
~~~
**执行结果**
在终端界面运行编程块,界面右下角出现“HelloWorld!”
**本次使用的功能**
入口方法:
~~~c#
public void Main(string argument, UpdateType updateSource)
该方法编程块每次运行时执行的入口
argument为运行时的参数,可由终端界面中argument项给出
在工具栏设定编程块时也可给出
updateSource则标识本次运行是由谁触发的。
public enum UpdateType
{
None = 0,
Terminal = 1,
Trigger = 2,
Antenna = 4,
Mod = 8,
Script = 0x10,
Update1 = 0x20,
Update10 = 0x40,
Update100 = 0x80,
Once = 0x100
}
~~~
打印字符串:
~~~c#
Echo(String str)
将字符串输出在编程块状态栏中
~~~
#### 持续运行
点击运行仅会运行一次,但有时候一些控制程序需要在游戏中持续运行。
在游戏内部可以通过计时器去反复触发,但也可以在脚本中设定其运行频率。
**代码**
~~~c#
int tick=0;
public Program()
{
// The constructor, called only once every session and
// always before any other method is called. Use it to
// initialize your script.
//
// The constructor is optional and can be removed if not
// needed.
//
// It's recommended to set RuntimeInfo.UpdateFrequency
// here, which will allow your script to run itself without a
// timer block.
Runtime.UpdateFrequency = UpdateFrequency.Update1;
}
public void Main(string argument, UpdateType updateSource)
{
// The main entry point of the script, invoked every time
// one of the programmable block's Run actions are invoked,
// or the script updates itself. The updateSource argument
// describes where the update came from.
//
// The method itself is required, but the arguments above
// can be removed if not needed.
Echo("Running "+tick++);
}
~~~
**执行结果**
游戏内编译后自动执行,在右下角可见每秒约60次。
**本次使用的功能**
~~~
内部变量tick
编程块的代码在编译时创建实例,其内部变量可以在多次运行中得到保存。
因此可以保存运行状态。
不过,在服务器重启时会重新实例化,所以内部变量可能无法跨过服务器重启,此时最好用storage存储。
~~~
~~~c#
Runtime
编程块可以调用的运行环境对象,包含了用户脚本的各种状态和信息。
由于游戏脚本有单次运行总执行量限制,超过限制后会被强制终止并需要重编译才可恢复。所以在执行量比较大时可以通过Runtime来判断是否该主动暂停执行(yield)。
public interface IMyGridProgramRuntimeInfo
{
// Properties
TimeSpan TimeSinceLastRun { get; }
double LastRunTimeMs { get; }
int MaxInstructionCount { get; }
int CurrentInstructionCount { get; }
int MaxCallChainDepth { get; }
int CurrentCallChainDepth { get; }
UpdateFrequency UpdateFrequency { get; set; }
}
~~~
~~~c#
UpdateFrequency
代码的运行频率,设定后,代码按照设定的频率会自动执行而无须人工触发,最快的执行频率为每(游戏内)秒60次,这是游戏处理机制决定的上限。
每秒60次即UpdateFrequency.Update1。
可选项如下:
public enum UpdateFrequency : byte
{
None = 0,
Update1 = 1,
Update10 = 2,
Update100 = 4,
Once = 8
}
~~~
~~~c#
编程块的可用对象
除了Runtime和Echo外,编程块中还可以直接使用一些对象或方法。这些对象或方法构成了基本API。
// Properties
public virtual IMyGridTerminalSystem GridTerminalSystem { get; [CompilerGenerated] protected set; }
public virtual IMyProgrammableBlock Me { get; [CompilerGenerated] protected set; }
[Obsolete("Use Runtime.TimeSinceLastRun instead")]
public virtual TimeSpan ElapsedTime { get; [CompilerGenerated] protected set; }
public virtual IMyGridProgramRuntimeInfo Runtime { get; [CompilerGenerated] protected set; }
public virtual string Storage { get; protected set; }
public Action<string> Echo { get; [CompilerGenerated] protected set; }
IMyGridTerminalSystem IMyGridProgram.GridTerminalSystem { get; set; }
IMyProgrammableBlock IMyGridProgram.Me { get; set; }
TimeSpan IMyGridProgram.ElapsedTime { get; set; }
string IMyGridProgram.Storage { get; set; }
Action<string> IMyGridProgram.Echo { get; set; }
IMyGridProgramRuntimeInfo IMyGridProgram.Runtime { get; set; }
bool IMyGridProgram.HasMainMethod { get; }
bool IMyGridProgram.HasSaveMethod { get; }
~~~
- 序言
- 写在前面的话
- 太空工程师
- MEA小组
- 一、入门
- 1.1 基础概念
- 1.2 编程工具
- 1.3 变量
- 1.4 函数 Function
- 1.5 基本语法
- 1.5.1 运算符
- 1.5.2 if
- 1.5.3 for
- 1.5.4 其他语法
- 1.3 类 Class
- 二、编程块
- 2.1 方块的概念
- 2.2 List<T>结构
- 2.3 获取方块
- 2.4 方块的使用
- 三、Ship 类
- 3.1 简介
- Ship v0.5
- 代码
- 手册(待更新)
- 例子(待更新)
- Ship v1.0
- 代码
- 例子
- 文档
- 实例化
- 内置变量
- 内置方法
- Target类
- 四、运动控制算法在SE中的应用
- 4.1 运动控制介绍
- 4.2 过程控制
- 4.3 震荡和动态误差
- 4.4 误差累加方案
- 4.5 PID算法
- 4.6 对PID算法的一点点简化
- 4.7 一阶惯性系统的PID算法优化的研究
- 五、MEA方块类
- 5.0 核心代码目录
- v1.0核心代码
- v1.1 核心代码
- v2.0 核心代码
- 5.1 类的概念
- 5.2 MEA的方块类(Block)
- 5.3 方块类文档
- 5.4 方块类2.0 全教程
- 5.4.1 安装和使用
- 5.4.2 方块类(Block)
- 5.4.3 显示屏类(DisplayScreen)
- 5.4.4 LCD类(LCD)
- 5.4.5 主控座椅类(Cockpit)
- 六、疯猴的编程笔记
- 第一个程序
- 获取和控制其他块
- 物流与生产
- 界面与通信
- 运动与姿态
- 侦测与导航
- 七、SteamZhou的笔记
- 有趣而花里胡哨的IDEA
- 八、质子对撞炮的笔记
- 属性 Property
- 接口 interface