【U.G.M.】これが令和最新版の壁抜け対策です。
どうも、U.G.M.です。
今回はUnityの機能とプログラミングの話題が主となります。
「壁あれ」
もうすっかり見慣れましたね、緑色の正方形が壁のマップチップです。
ところでUnity 2Dですが、オブジェクト同士の衝突を検出するOnTriggerEnter2D(これは衝突した時の関数。2D/3Dが用意されており、さらにはEnter・Stay・Exitがある)を使えば楽勝じゃろ、と今朝は思っていました。今朝は。
ここで、問題の衝突処理(修正前)をご覧ください。
角からキーをにじりにじり操作することにより、どんどん壁にめり込んでゆくのが確認できます。ひぎぃ、裂けちゃう!
朝からこの挙動をなんとかするべくググり力(ぐぐりぢから)を駆使したものの、一向に良い方向に向かいません。青ぎさんに「明日記事書くわ!落としたら全裸土下座するよ!」と宣言してしまった手前、いまさら撤回するのはカッコ悪い。じわじわ浮いてくる冷や汗、痛みを増す腰。(みんな、マジで腰は大事にしような!)
こりゃ根本的に発想を転換する必要があるな……と思ったのが11時頃。疲れも見えてきました。
ここで伝家の宝刀である「わかんない時は……敢えて、寝るっ!」を炸裂させます。
起きた時に良い手段が降ってくる、それだけを信じて……。
12時頃、起きました。
ベターかどうかはわからないけど、良い方法が覚醒とともに降ってきます。後は実装と設定のみ。
さて、ここで「浮かんだ良い方法」を開陳してみます。何故か「Unity 壁抜け」でググっても同様の手段が見あたらないので、ここに忘備録。
まず「やりたいこと」を明確にします。今回の場合「壁にめり込まず移動を快適にする」ですね。
次に「そのために試したこと」を思い起こします。すべて失敗した方法ですが、その中にヒントが潜んでいるかもしれません。今回は全然なかったけど。
そして、この段階でようやく「やるべきこと」が出てきます。
冒頭のスクリーンショットを見ていただければ分かる通り、今回の壁は、いわゆるマップチップ方式を採用しています。
……ということは、壁が「ここは通さねぇよ、お帰りください」という命令を自分に出してあげることで実現できそうです。
2時間後、それを実現するコードがあらかた完成します。なんとか全裸土下座だけは免れそうです。
ここで、OnTriggerStay2D関数の一部をご紹介。
switch (other.obj_type)
{
case ObjectManager.TYPE.WALL:
{
if (obj_type == ObjectManager.TYPE.MYSHIP)
{
if (other.obj_mode == ObjectManager.MODE.HIT)
{
param[0] = 1; // 衝突があったスイッチ
Vector3 newpos = new Vector3(0, 0, 0); // 最終的な移動先
if (other.parampos[0].x != 0.0f) // 壁マップチップに移動先を問い合わせる
{
newpos.x = other.parampos[0].x;
}
if (other.parampos[0].y != 0.0f)
{
newpos.y = other.parampos[0].y;
}
if ((newpos.x != 0.0f) && (newpos.y != 0.0f)) // 両方に値が入った場合
{
float xx = Mathf.Abs(parampos[0].x - newpos.x); // 横座標優先で移動先を決める
float yy = Mathf.Abs(parampos[0].y - newpos.y);
if (xx < yy)
{
newpos.y = parampos[0].y;
}
else
{
newpos.x = parampos[0].x;
}
}
if (newpos.x == 0.0f) // 1フレーム前の座標を書き戻す
{
newpos.x = parampos[0].x;
}
if (newpos.y == 0.0f)
{
newpos.y = parampos[0].y;
}
parampos[1] = newpos; // 新しい(補正された)座標
this.transform.localPosition = newpos;
}
}
}
break;
}
今は壁しか存在しないので、衝突処理を行うのは「自分→壁」しかありません。(どうでもいい話だけど「MYSHIP」って定義名、後で「MYGIRL」にでも変えておきましょうね) 何をしているか、軽く解説します。
Unityの衝突処理はコールバック関数になっており、物体と物体が「衝突した・している・し終えた」時に呼び出されます。今回は1フレーム犠牲にしてでも綺麗に起こしたいので「している」関数、OnTriggerStay2D(Collider2D collider)を使います。何度でも言いますけど、初期化処理などは省いてキモの部分だけ書き出してあります。
浮かんだアイデア、それは「壁に衝突した時、壁から値を拾って自分を飛ばす」方法です。今回のゲームでは部屋と壁がギュッと凝縮されているので、速度的にも問題なさそうです。
自分と壁の衝突が起きた時に、壁(other)から壁の情報を拾ってきます。今回はVector3 parampos[0]に予め「壁に当たった場合の移動先座標」を定義してあるので、ざっくり拾ってきます。ここで拾ってきた座標に自分を移動させます。
この手段のめんどくさいところは「壁マップチップに予め値を埋め込んでおく必要がある」ことですが、たかだか3部屋2通路、この規模感なら「できらぁ!」と言っていいと思います。
で、出来上がりました。まずは動画をご覧ください。副作用として「自分を壁に押し付けながら動くことができる」ようになりました。引っ掛かりがなくなったのはストレスフリーでいいですね。
そして今……17時10分なのですが、私はこの記事を書いています。なげーよ。
念のため、改めて実行ファイルをご用意しました。格段に操作感が良くなっていると自画自賛しても許される感じになったのではないでしょうか。
20230810_The Room(仮)_壁抜けテスト版.zip (22.26MB)
ダウンロードそんなこんなで実行ファイルを置いておきます。だいぶ快適になっていますので、よろしければお試しください。
以上を3行でまとめると、下記のようになります。
壁抜けされた。
なので直した。
見てね。
お後がよろしいようで……。