Unity

【脱出ゲーム制作4】視点をズームさせる方法

オリジナルのボクセルを使って脱出ゲーム制作に挑戦!

カメラをズームさせる機能を作成する

今回はオブジェクトをクリックした時に視点をズームする機能を実装します。

今回はドアをクリックしたらズームさせるよ

ドアをクリックしたらズームさせる

当たり判定とEventTriggerを設定

Box Colliderを追加

3Dモデルのドアを選択し、Add ComponentからBox Colliderを追加
EditColliderから当たり判定を設定

緑の枠が当たり判定になる

EventTriggerを追加

Add ComponentからEventTriggerを追加
EventTypeはPointerClickを選択

ズームカメラを作成

SubCameraを作成

まずはHierarchyにCameraを作成

OverlayMenuからワイプを表示し、カメラを切り替え

CameraのTransformを調整する

ワイプを見ながらドアの正面まで移動させよう

Game画面でUIが映った状態でも確認

名前をSubCameraに変更

SubCameraにAdd ComponentからPhysicsRaycasterを追加
当たり判定を取得するのに必要なのであらかじめ付けておきましょう

ズームするためのスクリプトを作成

クリックした時に実行するためのスクリプトを作成します

ZoomObj.csを作成

まずは新しくZoomObjのスクリプトを作成します

ZoomObj.cs
using UnityEngine;

public class ZoomObj : MonoBehaviour
{
    [SerializeField] Camera subCamera;

    private void Start()
    {
        subCamera.gameObject.SetActive(false);
    }

    public void OnClick()
    {
        subCamera.gameObject.SetActive(true);
    }
}

Startメソッドゲーム開始時にSubカメラを非表示にします。
OnClickメソッドクリックしたらSubカメラを表示させます。

ドアにスクリプトをアタッチ

ZoomObj.csをDoorへドラッグしてアタッチさせます。

ZoomObjのSubCameraの項目へHierarchyのSubCameraをドラッグし設定します。

EventTriggerにスクリプトを設定し、OnClickを選択

実行してみる

うまくできた

MainCameraを非表示にする

ズームした時にエラーが出るので、MainCameraを非表示にする

CameraManagerでシングルトンの実装し、ZoomObjのOnClickからChangeCameraメソッドを実行しMainCameraを非表示にする

CameraManager.cs
using UnityEngine;

public class CameraManager : MonoBehaviour
{
    //ここから追加
    //シングルトンの実装
    public static CameraManager instance;
    private void Awake()
    {
        instance = this;
    }
    //ここまで

    [SerializeField] Transform[] cameraPositions = default;
    int currentIndex = 0;

    void Start()
    {
        currentIndex = 0;
        SetCameraPosition(currentIndex);
    }

    public void TurnRight()
    {
        currentIndex ++;
        if (currentIndex >= cameraPositions.Length)
        {
            currentIndex = 0;
        }
        SetCameraPosition(currentIndex);
    }

    public void TurnLeft()
    {
        currentIndex--;
        if (currentIndex < 0)
        {
            currentIndex = cameraPositions.Length -1;
        }
        SetCameraPosition(currentIndex);
    }

    void SetCameraPosition(int index)
    {
        Camera.main.transform.position = cameraPositions[index].position;
        Camera.main.transform.rotation = cameraPositions[index].rotation;
    }
    
    //ここから追加
    Camera subCamera;

    public void ChangeCamera(Camera subCamera)
    {
        this.subCamera = subCamera;
        Camera.main.gameObject.SetActive(false);
    }
    //ここまで
}

ZoomObj.cs
using UnityEngine;

public class ZoomObj : MonoBehaviour
{
    [SerializeField] Camera subCamera;

    private void Start()
    {
        subCamera.gameObject.SetActive(false);
    }

    public void OnClick()
    {
        //ここから追加
        CameraManager.instance.ChangeCamera(subCamera);
        //ここまで
        subCamera.gameObject.SetActive(true);
    }
}

