2.5Dアクションゲームの経路制御
こんなゲーム作ってます
例大祭にこんなの持っていくよ pic.twitter.com/qhrS70zdtf
— FriendSea (@friendsea95) 2018年5月4日
秋例大祭で体験版頒布予定だよ pic.twitter.com/OFxDKZQHVo
— FriendSea (@friendsea95) 2018年10月8日
「風のクロノア」や「星のカービィ64」に似たシステムで,基本的には2Dのアクションゲームですが予め定められた経路に沿って進行します.
適当な曲線に沿ってキャラクターを動かせばいいのですが,地形との干渉や経路の分岐などを考えると少し工夫がいります.
使う曲線
適当な曲線に沿って動かすと書きましたが,ゲームでよく使われるBezierやB-Splineなどのスプライン曲線は今回の目的だとあまり向いていません. 代わりにインボリュート曲線のセグメントを組み合わせて経路を定義しています.
これには主に2つ理由があります.
- 曲線上の任意の点までの曲線の長さが欲しい
- 曲線の外の任意の点に対する曲線上の最近傍点を求めたい
これはスプライン曲線でやると近似や探索が必要になります.毎フレームキャラクターの数だけ呼ばれるので,できるだけ安い処理にしたくてインボリュート曲線を使っています.
経路に沿った座標変換
曲線には空間の向きの定義も含めて座標変換として実装しています.直交座標が曲線に沿ってグニャッと曲げられるような変換です.
左の座標系でのZ軸での動きが右曲線に沿った動きになります.そこで,キャラクターにローカルな座標を持たせてそれをZY平面で普通の2Dアクションゲームのように動かしてやればいいわけです.このローカル座標を座標変換に通してやれば経路に沿った動きになります.
ここで変換後のグリッドも等間隔にしているのが曲線の長さが欲しい理由です.等間隔でないとキャラクターの足の速さが場所によってバラついてしまいます.
地形との干渉の回避
当然ですがキャラクターは地面や壁に埋まっちゃだめです.Unityエンジンを使っているのでRigidbodyコンポーネントを使って移動させれば勝手に埋まらないようにしてくれます.
しかし,キャラクターの挙動を座標変換前のローカル座標で書いてるのに干渉の回避がUnityのワールド座標系で行われるから値がズレてくる.
そこで,さっきの座標変換の逆変換を使って干渉の回避後の座標をローカル座標に戻す処理を書いています.
ゲームエンジンの物理演算処理を順変換と逆変換で挟むような処理の流れです.
逆変換が必要なのが,曲線の最近傍点が欲しい理由です.求めた最近傍点までの曲線の長さが逆変換後のZ座標の値.
経路の分岐
ローカル座標の更新時に逆変換を使うと書きましたが,空間が曲がっている場合って逆変換が一意ではなくなります.例えばこういう例.
この図で赤い点Pを逆変換したとすると答えは2つあります.
- 上の曲がった経路でy<0のとこにいる
- 下のまっすぐな経路でy>0のとこにいる
ワールド座標の位置は同じなので実のところどっちでもいいのですが,より近い経路に属するように実装しています.今回の例だと上の曲がった経路になりますね.
ここでy軸方向に移動するとワールド座標は連続でありながら,属する経路が途中で切り替わるのがわかると思います.これを経路分岐に使ってます.
これはゲーム内での経路分岐の例で,見づらいですが黄色い線が経路です.足場に飛び乗ると左に曲がる経路に,そうでない場合は右に曲がる経路に乗ります.
その他にできること
経路の定義を工夫すれば様々な表現ができるようになります.
重力方向を変える
別に座標変換の上方向がワールド座標の上方向になっている必要はありません.上方向を変えれば天井や壁を歩けます.動的に方向を変えてもいいぞ.
動く足場
キャラクターを乗せて動く足場.キャラクターを足場と一緒に動かすのが結構面倒だったりします.
足場に1つ経路を定義して,その座標変換ごと動かせば実装できます.
この画像の月は,重力方向を曲げた経路を回転させています.キャラクターはそれに乗って回る.
おしまい
今回紹介したような仕組みでの経路制御,作っていて面白いです.
複雑にすればするほど組んでて楽しくなるのですが,ゲーム内ではかなりシンプルになっています.
なぜならカメラワークが爆発するから.
あまり賢いカメラ制御も思いつかず,経路分岐が複雑になれば手作業での指定ばかりになります.これがキツイので真横から映しただけでちゃんと見えるような分岐しか使えていません.