前回「MakeCode Arcade キャラクターの動かし方」で紹介した「タイルマップに合わせて動かす」方法についてもう少し移動している感じを出したくて工夫してみました。
この工夫について紹介しようと思います。(こだわりがなければ前回紹介した実装のままの方がブロック全体がすっきりしているのであまりおススメしません)
プログラム全体
まずはプログラム全体を見てください。
これだけのブロックを使っていますが実装できているのはキャラクターのマス目移動だけです。(デバッグ用のブロックも有りますが移動と直接関係ないブロックは全部で10個も無いと思います)
実際に中身を見たり動作の確認をしてみたい方にこのプログラムを公開しているので見てみてください。
各ブロック解説
それでは各ブロックのかたまりごとに解説していきます。
「最初だけ」ブロック群
「最初だけ」ブロックにはキャラクターとなるスプライトの生成と各種パラメータ値の初期化をしています。(変数「ゴースト」は説明用スプライトで実装は不要です)
基本的にスプライトの現在値はドット単位のXY座標となるのですがタイルのサイズが16×16になるため、これを考慮して座標の指定が必要になります。
そこで16×16を1マスと決め、左上を0行目の0列目とし、現在地と目的地を行列のマス目で指定できるように「現在列」「現在行」「目的列」「目的行」の4つの変数を用意しています。
「速度」は移動する速度、「目的座標しきい値」は目的地へ到達したか判定の振れ幅(詳しくは「「関数移動処理」ブロック群」で説明)、「移動中フラグ」は移動中かどうかを示す変数です。
「目的X座標」「目的Y座標」は目的地の行列指定を座標に変換した結果の値を保持する変数です。
その他のブロックは移動に直接関係ないため省略します。
「関数行列値を座標値に変換する」ブロック群
マス目の行列と座標値は相互に変換できる仕組みが必要になります。
そこで「関数行列値を座標値に変換する」ブロックでは引数(パラメータ)で要求した行列値に対して座標値に変換して戻します。
スプライトの座標は中心にあるため、行列値を16倍した座標値に対してオフセット分の8を足しています。
「関数座標値を行列値に変換する」ブロック群
「関数座標値を行列値に変換する」ブロックでは引数(パラメータ)で要求した座標値に対して行列値に変換して戻します。
スプライトの座標は中心にあるため、座標値に対してオフセット分の8を引き、16で割っています。
「ゲームが更新された時」ブロック群
ゲーム中は常に関数「移動処理」ブロックを呼び出し、細かい動作は関数内で制御しています。
「関数移動処理」ブロック群
移動に関する処理を一手に引き受ける、今回の主役の処理です。
最初のブロックはデバッグ用にスプライトの現在地(XY座標)を表示しています。
移動処理のため、「移動中フラグ」が「1(移動中)」の場合にのみ処理します。
次の分岐ブロックでは現在地と目的地を比較し、スプライトの進む方向を決めています。(それぞれ目的地のXが大きいなら右、小さいなら左、Yが大きいなら下、小さいなら上)
比較の目的座標に目的座標しきい値を足したり引いたりしている理由は現在地と目的地の差がピッタリ整数にならない事を想定し、誤差を吸収しています。
移動する方向が決まればスプライトをその方向に進むように変数「速度」の値を使って速度を指定し、移動させます。
目的座標しきい値内になった場合には分岐最後の処理(「でなければ」の部分)になります。
XYそれぞれの方向に対しての速度を「0」にし、目的座標しきい値内に居るため、目的地に到着したとみなしてスプライトの座標を目的座標に設定します。
目的地に到着したので現在行列に目的行列を設定して現在地と目的地を同じにします。
移動が完了したので「移動中フラグ」を「0(移動していない)」に設定します。
矢印ボタンイベントブロック群
矢印ボタンイベントブロックではプレイヤーからの矢印ボタン操作をきっかけに処理を実行します。
矢印は「上」「下」「左」「右」の全部で4つ、状態は「押した」と「くりかえし」の2つを使っています。
状態の「押した」は文字通り押したタイミングで実行され、「くりかえし」はボタンを押しっぱなしの間はずっと実行されます。
そのため、押したタイミングのみ移動を行いたい場合には「くりかえし」のイベントブロックは不要になります。
また、上記実装では押したタイミングで処理を実行し、そのまま押しっぱなしで移動を継続してくれるのですが「押した」から「くりかえし」までの時間が少し長く、速度が「100」では2マス目以降の移動がワンテンポ遅れる感じになりました。(ここら辺の細かい設定は見つからなかったので仕方無しとしました)
矢印ボタンを押した時の処理は各ボタン毎に処理を作ると冗長になるため、関数化しています。(中身の詳細は「「関数矢印ボタンを押した時処理」ブロック群」参照)
「関数矢印ボタンを押した時処理」ブロック群(全体)
この関数では矢印ボタンを押した時の処理を一括して行うための関数です。
他と比べて少し長くなってしまったので前半と後半に分けて説明していきます。
関数には引数(パラメータ)として「方向」を要求します。
これによりどの矢印ボタンが押された時の処理かを関数内で分岐する事ができます。
最初の分岐は「移動中フラグ」を確認しています。
「移動中フラグ」が「0(移動していない)」なら処理を続行し、それ以外(「1」の移動中を想定)ならそのまま関数を抜けます。(移動中であれば移動が完了するまで次の移動を受け付けないようにするためにこのような実装を行っています)
目的行列に現在行列を設定しているのは目的行列をリセットするためです。(処理が意図通りに動作していれば「「関数移動処理」ブロック群」にて一致していますが何らかの問題により相違となっている事を想定して保険で実装しました)
次の分岐では押された矢印ボタンに対応する目的行列に変更しています。(上なら現在行-1、下なら現在行+1、左なら現在列-1、右なら現在列+1)
ここでゴーストの配置を目的行列にしていますがデバッグとして目的地が正しいかを視覚的に表しているだけで実際の移動処理には必要ありません。
目的行列が更新されたため、その目的行列に行けるかどうかを判定しています。
本来、そのマスが壁かどうかを判定出来れば楽なのですがそのような判定ブロックが見当たらなかったので「tile ad is」ブロック代替えとして使っています。(このブロックではタイルを指定する必要があるため、スプライトが侵入できないタイルが複数ある場合はその分だけ判定が必要になります)
侵入不可のタイルだった場合には目的行列に現在行列を設定(リセット)して関数を抜けます。
侵入可能のタイルだった場合には目的行列を目的座標に変換して「移動中フラグ」を「1(移動中)」に変更して関数を抜けます。(「移動中フラグ」を「1」に変更したため、「関数移動処理」ブロック内の処理でスプライトは移動を開始します)
まとめ
これで色々なゲームに応用できると思いますがなかなかのボリュームだったと思います。
今回はマス目の移動をつなぎ目無くするための実装として紹介しましたがゲーム上どうしてもこの表現を使いたい場合を除けば使わない方が後々のメンテナンスも楽になると思います。
現在、拡張機能を自作する方法を調査中なのでそっちの進展があれば今回紹介した内容を拡張機能として提供できるかもしれません。(なかなかに道のりは険しそうですが)
コメント