## 太空工程师编程笔记4 - 界面与通信
### 交互
之前程序执行时,均是使用编程块的Terminal界面作为输出,输入也基本是手工进行。其实这并不便捷,程序要实际使用时,就要有更好的输入输出方式了。
简单总结了一下,游戏中程序可获得的输入有这几种:
1、命令行。即main函数运行的参数。可以由人工设定在控制台上,或者直接在run处输入,也可以由其他程序和事件调用。
2、其他块的状态和信息。由程序主动取得并获取输入。例如控制台可以得到鼠标的运动状态,各类其他块上的信息也可以参考。
而输出也基本是这么几类:
1、输出在编程块自己的界面上。
2、输出在其他块上。比如显示屏、比如直接产生运动或者各类操作。
2、调用其它程序。
这里比较有意思的是跨网格程序之间的通信,因为程序只能获取到自身所在网格的块,所以无法直接调用其它网格的编程块中的程序,所以要用专门的跨网格通信机制,这就是天线。
### 示例
OK,我们直接来看代码。
#### LCD显示字符串
让第一个LCD显示字符串。
~~~csharp
int tick=0;
public Program()
{
Runtime.UpdateFrequency = UpdateFrequency.Update1;
}
public void Main(string argument, UpdateType updateSource)
{
//先获取一个TextPanel
List<IMyTextPanel> panels=new List<IMyTextPanel>();
GridTerminalSystem.GetBlocksOfType<IMyTextPanel>(panels);
Echo("共有LCD个数:"+panels.Count);
if(panels.Count<1)return;
//取第一个
IMyTextPanel panel=panels[0];
Echo("目标LCD是:"+panel.CustomName);
//开启LCD显示
panel.ShowPublicTextOnScreen();
//输出字符
panel.WritePublicText("字符输出实验");
//追加字符
panel.WritePublicText(tick++.ToString(),true);
}
~~~
**执行结果**
第一个LCD上显示“字符输出实验”,并且数字快速跳动。
我们还能够观察到LCD上有一些非字符的残余图像,出于这个,我们其实可以合理推测,LCD上显示的其实是根据文本即时绘制的图像。
其实TextPanel也有 *AddImagesToSelection* 、*ClearImagesFromSelection*、*RemoveImageFromSelection*、*ChangeInterval*、*ShowTextureOnScreen*这些接口,应该就是用于显示图像。但是具体怎么用,这里就不做尝试了。
### Cockpit输入模拟鼠标键盘
尝试从Cockpit获取运动状态来获取鼠标键盘输入,当然,键盘肯定是不全的。
~~~csharp
public Program()
{
Runtime.UpdateFrequency = UpdateFrequency.Update1;
}
public void Main(string argument, UpdateType updateSource)
{
//先获取一个TextPanel
List<IMyTextPanel> panels=new List<IMyTextPanel>();
GridTerminalSystem.GetBlocksOfType<IMyTextPanel>(panels);
Echo("共有LCD个数:"+panels.Count);
if(panels.Count<1)return;
//取第一个
IMyTextPanel panel=panels[0];
//再获取名为Mouse&Arrow的驾驶舱
IMyShipController mak=(IMyShipController)GridTerminalSystem.GetBlockWithName("Mouse&Arrow");
if(mak==null){
Echo("Mouse&Arrow not Found.");
return;
}
//通过驾驶舱运动指示来判断鼠标和键盘
Vector2 mousemove=mak.RotationIndicator;
string key=getKey(mak);
panel.ShowPublicTextOnScreen();
panel.WritePublicText("鼠标X:"+mousemove.X);
panel.WritePublicText("\r\n鼠标Y:"+mousemove.Y,true);
panel.WritePublicText("\r\n键盘按键:"+key,true);
}
private string getKey(IMyShipController keyboard){
if(keyboard.MoveIndicator.X==-1)return "A";
if(keyboard.MoveIndicator.X==1)return "D";
if(keyboard.MoveIndicator.Y==-1)return "C";
if(keyboard.MoveIndicator.Y==1)return "Space";
if(keyboard.MoveIndicator.Z==-1)return "W";
if(keyboard.MoveIndicator.Z==1)return "S";
if(keyboard.RollIndicator>0)return "E";
if(keyboard.RollIndicator<0)return "Q";
return "";
}
~~~
**执行结果**
能够基本正确识别和显示鼠标和键盘输入。
这些运动状态除了翻译成鼠标键盘输入外,一般还可直接作为控制输入,用于实现各类运动控制,比如重力引擎。
#### 天线通信
使用天线发送和接收消息,并将消息显示在LCD上。
~~~csharp
public Program()
{
Runtime.UpdateFrequency = UpdateFrequency.Update1;
}
int tick=0;
int received=0;
public void Main(string argument, UpdateType updateSource)
{
//获取一个屏幕
List<IMyTextPanel> panels=new List<IMyTextPanel>();
GridTerminalSystem.GetBlocksOfType<IMyTextPanel>(panels);
Echo("共有LCD个数:"+panels.Count);
if(panels.Count<1)return;
IMyTextPanel panel=panels[0];
panel.ShowPublicTextOnScreen();
//获取一个IMyRadioAntenna
List<IMyRadioAntenna> antennas=new List<IMyRadioAntenna>();
GridTerminalSystem.GetBlocksOfType<IMyRadioAntenna>(antennas);
Echo("共有Antenna个数:"+antennas.Count);
if(antennas.Count<1)return;
IMyRadioAntenna antenna=antennas[0];
//设定广播接收
antenna.AttachedProgrammableBlock=Me.EntityId;
//处理通信
if(updateSource==UpdateType.Antenna){
Echo("Receiving...");
Echo(argument);
panel.WritePublicText(argument+"\n",received++>0);
}else{
if(received>0){Echo("Received "+received);received=0;}
Echo("Sending...");
antenna.TransmitMessage(tick++.ToString());
}
}
~~~
**执行结果**
将具备LCD和天线的实验网格复制多份,可以看到同时发出和收到多份消息。
说明目前版本的太空工程师允许全双工收发,并且可以多次接收消息。
### 资料
#### LCD的接口IMyTextPanel
~~~csharp
public interface IMyTextPanel : IMyTextSurface, IMyFunctionalBlock, IMyTerminalBlock, IMyCubeBlock, IMyEntity
{
// Methods
void AddImagesToSelection(List<string> ids, bool checkExistence = false);
void AddImageToSelection(string id, bool checkExistence = false);
void ClearImagesFromSelection();
void GetFonts(List<string> fonts);
[Obsolete("LCD private text is deprecated")]
string GetPrivateText();
[Obsolete("LCD private text is deprecated")]
string GetPrivateTitle();
string GetPublicText();
string GetPublicTitle();
void GetSelectedImages(List<string> output);
void ReadPublicText(StringBuilder buffer, bool append = false);
void RemoveImageFromSelection(string id, bool removeDuplicates = false);
void RemoveImagesFromSelection(List<string> ids, bool removeDuplicates = false);
void SetShowOnScreen(ShowTextOnScreenFlag set);
[Obsolete("LCD private text is deprecated")]
void ShowPrivateTextOnScreen();
void ShowPublicTextOnScreen();
void ShowTextureOnScreen();
[Obsolete("LCD private text is deprecated")]
bool WritePrivateText(string value, bool append = false);
[Obsolete("LCD private text is deprecated")]
bool WritePrivateTitle(string value, bool append = false);
[Obsolete("LCD public text is deprecated")]
bool WritePublicText(string value, bool append = false);
[Obsolete("LCD public text is deprecated")]
bool WritePublicText(StringBuilder value, bool append = false);
bool WritePublicTitle(string value, bool append = false);
// Properties
string CurrentlyShownImage { get; }
ShowTextOnScreenFlag ShowOnScreen { get; }
bool ShowText { get; }
float FontSize { get; set; }
Color FontColor { get; set; }
Color BackgroundColor { get; set; }
float ChangeInterval { get; set; }
string Font { get; set; }
}
~~~
#### IMyTextSurface
~~~csharp
public interface IMyTextSurface
{
// Methods
void AddImagesToSelection(List<string> ids, bool checkExistence = false);
void AddImageToSelection(string id, bool checkExistence = false);
void ClearImagesFromSelection();
MySpriteDrawFrame DrawFrame();
void GetFonts(List<string> fonts);
void GetScripts(List<string> scripts);
void GetSelectedImages(List<string> output);
void GetSprites(List<string> sprites);
string GetText();
Vector2 MeasureStringInPixels(StringBuilder text, string font, float scale);
void ReadText(StringBuilder buffer, bool append = false);
void RemoveImageFromSelection(string id, bool removeDuplicates = false);
void RemoveImagesFromSelection(List<string> ids, bool removeDuplicates = false);
bool WriteText(StringBuilder value, bool append = false);
bool WriteText(string value, bool append = false);
// Properties
float FontSize { get; set; }
Color ScriptForegroundColor { get; set; }
Color ScriptBackgroundColor { get; set; }
float TextPadding { get; set; }
bool PreserveAspectRatio { get; set; }
Vector2 TextureSize { get; }
Vector2 SurfaceSize { get; }
ContentType ContentType { get; set; }
string Script { get; set; }
TextAlignment Alignment { get; set; }
string Font { get; set; }
float ChangeInterval { get; set; }
byte BackgroundAlpha { get; set; }
Color BackgroundColor { get; set; }
Color FontColor { get; set; }
string Name { get; }
string DisplayName { get; }
string CurrentlyShownImage { get; }
}
~~~
#### IMyShipController
~~~csharp
public interface IMyShipController : IMyTerminalBlock, IMyCubeBlock, IMyEntity
{
// Methods
MyShipMass CalculateShipMass();
Vector3D GetArtificialGravity();
Vector3D GetNaturalGravity();
double GetShipSpeed();
MyShipVelocities GetShipVelocities();
Vector3D GetTotalGravity();
bool TryGetPlanetElevation(MyPlanetElevation detail, out double elevation);
bool TryGetPlanetPosition(out Vector3D position);
// Properties
bool CanControlShip { get; }
bool IsUnderControl { get; }
bool HasWheels { get; }
bool ControlWheels { get; set; }
bool ControlThrusters { get; set; }
bool HandBrake { get; set; }
bool DampenersOverride { get; set; }
bool ShowHorizonIndicator { get; set; }
Vector3 MoveIndicator { get; }
Vector2 RotationIndicator { get; }
float RollIndicator { get; }
Vector3D CenterOfMass { get; }
bool IsMainCockpit { get; set; }
}
~~~
#### IMyRadioAntenna
~~~csharp
public interface IMyRadioAntenna : IMyFunctionalBlock, IMyTerminalBlock, IMyCubeBlock, IMyEntity
{
// Methods
bool TransmitMessage(string message, MyTransmitTarget target = 3);
// Properties
float Radius { get; set; }
bool ShowShipName { get; set; }
bool IsBroadcasting { get; }
bool EnableBroadcasting { get; set; }
long AttachedProgrammableBlock { get; set; }
bool IgnoreAlliedBroadcast { get; set; }
bool IgnoreOtherBroadcast { get; set; }
}
~~~
#### IMyLaserAntenna
~~~csharp
public interface IMyLaserAntenna : IMyFunctionalBlock, IMyTerminalBlock, IMyCubeBlock, IMyEntity
{
// Methods
void Connect();
void SetTargetCoords(string coords);
bool TransmitMessage(string message);
// Properties
bool RequireLoS { get; }
Vector3D TargetCoords { get; }
bool IsPermanent { get; set; }
[Obsolete("Check the Status property instead.")]
bool IsOutsideLimits { get; }
MyLaserAntennaStatus Status { get; }
long AttachedProgrammableBlock { get; set; }
float Range { get; set; }
}
~~~
#### UpdateType
~~~csharp
public enum UpdateType
{
None = 0,
Terminal = 1,
Trigger = 2,
Antenna = 4,
Mod = 8,
Script = 0x10,
Update1 = 0x20,
Update10 = 0x40,
Update100 = 0x80,
Once = 0x100
}
~~~
- 序言
- 写在前面的话
- 太空工程师
- 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