複数のブロックに一括してアクションを行うプログラムを解説します。
適宜Official Guide: Programmable block(以下「公式ガイド」)を参照してください。
サンプルプログラム
まずは電力が供給されているステーションやラージシップを用意し、Programmable Blockといくつかのドアを設置しましょう。
ブロックAでブロックBを操作する際は、ブロックAが、Bを操作して良い立場である必要があります。今回の場合はプログラマブルブロックとドアのOwner(所有権者)が「Nobody」か「Me」でどちらのブロックも同じになっていればとりあえずOKです。
Programmable Blockのエディタを開いて、以下のプログラムを貼り付けてください。
void Main(string argument){ List<IMyTerminalBlock> targets = new List<IMyTerminalBlock> (); GridTerminalSystem.GetBlocksOfType<IMyDoor>(targets);
for (int i = 0; i < targets.Count; i++) { targets[i].GetActionWithName("Open").Apply(targets[i]); } }
このプログラムをRun(実行)すると、その船にある全てのドアの開閉が切り替わります。
解説
上記は「Interface nameがIMyDoorのブロックを全て取得し、Openというアクションを行う」プログラムです。以下で各部について解説します。
2行目:ブロックを入れるリストを作る
List<IMyTerminalBlock> リストの名前 = new List<IMyTerminalBlock> ();
↓
List<IMyTerminalBlock> taegets = new List<IMyTerminalBlock> ();
まずは2行目。ブロックを入れるリストを任意の名前で作ります。今回はリストの名前を「targets」としました。
3行目:ブロックをリストに追加する
GridTerminalSystem.GetBlocksOfType<ブロックのインターフェースネーム>(リストの名前);
↓
GridTerminalSystem.GetBlocksOfType<IMyDoor>(targets);
3行目、先ほど作った「targets」リストに、その船にある特定のInterface name(ブロック名とは別の、プログラムで扱うための名前)を持ったブロックをすべて追加する部分です。
今回扱いたいのはドアなので、公式ガイドからDoorのInterface nameを探すと、「IMyDoor」とあるのでこれを設定しました。
5行目:リストに入れた全てのブロックにアクションを実行させる
for (int i = 0; i < リストの名前.Count; i++) { 操作対象.GetActionWithName("アクション名").Apply(操作対象); }
↓
for (int i = 0; i < targets.Count; i++) { targets[i].GetActionWithName("Open").Apply(targets[i]); }
5行目から7行目。for文を使って、変数「i」がリストの最後と同じ番号になるまで{}の中を繰り返します。任意のリストの全部に対して何かする以外の使い方は最初のうちはあまり無いでしょうから、for文自体については割愛します。
このゲーム的には6行目、for文の中が重要です。.GetActionWithName().Apply()は1つ目の()内にアクション名を入れることで、対象のブロックにそのアクションを行わせます。上記では扱う対象として先ほどのtargrtsリストのi番目*1ということで「targets[i]」と設定しました。また、最後の括弧にも同じ対象を書いておく必要があります。
次にアクションの内容です。公式ガイドからDoorのActionsを探すと、
「Openというアクション名が、ターミナルやツールバーでのOpen/Closedを指す」ことがわかります。なので、括弧内には「"Open"」と入力しました。
ちなみに、ガイドのとおり開けるだけは"Open_On"、閉じるだけは"Open_Off"です。実用的にはこれらのほうが使用機会が多いと思います。
引用符の有無についてですが、初心者はとりあえず「GetActionWithName()の括弧内にアクションの名前を直接書くときは""で囲う」とだけ把握しておきましょう。
基本の解説は以上です。
応用編:特定の名前を持つドアだけを開閉する
for文の中で条件を指定して、特定の名前を持つドアだけが開閉するようにしてみましょう。
先にターミナルメニューでいくつかのドアの名前を変更し、名前の適当な部分に「Alpha」と入れておきましょう(例:「Door 2 Alpha」)。
先ほどのプログラムから6行目の
targets[i].GetActionWithName("Open").Apply(targets[i]);
を削除して代わりに次のように書きます。
string name = targets[i].CustomName; if (name.Contains("Alpha")){ targets[i].GetActionWithName("Open").Apply(targets[i]); }
if文は()の中の条件が満たされている(true)場合に{}の内容を実行します。
今回は、for文の中でまず今扱っているブロック名を読み取っておき、その中に「Alpha」という文字列が含まれていれば開閉を実行し、そうでなければ無視するプログラムになります。
変更結果の1行目:ブロック名の読み取り
string 変数の名前 = 対象.CustomName;
↓
string name = targets[i].CustomName;
ブロックの名前を入れる場所として、今回は「name」という名前でstring(文字列)型の変数を作りました。
.CustomNameは前に付けた対象となるブロックのCustomName、つまりターミナルメニューで設定できるブロックの名前を読み取る機能なので、先ほどと同様にtargetsのi番目のブロック(targets[i])を指定し、そのCustomNameをname変数に代入します。
変更結果の2行目:if文の条件
if (比較対象となる文字列.Contains("文字列")){
↓
if (name.Contains("Alpha")){
.Contains()は、その前の文字列の中に、()内の文字列が含まれていればtrueになります。
先ほどブロックの名前を書き込んだ「name」変数を前の文字列として設定し、比較用の文字列は「Alpha」としました。
ここも「任意の文字列を直接書くときは""が要る」とだけ把握して追々知っていけば良いでしょう。
if (targets[i].CustomName.Contains("Alpha")){
おまけ
応用編のプログラム完成系
void Main(string argument){ List<IMyTerminalBlock> targets = new List<IMyTerminalBlock> (); GridTerminalSystem.GetBlocksOfType<IMyDoor>(targets);
for (int i = 0; i < targets.Count; i++) { string name = targets[i].CustomName; if (name.Contains("Alpha")){ targets[i].GetActionWithName("Open").Apply(targets[i]); } } }
文字列をArgument欄から読ませた例
void Main(string argument){ List<IMyTerminalBlock> targets = new List<IMyTerminalBlock> (); GridTerminalSystem.GetBlocksOfType<IMyDoor>(targets);
for (int i = 0; i < targets.Count; i++) { string name = targets[i].CustomName; if (name.Contains(argument)){ targets[i].GetActionWithName("Open").Apply(targets[i]); } } }