0
\$\begingroup\$

I have 5 cameras in the scene. In the Inspector I added to the cameras array 3 cameras. In the script I store this cameras using storedCameras array. In cameras array there are 5 cameras.

I added a flag allCameras to change between the stored cameras mode and all cameras mode.

And I'm using the G key to switch between the cameras either the stored or the all cameras.

But when I press on G I see in the Inspector that each press on G remove one camera from the array cameras in the end I left with one camera. And the idea is to press G and switch between the cameras either if it's on stored cameras mode or all cameras mode depending on the flag.

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

public class SwitchCameras : MonoBehaviour
{
    [Header("Cameras Init")]
    public Camera[] cameras;
    public Vector3[] originalPosition;
    [HideInInspector]
    public Vector3 currentCameraOriginalPosition, currentCameraPosition;
    public bool allCameras = false;
    private Camera[] storedCameras;

    [Space(5)]

    [Header("Cameras Switch")]
    public string currentCameraName;
    public Vector3[] lastCameraPosition;

    private int currentCamera = 0;

    void Start()
    {
        storedCameras = cameras;

        if (allCameras == true)
        {
            cameras = new Camera[Camera.allCameras.Length];
            cameras = Camera.allCameras;
        }

        lastCameraPosition = new Vector3[cameras.Length];

        if (cameras.Length >= 1)
        {
            originalPosition = new Vector3[cameras.Length];
            for (int i = 0; i < cameras.Length; i++)
            {
                originalPosition[i] = cameras[i].transform.position;
            }
        }

        if (cameras.Length == 1)
        {
            Debug.LogError("Need more then 1 camera for switching..");
        }
        else
        {
            Debug.Log("Found " + cameras.Length + " cameras");
        }

        for (int i = 0; i < cameras.Length; i++)
        {
            cameras[i].enabled = false;
        }
        cameras[0].enabled = true;
        currentCameraName = cameras[0].name;
        currentCameraOriginalPosition = originalPosition[0];
    }

    void LateUpdate()
    {
        if (allCameras == true)
        {
            cameras = new Camera[Camera.allCameras.Length];
            cameras = Camera.allCameras;
        }
        else
        {
            cameras = new Camera[storedCameras.Length];
            cameras = storedCameras;
        }

        if (Input.GetKeyDown(KeyCode.G))
        {
            cameras[currentCamera].enabled = false;

            if (++currentCamera == cameras.Length)
                currentCamera = 0;

            cameras[currentCamera].enabled = true;
            currentCameraName = cameras[currentCamera].name;
            currentCameraOriginalPosition = originalPosition[currentCamera];
        }

        lastCameraPosition[currentCamera] = cameras[currentCamera].transform.position;
        currentCameraPosition = lastCameraPosition[currentCamera];
    }
}
\$\endgroup\$
  • 2
    \$\begingroup\$ note that the first line of this pattern: cameras = new Camera[...]; cameras = someExistingArrayOfCameras; doesn't do anything for you. You create a new array of type Camera then immediately discard it by overwriting your only reference to it. As a reference type, using the assignment operator= on an array just sets the variable on the left to reference the same array listed on the right. It does not copy the entries from the array on the right into the array on the left - use the CopyTo() method if that's the behaviour you want. But since your use is read-only,you don't need a copy. \$\endgroup\$ – DMGregory Feb 26 '18 at 0:06
2
\$\begingroup\$

Calling Camera.allCameras returns only the enabled cameras in your scene. Since you disable cameras you're not using, that means you have fewer to choose from the next time you query it.

Instead, you can cache a private list of _allCameras in Start(), provided all your cameras are enabled at the time the scene loads, and you're not spawning more or deleting them dynamically over time.

You also only need to swap your lists when you're actually performing a camera swap, rather than every frame.

public bool cycleAllCameras = false;
public Camera[] storedCameras;

Vector3[] _originalPositions;
Vector3[] _lastPositions;
Camera[] _currentCameraSequence;
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.
    _lastPosition[_currentCameraIndexInAll] = currentCamera.transform.position;

    // Use a named input so the user can remap it.
    if(Input.GetButtonDown("CycleCamera"))
        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 = (_currentCameraIndex + 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;
}
\$\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.