【UnityでC#】スクリプトで移動を作ろう！

# はじめに
　今回はUnityのC#を用いて2Dアクションゲームの移動を実装したいと思います。プレイヤーが立ち絵から走る状態へのアニメーションは実装していますが、移動ができていないので実装していきます。アニメーション制御までは終わっているとの前提で話します。
※Unityはバージョンアップの頻度が多く、メニューやレイアウト、表示が変わっていることがあります。

# \<移動処理を作る前に！\>
　移動処理を作る前に注意点があります。実はUnityには移動処理というのは何種類もあって、それぞれ一長一短の動きをします。かと言って、すべての移動方法を説明していては頭が痛くなってしまうので、1つの移動方法と何故、この方法を使用するのか、そのメリットとデメリットについて解説しようと思います。
　ちなみにですが、別にtransform.positionでも動かせます。ですが適切ではないのです。これが、Unityの怖いところで、動くんですよ。何種類もある移動方法のどれを使用しても動きます。適切でない方法だと、バグりやすくなったり、重くなったりします。
　重くなる場合が一番厄介で、少しずつ、少しずつ重さを蓄積していって、ある日突然カクカクしだすので、こういう手法を取ってしまうと原因を非常に特定しづらくなります。原因が一つではなく蓄積が原因ですので。ググると移動方法って色々出てきますが、何が適切なのか十分に注意しましょう。

# \<移動方法について\>
　Unityで物体を移動させようとした時、大きく分けると2パターンに分けることができます。

①Transform操作・・・位置情報を直接制御する方法です（ただ移動するだけ）
②物理エンジン操作・・・物理演算でオブジェクトの位置を制御する方法です（オブジェクトを動かした時周囲の状況により様々な計算が入る）

　基本この2パターンの中から移動方法を選ぶことになります。特に自分からプログラムで指定しない場合、デフォルトの状態ではTransform操作になります。今回は、移動の際に当たり判定を使って地面に接地したいです。その為、当たり判定は利用したいです。ですが、2Dアクションでよく利用されるアクション類は大抵物理法則を無視することが多いです。例えば2段ジャンプとか空中ダッシュとか物理法則もくそもありません。そのため、様々なアクションをしたい場合、物理演算は使用したくありません。今回のポイントをまとめると

:::note
Key Point
・当たり判定を利用したい
・物理挙動は無視したい
:::
と、なります。当たり判定を使用する場合、物理エンジンを利用しなければなりません。ですが、物理的挙動は無視する移動を作っていこと思います。ちなみに、当たり判定を持っている状態でTransform系の移動をした場合とても重くなってしまいます。そのため、当たり判定を付けた時点でほぼほぼTransform系はNGと考えてもらっていいです。

