Unityに関連する記事です

 Cinemachine を利用した、対象へのフォーカス切り替え機能の実装方法です。

 特定の地点にプレイヤーが侵入した際、Cinemachine の切り替えを行って、フォーカスする対象をプレイヤー以外のオブジェクトにします。
この際には、フォーカスされているオブジェクトが移動を伴うことができ、その場合にも Cinemachine が追従して動きます。

 フォーカス対象のオブジェクトの移動がない場合、あるいは移動が終了した時点で、元の Cinemachine に切り替えます。

 2点間のカメラの切り替え速度、待機時間、オブジェクトの移動時間なども調整できます。


<実装動画 
動画ファイルへのリンク


<実装動画◆
動画ファイルへのリンク




事前準備


 この実装例では Cinemachine の他に、UTAGE アセットを利用しています。
また、イベント処理自体には GameEventHandler を利用した処理になっています。

 UTAGE については会話イベントへの紐付けなどで利用するため、アセットの利用は任意です。

 ただし、GameEventHandler の機能は必ず利用しますので、こちらから事前に実装しておいてください。

   => 処理の抽象化による実装例
     処理の抽象化による実装例
     処理の抽象化による実装例
     処理の抽象化による実装例
     処理の抽象化による実装例
 

設計


 処理の全体の流れです。

1.シーン内に、見えないコライダーのゲームオブジェクト LocationEventTrigger を配置して、設定する
   ↓
2.【1】のゲームオブジェクトに侵入すると、PlayerManager の OnTriggerEnter2D メソッドが感知し、
  そのゲームオブジェクトに LocationEventTrigger クラスがアタッチされているか確認する
  確認のときには一緒に、UTAGE の会話イベントに関連しているか、会話イベントは関係なく単純にその位置にいけばいいのかで判定する
   ↓
3.【2】の判定で OK が出たら、LocationEventTrigger ゲームオブジェクトの GameEventHandler に登録されている GameEvent を実行する
  このとき、GameEventCameraManipulator クラスをアサインしておくことで、Cinemachine の制御を行う GameEvent が発生する
  他の GameEvent と同じで、キャラの移動はできない状態になり、他の GameEvent にも組み込める

 こちらの流れを念頭に置いていただいて、実装にチャレンジしてください。

 また、このための準備として、2台目の Cinemachine の作成と、MainCamera の設定があります。

 最初にそちらから準備していきます。


2台目のCinemachine の追加


 現在ある、主の Cinemachine を複製して作成していくと便利です。名前は任意の名前に変えてください。
ただし、複製した場合、Cinemachine Confiner コンポーネントがアタッチされている場合には、それは削除してください。

 Priority は、最初にある主で Cinemachine よりも低い数字にしておきます。

 Follow には、フォーカスしたいゲームオブジェクトをアサインしておいてください。

 下記は参考値です。


インスペクター画像



Main Camera の CinemachineBrain への Custom Blends の設定を行う


 Main Camera の CinemachineBrain への Custom Blends の設定を行います。

 ここに個別に設定しない場合、その上にある Default Blends の設定が有効になって動きます。

 そのため、基本的には、A → B、B → A のように動かしたいときに、往復で時間を変えたいときや、Style でアニメの動作を変えたいときには
今回のように Custom Blends を使います。最初に作ったアセットに、この情報が記録されます。

 そうでなければ、Default Blends をそのまま使っていただいて問題ありません。
この辺りも多くの記事がありますので、それらも調べたり、あるいは、実際に操作して確認していってみてください。
この教材内にも紹介記事があります。

  => 複数の Cinemachine Virtual Camera の切り替え実装例


インスペクター画像



シーン内に見えないコライダーのゲームオブジェクトLocationEventTriggerを配置し、設定する


 ヒエラルキーにて Create Empty を行い、新しいゲームオブジェクトを作成し、名前を LocationEventTrigger にします。
このゲームオブジェクトは、プレイヤーが侵入することでイベントを発生させる役割を持ちます。
そのため、任意の形状のコライダーを設定してください。

 下記は参考値です。

 空のゲームオブジェクトの場合、Sceneビューでも見えないため、ラベルを設定しておきましょう。


Sceneビュー画像



インスペクター画像




LocationEventTriggerスクリプトを作成する


 続いて、LocationEventTriggerスクリプトを作成します。

 UTAGE を利用する場合と、利用しない場合で提示します。


<UTAGE を利用する場合>


using UnityEngine;

