動く足場と一緒にキャラクターを移動させる

キャラクターが上に乗って移動できるオブジェクトを作っていきます。
実効環境はUnity 2022.3.10です。

# 普通の床の作成
最初に動かない通常の床を作ります。Hierarchyビューで右クリックして3D Object→Cubeを選択します。オブジェクトの名前をFloor1にしておきます。Tagの名前をFloorにしておきます。参考までにこのオブジェクトのInspectorビューの画像を以下に載せておきます。
![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3644510/df83a6ae-db90-017f-fdd8-b38ba27e7d68.png)

同様にしてFloor1と同じ床のオブジェクトを作成します。オブジェクトの名前はFloor2にします。Floor1からは少し離れた場所に配置します。
![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3644510/571e7736-0c62-92c9-1ea7-912d6b8dab0b.png)

# 移動する床の作成
2つの床の間を往復する床を作成します。Hierarchyビューで右クリックして3D Object→Cubeを選択します。オブジェクトの名前をCubeにします。Tagの名前をMovingFloorにします。
![image3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3644510/36a19f51-d325-2357-07ae-140bc892c481.png)

床を動かすために以下のスクリプトを作って移動床のオブジェクトにアタッチします。

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

public class MovingCube : MonoBehaviour
{
    void Start()
    {
        GetComponent<Renderer>().material.color = Color.blue; //オブジェクトの色を青色にする
    }

    void Update()
    {
        transform.position = new Vector3(Mathf.PingPong(Time.time, 10), 0, 0);
    }
}
```

Update関数の中に床を往復させる処理を書きます。
Mathf.PingPongは第1引数の値を、0と第2引数の間で往復させます。Time.timeを第1引数に代入することによって、決められた範囲内で時間とともにオブジェクトを往復させることが出来ます。

# キャラクターを操作する
キャラクターを操作するためのスクリプトを作成します。以下のスクリプトを作って任意のキャラクターにアタッチします。キャラクターにColliderをアタッチすることを忘れないようにしてください。

```C#:PlayerController.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;
    private Transform parent;

    void Start()
    {
        rigidbody = gameObject.AddComponent<Rigidbody>(); //Rigidbodyをアタッチする
        rigidbody.constraints = RigidbodyConstraints.FreezeRotation; //キャラクターの回転を防止
        parent = GameObject.Find("Cube").transform; //名前がCubeのオブジェクトのTransformを取得する
    }

    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; //ジャンプしたらジャンプ判定をtrueにする
        }
    }

    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Floor"))
        {
            isJumping = false; //床に着地したらジャンプ判定をfalseにする
        }

        if (collision.gameObject.CompareTag("MovingFloor"))
        {
            isJumping = false;
            this.transform.SetParent(parent); //親オブジェクトを設定する
        }
    }

    void OnCollisionExit(Collision collision)
    {
        if (collision.gameObject.CompareTag("MovingFloor"))
        {
            this.transform.SetParent(null); //親オブジェクトの設定を解除
        }
    }
}
```

OnCollisionEnter関数はオブジェクト同士が衝突した時に呼び出されます。OnCollisionEnter関数内if文では、タグがFloorのオブジェクトと衝突した際にジャンプ判定をfalseにしてジャンプ可能な状態にします。さらに衝突したオブジェクトのタグがMovingFloorだった場合には、SetParentで親オブジェクトを設定します。この場合は親は移動床であるCubeになります。移動床を親にすることによってキャラクターが床に乗って一緒に移動できるようになります。
OnCollisionExit関数はオブジェクトが離れた時に呼び出されます。移動する床からキャラクターが離れたらSetParentにnullを設定することで親オブジェクトの設定を解除します。この処理を書かないと移動床から別の床に飛び移った後も移動床と共に往復運動をすることになります。
最後に変数であるspeedとjumpPowerに適当な値を入れて調整すれば完成です。
動く床に乗って離れた足場に移動できます。
![image4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3644510/f5cfe95c-b019-acb5-a6bf-2af4f155208a.png)

# 回転する床
回転する円盤上の床に乗って移動することも同様にして実現できます。
Hierarchyビューを右クリックして3D ObjectのCylinderを選択します。
先程設置した2つの足場の間に移動床の代わりに配置します。InspectorビューからタグをMoovingFloorに設定します。Cylinder Colliderは無いのでAdd ComponentをクリックしてMesh Colliderをアタッチします。Mesh ColliderのMeshの項目でCylinderを選択します。
以下のスクリプトを作成してCylinderオブジェクトにアタッチしてください。

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

public class RotateCylinder : MonoBehaviour
{
    void Start()
    {
        GetComponent<Renderer>().material.color = Color.red; //オブジェクトの色を赤色にする
    }

    void Update()
    {
        this.transform.Rotate(0.0f, 0.5f, 0.0f); //オブジェクトをY軸を中心に回転させる
    }
}
```

さらにPlayerController.csのStart関数の中の
parent = GameObject.Find("Cube").transform;
を
parent = GameObject.Find("Cylinder").transform;
に書き換えます。
これで実行すれば回転する足場に乗って離れた足場に行くことが出来ます。
![image5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3644510/32a3babd-f873-e413-b8b5-22fa564f82ea.png)

# 参考
https://nekojara.city/unity-pingpong
https://beginne28949926.com/unity/rotating-floor/