高速ループ番外編3

前回は高速ループで出来なかったことを、ForEach ループ使って実現しました。

今回はインスタンスに対してループを回す ForEach についてもうすこし詳しく、特にインスタンスに対してループを回すというのはどういうことかについて書いていきます。

ちなみにビルドタイプは SWF に縛った内容(あるいは ForEach が使えるビルドタイプ)の記事となっているため、PC 用ビルドなら使えるその他のエクステンションを使うという選択肢を除外した内容になっています。


英語版を持ってない日本語版ユーザには申し訳ないのですが、ForEach を使いたい場合「販売代理店経由のサービス」を諦めて、英語版へのクロスアップグレードサービスを利用して格安で英語版に乗り換えちゃってください。発売からもう 3 年以上経っていますし、代理店への対応を期待するのは時間のムダです。ForEach 自体はユニコード版での動作を保障されています。オファーについての詳細は以下のリンクをみてね。

※クロスライセンス・オファーについて

インスタンスとは

インスタンス( instance )とはオブジェクト指向プログラミングでいうところのクラスを基にした実際の値としてのデータのこと。でも「クラスは型、インスタンスは実体(データ)」とかいきなり言われましても、うん……おそらく意味がさっぱりわかりませんよね??

これをむっちゃ噛み砕いて説明すると、MMF2 的にはコピーして作ったオブジェクトのことを指します。例えばアクティブオブジェクトならフレームエディタ上で最初に作ったアクティブ、さらにこれを複数コピーしてフレームに配置すればこれらがインスタンスと呼べます。

忍者が使う分身の術ってあるじゃないですか。いわゆる例えば少年ジャンプ的には、うずまきナルトが使う「影分身の術」がインスタンスです。残像で複数に見えるのではなくチャクラを使って術者の実体を複数作り出す、この術を MMF2 が使うとその分身をインスタンスと呼ぶのです。まず元となる型があって、そこから実体を作り出すとこれが実際のデータ=インスタンスになるわけです。ナルト見てない?

naruto_kagebunsin

じゃあ、その他物語(ストーリー)とかキャラクタという要素を取り除けば世の中にはインスタンス的なものはインターネットで探せばいろいろあふれています。ここでわざと話をすこし脱線してその他のちょっと危険な例を示して見ましょう。

instance_samples
クラス = 型(テンプレ)、インスタンス=データ(はんこ絵)です。

インスタンスへのループ処理について

インスタンスへの理解ができたところでドヤ顔してはまだいけません。次にインスタンスへのループ処理を考えるのが今回のテーマだからです。

インスタンスへのループ処理を MMF2 では標準で「アクションループ」と「通し番号 + 高速ループ + ループインデックス」という方法が使えます。これに 3 つ目のループ処理である「 ForEach (エクステンション)」が使えるようになると、日々の暮らしと日常のループ処理がいっそう便利になりますねってお話を前回しました。ForEach エクステンションはインスタンスを区別し、インスタンスに対してループを行うことに特化したエクステンションです。では高速ループとの違いはいったいなんなのでしょうか。

高速ループと ForEach の違い

MMF2 の高速ループもやはりすべてのインスタンスに対して命令を出せるけど、インスタンスを区別しながらループを回し命令を出す際に「通し番号」という連番をインスタンスがもつ固有の可変変数を使って ID として事前にナンバリングをしておかなければいけません。つまり影分身 1、影分身 2、影分身 3……って具合にです。

実際に影分身 1 ~ 3 に対してそれぞれ異なった命令を出すためには都合3回命令するんだけど、「おい、影分身!あれやって、あれ!」と言っても影分身は 1 ・ 2 ・ 3 といるのですからどの影分身が何したらいいか分からない指示では影分身が困ってしまう、だから事前にナンバーを与えて、「おい、影分身 1 、これやって」「影分身 2 はそれやって」「影分身 3 は何しなくていい」、こうなると具体的に誰が何をしたら良いか間違えない正しい指示が出せるわけです。通し番号が必要な理由はこんな感じ。