public class LocationEventTrigger : MonoBehaviour
{
    [SerializeField]
    protected UtageParamBoolName utageParamBoolName;

    
    /// <summary>
    /// 特定の地点にプレイヤーが侵入した際に GameEvent が発生するか確認
    /// </summary>
    /// <returns></returns>
    public bool JudgeLocationEvent() {

        // 会話イベントに関係なく発生するか確認
        if (utageParamBoolName == UtageParamBoolName.NoItem) {

	    Debug.Log("UTAGE の会話イベントに関係なく、この地点に侵入したら GameEvent 発生");
            return true;
        }
        
        // 会話イベントに紐づいたイベントであるか確認
        if (AdvEngineController.instance.AdvEngine.Param.GetParameterBoolean(utageParamBoolName.ToString())) {

            Debug.Log("UTAGE の会話イベントに紐づいた形で GameEvent 発生");
            return true;
        }

        // それ以外
	Debug.Log("条件に合致しないため、この地点における GameEvent の発生なし");

        return false;
    }
}

 スクリプトの内容ですが、6行目の utageParamBoolName は、会話イベントに紐づく場合に利用します。
13行目でこの値をチェックしていますので、会話イベントに関係なく発生させたい場合には「」をインスペクターで設定してください。
特定のUTAGE の会話が終わってからでなければ反応しないようにするには、関連する値を設定してください。


<UTAGE を利用しない場合>



using UnityEngine;

public class LocationEventTrigger : MonoBehaviour
{    
  // TODO イベントの条件などを、変数を作成して任意に設定する


    /// <summary>
    /// 特定の地点にプレイヤーが侵入した際に GameEvent が発生するか確認
    /// </summary>
    /// <returns></returns>
    public bool JudgeLocationEvent() {

        // イベントによって発生するか確認
        if ('イベントに紐づく条件があれば任意設定')
            Debug.Log("イベントの条件に合致したのでGameEvent 発生");
            return true;
        }

        // それ以外
	Debug.Log("条件に合致しないため、この地点における GameEvent の発生なし");

        return false;
    }
}

 どちらの場合もスクリプトを作成したらセーブしてください。


LocationEventTrigger スクリプトを LocationEventTrigger ゲームオブジェクトにアタッチする


 LocationEventTrigger スクリプトを、先ほど作成した LocationEventTrigger ゲームオブジェクトにアタッチして利用します。

 このあと、プレイヤー側にも新しく処理を追加し、この地点にプレイヤーが侵入した際に、イベントが発生するか、チェックします。


インスペクター画像



 ここでは UTAGE 用の設定を利用するものをアタッチしています。


GameEventCameraManipulator スクリプトを作成する


 次に、新しい GameEvent を作成します。

 GameEventBase クラスを継承した、GameEventCameraManipulator スクリプトを作成します。

 これは、ほかの GameEvent と同じように、発生させたいゲームオブジェクトに GameEventHander とこれをアタッチして、そこにアサインして利用します。
よって、Cinemachine によるイベントも、他のイベントと同様、組み合わせて利用することが出来ます。


using System.Collections;
using UnityEngine;
using Cinemachine;
using DG.Tweening;

/// <summary>
/// Main Camera の Cinemachine Brain にて2台のカメラの切り替え速度の設定をおこなっておく
/// </summary>
public class GameEventCameraManipulator : GameEventBase
{
    [SerializeField] private CinemachineVirtualCamera mainCamera;    // メインの Cinemachine カメラ
    [SerializeField] private CinemachineVirtualCamera eventCamera;   // イベント用の Cinemachine カメラ

    [SerializeField] private Transform target;                 // 移動させたい対象物(なければ不要)
    [SerializeField] private Transform destinationTran;        // 移動先
    [SerializeField] private float moveDuration;               // 移動時間

    // 非選択時のバーチャルカメラの優先度
    [SerializeField] private int unselectedPriority = 0;

    // 選択時のバーチャルカメラの優先度
    [SerializeField] private int selectedPriority = 10;

    private bool isCameraTransitionComplete = false;
    private float intervalTime = 2.0f;


    /// <summary>
    /// Cinemachine の挙動制御
    /// </summary>
    /// <returns></returns>
    public override IEnumerator ExecuteEventCoroutine() {

        if (isCameraTransitionComplete) yield break;

        // カメラの優先順位の切り替え
        mainCamera.Priority = unselectedPriority;
        eventCamera.Priority = selectedPriority;

        // カメラが目標となる対象の位置に移動するまで待機
        yield return new WaitForSeconds(intervalTime);

        // 目標の移動(カメラは自動追従)
        if (target) {
            yield return target.DOMove(destinationTran.position, moveDuration)
                .SetEase(Ease.Linear)
                .SetLink(gameObject)
                .WaitForCompletion();
        }

        // カメラの優先順位の切り替え(元に戻す)
        eventCamera.Priority = unselectedPriority;
        mainCamera.Priority = selectedPriority;

        // カメラがプレイヤーの位置に戻るまで待機
        yield return new WaitForSeconds(intervalTime);
        
        isCameraTransitionComplete = true;
        
        yield return false;
    }
}

 この機能は、Cinemachine のカメラの切り替えと、カメラが切り替わったあと、新しい対象物の移動有無を想定して作成しています。

 11行目と12行目には、それぞれのカメラをアサインします。どちらも Cinemachine です。

 14行目〜16行目は、カメラが切り替わったあとに、切り替わった先にある対象物が移動する場合の設定値です。