シングルトンの実装他のスクリプトからアクセスできるようにするための「お決まりの記述」です。
CameraManager.instanceを使えば、どこからでも現在のCameraManagerのインスタンスにアクセスできます。
引数 subCamera引数を渡すことで汎用性を高めることができます。
例えば、CameraManagerに引数としてsubCameraを渡すことで、サブカメラを非表示にすることができます。

戻るボタンを作成

元の視点へ戻るためのボタンを作成します。

BackPanel

前回作ったBackPanelを使用します。
BackPanelを押したら元の視点へ戻るようにします。

SerializeFieldを使ってBackPanelのUIを取得し、ChangeCameraメソッドが実行されたときに表示させるようにします。
OnBackButtonメソッドでは、BackPanelとSubCameraを非表示にし、MainCameraを表示するようにします。

CameraManager.cs
using UnityEngine;

public class CameraManager : MonoBehaviour
{
    //ここから追加
    [SerializeField] GameObject backPanel;
    //ここまで

    public static CameraManager instance;
    private void Awake()
    {
        instance = this;
    }

    [SerializeField] Transform[] cameraPositions = default;
    int currentIndex = 0;

    void Start()
    {
        currentIndex = 0;
        SetCameraPosition(currentIndex);
    }

    public void TurnRight()
    {
        currentIndex ++;
        if (currentIndex >= cameraPositions.Length)
        {
            currentIndex = 0;
        }
        SetCameraPosition(currentIndex);
    }

    public void TurnLeft()
    {
        currentIndex--;
        if (currentIndex < 0)
        {
            currentIndex = cameraPositions.Length -1;
        }
        SetCameraPosition(currentIndex);
    }

    void SetCameraPosition(int index)
    {
        Camera.main.transform.position = cameraPositions[index].position;
        Camera.main.transform.rotation = cameraPositions[index].rotation;
    }

    Camera subCamera;

    public void ChangeCamera(Camera subCamera)
    {
        this.subCamera = subCamera;
        //ここから追加
        backPanel.SetActive(true);
        //ここまで
        Camera.main.gameObject.SetActive(false);
    }

    //ここから追加
    public void OnBackButton()
    {
        backPanel.SetActive(false);
        this.subCamera.gameObject.SetActive(false);
        Camera.main.gameObject.SetActive(true);
    }
    //ここまで
}

