PUN Classic (also called PUN1) is the original and first major version of PUN. It is now replaced by PUN2 which is refactored and enhanced. We highly recommend starting new projects with PUN2 and if possible migrating existing ones from PUN1 to PUN2 by following our "Migration Notes". PUN Classic will be maintained for the coming months. We will fix important bugs and support new Unity versions but new features will be added only to PUN2.

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

このセクションでは、プレイヤを追従するCameraWorkスクリプトを作成する方法を説明します。 このセクションはネットワーキングと関連性がないため、簡潔に説明します。

Contents

CameraWork Scriptを作成

  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 Public Properties
    
    
            [Tooltip("The distance in the local x-z plane to the target")]
            public float distance = 7.0f;
    
    
            [Tooltip("The height we want the camera to be above the target")]
            public float height = 3.0f;
    
    
            [Tooltip("The Smooth time lag for the height of the camera.")]
            public float heightSmoothLag = 0.3f;
    
    
            [Tooltip("Allow the camera to have a vertical from the target, for example giving more view of the scenery and less ground.")]
            public Vector3 centerOffset = Vector3.zero;
    
    
            [Tooltip("Set this as false if a component of a prefab being instantiated by Photon Network and manually call OnStartFollowing() when and if needed.")]
            public bool followOnStart = false;
    
    
            #endregion
    
    
            #region Private Properties
    
    
            // cached transform of the target
            Transform cameraTransform;
    
    
            // maintain a flag internally to reconnect if the target is lost or the camera is switched
            bool isFollowing;
    
    
            // Represents the current velocity, this value is modified by SmoothDamp() every time you call it.
            private float heightVelocity = 0.0f;
    
    
            // Represents the position we are trying to reach using SmoothDamp()
            private float targetHeight = 100000.0f;
    
    
            #endregion
    
    
            #region MonoBehaviour Messages
    
    
            /// <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 every time 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やVectors、ベクトル、クォータニオンを使い始めたばかりの場合には、プレイヤーを追従する数学理論は難しいかもしれません。 このチュートリアルでは説明しませんが、ご興味のある場合には説明しますので、お気軽にお問い合わせください。

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

プレイヤーを常に追従した場合に、一般的に何が起きるかを想像してください。 プレイヤーでいっぱいのルームに接続すると、相手のプレイヤーのインスタンスのCameraWorkスクリプトは、プレイヤーを映すためにそれぞれ「メインカメラ」を制御しようとぶつかり合います。 こういった状態を避けて、コンピュータの前にいるユーザーを表すローカルプレイヤーのみを追従するべきです。

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

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

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

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

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

前に戻る

To Document Top