
【GameMaker】Callback Method(コールバックメソッド)について②
GameMakerアドベントカレンダー2024 の18日目です。
GameMakerで配列関数などを扱う上でよく出るコールバックメソッドについての記事ですが、あまりにも長くなったため記事を分割しました。今回はその②です。
配列関数の中でもよく使うソート・フィルターに絡めて書いていきます。
構造体を含む配列に対する処理
私が今作ってるゲームは、大体こんな感じのデータ構造を使ってアイテムを管理しています。
global.itemlist = [
{name : "sword", material : "iron", attack : 5 , defense : 0},
{name : "shield", material : "wood", attack : 0 , defense : 2},
{name : "spike_shield", material : "iron", attack : 3, defense : 5}
]
名前やステータスを含む構造体を、一つの配列にまとめて作っています。
こういうものを作ると、特定の中身を抽出したり並び替えたりで配列関係の関数を死ぬほど使います。
従って、コールバックメソッド・述語メソッドを死ぬほど書くようになりましたのでその一例を紹介したいと思います。
配列のフィルター
例えばこれらのアイテムを販売する武器屋さんを実装するとして、
例ではまだ3つのアイテムしかありませんが、実際にはウン百種類くらいの・・・いやウン十種類くらいのアイテムは用意したいので(妥協)
そうなると絶対に欲しいのがフィルター・ソート機能です。
array_filter(array, function, [offset], [length]);まず、フィルターですが、array_filter関数を使います。
記事①で出てきたarray_find_index同様第二引数に述語メソッドが入ります。
例として、上記global.itemlistからmaterialが"iron"のアイテムを抽出してみたいと思いますので、そのように述語メソッドを書きます。
var _filter_iron = function(_element)
{
return _element.material == "iron";
}
filtered_list = array_filter(global.itemlist,_filter_iron);今回配列の各要素(_element)は構造体になりますので、
ピリオドをつけてメンバ変数materialを参照し、値が"iron"かどうかを判定します。
また、array_filter関数は、フィルターした要素を新しい配列にして返しますので、新たにfilterd_list変数を作ってそこに入れます。
show_debug_messageなどで中身を見るとこうなっています。
[ { name : "sword", material : "iron", attack : 5, defense : 0 },
{ name : "spike_shield", material : "iron", attack : 3, defense : 5 } ]
materialを"wood"にしていたアイテムを除いた二つのアイテムが格納されていることがわかります。
応用:method関数を使用し動的に条件付けを行う
さて、お次にmaterialが"wood"のアイテムを抽出するために
述語メソッド_filter_woodを作ってもよいのですが、
materialがさらにstoneだのbronzeだのcopperだのorihalconだの増えた時に、一つ一つの条件を述語メソッドとして用意するのは面倒極まりないです。
そこで、method関数の出番です。メソッドと名のつくものが多すぎないか??
method関数については、私も全容を把握しているわけではないので、
コードだけ載せておきます。詳しい解説は下記記事で(数少ないGameMakerの日本語解説神ブログです。しかも結構マニアックなネタも多い。初学者も慣れているユーザーも見て)
global.itemlist = [
{name : "sword", material : "iron", attack : 5 , defense : 0},
{name : "shield", material : "wood", attack : 0 , defense : 2},
{name : "spike_shield", material : "iron", attack : 3, defense : 5}
]
var _filter_material = function(_element)
{
return _element.material == material; // 後述のmethod関数によってインスタンス変数materialと照合できるようになる
}
material = "wood"; // ここに抽出したいmaterialを代入
//構造体化したインスタンス変数materialに、method関数で述語メソッド_filter_materialをバインド
filtered_list = array_filter(global.itemlist,method({material:material},_filter_material));
show_debug_message(filtered_list); //materialで指定した種類のアイテムが抽出される
//show_debug_messageの出力
[ { name : "shield", material : "wood", attack : 0, defense : 2 } ]配列のソート
お次はソート機能ですが、ちょっと述語メソッドの毛色が変わります。
というかこれはもう述語メソッドですらなくてカスタム関数とかいう名前で呼ばれてます。まあ似たようなもんなので本記事で解説します。
array_sort(variable, sorttype_or_function)第一引数variableには配列が入ります。なんでarrayじゃないんだ・・・?
第二引数sorttype_or_functionには、Booleanもしくはfunction、つまりカスタム関数が入ります。
というわけで、attackが高い順に並び替えるカスタム関数を書きます。
var _attack_sort = function(_elm1,_elm2)
{
return _elm2.attack - _elm1.attack;
}ちょっとよくわかりませんね。
var _attack_sort = function(_elm1,_elm2)まず、カスタム関数の引数です。
述語メソッド同様名前はなんでもいいんですが、数は2個必須です。たぶん。次で使うし。
このカスタム関数も配列内の要素に繰り返し実行されるわけですが、_elm1に現在のインデックスの要素、_elm2に次のインデックスの要素が自動的に代入されます。
この二つの値を比較することによってソートするわけですね。
{
return _elm2.attack - _elm1.attack;
}というわけで、二つの要素のattack値を比較するわけなんですが。
なんだこの引き算は。
理屈はよくわからないですが、このように書くと降順(高い順)にソートができます。
反対に
{
return _elm1.attack - _elm2.attack;
}のように書くと昇順(低い順)にソートすることができます。
var _attack_sort = function(_elm1,_elm2)
{
return _elm2.attack - _elm1.attack;
}
array_sort(global.itemlist,_attack_sort);
show_debug_message(global.itemlist);というわけで作ったカスタム関数をarray_sort関数の第二引数にぶちこんで実行します。
ちなみにarray_sortは新しい配列を返すのではなく元の配列の順序を直接書き換えます。
//show_debug_messageの出力結果
[ { name : "sword", material : "iron", attack : 5, defense : 0 },
{ name : "spike_shield", material : "iron", attack : 3, defense : 5 },
{ name : "shield", material : "wood", attack : 0, defense : 2 } ]
見づらいですがattackが高い順(5→3→0)に並び替えられましたね。
おわり
配列のフィルター・ソートに絡めたコールバックメソッドの説明は以上になります。
ここまで、コールバックメソッドの中でも、
「〇〇かどうか」の判定条件として使われる
述語メソッドにフォーカスしてお伝えして参りました。
最後にもう一つ、array_foreachやarray_mapなどで使われるコールバックメソッドについての説明がありますが、それはまた次回にします。