0
\$\begingroup\$

when one camera is active the other/s are disabled. Each time only the current switched to the camera is active and the others are disabled.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class SwitchCameras : MonoBehaviour
{
    private bool cycleAllCameras = false;
    public Camera[] storedCameras;

    public Vector3[] _originalPositions;
    public Vector3[] _lastPositions;
    public Camera[] _currentCameraSequence;
    public Camera[] _allCameras;
    int _currentCameraIndexInCycle;
    int _currentCameraIndexInAll;

    // Rather than store the current camera's index, AND its reference,
    // AND its name, AND its position, AND its original position...
    // let's just store its index, and everything else can be inferred
    // from that, so we don't have redundant data that can go out of sync.
    public Camera currentCamera
    {
        get
        {
            return _currentCameraSequence[_currentCameraIndexInCycle];
        }
    }

    void Start()
    {
        // Cache all cameras enabled at the start of the scene; 
        _allCameras = Camera.allCameras;
        _originalPositions = new Vector3[_allCameras.Length];
        _lastPositions = new Vector3[_allCameras.Length];

        // Select the first camera in whichever list we're using
        _currentCameraSequence = cycleAllCameras ? _allCameras : storedCameras;
        Camera currentCamera = this.currentCamera;

        // Disable every camera except the one we're using,
        // and cache their original positions to use later.
        for (int i = 0; i < _allCameras.Length; i++)
        {
            _allCameras[i].enabled = (_allCameras[i] == currentCamera);
            _lastPositions[i] = _originalPositions[i] = _allCameras[i].transform.position;
        }
    }

    void LateUpdate()
    {
        // Update the current camera's last position,
        // before we switch from it to a dormant one.
        _lastPositions[_currentCameraIndexInAll] = currentCamera.transform.position;

        // Use a named input so the user can remap it.
        if (Input.GetKeyDown(KeyCode.G))
            CycleCamera();
    }

    public void SwitchSequence()
    {
        if (_currentCameraSequence.Length > 0)
        {
            cycleAllCameras = !cycleAllCameras;
            CycleCamera();
        }
    }

    // Cycle action is in its own public method, in case you 
    // want to trigger it from other scripts or UI buttons.
    public void CycleCamera()
    {
        currentCamera.enabled = false;

        // If we've swapped sequences, re-index the camera.
        if (_currentCameraSequence == storedCameras)
        {
            if (cycleAllCameras == true)
            {
                _currentCameraIndexInCycle = _currentCameraIndexInAll;
                _currentCameraSequence = _allCameras;
            }
        }
        else if (cycleAllCameras == false)
        {
            _currentCameraIndexInCycle = Mathf.Max(0, System.Array.IndexOf(storedCameras, currentCamera));
            _currentCameraSequence = storedCameras;
        }

        // Step to the next camera, and wrap around the sequence.
        _currentCameraIndexInCycle = (_currentCameraIndexInCycle + 1) % _currentCameraSequence.Length;

        // Ensure we have a valid index into the "all" list
        // for looking up original position / last position / etc.
        _currentCameraIndexInAll = System.Array.IndexOf(_allCameras, currentCamera);

        //
        currentCamera.enabled = true;
    }
}

I have two problems.

The first is that in the editor in the Inspector of the SwitchCameras script I cant change the size of the storedCameras variable. No matter what value I type in the array in the inspector if it's 2 or 1 or 30 it's all the time turn back to 0. And the game is not running yet. I can drag cameras to the storedCameras but I cant change its size if I'm typing a number.

The second problem is that one of the cameras is First Person and there is a script attached to it: FirstPersonController and this script are using the Main Camera with the variable m_Camera. The problem is when I'm running the game the first active camera is another camera and the main camera is disabled so I'm getting many exceptions null exceptions since the main camera is disabled(NULL) so also m_camera is null:

using System;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;
using UnityStandardAssets.Utility;
using Random = UnityEngine.Random;

namespace UnityStandardAssets.Characters.FirstPerson
{
    [RequireComponent(typeof (CharacterController))]
    [RequireComponent(typeof (AudioSource))]
    public class FirstPersonController : MonoBehaviour
    {
        [SerializeField] private bool m_IsWalking;
        [SerializeField] private float m_WalkSpeed;
        [SerializeField] private float m_RunSpeed;
        [SerializeField] [Range(0f, 1f)] private float m_RunstepLenghten;
        [SerializeField] private float m_JumpSpeed;
        [SerializeField] private float m_StickToGroundForce;
        [SerializeField] private float m_GravityMultiplier;
        [SerializeField] private MouseLook m_MouseLook;
        [SerializeField] private bool m_UseFovKick;
        [SerializeField] private FOVKick m_FovKick = new FOVKick();
        [SerializeField] private bool m_UseHeadBob;
        [SerializeField] private CurveControlledBob m_HeadBob = new CurveControlledBob();
        [SerializeField] private LerpControlledBob m_JumpBob = new LerpControlledBob();
        [SerializeField] private float m_StepInterval;
        [SerializeField] private AudioClip[] m_FootstepSounds;    // an array of footstep sounds that will be randomly selected from.
        [SerializeField] private AudioClip m_JumpSound;           // the sound played when character leaves the ground.
        [SerializeField] private AudioClip m_LandSound;           // the sound played when character touches back on ground.