しかしこれが MMF2 の大変ややこしい部分で、初心者には最初なかなか理解しにくいでしょうと思われます。実際コレはなかなかふざけたループで場合によっては非常に効率が悪いことをやってる。

高速ループは通し番号とループインデックスをループ中に条件一致させることでインスタンスの区別を行っている……つまり「通し番号」は高速ループ使ってインスタンスにループ処理させる場合に必須になってしまうのがネックポイントなんです。

一方 ForEach についてはこれが高速ループと異なり、オブジェクト(のインスタンス)に対するループ処理を行うことに特化されたループ処理なのでまず通し番号が不要になります。通し番号がいらないってのは処理的に結構大きなメリットなのです。そして通し番号がいらないってことはオブジェクトが可変変数を持てない場合でも、例えばカウンタですね、高速ループではなかなか簡単に出来なかったインスタンスの区別が ForEach なら簡単にできちゃいますってのが前回までのお話。

しかし前回のサンプル、ForEach の使い方としては間違っていた部分があったんです。まずそれの訂正をしましょう。

インスタンスの区別には「 2 値の比較」は使えない件

「 2値の比較( compare two values )」という機能があるのはご存知かと思われます。しかし固定値でインスタンスの区別をする場合にはこれは機能せず、「値を参照してオブジェクトを選択( pick objects with reference to their value )」の「 fixed 」を利用しなければいけません。固定値でオブジェクトを選択する機能については、ブログ内の記事にあります

参考:MMF2 の基礎概念:固定値を指定する

pick_objects_reference_values

前回出したサンプルが一部イベントの解釈を間違えていた部分です。インスタンスに指示を出す場合、「値を参照してオブジェクトを選択」(固定値を使ってインスタンスを抽出)は使えるけど「 2 値の比較」からだと固定値を使ってもインスタンスの区別は MMF2 にはできません。この辺が諸事情あって前回のサンプルではごっちゃになってしまっていました(高速ループにはちゃんと固定値で抽出を使ってた)。

前回サンプルを作ったときの手順を時系列で考えると、最初に高速ループ使って作りその後 ForEach を使って作ったのですが、高速ループのときと同様にピックを使ったインスタンスへの指示を ForEach でも行っていたのですが、これが ForEach で置き換えて作り直したら上手に動かなかったのでForEachはループ仕様が高速ループと異なるからループ処理結果も異なるのだと筆者は最初考えていました。だから代用として試しに「 2 値の比較」を用いてしまいましたがまずこれが間違い。

しかも間違えてもこれでうまく動いてしまったのでてっきり解釈が正しいものと勘違いしていたのですが、ForEach でも「 2 値の比較」から固定値を使ったインスタンスの区別はできません。

しかし前回のサンプルがなぜちゃんと動いていたかというと、ForEach の機能ではなく MMF2 標準の内部仕様にある「インビルト・ペア」が働いていたからあたかも ForEach ループ処理で動いていたかのように振舞っていただけのようです。しかし「インビルト・ペア」の機能なのでエクステンションである ForEach は関係なく、MMF2 は本来アクションループを回すだけで自動的にカップリングされたインスタンスに指示が出せる機能があったのです。だからループ処理は ForEach も高速ループも不要になるため、実は「常に実行」だけで可能な命令でしたってわけ。

ちょっとまて、インビルト・ペアってなんだ?

MMF2 に標準で付いてた関連オブジェクトを自動的にペアにできる、昨日の夜知った機能のことです。[ 2013/02/26 ]

いつからこの機能が付いていたのかまったく知りませんでしたが、すでに 2011 年頃、某 ●SD さんがCT本家フォーラムで質問したときに「 inbuilt object pairing … 」という回答と動作サンプルを貰ってるのでこれ以前からあったものと思われます。ちな「インビルト・ペア」ってのは例のごとく筆者が勝手に命名した機能なので正式になんて呼ぶのか知りません。誰か教えてください。