対象物が移動しない場合には、Target と DestinationTran は None のままで構いません。



 メソッド内の処理の内容は

1.カメラの Priority の切り替え
2.DOTween による対象物の移動(今回は、これはしない)
3.元のカメラに戻すために、再度カメラの Priority の切り替え

 という流れです。処理の内容を読み解いた上で活用してください。
記述する際には、どんな処理を書いているのかをイメージしながら書いていくとよいと思います。


LocationEventTrigger ゲームオブジェクトに GameEventHandler スクリプトと GameEventCameraManipulator スクリプトをアタッチして設定を行う


 通常のゲームイベントと同じように、GameEventHandler スクリプトをアタッチし、
さらに今回のために作成した GameEventCameraManipulator スクリプトをアタッチして、GameEventHandler にアサインします。

 この実装例では GameEventCameraManipulator しかアサインしてませんが、他のイベントも一緒に設定可能です。

 GameEventCameraManipulator スクリプトには2台の Cinemachine をアサインします。
2台目の Cinemachine のフォーカス対象物が移動する場合のみ、Target と Destination Tran は設定してください。


インスペクター画像



 以上でこのゲームオブジェクトは完成です。


PlayerManager スクリプトにメソッドを追加する


  最後に PlayerMangaer スクリプトに、メソッドを1つ追加します。
どこでもよいので追加してください。多分、一番最後に追加するとわかりやすいかと思います。


private void OnTriggerEnter2D(Collider2D other) {
    
    // イベントの発生する特定の地点に侵入し、かつ、イベントの発生条件を満たしている場合
    if (other.TryGetComponent(out LocationEventTrigger locationEventTrigger) && locationEventTrigger.JudgeLocationEvent()) {
        
        // GameEvent 実行
        StartCoroutine(StartEventCoroutine(locationEventTrigger.gameObject));
    }
}

 プレイヤーから見て、特定の地点に用意してある、コライダーのある見えないゲームオブジェクト(LocationEventTrigger)に侵入したとき、
そのゲームオブジェクトに、LocationEventTrigger スクリプトがアタッチされており、
かつ、LocationEventTrigger 内のJudgeLocationEventメソッドを実行して、イベントの発生条件に適合している(true)なら、
GameEventHandler に登録したイベントを発生させます。

 つまり、ボタンを押して Ray による判定をしている部分を、侵入判定を使って同じような形で実装しています。
このとき、実行するメソッドは同じ StartEventCoroutine メソッドなので、GameEventHandler に登録されているイベントを発生させます。

 今回は、GameEventCameraManipulator のみを発生させるようにしていますが、これ自体も GameEvent であるため、会話イベントを前後に挟んだり、他のイベントを挟んだり、自由に設定できます。


ゲームを実行して動作を確認する


 最初に提示した処理の内容を復習した上で、どういった挙動になっていればよいか、イメージを作った上でデバッグを行ってください。

 プレイヤーから見て、特定の地点に用意してある、コライダーのある見えないゲームオブジェクト(LocationEventTrigger)に侵入したとき、
GameEvent が実行されて Cinemachine が切り替わる処理が発生し、イベント終了後、再度、Cinemachine が元の状態に戻れば制御成功です。


<実装動画 
動画ファイルへのリンク


<実装動画◆
動画ファイルへのリンク

 


 以上で完成です。

 応用できますので、是非、自分で考えて動かしてみてください。

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Menu



技術/知識(実装例)

2Dおはじきゲーム(発展編)

2D強制横スクロールアクション(発展編)

3Dダイビングアクション(発展編)

2Dタップシューティング(拡張編)

レースゲーム(抜粋)

2D放置ゲーム(発展編)

3Dレールガンシューティング(応用編)

3D脱出ゲーム(抜粋)

2Dリアルタイムストラテジー

2Dトップビューアドベンチャー(宴アセット使用)

3Dタップアクション(NavMeshAgent 使用)

2Dトップビューアクション(カエルの為に〜、ボコスカウォーズ風)

VideoPlayer イベント連動の実装例

VideoPlayer リスト内からムービー再生の実装例(発展)

AR 画像付きオブジェクト生成の実装例

AR リスト内から生成の実装例(発展)

private



このサイト内の作品はユニティちゃんライセンス条項の元に提供されています。

管理人/副管理人のみ編集できます