        private Camera m_Camera;
        private bool m_Jump;
        private float m_YRotation;
        private Vector2 m_Input;
        private Vector3 m_MoveDir = Vector3.zero;
        private CharacterController m_CharacterController;
        private CollisionFlags m_CollisionFlags;
        private bool m_PreviouslyGrounded;
        private Vector3 m_OriginalCameraPosition;
        private float m_StepCycle;
        private float m_NextStep;
        private bool m_Jumping;
        private AudioSource m_AudioSource;

        // Use this for initialization
        private void Start()
        {
            m_CharacterController = GetComponent<CharacterController>();
            m_Camera = Camera.main;
            m_OriginalCameraPosition = m_Camera.transform.localPosition;
            m_FovKick.Setup(m_Camera);
            m_HeadBob.Setup(m_Camera, m_StepInterval);
            m_StepCycle = 0f;
            m_NextStep = m_StepCycle/2f;
            m_Jumping = false;
            m_AudioSource = GetComponent<AudioSource>();
            m_MouseLook.Init(transform , m_Camera.transform);
        }


        // Update is called once per frame
        private void Update()
        {
            if (Camera.main != null)
            {
                m_Camera = Camera.main;
            }
            else
            {
                return;
            }
            RotateView();
            // the jump state needs to read here to make sure it is not missed
            if (!m_Jump)
            {
                m_Jump = CrossPlatformInputManager.GetButtonDown("Jump");
            }

            if (!m_PreviouslyGrounded && m_CharacterController.isGrounded)
            {
                StartCoroutine(m_JumpBob.DoBobCycle());
                PlayLandingSound();
                m_MoveDir.y = 0f;
                m_Jumping = false;
            }
            if (!m_CharacterController.isGrounded && !m_Jumping && m_PreviouslyGrounded)
            {
                m_MoveDir.y = 0f;
            }

            m_PreviouslyGrounded = m_CharacterController.isGrounded;
        }


        private void PlayLandingSound()
        {
            m_AudioSource.clip = m_LandSound;
            m_AudioSource.Play();
            m_NextStep = m_StepCycle + .5f;
        }


        private void FixedUpdate()
        {
            if (Camera.main != null)
            {
                m_Camera = Camera.main;
            }
            else
            {
                return;
            }

            float speed;
            GetInput(out speed);
            // always move along the camera forward as it is the direction that it being aimed at
            Vector3 desiredMove = transform.forward * m_Input.y + transform.right * m_Input.x;

            // get a normal for the surface that is being touched to move along it
            RaycastHit hitInfo;
            Physics.SphereCast(transform.position, m_CharacterController.radius, Vector3.down, out hitInfo,
                               m_CharacterController.height / 2f, Physics.AllLayers, QueryTriggerInteraction.Ignore);
            desiredMove = Vector3.ProjectOnPlane(desiredMove, hitInfo.normal).normalized;

            m_MoveDir.x = desiredMove.x * speed;
            m_MoveDir.z = desiredMove.z * speed;


            if (m_CharacterController.isGrounded)
            {
                m_MoveDir.y = -m_StickToGroundForce;

                if (m_Jump)
                {
                    m_MoveDir.y = m_JumpSpeed;
                    PlayJumpSound();
                    m_Jump = false;
                    m_Jumping = true;
                }
            }
            else
            {
                m_MoveDir += Physics.gravity * m_GravityMultiplier * Time.fixedDeltaTime;
            }
            m_CollisionFlags = m_CharacterController.Move(m_MoveDir * Time.fixedDeltaTime);

            ProgressStepCycle(speed);
            UpdateCameraPosition(speed);

            m_MouseLook.UpdateCursorLock();
        }


        private void PlayJumpSound()
        {
            m_AudioSource.clip = m_JumpSound;
            m_AudioSource.Play();
        }


        private void ProgressStepCycle(float speed)
        {
            if (m_CharacterController.velocity.sqrMagnitude > 0 && (m_Input.x != 0 || m_Input.y != 0))
            {
                m_StepCycle += (m_CharacterController.velocity.magnitude + (speed*(m_IsWalking ? 1f : m_RunstepLenghten)))*
                             Time.fixedDeltaTime;
            }

            if (!(m_StepCycle > m_NextStep))
            {
                return;
            }

            m_NextStep = m_StepCycle + m_StepInterval;

            PlayFootStepAudio();
        }


