6 - プレイヤーのカメラワーク

このセクションプレイヤーを追従するCameraWorkスクリプトを作成する方法を説明します。

このセクションはネットワーキングとは関係がないため、簡潔に説明します。

CameraWorkスクリプトを作成

  1. CameraWorkという名前の新しいC#スクリプトを作成します。
  2. CameraWorkの内容を以下のように置換します。

        using UnityEngine;
    using System.Collections;
    
    
    namespace Com.MyCompany.MyGame
    {
        /// <summary>
        /// Camera work. Follow a target
        /// </summary>
        public class CameraWork : MonoBehaviour
        {
            #region Private Fields
    
    
            [Tooltip("The distance in the local x-z plane to the target")]
            [SerializeField]
            private float distance = 7.0f;
    
    
            [Tooltip("The height we want the camera to be above the target")]
            [SerializeField]
            private float height = 3.0f;
    
    
            [Tooltip("The Smooth time lag for the height of the camera.")]
            [SerializeField]
            private float heightSmoothLag = 0.3f;
    
    
            [Tooltip("Allow the camera to be offseted vertically from the target, for example giving more view of the sceneray and less ground.")]
            [SerializeField]
            private Vector3 centerOffset = Vector3.zero;
    
    
            [Tooltip("Set this as false if a component of a prefab being instanciated by Photon Network, and manually call OnStartFollowing() when and if needed.")]
            [SerializeField]
            private bool followOnStart = false;
    
    
            // cached transform of the target
            Transform cameraTransform;
    
    
            // maintain a flag internally to reconnect if target is lost or camera is switched
            bool isFollowing;
    
    
            // Represents the current velocity, this value is modified by SmoothDamp() every time you call it.
            private float heightVelocity;
    
    
            // Represents the position we are trying to reach using SmoothDamp()
            private float targetHeight = 100000.0f;
    
    
            #endregion
    
    
            #region MonoBehaviour CallBacks
    
    
            /// <summary>
            /// MonoBehaviour method called on GameObject by Unity during initialization phase
            /// </summary>
            void Start()
            {
                // Start following the target if wanted.
                if (followOnStart)
                {
                    OnStartFollowing();
                }
    
    
            }
    
    
            /// <summary>
            /// MonoBehaviour method called after all Update functions have been called. This is useful to order script execution. For example a follow camera should always be implemented in LateUpdate because it tracks objects that might have moved inside Update.
            /// </summary>
            void LateUpdate()
            {
                // The transform target may not destroy on level load,
                // so we need to cover corner cases where the Main Camera is different everytime we load a new scene, and reconnect when that happens
                if (cameraTransform == null && isFollowing)
                {
                    OnStartFollowing();
                }
                // only follow is explicitly declared
                if (isFollowing)
                {
                    Apply ();
                }
            }
    
    
            #endregion
    
    
            #region Public Methods
    
    
            /// <summary>
            /// Raises the start following event.
            /// Use this when you don't know at the time of editing what to follow, typically instances managed by the photon network.
            /// </summary>
            public void OnStartFollowing()
            {         
                cameraTransform = Camera.main.transform;
                isFollowing = true;
                // we don't smooth anything, we go straight to the right camera shot
                Cut();
            }
    
    
            #endregion
    
    
            #region Private Methods
    
    
            /// <summary>
            /// Follow the target smoothly
            /// </summary>
            void Apply()
            {
                Vector3 targetCenter = transform.position + centerOffset;
                // Calculate the current & target rotation angles
                float originalTargetAngle = transform.eulerAngles.y;
                float currentAngle = cameraTransform.eulerAngles.y;
                // Adjust real target angle when camera is locked
                float targetAngle = originalTargetAngle;
                currentAngle = targetAngle;
                targetHeight = targetCenter.y + height;
    
    
                // Damp the height
                float currentHeight = cameraTransform.position.y;
                currentHeight = Mathf.SmoothDamp( currentHeight, targetHeight, ref heightVelocity, heightSmoothLag );
                // Convert the angle into a rotation, by which we then reposition the camera
                Quaternion currentRotation = Quaternion.Euler( 0, currentAngle, 0 );
                // Set the position of the camera on the x-z plane to:
                // distance meters behind the target
                cameraTransform.position = targetCenter;
                cameraTransform.position += currentRotation * Vector3.back * distance;
                // Set the height of the camera
                cameraTransform.position = new Vector3( cameraTransform.position.x, currentHeight, cameraTransform.position.z );
                // Always look at the target    
                SetUpRotation(targetCenter);
            }
    
    
            /// <summary>
            /// Directly position the camera to a the specified Target and center.
            /// </summary>
            void Cut()
            {
                float oldHeightSmooth = heightSmoothLag;
                heightSmoothLag = 0.001f;
                Apply();
                heightSmoothLag = oldHeightSmooth;
            }
    
    
            /// <summary>
            /// Sets up the rotation of the camera to always be behind the target
            /// </summary>
            /// <param name="centerPos">Center position.</param>
            void SetUpRotation( Vector3 centerPos )
            {
                Vector3 cameraPos = cameraTransform.position;
                Vector3 offsetToCenter = centerPos - cameraPos;
                // Generate base rotation only around y-axis
                Quaternion yRotation = Quaternion.LookRotation( new Vector3( offsetToCenter.x, 0, offsetToCenter.z ) );
                Vector3 relativeOffset = Vector3.forward * distance + Vector3.down * height;
                cameraTransform.rotation = yRotation * Quaternion.LookRotation( relativeOffset );
            }
    
    
            #endregion
        }
    }
  3. スクリプトCameraWorkを保存します。

