This document is about: PUN 2
SWITCH TO

PUN Classic (v1), PUN 2 and Bolt are in maintenance mode. PUN 2 will support Unity 2019 to 2022, but no new features will be added. Of course all your PUN & Bolt projects will continue to work and run with the known performance in the future. For any upcoming or new projects: please switch to Photon Fusion or Quantum.

6 - Player Camera Work

This section will guide you to create the CameraWork script to follow along your player as you play this game.

This section has nothing to do with networking, so it will be kept short.

Creating the CameraWork Script

  1. Create a new C# script called CameraWork

  2. Replace the the content of CameraWork with the following:

    C#

    // --------------------------------------------------------------------------------------------------------------------
    // <copyright file="CameraWork.cs" company="Exit Games GmbH">
    //   Part of: Photon Unity Networking Demos
    // </copyright>
    // <summary>
    //  Used in PUN Basics Tutorial to deal with the Camera work to follow the player
    // </summary>
    // <author>[email protected]</author>
    // --------------------------------------------------------------------------------------------------------------------
    
    using UnityEngine;
    
    namespace Photon.Pun.Demo.PunBasics
    {
        /// <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("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;
    
            [Tooltip("The Smoothing for the camera to follow the target")]
            [SerializeField]
            private float smoothSpeed = 0.125f;
    
            // cached transform of the target
            Transform cameraTransform;
    
            // maintain a flag internally to reconnect if target is lost or camera is switched
            bool isFollowing;
    
            // Cache for camera offset
            Vector3 cameraOffset = Vector3.zero;
    
            #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();
                }
            }
    
    
            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) {
                    Follow ();
                }
            }
    
            #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 Follow()
            {
                cameraOffset.z = -distance;
                cameraOffset.y = height;
    
                cameraTransform.position = Vector3.Lerp(cameraTransform.position, this.transform.position +this.transform.TransformVector(cameraOffset), smoothSpeed*Time.deltaTime);
    
                cameraTransform.LookAt(this.transform.position + centerOffset);
            }
    
    
            void Cut()
            {
                cameraOffset.z = -distance;
                cameraOffset.y = height;
    
                cameraTransform.position = this.transform.position + this.transform.TransformVector(cameraOffset);
    
                cameraTransform.LookAt(this.transform.position + centerOffset);
            }
            #endregion
        }
    }
    

3. Save the Script `CameraWork`

The logic behind following the player is simple. We compute the desired camera position with added offset to fall behind using the `Distance` and above using the `Height`. Then it uses `Lerping` for smoothing out the movement to catch up with the desired position and finally, a simple `LookAt` for the camera to always point at the Player.

Aside the Camera work itself, something important is also set up; the ability to control when the behaviour should actively follow the player. 
And this is important to understand: When would we want to have the camera to follow the player?

Typically, let's imagine what would happen if it was always following the player. 
As you connect to a room full of players, each `CameraWork` script on the other players' instances would fight to control the "Main Camera" so that it looks at its player... 
Well we don't want that, we want to only follow the local player that represents the user behind the computer.

Once we've defined the problem that we have only one camera but several player instances, we can easily find several ways to go about it.

1. Only attach the `CameraWork` script on the local player. 
2. Control the `CameraWork` behaviour by turning it off and on depending on whether the player it has to follow is the local player or not.
3. Have the `CameraWork` attached to the Camera and watch out for when there is a local player instance in the scene and follow that one only.


These 3 options are not exhaustive, many more ways can be found, but out of these 3, we'll arbitrary pick the second one. 
None of the above options are better or worse, however this is the one that possibly requires the least amount of coding and is the most flexible... "interesting..." I hear you say :)

- We've exposed a field `followOnStart` which we can set to true if we want to use this on a non networked environment, for example in our test scene, or in completely different scenarios

- When running in our networked based game, we'll call the public method `OnStartFollowing()` when we detect that the player is a local player. 
This will be done in the script `PlayerManager` that is created and explained in the chapter [Player Prefab Networking](./player-networking#camera-control)


[Previous Part](./player-prefab).
Back to top