        private void PlayFootStepAudio()
        {
            if (!m_CharacterController.isGrounded)
            {
                return;
            }
            // pick & play a random footstep sound from the array,
            // excluding sound at index 0
            int n = Random.Range(1, m_FootstepSounds.Length);
            m_AudioSource.clip = m_FootstepSounds[n];
            m_AudioSource.PlayOneShot(m_AudioSource.clip);
            // move picked sound to index 0 so it's not picked next time
            m_FootstepSounds[n] = m_FootstepSounds[0];
            m_FootstepSounds[0] = m_AudioSource.clip;
        }


        private void UpdateCameraPosition(float speed)
        {
            Vector3 newCameraPosition;
            if (!m_UseHeadBob || m_Camera == null)
            {
                return;
            }
            if (m_CharacterController.velocity.magnitude > 0 && m_CharacterController.isGrounded)
            {
                m_Camera.transform.localPosition =
                    m_HeadBob.DoHeadBob(m_CharacterController.velocity.magnitude +
                                      (speed*(m_IsWalking ? 1f : m_RunstepLenghten)));
                newCameraPosition = m_Camera.transform.localPosition;
                newCameraPosition.y = m_Camera.transform.localPosition.y - m_JumpBob.Offset();
            }
            else
            {
                newCameraPosition = m_Camera.transform.localPosition;
                newCameraPosition.y = m_OriginalCameraPosition.y - m_JumpBob.Offset();
            }
            m_Camera.transform.localPosition = newCameraPosition;
        }


        private void GetInput(out float speed)
        {
            // Read input
            float horizontal = CrossPlatformInputManager.GetAxis("Horizontal");
            float vertical = CrossPlatformInputManager.GetAxis("Vertical");

            bool waswalking = m_IsWalking;

#if !MOBILE_INPUT
            // On standalone builds, walk/run speed is modified by a key press.
            // keep track of whether or not the character is walking or running
            m_IsWalking = !Input.GetKey(KeyCode.LeftShift);
#endif
            // set the desired speed to be walking or running
            speed = m_IsWalking ? m_WalkSpeed : m_RunSpeed;
            m_Input = new Vector2(horizontal, vertical);

            // normalize input if it exceeds 1 in combined length:
            if (m_Input.sqrMagnitude > 1)
            {
                m_Input.Normalize();
            }

            // handle speed change to give an fov kick
            // only if the player is going to a run, is running and the fovkick is to be used
            if (m_IsWalking != waswalking && m_UseFovKick && m_CharacterController.velocity.sqrMagnitude > 0)
            {
                StopAllCoroutines();
                StartCoroutine(!m_IsWalking ? m_FovKick.FOVKickUp() : m_FovKick.FOVKickDown());
            }
        }


        private void RotateView()
        {
            m_MouseLook.LookRotation (transform, m_Camera.transform);
        }


        private void OnControllerColliderHit(ControllerColliderHit hit)
        {
            Rigidbody body = hit.collider.attachedRigidbody;
            //dont move the rigidbody if the character is on top of it
            if (m_CollisionFlags == CollisionFlags.Below)
            {
                return;
            }

            if (body == null || body.isKinematic)
            {
                return;
            }
            body.AddForceAtPosition(m_CharacterController.velocity*0.1f, hit.point, ForceMode.Impulse);
        }
    }
}

I tried to add in the Update and FixedUpdate in both places the same small code:

if (Camera.main != null)
            {
                m_Camera = Camera.main;
            }
            else
            {
                return;
            }

But it didn't solve the problems. Once I'm switching to the main camera I'm getting null exceptions on m_camera.

The main goal is to switch between stored cameras for example if I added 2 cameras to stored cameras switch between them. If I clicked on the button Switch Sequence then now I will be able to switch between all the cameras and not only the two stored.

But something here I messed up.

CamS

\$\endgroup\$
  • 1
    \$\begingroup\$ Write a title that summarizes the specific problem.you should include some code, But don't just copy in your entire program! Not only is this likely to get you in trouble if you're posting your employer's code, it likely includes a lot of irrelevant details that readers will need to ignore when trying to reproduce the problem. please read how to ask a question here \$\endgroup\$ – Seyed Morteza Kamali Mar 5 '18 at 4:58
1
\$\begingroup\$

m_Camera is a cached reference; Camera.main is not

When you change what camera is enabled, this changes the result of Camera.main (as Camera.main litterally does an uncached GameObject.FindWithTag("MainCamera") call), but you do nothing to update the value of your m_Camera.

This is why

if (Camera.main != null) //not that this should ever be false...
{
    m_Camera = Camera.main;
}

"fixes" your issue: it reassigns the current value of Camera.main. If you did this inside CycleCamera instead, you wouldn't need your check at all.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.