テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】Raycastを使わず地面に接触しているか判定する

f:id:tsubaki_t1:20180407224310g:plain

今回は2Dゲームで地面に接触しているかを判定するアプローチについてです。

 

 

ピヨちゃん大地に立つ判定

2Dゲーム…というか横スクロールなアクションゲームにおいては、キャラクターが地面に接触しているのかを判断する機能は、重要な要素の一つです。

 

例えば「二段ジャンプ」が出来るゲームでは、二回以上ジャンプさせない為に、「一度地面に接触したらリセット」のような機能が必要になります。

同様に「ジャンプ中はモーションが変わる」みたいな機能がある場合も、地面に接触しているか判定する必要が出てきます。

f:id:tsubaki_t1:20180407230329j:plain

 

ただし壁や天井は除く

接触しているかどうかを判断したい場合は、単純にColliderのコールバックにでも登録しておけば済む話です。

ここで問題になってくるのが、壁や天井といったものの存在です。
地面の判定を下したいのはあくまでも地面で、壁や天井といった部分に接触したときにも「着地」判定になるのは本意ではないです。

f:id:tsubaki_t1:20180407225726j:plain

上の画像で言えば、①のみ反応して②・③には反応してほしくないという感じです。

 

地面の接触判定をする

という事で、地面の接触判定を作っていきます。

やり方は色々あるのですが、今回はGetContacts系とContactFilter2Dを活用してみます。

 

Colliderに接触しているかどうかを取得する GetContactsやIsTouching

まずColliderに接触しているかどうかを取得します。

古いマニュアルを漁るとOnCollisionEnter2D的な物が見つかりますが、これはコールバックで、管理するためにはプロパティを持ったり色々と面倒くさいので、今回はGetContactsIsTouchingを使用します。

 

gist.github.com

IsTouchやGetContactsでは現在Rigidbodyが接触中のCollider一覧や情報が取得できるので、それでオブジェクトの情報を取得します。
これでメインループ…Updateの中で処理を記述出来て色々とスッキリします。

f:id:tsubaki_t1:20180407233709g:plain

GetContactsやTouchesで得る値を絞り込むフィルター

単純にGetContactsやTouchesをとるだけだと、壁や天井といった部分で接触判定が発生してしまいます。なので、取得時にフィルターをかけます。
例えば下のように、接触はするが「接触した」と判定するのは、Colliderの下の70度~110度の間のみ…という感じです(右が0度)

f:id:tsubaki_t1:20180407233202j:plain

 

[SerializeField] ContactFilter2D filter2d; でColliderのフィルターを公開して、UseNormal Angleにチェックを入れます。後は判定に利用する角度をMin Normal AngeltとMax Normal Angleに指定します。

f:id:tsubaki_t1:20180407233104j:plain

後はGetContactsIsTouchingの引数にContactFilter2Dを設定してやれば、この二つでの接触判定は特定の角度のもののみとなります。


Rayを飛ばすアプローチを考える

よく言われているアプローチは、地面に向けてRaycastやCircleCastを飛ばすアプローチです。これは大抵の場合は問題ないですが、実は少しだけ面倒くさい所があります。

問題は三つ。

  • Rayの長さ調整が面倒くさい
  • 離れても反応することがある
  • 自身のColliderと接触しないようにLayerを分けないといけない

1と2は実際には長さに関する問題で、Rayの長さ判定が短すぎると接触判定にならず、同様に長過ぎるとジャンプ開始しても地面に接触中の扱いを受けることがあります。
またRayを発射したときに自身のColliderに反応する事があるので、キャラクターのColliderを分けて接触判定を行う必要が結構あります。そうなってくると、段々と管理が面倒になってきます。