ゲーム化!tomo_manaのブログ

ゲーム化!tomo-manaのブログ

Unityでゲームを作る方法について紹介しています

Unity学習#7 (Unity 2019.4.1f1) マップの再描画~カメラワーク

最低限のマップができ、キャラクターが四方を向くようになったので、いよいよ町やダンジョンへのワープを実装していこうと思いましたが、先にキャラクターに合わせてマップの再描画(古い表現ですね)が必要なことに気が付きました。
町やダンジョンへのワープは、目的地と衝突→画面フェードアウト→キャラクター移動→画面フェードイン、となると考えています。目指すのは最小形のRPGですが、キャラクター移動が「同一平面」か「複数の平面」か想像できていませんでした。まず敷居が低そうな同一平面で作る方向ですが、同一平面上にワールド、町、ダンジョンをスクロール無しで表示できるほど小さく作っても、ワープする時に画面の再描画が必要になります。
Unityでは、キャラクターに合わせてマップがスクロールする描画を、カメラワークで表現しています。これは3Dの機能に2Dでも使えるようにしたUnityの非常に優れた設計で、カメラをキャラクターに追従させることで、キャラクターが動く時に、マップが追従するのを実現します。

カメラの追従

毎回ですが、分かってしまえば大した苦労もなくカメラを追従できるようになります。ただ分かるまでがハマりました。

C#スクリプトを作成する

スクリプトをオブジェクトに割り当てる方法は第2回と同じです。

1) Projectウィンドウで右クリック > Create > C# Script をクリック
(ProjectウィンドウにC#スクリプトのアイコンが生成される。ファイル名を入力できる状態になっている)
2) ファイル名を入力してから確定
※注意:ファイル名を最初に確定した時に、中のファイルが生成される仕組みで、最初に付けたファイルの名前がクラス名になります。最初に確定する前にファイル名を入力します。
(ここではCameraControllerと名前を付けました)

C#スクリプトを記述する

次に、カメラ用のスクリプトを記述します。
カメラも、キャラクターと同じくXYZ座標を持っていますので、カメラのXY座標をキャラクターのXY座標に合わせる処理を追加します。この時に大事なのは、カメラはキャラクターよりも手前(Z座標ではマイナス方向)にいる必要があります。そのため、Z座標だけは、キャラクターのZ座標を使わないのがポイントになります。ここでは、ゲームの開始時(Start)に、カメラ自身のZ座標だけを取り出して、画面が描画されるたび(Update)に使いまわしています。thisは自分(ここではカメラ)、targetは被写体です。

コード

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

public class CameraController : MonoBehaviour
{
    public GameObject target;
    private Vector3 pos;
    
    // Start is called before the first frame update
    void Start()
    {
        pos.z = this.transform.position.z;
    }
    
    // Update is called once per frame
    void Update()
    {
        pos.x = target.transform.position.x;
        pos.y = target.transform.position.y;
        this.transform.position = pos;
    }
}

C#スクリプトをカメラに割り付ける

1) Unityの画面に戻ります
2) HierarchyウィンドウからMain Cameraを選択します
3) ProjectウィンドウにあるC#スクリプトをInspectorウィンドウの空きスペースにドラッグ&ドロップ
(ここでは、CameraController を Main Camera の Inspector に追加します)

カメラをキャラクターに追従させる

先ほど作成したコードは、被写体をUnityの画面から設定できるようになっています。
先ほどのC#スクリプトに、ゲームオブジェクト「target」を定義しましたが、スクリプトに定義したGameObjectは、画面上で設定ができるようになります。

コード

public class CameraController : MonoBehaviour
{
    public GameObject target;    // ← これです

定義した名前の先頭を大文字にしたものが、画面に表示されます。
この場合は、Target の欄が追加されます。
(図)
また、上の「target」を「myTarget」などのように、複数の単語をつなげた場合、大文字の間にスペースを入れた名前が、画面に表示されます。
「myTarget」の場合は、「My Target」の欄が追加されます。

このように作っておくことで、スクリプトを変更しなくても被写体を画面上から入れ替えることができるようになります。
1) 上記のパラメータ欄に、Hierarchyウィンドウ上のオブジェクトをドラッグ&ドロップします。

動作の確認

Unityの画面から再生ボタンを押して、キャラクターを上下左右に動かして、カメラが追従する動きを確認します。カメラが追従しなかったり、画面の表示がおかしい場合は、次の項目を確認します。

カメラの制約

カメラを追従することによって表示がおかしくなる時は、キャラクター、マップ、カメラのZ座標を確認します。
Z座標は、2D画面上ではプラスが奥、マイナスが手前です。
デフォルトでは、TileMapやキャラクター用に作成されたオブジェクトのZ座標は0、MainCameraのZ座標は-10です。

カメラのZ座標はキャラクターに追従させない

3Dの場合、カメラをZ座標に追従させるのですが、2DではカメラをZ座標に追従させません。
上記のコードで、ゲームの開始時にMainCameraのZ座標だけを取り出して、画面が更新されるたびに使いまわしていますが、この処理が抜けると、MainCameraのZ座標がキャラクターよりも手前(マイナス値)でなくなる可能性があります。
コード

public class CameraController : MonoBehaviour
{
    private Vector3 pos;
    
    // Start is called before the first frame update
    void Start()
    {
        pos.z = this.transform.position.z;
    }

カメラの座標はZ座標も必ず正しく更新する(Vector2は使わない)

また、同じ理由で、カメラの座標を更新する時に、XYしか保存ができないVector2を使うことができません。(代入できますが、画面表示がおかしくなる可能性があります)

カメラをTileMapより手前(Z座標を小さく)

メインカメラとTileMapとの相性があるのでしょうか、少しクセがありますので注意が必要です。
キャラクターとTileMapは同じZ座標でもキャラクターが手前に描画されるのですが、MainCameraがTileMapと同じZ座標になると、キャラクターがTileMapより先に描画されてしまい、画面からキャラクターが見えなくなってしまいます。

表示範囲の変更

最後に、MainCameraを始めて触ったので、表示範囲を変更してみます。
1) Hierarchyウィンドウから Main Camera を選択
2) Inspectorウィンドウから Size を変更します。
 デフォルトでは 5 ですが、これを 7 に変えると、より広い領域を同時に表示しますが、キャラクターは小さくなります。

次回やること
カメラの追従は、いくつか作業が残っていますが、単純な追従だけでけっこうなボリュームになってしまったので、またどこかで挑戦します。
●キャラクターが画面の端に行くまでスクロールしない
●キャラクターがマップの端に行ったらスクロールしない

次回は、キャラクターを町、ダンジョンにそれぞれワープさせる方法を調べます。
●キャラクターのワープ