猫でもわかるUnity入門(第11回 玉転がし作成 その3)

f:id:enia:20210228230354p:plain

えにあです。 前回はキーボード入力でプレイヤーオブジェクト(球体)を移動できるようにしました。 今回はよりゲームらしくしていきます。

目次

ボールのスピードを変更しよう

前回まででボールを移動できるようになったものの、ボールのスピードが遅く感じますね。 そこで、ボールのスピードを速くするようにコードを修正していきましょう。

まず、PlayerControllerにspeedというpublicフィールドを追加します。 コードは以下のようになります。

public class PlayerController : MonoBehaviour
{
    public float speed;
    private Rigidbody rb;
    ~中略
}

次に、speedを使って移動の力を増幅させます。 FixedUpdateメソッドをの最後の1行を以下のように修正します。

    void FixedUpdate ()
    {
    float moveHorizontal = Input.GetAxis("Horizontal");
    float moveVertical= Input.GetAxis("Vertical");

        Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
        rb.AddForce(movement * speed);
    }

speedをpublicフィールドにしたのには理由があります。 例えば、rb.AddForce(movement * 10);と書いても、力を増幅させることはできますね。 この場合、もし10を20に変えたい場合、スクリプトを修正して再度コンパイルする必要があります。 これは時間のかかる作業です。

publicフィールドを追加すると、Unityのインスペクタ上で値を変更できるようになります。 こにれより、簡単に速度を調整することが可能になります。

スクリプトの変更を保存して、Unityに戻りましょう。 Playerオブジェクトを選択してインスペクターを見てください。 先ほど定義したspeedパブリックフィールドが表示されているのが分かります。 f:id:enia:20210301140040p:plain

NOTE:
日本語化しているため「speed」が自動的に「速度」に翻訳されています。 試しにフィールド名をplayerSpeedにしたら、インスペクターでは「Player Speed」と表示されました。

では、speedを10に設定して、プレイモードで試してみましょう。 スムーズにボールを移動できるようになりましたね!

カメラの位置を変えてみよう

現状だとボールをほぼ水平の位置から撮影しているように表示されています。 まず、カメラの位置を設定しましょう。 ヒエラルキーでMain Cameraを選択します。 f:id:enia:20210301140900p:plain

次にインスペクターで値を設定していきます。

NOTE:
この作業はプレイモードにしながら行うとシーンビューにダイナミックに変更が反映されるためわかりやすいです。ただし、プレイモードを終了すると変更が戻ってしまうため、プレイモード終了後に再度値を設定するようにしましょう。

まず位置のY軸には現在1が設定されていると思いますが、これを10にします。 f:id:enia:20210301141641p:plain

すると、シーンビュー上にボールが映らなくなったと思います。 f:id:enia:20210301141734p:plain

つまり、カメラのY軸の位置とは目の高さということですね。 先ほどまでは目の高さが地面すれすれにありましたが、目の高さを上げてしまったため、足元のボールが見えなくなってしまいました。

そこで、カメラの角度を変えてボールが映るようにします。 回転のX軸の値を45に変更しましょう。 f:id:enia:20210301142013p:plain

すると、カメラが斜め下を向き、ボールが映るようになったと思います。 f:id:enia:20210301142047p:plain

NOTE:
カメラを下に向けるのになぜX軸の値を設定するのでしょう。私は最初Y軸なんじゃないかと思ってしまいました。おそらく、X軸を中心にしてぐるぐる回していくということですね。

カメラとプレイヤーを関連付けよう

現状だとカメラの位置が固定なので、カメラの外までプレイヤーが動くと見えなくなってしまいます。 そこで、カメラがプレイヤーに追従するようにしていきます。

関連付けはカメラにスクリプトを追加することで行います。 カメラオブジェクトを選択した状態で、インスペクターから新しいスクリプトを作成しましょう。 名前はCameraControllerにします。 f:id:enia:20210301154028p:plain

プロジェクトビューを見ると、プロジェクトルートにファイルが置かれていると思います。 ドラッグアンドドロップでScriptsフォルダに移動させましょう。 f:id:enia:20210301154133p:plain

CameraControllerスクリプトを開いて修正していきます。 CameraControllerに二つのフィールドを追加します。 GameObject型のplayerと、Vector3型のoffsetです。 コードは以下のようになります。

public class CameraController : MonoBehaviour
{
    public GameObject player;
    private Vector3 offset;
    ~中略~
}

offsetは、ゲームスタート時におけるカメラの位置とプレイヤーの位置の差を表します。 インスペクターで設定したカメラの位置と、プレイヤーの位置を引き算することで算出できます。 ゲームスタート時に一回だけ算出すればよいわけですが、どこに書けばよいでしょう。 そう、Startメソッドの出番ですね。

Startメソッドのコードは以下のようになります。

    void Start()
    {
        offset = transform.position - player.transform.position;
    }

次に、このoffsetを使ってカメラの位置を計算します。 カメラの位置は常にPlayerオブジェクトからoffset分だけ離れた位置にすれば、カメラは常にプレイヤーを追いかけ続けますね。
この処理はフレームごとに行う必要があります。フレームごとに呼び出されるメソッドと言えばそう、Updateメソッドです。

Updateメソッドのコードを以下のように修正します。

    void Update()
    {
        transform.position = player.transform.position + offset;
    }

これで、カメラの位置がプレイヤーの位置に追従するようになりました。 しかし、実はUpdateメソッドはカメラの位置を更新するには適した場所ではありません。 この処理を書くのに本当に適切な場所はLateUpdateメソッドです。

LateUpdateメソッドはUpdateメソッドと同様にフレームごとに呼び出されますが、全てのオブジェクトのUpdate処理が終わった後に呼び出されることが保証されています。 LateUpdateメソッドでカメラの位置を変えることで、確実にPlayerの移動が終わったあとにカメラの移動が行われます。

NOTE:
PlayerControllerとCameraControllerでは、どちらのUpdateが先に実行されるか、順序の保証がないということでしょう。LateUpdateメソッド同士の実行順序も保証されないと思いますが、LateUpdateには実行順序に依存するコードを書くべきでないということだと思われます。

最終的なコードは以下のようになります。

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

public class CameraController : MonoBehaviour
{
    public GameObject player;
    private Vector3 offset;

    void Start()
    {
        offset = transform.position - player.transform.position;
    }

    void LateUpdate()
    {
        transform.position = player.transform.position + offset;       
    }
}

実際に動かしてみよう

動かす前にUnityに戻り、ヒエラルキービューでMain Cameraを選択した状態でインスペクターを見てください。 CameraControllerにPlayerというプロパティが表示されていますね。 f:id:enia:20210301171506p:plain

これが表示されるのは、先ほどCameraControllerにPublicフィールドとしてplayerを定義したからです。 一方で、offsetはPrivateフィールドとして定義したので表示されていません。

しかし、Playerプロパティの値が「なし」になっています。 Playerオブジェクトを関連づけるために、ヒエラルキーからPlayerオブジェクトをドラッグし、Playerプロパティの上にドロップしましょう。

すると、PlayerプロパティにPlayerオブジェクトへの参照が設定されました。 f:id:enia:20210301171827p:plain

さあ、プレイモードに変更して試してみましょう。 カメラがボールを追いかけてくるので、常にボールが画面の中心に表示されますね!

よりゲームらしさが増したのではないでしょうか。 少し短いですが、今回はここまで。