【Unity】レイヤーを使わずに衝突判定をなくす方法【Physics】

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Physics.IgnoreCollision<a href="https://t.co/0vViTb5uJ0">https://t.co/0vViTb5uJ0</a><br><br>public static void IgnoreCollision (<br>　Collider collider1,<br>　Collider collider2,<br>　bool ignore=true<br>);<br><br>&gt;2 つのコライダーの衝突判定を無効化します<br><br>レイヤーとコリジョンマトリックスの設定なしでCollider単位で衝突制御できるのでは！？🤔</p>&mdash; su10@ハイパーカジュアルゲーム開発 (@su10_dev) <a href="https://twitter.com/su10_dev/status/1432904354437029890?ref_src=twsrc%5Etfw">September 1, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# はじめに
Unityを使った開発でチームの人数が増えるとレイヤーがカオス化しがちです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27863/a205662d-8ac3-0e2b-f32b-aa68d33878e3.png" width=400/>

具体的には以下のような問題が発生したりします。

* レイヤーが無秩序に追加されて枯渇する
    * カスタムレイヤーは24個しか追加できない！
* コリジョンマトリックスの衝突する・しないがいつの間にか変更されていて既存の実装が壊れる
    * しかも原因が追いにくい

ある程度大きな規模の開発ならルールを作ったり安全な仕組みを作って対応するところですが、小規模開発だとそもそも可能な限りレイヤー使わないほうが良くない？と思ってます。

いろいろ調べていたら少なくとも衝突判定をなくす方法についてはレイヤーを使わずに済む方法を見つけたのでご紹介します。

# Physics.IgnoreCollision

[Physics.IgnoreCollision](https://docs.unity3d.com/ja/2018.4/ScriptReference/Physics.IgnoreCollision.html)
`public static void IgnoreCollision (Collider collider1, Collider collider2, bool ignore= true);`

>2つのコライダーの衝突判定を無効化します

[Physics2D.IgnoreCollision](https://docs.unity3d.com/2018.4/Documentation/ScriptReference/Physics2D.IgnoreCollision.html)
`public static void IgnoreCollision (Collider2D collider1, Collider2D collider2, bool ignore= true);`

>collider1 と collider2 のペア間の衝突/トリガーを検知するか無視するようにします

`Collider` を２つとフラグを受け取って衝突判定を無効化。良さそう。

# 試してみた
`Default` レイヤーに設定してあるオブジェクト２つ（`Cube` と `Floor`）に対してAPIを呼んでみました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27863/a012e271-4ad5-fdd1-fcf5-706cac0d9161.png" width=400/>

```c#
using UnityEngine;

public class CollisionSwitcher : MonoBehaviour
{
    [SerializeField] private Collider _colA;
    [SerializeField] private Collider _colB;

    public bool ignoreCollision;
    private bool _ignoreCollisionCache;

    void Start()
    {
        _ignoreCollisionCache = ignoreCollision;
    }

    void Update()
    {
        if (_ignoreCollisionCache != ignoreCollision)
        {
            Physics.IgnoreCollision(_colA, _colB, ignoreCollision);

            _ignoreCollisionCache = ignoreCollision;
        }
    }
}
```

![Sep-01-2021 17-28-19](https://user-images.githubusercontent.com/1687366/131641733-3eafb2bf-03ab-4b6e-a821-2019258579eb.gif)

ちなみにレイヤーとコリジョンマトリックスの設定で最初から衝突しないようにしてある場合は `Physics.IgnoreCollision()` を呼んでも衝突するようにはできませんでした。

あくまで「衝突させないようにする、もしくは元通り衝突するように戻す」専用。

# APIの制限
リファレンスにいろいろ書いてあるので載せておきます。

[Physics.IgnoreCollision](https://docs.unity3d.com/ja/2018.4/ScriptReference/Physics.IgnoreCollision.html)

>This is useful, say, for preventing projectiles from colliding with the object that fires them.
>
>Note that IgnoreCollision will reset the trigger state of affected colliders, so you might receive OnTriggerExit and OnTriggerEnter messages in response to calling this. IgnoreCollision has a few limitations:
>
>1) It is not persistent. This means ignore collision state will not be stored in the editor when saving a Scene.
>2) 有効なゲームオブジェクトにしか IgnoreCollision を適用できません。



[Physics2D.IgnoreCollision](https://docs.unity3d.com/2018.4/Documentation/ScriptReference/Physics2D.IgnoreCollision.html)

>選択されたコライダー間のいかなる衝突判定も無視します。つまり、すべての衝突判定とトリガー判定が起こらなくなります。まず最初に衝突レイヤーで2つのレイヤーが接触できるか確認し、その後接触が起こらないようにします。それらの処理の次に、特定のコライダーが作用しないようにします。
>
>IgnoreCollision にはいくつかの制約があります:
>
>1) It is not persistent. This means that the ignore collision state will not be stored in the editor when saving a Scene.
>2) You can only apply the ignore collision to colliders in active game objects. When deactivating the collider the IgnoreCollision state will be lost and you have to call Physics2D.IgnoreCollision again.



# あとがき
>小規模開発だとそもそもレイヤー使わないほうが良くない？と思ってます。

こうは書いたものの、カメラで写す対象を絞るとかはどうしてもレイヤーを使う必要があったりして、現実的には使う必要が出てくると思います。

コリジョンマトリックスは実行時に動的に変更できるみたいなので、Unityの素のレイヤーの上に一つ抽象レイヤー(層)を作ってシーンごとに衝突判定を切り替えたりすることは可能そうです。鋭意調査中