企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持知识库和私有化部署方案 广告
## 太空工程师编程笔记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; } ~~~