この従来あまり知られていなかったはずの「インビルト・ペア」について。「インビルト・ペア」っていうのはオブジェクトを自動的にペアとして扱ってくれるというもの。アクティブとカウンターといった具合に種類の異なったオブジェクトでもカップリングできるし、数が一致して無くても正常にカップリングしてくれる場合もあるなんだかファジーな便利機能です。ちなみにカップルからあぶれたインスタンスがあっても、それらがどっか行って画面上で見えないんだけど、ほんとに内部的にはなにやってんのかわかんないけどとにかくすごい機能だ!とりあえずこれをみてくれ、どう思う?

inbuilt_sample02

inbuilt_sample00

inbuilt_sample01

サンプルのダウンロード:1

かなり便利な機能なんですが、MMF2 使い始めて 3 年ほど経った筆者でも国内ではいままで聞いたことの無い MMF2 の機能でした。しかも、この機能を意識して作ると高速ループの黄金パターンすらいらなくなる場合があるという超強力なもの。実際この機能を意識して作ると通し番号がいらないから、超ベンリなんです。

ここでおさらいですがイベントループ(ゲームループ)、高速ループ、そしてアクションループが MMF2 におけるループ処理の標準機能です。

ビルトイン・ペアというのは従来から知られていた「アクションループ」機能の一部です。「アクションループ」については以前からブログでも書かれていますが、これも MMF2 では一種の ForEach ループの仲間で存在が地味だけどインスタンスに対するループ処理を行うための非常に重要な機能です。

なら ForEach イラネーんじゃん?

いいえ、必要です。ただしインビルト・ペアを使うと従来だと高速ループなどが必要とされていた処理について、一部不要にできる部分はあるはずです。

オブジェクトを減らすときなどにやっぱりインスタンスに対するループ処理が必要になります。自動的にペアとして同期してくれるけど、オブジェクトの破壊が必要な場合には条件が一致したオブジェクトを確実に破壊するためにはインスタンスに対してループを回す必要が出てくる。ペアとして動作はするけど、破壊にまでは付き合ってくれないんですね。

この時ループを回す際には前述したように「通し番号」が不要になるので ForEach は相変わらず便利です。しかし通し番号が振れる場合には従来どおり高速ループでもインスタンスに対してループを回すことで適切な破壊処理ができるようになります。ForEach が使えない日本語版ユーザの場合、仕方ないから高速ループという選択肢があります。

というわけで、今回はやたら苦労して作ったシンプルなサンプルです。高速ループ版のサンプルは ASD さんが作ってくれたものです。どちらのサンプルも同じような動作ですが、今回のサンプルは共通して「同時に複数の衝突判定で破壊」する場合の処理が含まれています。つまり複数同時破壊の検出と破壊にまで対応してます。

サンプルは前回同様オブジェクトの追加、削除ができます。さらに今回は意見を取り入れてオブジェクトに「体力」という概念もつけてみました。もちろんミニマップサンプルから派生したものなのでミニマップも実装されてます。完成したイベント行は ForEach 版が 9 行になりました。そして今回は ForEach 使っているけど SWF 出力でも目立ったバグがありません。

サンプルのダウンロード:2

その他まとめ

高速ループ回す時はイベント行を分けたほうが無難です。どうしても行数減らしたいという欲求があるため、ついつい同じイベントラインに高速ループの開始アクションも含めてしまいがちなのですが、高速ループの仕様上ラインを分けたほうが良いです。

なぜこうなるのかはよくわかりませんが、経験則です。ASD さんの出してくれた高速ループのサンプルをみてください。実際にやってみればわかりますが、二行に分けないで一行にまとめるとおかしな動作をするときがあります。
notice_fastloop00