リアルタイム3Dやベクトル、クォータニオン始めたばかりの場合にはプレイヤーを追従する数学理論は難しいかもしれません。 このチュートリアルでは説明しませんが、ご興味のある場合には説明しますので、お気軽にお問い合わせください。

このスクリプトは難しい数学に関するだけのものではありません。アクティブにプレイヤーを追従するタイミングを制御するという重要な機能も設定されています。 なぜプレイヤーを追従するタイミングを制御する必要があるのでしょうか。

いつもいつもプレイヤーを追従しているとどうなるのか考えてみましょう。 プレイヤーが多くいるルームに接続すると、相手のプレイヤーインスタンスのCameraWorkスクリプトが、自分のプレイヤーを写すため「メインカメラ」を制御しようとぶつかり合います。 このような事態は避け、コンピューターの前にいるユーザーを表すローカルプレイヤーのみを追従すべきです。

1つのカメラに対して複数のプレイヤーインスタンスが存在する場合の問題を把握できたので、それを防ぐ方法を以下で説明します。

  1. ローカルプレイヤーにのみCameraWorkスクリプトを添付します。
  2. 追従するプレイヤーがローカルかどうかに応じてオンとオフを切り替え、CameraWorkの動作を制御します。
  3. CameraWorkをカメラに取り付け、シーンの中にローカルプレイヤーインスタンスがある場合は注意して、そのプレイヤーのみを追従するようにします。

他にも方法はありますが、これらの3つの方法の中から選ぶとしたら2つ目のオプションをお勧めします。 3つの方法に大差はありませんが、2つ目のオプションはコーディングの量が最も少なく非常に高い柔軟性があります。

  • フィールドfollowOnStartを公開しましたので、非ネットワークされた環境で使用する場合trueに設定してください。例:テストシーンや、まったく異なるシナリオ

  • ネットワークベースのゲームで動作する場合、プレイヤーがローカルであると検出した場合にはパブリックメソッドOnStartFollowing()を呼び出します。 これは作成されたスクリプトPlayerManagerで実行されます。このスクリプトについてはプレイヤーネットワーキングの章で説明します。

前に戻る.

ドキュメントのトップへ戻る