流砂のステージを作る

アクションゲームで時々見かける流砂のステージをUnityとC#を使って再現してみました。動作環境はUnity 2022.3.10です。

# ステージの設定
流砂の底となるオブジェクトを作成します。
Hierarchyビューを右クリックし、3D Object → Cubeを選択します。名前はFloorにしておきます。InspectorビューのTagもFloorに設定しておきます。
![Unity1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3644510/141f0c09-1167-7dc8-fcab-7d82e9579986.png)

次は流砂のオブジェクトを作成します。
Hierarchyビューを右クリックし、3D Object → Cubeを選択します。名前はQuickSandにしておきます。ここでキャラクターが流砂に沈むようにするためQuickSandのInspectorビューのBox ColliderのIs Triggerにチェックを入れておきます。TagはQuickSandにします。
![Unity2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3644510/a621a298-af84-5901-8ecb-acf1604f6ded.png)

作成した流砂オブジェクトに砂の色を付けるためのスクリプトを用意します。以下のスクリプトを作成してQuickSandにアタッチします。

```C#:QuickSand.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class QuickSand : MonoBehaviour
{
    void Start()
    {
        GetComponent<Renderer>().material.color = new Color32(211, 161, 39, 1);
    }
}
```

最後に流砂の上に普通の地面を作ります。
Hierarchyビューを右クリックし、3D Object → Cubeを選択します。名前はGreenGroundにしておきます。TagはFloorオブジェクトと同じくFloorに設定します。
![Unity3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3644510/07945526-f0f8-bda1-9bc4-6cc41c770fa6.png)

草原をイメージしてオブジェクトの色を緑にします。以下のスクリプトを作成してGreenGroundにアタッチします。緑色は砂の色と違って定数Color.greenとして用意されています。

```C#:GreenGround.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GreenGround : MonoBehaviour
{
    void Start()
    {
        GetComponent<Renderer>().material.color = Color.green;
    }
}
```

# キャラクターの操作
操作するためのキャラクターを置きます。キャラクターは何でもいいです。本環境の例では無料AssetのLevel 1 Monster PackのSlime_Blueを使用しました。当たり判定のためにBox Colliderをアタッチします。キャラクター操作のためのスクリプトPlayerController.csを作成してキャラクターのオブジェクトにアタッチします。

```C#:PlayerControllers.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public float speed; //移動スピード
    public float jumpPower; //ジャンプ力
    private bool isJumping = false; //ジャンプ判定
    Rigidbody rigidbody;

    void Start()
    {
        rigidbody = gameObject.AddComponent<Rigidbody>(); //キャラクターのオブジェクトにRigidbodyをアタッチ
        rigidbody.drag = 0.0f; //空気抵抗
        rigidbody.constraints = RigidbodyConstraints.FreezeRotation; //キャラクターを回転させない
    }

    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow)) //上矢印キーで奥へ移動
        {
            transform.position += speed * transform.forward * Time.deltaTime;
        }

        if (Input.GetKey(KeyCode.DownArrow)) //下矢印キーで手前へ移動
        {
            transform.position -= speed * transform.forward * Time.deltaTime;
        }

        if (Input.GetKey(KeyCode.RightArrow)) //右矢印キーで右へ移動
        {
            transform.position += speed * transform.right * Time.deltaTime;
        }

        if (Input.GetKey(KeyCode.LeftArrow)) //左矢印キーで左へ移動
        {
            transform.position -= speed * transform.right * Time.deltaTime;
        }

        if (Input.GetKeyDown(KeyCode.Space) && isJumping == false) //スペースキーでジャンプ
        {
            rigidbody.AddForce(Vector3.up * jumpPower);
            isJumping = true; //ジャンプ中はジャンプさせない
        }
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Floor"))
        {
            isJumping = false; //FloorのTagのオブジェクトに着地したらジャンプ可能にする
        }
    }

    private void OnTriggerStay(Collider collider)
    {
        if (collider.gameObject.CompareTag("QuickSand"))
        {
            isJumping = false;
            rigidbody.drag = 20;
        }
    }

    private void OnTriggerExit(Collider collider)
    {
        if (collider.gameObject.CompareTag("QuickSand"))
        {
            rigidbody.drag = 0.0f;
        }
    }
}
```

OnTriggerStay関数はオブジェクトが対象のオブジェクトと接触している間呼ばれ続けます。流砂に触れている間、ジャンプを何回でもできるようにし、空気抵抗を20くらいにして大きくします。これでキャラクターが流砂をゆっくり沈んでいきます。
OnTriggerExit関数はオブジェクトが対象のオブジェクトと離れた瞬間に呼ばれます。流砂を抜けたらrigidbody.dragの値を0に戻します。
実行するとキャラクターが緑の地面に乗っている間は普通に移動しますが、流砂の上に降りたらゆっくり沈んでいきます。ジャンプ連打で脱出できます。
![Unity4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3644510/7b7287f2-f2e8-56cc-7e98-4fb9ae3f3140.png)

# 参考
https://xr-hub.com/archives/7499
https://mono-pro.net/archives/7562