【Unity 2D】横スクロールにおける移動床ブロック

# 移動床ブロック　
![3.gif](https://qiita-image-store.s3.amazonaws.com/0/370076/88efa1c3-88dd-7507-2b9e-0f07804c7d28.gif)
取り敢えずで作ってみた。[この記事](https://qiita.com/SpaceCore/items/c079cdeb211d8f31a005)で書いた容量でタイルを設置してそれを動かしてみました。以下のスクリプトをタイルにアタッチすればOKです。

```ruby:移動ブロックにアタッチするスクリプト
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveGround01 : MonoBehaviour
{
    //変数定義
    private Rigidbody2D rb;
    private Vector2 defaultpass;
       
    void Start()
    {
    rb = GetComponent<Rigidbody2D>();
        defaultpass = transform.position;
    }
    
    void Update()
    {
    //X座標のみ横移動
    rb.MovePosition(new Vector2(defaultpass.x + Mathf.PingPong(Time.time, 3), defaultpass.y));
    }
}
```
これでひとまずは動いた。

# 課題点
- 移動床ブロックにプレイヤーが乗ったときに、連動して移動しない。衝突判定があった時に、移動床ブロックを親要素にしてプレイヤーを小要素にしてみたが駄目。どちらともリジッドボディだから駄目っぽい。

```ruby:プレイヤーにアタッチするスクリプト（駄目なバージョン）
    void OnCollisionEnter2D(Collision2D col)
    {
        //衝突したオブジェクトにMoveBlockタグが設定してあるかどうか
        if (col.gameObject.tag == "MoveBlock")
        {
            this.gameObject.transform.parent = col.gameObject.transform; //プレイヤーを親要素に設定
        }
     }

    void OnCollisionExit2D(Collision2D col)
    {
        this.gameObject.transform.parent = null;
    }
```
これでプレイヤーが子要素にはなったが、上記の課題点どおり連動しては動いてはくれなかった。

# 移動床ブロックに乗ったらプレイヤーも連動して動くパターン（解決Ver）
![4.gif](https://qiita-image-store.s3.amazonaws.com/0/370076/6f3f08af-a6aa-79d1-cab6-5356b9454827.gif)
コメント欄でmamemoyassyさんに色々と教えていただき、無事に連動するものも作れました。SurfaceEffector2Dを利用することで、ベルトコンベアのように上に乗ったオブジェクトに力を加える形で解決しました。

```ruby:プレイヤーにアタッチするスクリプト

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    //変数定義
    float Slide = 30f;  //横移動の力の強さ
    float flap = 800f;  //ジャンプ時の力の大きさ
    Rigidbody2D rb2d;
    bool jump = false;  //ジャンプが可能かの判定

    void Start()
    {
        //コンポーネント読み込み
        rb2d = GetComponent<Rigidbody2D>();
    }


    void Update()
    {

        //キーボード操作
        if (Input.GetKey(KeyCode.RightArrow))
        {
            //左入力時
            rb2d.AddForce(Vector2.right * Slide);
        }
        else if (Input.GetKey(KeyCode.LeftArrow))
        {
            //右入力時
            rb2d.AddForce(Vector2.left * Slide);
        }
        
        
        if (Input.GetKeyDown("space") && !jump)
        {
            //ジャンプ時
            rb2d.AddForce(Vector2.up * flap);
            jump = true;
        }
    }

    //設置判定があればジャンプ判定をfalseへ変更
    void OnCollisionEnter2D(Collision2D other)
    {
        jump = false;
      }
}
```
プレイヤーのオブジェクトにはRigidbody2Dと何でもいいからcollisionを付けておくこと。

```ruby:移動ブロックにアタッチするスクリプト
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveGround01 : MonoBehaviour
{
    //変数定義
    Rigidbody2D rb;
    SurfaceEffector2D surfaceEffector;
    Vector2 DefaultPos;
    Vector2 PrevPos;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        DefaultPos = transform.position;
        surfaceEffector = GetComponent<SurfaceEffector2D>();
    }

    void FixedUpdate()
    {
        PrevPos = rb.position;

        // X座標のみ横移動　Mathf.PingPongの数値部分変更で移動距離が変わる
        Vector2 pos = new Vector2(DefaultPos.x + Mathf.PingPong(Time.time, 3), DefaultPos.y);
        rb.MovePosition(pos);

        // 速度を逆算する
        Vector2 velocity = (pos - PrevPos) / Time.deltaTime * 50;

        // 速度のX成分を SurfaceEffector2D に適用
        surfaceEffector.speed = velocity.x;
    }
}
```
移動床タイルにはRigidbody2DとSurfaceEffector2Dと何でもいいからcollisionを付けておくこと。Mathf.PingPong(Time.time, 3)の3の部分の数値を変えると移動距離も変わります。Y軸にも動かしたいならDefaultPos.yの箇所を変えるとGOODです。

# 終わりに
mamemoyassyさん色々と教えていただきありがとうございました。