おじさんの適当ブログ

技術のことやゲーム開発のことやゲームのことなど自由に雑多に書き連ねます

Layer Collision Matrixを使って雑に「緊急回避」を実現する

「緊急回避」とは、アクションゲームでよくある「前転時などに一定時間無敵になり、敵の攻撃回避を目的としたアクション」のことを指しています。 ほかにも良い方法があるかもしれませんが、お手軽さを優先して、対象のGameObjectのレイヤーを切り替える方法を取りました。「ダメージを受けてノックバックした時の無敵状態」の実現などにも使えます。

なお、私が作成しているゲームが2Dであるため、2Dを題材としています。

Layer Collision Matrixを設定する

Layer Collision Matrix とは

Layer Collision Matrix を使うことで、Colliderが衝突検知するか否かをLayerレベルで設定できます。Layer Collision Matrix の設定項目は、 Edit > Project Settings > Physics 2D で開かれる inspector に存在します。

f:id:subarunari:20170701003421p:plain:w300

Default, TransparentFX, ignore Raycast, Water, UI 5つは Built in なレイヤーです。レイヤー名の交点にあるチェックボックスは「衝突検知するか否か」を表しています。チェックが入っている場合に衝突を検知します。デフォルトではすべてチェックが入っているので、すべてのレイヤー間で衝突が検知されます。

雑な緊急回避を実装するにあたって、以下の2つのLayerを新たに作成しています。

  • Damage Layer : ダメージを与える物体が存在するレイヤー
  • Dodge Layer : 緊急回避時に一時的に切り替えるレイヤー

Layerの作成は、Game ObjectのInspector上にある Layerのドロップダウンリスト > Add Layer… からできます。

衝突回避の設定

衝突を検知したくないレイヤーの交点のチェックボックスを外します。今回は、緊急回避時のレイヤーとして「Dodge Layer」を使うので、「Dodge Layer」と「Damage Layer」の交点となるところのチェックを外します。これで下準備は終わりです。

f:id:subarunari:20170701011133p:plain:w300

コードからGameObjectのレイヤーを切り替える

以下のコードでレイヤーの切り替えは簡単にできます。レイヤーを切り替えたら元に戻すのを忘れずに。

// レイヤー番号を名前から取得し、GameObjectのlayerプロパティに指定する。
gameObject.layer = LayerMask.NameToLayer("DodgeLayer");

与えられた名前のレイヤーが見つからない場合、NameToLayerメソッドは -1 を返します。gameObject.layerに -1 が設定されると以下のエラーが吐かれます。

A game object can only be in one layer. The layer needs to be in the range [0...31]
UnityEngine.GameObject:set_layer(Int32)

NameToLayerメソッドを使えば、うっかり名前を変えてしまった場合にすぐ気付けますし、なんらかの事情でレイヤーの順番を変更しなければならない場合にコードレベルの対応も必要ありません。

雑なサンプル

はじめにこんな感じで、プレイヤーにダメージを与えることのできるGameObjectのLayerを 「DamageLayer」 にしておきます。プレイヤーのLayerはDefaultのままです。

f:id:subarunari:20170701015643p:plain

続いてコードです。単純にするために対応するキーを押した時にレイヤーが切り替わるようにしています。「Z」キーを押したらレイヤーが DodgeLayer に変わり、衝突が検知されなくなります。

public class Sample: MonoBehaviour
{
    void Update()
    {
        GetComponent<Rigidbody2D>().velocity = new Vector2(Input.GetAxis("Horizontal") * 5, 0);
        if(Input.GetKeyUp(KeyCode.Z))
        {
            // Zキーを押したら、DodgeLayerに切り替え
            gameObject.layer = LayerMask.NameToLayer("DodgeLayer");
            Debug.Log(gameObject.layer);
        } 
        else if (Input.GetKeyUp(KeyCode.X))
        {
            // Xキーを押したら、DefaultLayerに戻す
            gameObject.layer = LayerMask. NameToLayer("Default");
            Debug.Log(gameObject.layer);
        }
    }
}

今回は単純化のためにキーを押した時にレイヤーを切り替えていますが、実際は「緊急回避」のアクション開始時と終了時に、レイヤーの切り替えを行うことになるかと思います。

まとめ

以上、Layer Collision Matrixを用いた雑な緊急回避の実現でした。

今回は、DamageLayerを用意しましたが、ダメージを与えうる全てのGameObjectにLayerを設定するのは骨が折れます。DamageLayerの代わりに、床、壁、天井などを表すFloorLayerを用意し、DefaultとDodgeLayerのチェックを外すという手もあります。どちらもLayer設定の手間はかかってしまいますが・・・。設定量が少なく済むようにすれば幸せになれそうです。

補足

Collider 2Dの基礎についてはこちらの記事に記載しています。

subarunari.hatenablog.com