トラッカー
1| IMyTextPanel DataPanelL;
2| IMyTextPanel DataPanelR;
3| IMyRadioAntenna ant;
4| IMyGyro gyro;
5| List<IMyCameraBlock> cams = new List<IMyCameraBlock>();
6| MyDetectedEntityInfo curr_info;
7| int count = 0;
8| string status = "Ready";
9| int cycle_time = 166;
10|
11| public Program()
12| {
13| DataPanelL = GridTerminalSystem.GetBlockWithName("LCD Panel L") as IMyTextPanel;
14| DataPanelR = GridTerminalSystem.GetBlockWithName("LCD Panel R") as IMyTextPanel;
15| ant = GridTerminalSystem.GetBlockWithName("SS Antenna") as IMyRadioAntenna;
16| gyro = GridTerminalSystem.GetBlockWithName("Gyroscope") as IMyGyro;
17| GridTerminalSystem.GetBlocksOfType(cams);
18| Runtime.UpdateFrequency = UpdateFrequency.Update10;
19|
20| foreach(var cam in cams)
21| cam.EnableRaycast = true;
22| }
23|
24| public void Main(string argument, UpdateType updateSource)
25| {
26| var com = argument.Split(',');
27| count = count >= 60 ? 0 : count+1;
28| ShowData();
29|
30| switch (com[0]){
31| case "Launch":
32| ant.TransmitMessage("Launch,"+com[1]);
33| status = "Tracking";
34| break;
35| case "Detonate":
36| ant.TransmitMessage("Detonate");
37| break;
38| }
39|
40| switch (status){
41| case "Ready":
42| status = Search();
43| break;
44| case "Tracking":
45| status = Tracking();
46| string msg = "Target,";
47| msg += curr_info.Position.X.ToString()+",";
48| msg += curr_info.Position.Y.ToString()+",";
49| msg += curr_info.Position.Z.ToString()+",";
50| msg += curr_info.Velocity.X.ToString()+",";
51| msg += curr_info.Velocity.Y.ToString()+",";
52| msg += curr_info.Velocity.Z.ToString();
53| ant.TransmitMessage(msg);
54| break;
55| }
56|
57| if (count % 2 == 0 && status != "Ready")
58| AttitudeControl();
59| else
60| gyro.SetValue("Override", false);
61| }
62|
63| private void ShowData()
64| {
65| string info = "Status: " + status + "\n";
66| info += "Position X: " + curr_info.Position.X.ToString("0.0\n");
67| info += "Position Y: " + curr_info.Position.Y.ToString("0.0\n");
68| info += "Position Z: " + curr_info.Position.Z.ToString("0.0\n");
69| info += "Distance: ";
70| info += !curr_info.IsEmpty() ? (curr_info.Position-Me.GetPosition()).Length().ToString("0.0\n") : "0.0\n";
71| info += "Speed: " + curr_info.Velocity.Length().ToString("0.0\n");
72| info += "Velocity X: " + curr_info.Velocity.X.ToString("0.0\n");
73| info += "Velocity Y: " + curr_info.Velocity.Y.ToString("0.0\n");
74| info += "Velocity Z: " + curr_info.Velocity.Z.ToString("0.0\n");
75| DataPanelL.WriteText(info);
76| }
77|
78| private string Tracking()
79| {
80| Vector3D Est_Pos = curr_info.Position + (curr_info.Velocity * cycle_time / 1000);
81| curr_info = cams[count].Raycast(Est_Pos);
82| return curr_info.Relationship == MyRelationsBetweenPlayerAndBlock.Enemies ? "Tracking" : "Ready";
83| }
84|
85| private string Search()
86| {
87| string result = "Ready";
88| curr_info = cams[count].Raycast(15000, 0, 0);
89|
90| if (curr_info.Relationship == MyRelationsBetweenPlayerAndBlock.Enemies){
91| var SB = GridTerminalSystem.GetBlockWithName("Sound Block") as IMySoundBlock;
92| SB.ApplyAction("PlaySound");
93| result = "Tracking";
94| }
95| return result;
96| }
97|
98| private void AttitudeControl()
99| {
100| float scaling_factor = 10;
101| Vector3D destV = curr_info.Position - Me.GetPosition();
102| destV.Normalize();
103| double angleL = Math.Acos(Vector3D.Dot(Me.WorldMatrix.Left, destV));
104| double angleD = Math.Acos(Vector3D.Dot(Me.WorldMatrix.Down, destV));
105|
106| gyro.SetValue("Override", true);
107| gyro.SetValue("Yaw", (Single)(scaling_factor*(Math.PI/2-angleL)));
108| gyro.SetValue("Pitch", (Single)(-scaling_factor*(Math.PI/2-angleD)));
109| }
Runtime.UpdateFrequency を設定(18 行)して Main メソッドを 10 tick 毎に実行します。Main メソッドではミサイルの発射・自爆コマンドの処理(30-38 行)と、トラッカーの状態に応じた処理(40-55 行)を行います。また、トラッカーがターゲット追跡状態の場合、姿勢制御を行う AttitudeControl() を呼びます。ただ、追跡状態のときに常に AttitudeControl() を呼んでしまうと、ターゲットが捕捉範囲外になるまで手動制御が完全にできなくなってしまうので、2 回に 1 回呼び出しています(57‐58 行)。
57| if (count % 2 == 0 && status != "Ready")
58| AttitudeControl();
トラッカーが追跡状態の場合 (status == "Tracking")、10 tick 毎に Tracking() が呼ばれます。直近のスキャンで得られたターゲットの座標 (curr_info.Position) と速度 (curr_info.Velocity) から、現在の予測座標 (Est_Pos) を計算してRaycast を実行します。スキャンの結果、予測座標にターゲットを検出したら curr_info を更新します(80-81 行)。ターゲット座標の予測に必要な、前回のスキャンからの経過時間は cycle_time = 166 に設定しています。実行環境に依存するのかわかりませんが、Main メソッドの実行間隔を実測したところ 166 ms/回だったのでこの値に設定しています。
78| private string Tracking()
79| {
80| Vector3D Est_Pos = curr_info.Position + (curr_info.Velocity * cycle_time / 1000);
81| curr_info = cams[count].Raycast(Est_Pos);
82| return curr_info.Relationship == MyRelationsBetweenPlayerAndBlock.Enemies ? "Tracking" : "Ready";
83| }
トラッカーが Ready 状態の場合、10 tick 毎に Search() が呼ばれます。トラッカー前方 15,000 m までをスキャンし(88 行)、ターゲットが検出されたら status を "Tracking" に変更し(90-94 行)、追跡状態に移行します。
85| private string Search()
86| {
87| string result = "Ready";
88| curr_info = cams[count].Raycast(15000, 0, 0);
89|
90| if (curr_info.Relationship == MyRelationsBetweenPlayerAndBlock.Enemies){
91| var SB = GridTerminalSystem.GetBlockWithName("Sound Block") as IMySoundBlock;
92| SB.ApplyAction("PlaySound");
93| result = "Tracking";
94| }
95| return result;
96| }
トラッカーが追跡状態の場合、Main メソッドから姿勢制御のため AttitudeControl() が呼ばれます。まずトラッカーからターゲットまでのベクトル destV を計算し(101 行)、トラッカーの左向きベクトル Me.WorldMatrix.Left と 下向きベクトル Me.WorldMatrix.Down それぞれが destV なす角を求めます(103-104 行)。最後にジャイロブロックをオーバーライドして、それぞのなす角が 90° に近づくように Yaw と Pitch を設定します(106-108 行)。
98| private void AttitudeControl()
99| {
100| float scaling_factor = 10;
101| Vector3D destV = curr_info.Position - Me.GetPosition();
102| destV.Normalize();
103| double angleL = Math.Acos(Vector3D.Dot(Me.WorldMatrix.Left, destV));
104| double angleD = Math.Acos(Vector3D.Dot(Me.WorldMatrix.Down, destV));
105|
106| gyro.SetValue("Override", true);
107| gyro.SetValue("Yaw", (Single)(scaling_factor*(Math.PI/2-angleL)));
108| gyro.SetValue("Pitch", (Single)(-scaling_factor*(Math.PI/2-angleD)));
109| }