ポイント解説

    public void ChangeCamera(Camera subCamera)
    {
        //↓
        this.subCamera = subCamera;
なぜthisを使うのか?

ローカル変数とフィールド変数を区別するため
メソッドの引数やローカル変数の名前がクラスのフィールド変数と同じ場合、thisを使うことでクラスのフィールド変数であることを明示的に区別できます。

    Camera subCamera; //フィールド変数

    public void ChangeCamera(Camera subCamera //ローカル変数)
    {
        this.subCamera = subCamera;
        //ここから追加
        backPanel.SetActive(true);
        //ここまで
        Camera.main.gameObject.SetActive(false);
    }
ローカル変数

ローカル変数は、ZoomObjから受け取った引数で、このメソッドの中だけで使える一時的なもの。

フィールド変数

フィールド変数は、クラス内のすべてのメソッドからアクセス可能で、このクラス(CameraManager)が存在している間は保持されます。

フィールド変数を定義することで、受け取ったChangeCameraメソッド以外でもsubCameraを使うことができる

スクリプトを実行できるようにする

CameraManagerの項目にBackPanelをアタッチ

BackPanelのボタンにCameraManagerをドラッグし、OnBackButtonを選択

実行してみる

ズームした後BackPanelを押すとエラーが出る

MainCameraが非表示になっていて切り替えることができないのが原因らしい。

CameraManagerを修正する

エラーを回避するためにCameraManagerを修正します

  • Startメソッドに「mainCamera = Camera.main;」を追加し、メインカメラを保持します。
  • フィールド変数として「Camera mainCamera;」を定義します。
  • 各箇所の「Camera.main」を「mainCamera」に変更します。
using UnityEngine;

public class CameraManager : MonoBehaviour
{
    [SerializeField] GameObject backPanel;

    public static CameraManager instance;
    private void Awake()
    {
        instance = this;
    }

    [SerializeField] Transform[] cameraPositions = default;
    int currentIndex = 0;

    void Start()
    {
        currentIndex = 0;

        // メインカメラを一度だけ取得し、mainCameraに保持することで再利用可能にする
        mainCamera = Camera.main;

        SetCameraPosition(currentIndex);
    }

    public void TurnRight()
    {
        currentIndex ++;
        if (currentIndex >= cameraPositions.Length)
        {
            currentIndex = 0;
        }
        SetCameraPosition(currentIndex);
    }

    public void TurnLeft()
    {
        currentIndex--;
        if (currentIndex < 0)
        {
            currentIndex = cameraPositions.Length -1;
        }
        SetCameraPosition(currentIndex);
    }

    void SetCameraPosition(int index)
    {
        // Camera.main→mainCameraに変更
        mainCamera.transform.position = cameraPositions[index].position;
        mainCamera.transform.rotation = cameraPositions[index].rotation;
    }

    Camera subCamera;

    // MainCameraをフィールド変数として定義し、一度だけ検索して再利用する
    Camera mainCamera;

    public void ChangeCamera(Camera subCamera)
    {
        this.subCamera = subCamera;
        backPanel.SetActive(true);
        // Camera.main→mainCameraに変更
        mainCamera.gameObject.SetActive(false);
    }

    public void OnBackButton()
    {
        backPanel.SetActive(false);
        this.subCamera.gameObject.SetActive(false);
        // Camera.main→mainCameraに変更
        mainCamera.gameObject.SetActive(true);
    }
}

ポイント解説

StartメソッドでMainCameraを一度だけ検索し、mainCamera フィールドに保持します。これにより、それ以降はmainCameraを直接操作できるため、毎回検索処理を行う必要がなくなり、効率的なコードになります。

実行

できたけど、ズームした時に左右のボタンがあるのはよくない

ズーム時のUIを調整

ドアにズームした際、左右のボタンは必要ないので表示させないようにする

左右のPanelをまとめる

まずは左右のパネルをまとめます。

CreateEmpty→名前を「MovePanels」にする
RightPanel、LeftPanelをMovePanelsにまとめる

CameraManagerを修正する

[SerializeField]でMovePanelsを取得し、ChangeCameraとOnBackButtonそれぞれにMovePanelsを非表示,表示させるようにする

using UnityEngine;

public class CameraManager : MonoBehaviour
{
    [SerializeField] GameObject backPanel;

    // MovePanelsを取得
    [SerializeField] GameObject movePanels;

    public static CameraManager instance;
    private void Awake()
    {
        instance = this;
    }

    [SerializeField] Transform[] cameraPositions = default;
    int currentIndex = 0;

    void Start()
    {
        currentIndex = 0;
        mainCamera = Camera.main;
        SetCameraPosition(currentIndex);
    }

    public void TurnRight()
    {
        currentIndex ++;
        if (currentIndex >= cameraPositions.Length)
        {
            currentIndex = 0;
        }
        SetCameraPosition(currentIndex);
    }

    public void TurnLeft()
    {
        currentIndex--;
        if (currentIndex < 0)
        {
            currentIndex = cameraPositions.Length -1;
        }
        SetCameraPosition(currentIndex);
    }

    void SetCameraPosition(int index)
    {
        mainCamera.transform.position = cameraPositions[index].position;
        mainCamera.transform.rotation = cameraPositions[index].rotation;
    }

    Camera subCamera;
    Camera mainCamera;

    public void ChangeCamera(Camera subCamera)
    {
        this.subCamera = subCamera;
        backPanel.SetActive(true);
        mainCamera.gameObject.SetActive(false);
        
        // MovePanelsを非表示
        movePanels.SetActive(false);
    }

    public void OnBackButton()
    {
        backPanel.SetActive(false);
        this.subCamera.gameObject.SetActive(false);
        mainCamera.gameObject.SetActive(true);
        
        // MovePanelsを表示
        movePanels.SetActive(true);
    }
}

CameraManagerに項目にMovePanelsをアタッチ

完成

とりあえず今回はここまで

参考にさせていただきました。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です