# \<物理的挙動を無視した物理エンジン操作\>
### Rigidbody2D.velocity
　この方法を利用する場合、まず、移動させたいゲームオブジェクトにRigidbodyかRididbody2Dを追加してください。今回は2Dアクションなので、Rigidbody2Dを追加します。このRigidbodyというコンポーネントはアタッチしたゲームオブジェクトとその子オブジェクトを物理演算で動かせるようにするコンポーネントです。[アニメーション制御の回](https://qiita.com/hiroyuki-tabuchi/items/26271a4ea34ab191c836)で説明した数リプとから今、以下のようなスクリプトがプレイヤーにアタッチされていると思います。

<details><summary>アニメーション制御の回でプレイヤーにつけたスクリプト（クリックすると展開されます）</summary>

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

public class Player : MonoBehaviour
{
    private Animator anim = null; 

    void Start()
    {
        anim = GetComponent<Animator>();
    }

    void Update()
    {
        float horizontalKey = Input.GetAxis("Horizontal"); 
        if (horizontalKey > 0) 
        {
            transform.localScale = new Vector3(1, 1, 1);
            anim.SetBool("run", true);
        }
        else if (horizontalKey < 0) 
        {
            transform.localScale = new Vector3(-1, 1, 1);
            anim.SetBool("run", true);
        }
        else
        {
            anim.SetBool("run", false);
        }
    }
}
```
</details>
　ここに、右ボタンか左ボタンを押された時移動する処理を追加したいと思います。まずは、Rigidbody2Dのインスタンスを取得します。

```C#:プレイヤーのスクリプトに追加する文
private Animator anim = null;
private Rigidbody2D rb = null;

void Start()
{
    anim = GetComponent<Animator>();
    rb = GetComponent<Rigidbody2D>();
```
　このRigidbody2Dを使うことによってゲームオブジェクトが物理エンジンによる操作を行うことができます。が、素直に物理エンジンを使用すると2Dアクション特有の物理法則を無視することができないので変則的な使い方をします。
　どうするかと言いますと、**Rigidbody2D.velocity**を利用します。このvelocityというのはRigidbody2Dの変数になります。何を表しているかというと「速度」を表しています。velocityの型はVector2です。Vector3はx,y,zでしたので、一個少ないこれはxとyだけになります。2Dというわけですね。ということで、velocity > (x, y)でxが横方向の速度を表します。yが縦方向の速度を表します。
　本当はvelocityを直接いじることは推奨されていません。というのも、物理エンジンがたくさんいろいろな計算を行って、物理法則になるべく近い形にするように計算した結果をこの変数に入れているからです。つまり、この変数を直接触るということは物理演算の計算結果を捨てて、新たに数値を書き換えることに該当します。計算結果を捨てるのはもったいない気がしますが、物理的な計算を極力やらせないようにすればOKです。物理的な計算は当たり判定さえ取れればいいので、余計なことをしなければ大丈夫です。計算結果は捨て去られますが当たり判定の利用はできるのでちょうどいい感じだといえます。

### スクリプトからキャラクターに速度を与えよう
では、左ボタンか右ボタンを押された時velocityを変更して速度を与えてみましょう。
```C#:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    //インスペクターで設定する
    public float speed;

    //プライベート変数
    private Animator anim = null;
    private Rigidbody2D rb = null;
    
    void Start()
    {
        //コンポーネントのインスタンスを捕まえる
        anim = GetComponent<Animator>();
        rb = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        //キー入力されたら行動する
        float horizontalKey = Input.GetAxis("Horizontal");
        float xSpeed = 0.0f; 
        if (horizontalKey > 0)
        {
            transform.localScale = new Vector3(1, 1, 1);
            anim.SetBool("run", true);
            xSpeed = speed;
        }
        else if (horizontalKey < 0)
        {
            transform.localScale = new Vector3(-1, 1, 1);
            anim.SetBool("run", true);
            xSpeed = -speed;
        }
        else
        {
            anim.SetBool("run", false);
            xSpeed = 0.0f;
        }
        rb.velocity = new Vector2(xSpeed, rb.velocity.y);
    } 
}
```
```C#:
public float speed;
```
で、インスペクターに走る速さを調整できるようにします。
```C#:
float xSpeed = 0.0f;
```
この変数は横方向の速さを表しています。プラスなら右方向、マイナスなら左方向になります。
```C#:
xSpeed = speed;
```
これで、インスペクターで調整したスピードを入れます。
```C#:
xSpeed = -speed;
```
反対方向はマイナスをつけてあげることで表現できます。右ボタンも左ボタンも押されていないとき、止まって欲しいので
```C#:
xSpeed = 0.0f;
```
で、X軸の速度を0にします。そして最終的に算出された速度を
```C#:
rb.velocity = new Vector2(xSpeed, rb.velocity.y);
```
でvelocity（速さ）を代入しています。この時、上に上昇したり、下に落ちたりする速度は維持したいので、velocityのY軸方向の速さをそのまま代入しています。なぜ、このように回りくどいやり方をするかというと、途中でスピードを変更しやすいからです。後々、横方向のスピードを変化させる処理が出てきたとき（暴風が吹いているなど）に処理を追加しやすいです。そのため、いったん変数にいれて、最後に代入するという手法を取っています。

### 回転を固定する
　さて、では再生してみましょう。speedが0になっていると思うのでインスペクターを調節しながら動かしてみてください。動かしたら、コケましたね。。。そういった時は、回転しないように動きを固定しましょう。
　インスペクターのRigidbody2Dを見てください。ConstraintsのFreeze RotationのZにチェックを入れると回転しなくなります。動かしてもコケなくなりました。これで横方向の移動は完了です。

# おわりに
　いかがでしたでしょうか。移動を実装する方法にはいくつか手段がありますが時と場合によって、選ぶものを間違えるとバグや処理遅延の原因になります。少しずつでもよいので理論がわかったうえでゲーム製作をしていくとクオリティの高いゲームが作れるようになるでしょう。

##### 投稿者
エンジニアファーストの会社 [株式会社CRE-CO](https://www.cre-co.jp/#top)　田渕浩之

##### 参考文献
[→Unity入門【初心者為の使い方講座】【実際にゲームを作って解説】](https://dkrevel.com/makegame-beginner/)