TATIKUNLOG.

主にゲーム作りについて書き留めるブログ。

Cinemachineで特定の座標軸を固定して対象を追跡する【Unity】


PR

(2024/03/10 追記)

cinemachine3でAPIが若干変わるらしく、このコードが使えなくなるのかなと書きかえに来ましたが、「using Cinemachine;」を「using Unity.Cinemachine;」に書きかえたら普通に動きました。

(追記終了)


Unity1週間ゲームジャムで横方向だけ追跡するカメラをCinemachineで実装しようとしたら、良く分からなくて調べたのでここにまとめます。

※詳しいコードの内容の説明はしません

目的

左:固定無し 右:Y軸座標固定

動画のように、追跡対象がY軸(画面上下)方向に移動してもカメラがついていかないようにします。

環境

Unity 2021.1.20f1

Cinemachine 2.7.8

実装

手順

①下記の軸を一定の座標で固定するCinemachineExtensionを継承したスクリプトを作成(今回はY軸を固定)

CinemachineVirtualCameraのExtensionsから①で作成したスクリプトを追加

Y軸固定のみの簡単なコード

using UnityEngine;
using Cinemachine;
 
[ExecuteInEditMode] [SaveDuringPlay] [AddComponentMenu("")] // Hide in menu
public class LockCameraY : CinemachineExtension
{
    [Tooltip("カメラのY座標を固定する値")]
    public float m_YPosition = 10;
 
    protected override void PostPipelineStageCallback(
        CinemachineVirtualCameraBase vcam,
        CinemachineCore.Stage stage, ref CameraState state, float deltaTime)
    {
        if (stage == CinemachineCore.Stage.Body)
        {
            var pos = state.RawPosition;
            pos.y = m_YPosition;
            state.RawPosition = pos;
        }
    }
}

汎用化版

スクリプト一本で全ての軸を制御可能にするために慣れないエディタ拡張を書いて汎用化させました。

インスペクタ上から固定したい軸のトグルにチェックを入れると、特定の座標で固定することができます。

using UnityEngine;
using Cinemachine;

#if UNITY_EDITOR
using UnityEditor;
#endif


[ExecuteInEditMode]
[SaveDuringPlay]
[AddComponentMenu("")] // Hide in menu
public class LockAxisCamera : CinemachineExtension 
{
    public bool x_islocked,y_islocked,z_islocked;
    public Vector3 lockPosition;

    protected override void PostPipelineStageCallback(
        CinemachineVirtualCameraBase vcam,
        CinemachineCore.Stage stage, ref CameraState state, float deltaTime)
    {
        if (stage == CinemachineCore.Stage.Body)
        {
            var newPos = state.RawPosition;
            if (x_islocked) newPos.x = lockPosition.x;
            if (y_islocked) newPos.y = lockPosition.y;
            if (z_islocked) newPos.z = lockPosition.z;
            state.RawPosition = newPos;
        }
    }
}

#if UNITY_EDITOR
[CustomEditor(typeof(LockAxisCamera))]
public class LockAxisCameraEditor : Editor
{
    public override void OnInspectorGUI()
    {
        var lockAxisCamera = target as LockAxisCamera;

        using (new EditorGUILayout.HorizontalScope())
        {
            EditorGUIUtility.labelWidth = 10;
            EditorGUILayout.LabelField("固定する軸");
            lockAxisCamera.x_islocked = EditorGUILayout.Toggle("X", lockAxisCamera.x_islocked);
            lockAxisCamera.y_islocked = EditorGUILayout.Toggle("Y", lockAxisCamera.y_islocked);
            lockAxisCamera.z_islocked = EditorGUILayout.Toggle("Z", lockAxisCamera.z_islocked);
        }

        EditorGUILayout.LabelField("固定する座標");
        using (new EditorGUILayout.HorizontalScope())
        {
            EditorGUIUtility.labelWidth = 10;
            lockAxisCamera.lockPosition.x = EditorGUILayout.FloatField("X", lockAxisCamera.lockPosition.x);
            lockAxisCamera.lockPosition.y = EditorGUILayout.FloatField("Y", lockAxisCamera.lockPosition.y);
            lockAxisCamera.lockPosition.z = EditorGUILayout.FloatField("Z", lockAxisCamera.lockPosition.z);
        }

    }
}
#endif

おわりに

スクリプトを書かなくてもCinemachineの機能だけで完結できれば便利だったのですが、調べた限りでは出てきませんでした。

特定の軸を固定するのってあまり需要ないんでしょうかね?

参考

docs.unity3d.com

forum.unity.com

nekojara.city