ゴマちゃんフロンティア

アザラシが大好きなエンジニアの開発日記です

【Unity】「ある地点から一定範囲内に移動を制限する」をColliderの反転で実現したお話

time 2020/04/02

ちょっと久しぶりのUnityな話題です。
開発中のゲームで「ある一定地点を基準に、円状に移動範囲を制限」をやりたくなりました。

「Unityで移動範囲を制限する」旨を検索すると、「Mathf.Clamp()transform.positionを一定範囲内に収める」みたいなお話が多くヒットします。
しかし私はキャラクターの制御にRigidBodyやCharacterControllerを使用しているので、positionへの直代入は行いたくありません。
そこでタイトル通り、筒状のオブジェクトのColliderを反転させ、中から外へ出れなくすることで実現してみました。

Colliderの基準とするオブジェクトの配置

まずはシーン上に空のオブジェクトを配置し、これを基準とします。
私のゲームではずばり「戦闘中の移動範囲制限」に使いたいので、Colliderは動的に生成する必要がありました。
なのでオブジェクトにはSphereColliderを設定し、「判定に触れたらColliderを生成して移動を制限」とします。

スクリプト作成

次はこのお話の要となるスクリプトの作成です。

using System.Linq;
using UnityEngine;

public class InverseColliderTest : MonoBehaviour
{
    [SerializeField]
    private float colliderSize;

    private bool isActivated = false;
    private GameObject colliderObject;

    private void OnTriggerEnter(Collider c)
    {
        if (!isActivated && c.gameObject.CompareTag("Player")) {
            isActivated = true;
            CreateInverseCollider();
        }
    }

    private void CreateInverseCollider()
    {
        // Cylinderの生成
        colliderObject = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
        colliderObject.transform.position = transform.position;
        colliderObject.transform.SetParent(transform);
        colliderObject.transform.localScale = new Vector3(colliderSize, colliderSize, colliderSize);

        // Colliderオブジェクトの描画は不要なのでRendererを消す
        Destroy(colliderObject.GetComponent<MeshRenderer>());

        // 元々存在するColliderを削除
        Collider[] colliders = colliderObject.GetComponents<Collider>();
        for (int i = 0; i < colliders.Length; i++) {
            Destroy(colliders[i]);
        }

        // メッシュの面を逆にしてからMeshColliderを設定
        var mesh = colliderObject.GetComponent<MeshFilter>().mesh;
        mesh.triangles = mesh.triangles.Reverse().ToArray();
        colliderObject.AddComponent<MeshCollider>();
    }
}

Colliderは単純な形でよいので GameObject.CreatePrimitive()でシリンダーを生成。位置やサイズを調整します。
次にCollider自体は視認できる必要はないので、MeshRendererコンポーネントを削除します。

以降のColliderの反転は以下の記事を参考にさせていただきました!

スクリプトの設定と確認

最後にオブジェクトに作成したスクリプトを追加し、インスペクターからColliderの大きさを入力します。

大きさは実際に動かしながら調整するか、メニューの「GameObject→3D Object→Cylinder」で実物を出して調整するのも良いです。

設定し終わったら再生して動作を確認します。キャラクターには「Player」というタグを付けておきましょう。

SphereColliderに触れると筒状のColliderが生成され、外に出られなくなりますね。

ちょっと改良する場合

Colliderの縦方向のサイズを固定にする

でもこのCollider、縦に不必要にでかすぎじゃない?

そんな人はtransform.localScaleへ代入するVector3のY軸を固定値にしてみましょう。

ただしシリンダーの天井にも判定はあるので、ゲームによっては低くしすぎると支障が出るかもしれません。

Colliderを消して出られるようにする

移動制限を解除したい場合はcolliderObjectDestroy()で削除してください。
この条件はゲームによって変わってくるので、今回のスクリプトでは実装していません。私のゲームの場合、エリア内の敵をすべて倒したら削除するようにしています。

Colliderの境界線にエフェクトを表示

透明だとどこからどこまでが範囲か分からないね…。

パーティクルを使ってColliderの境界にエフェクトを出してみましょう。
手始めにBlenderでシンプルなCylinderを作ってインポートします。Cylinderの上面と下面は不要なので消しておきます。

次にUnity上にパーティクルシステムを作成。
Rendererの「Render Mode」を「Mesh」に変更し、インポートしたCylinderのメッシュを設定します。

その他の各種パラメータやマテリアル・テクスチャなどは以下の記事のサンプルを参考にさせていただきました。
竜巻の下に出ている衝撃波の部分を使用しました。

あとはシーン上でColliderに合わせて大きさを調整し、スクリプトのCollider生成時にParticleSystem.Play()を実行するだけ。
パーティクルの参照はインスペクターから設定するなりGetComponentInChildren()するなりでどうぞ。

うまく調整するとこんな感じにできます。

あとがき

そんなわけで、移動範囲の制限に反転させたColliderを使ってみました。
四方向にBoxColliderを置くより柔軟で管理もしやすいです。メッシュを変えれば他の形にもできるのが良いですね。

down

コメントする