cycling Camera

Hi,
I´m new to Scripting and ran into a fundamental problem, i think.
I read and watched allot Tutorials online, and have three DVD´s about Unity and Unity Scripting and still learning! :wink: I´m stuck for days with this and hope you can guide me the right direction.

I setup this simple testing scene, as shown in the image attached.
I´m trying to have two kinds of Buttons to choose from for the User.
First, the “Main”- & “FPS”-Buttons. “Main” mainly as Intrudctionperspective as one starts the Level. And “FPS” as a User controlled Freeroamingperspective.
Second, the “<”- & “>”-Buttons. These should cycle through various set Cameras. (The goal is to have the count of these customizable in the end. But this is the next step. First small.)

I had testes this seperatly. One Project with only a Back&Forth-Buttons which cycle through all cameras, which worked. And another project where every Camera had his own Button, worked although.
But I´m not able to combine these two.

I tried diefferents things, last for example was to exchange every “GameObject”-Type with the “Camera”-Type and vice-a-versa.
My problem is that I learned to use “GameObject” with cameras to de-/activate cameras. But now the Camera-Type confuses me. And I think this maybe the problem : “GameObject.SetActive” VS. “Camera.enabled”

and please, if it´s no problem for you C#. Always switching between C# and JS confuses me and costs more time to me than as it helps. But if it´s the only way you know… I try again to translate it wtihout mistakes^^
…altough a big SORRY for my bad english!

using UnityEngine;
using System.Collections;

public class CameraButtons : MonoBehaviour
{
    //Slots for Cameras & help me understand better
    public GameObject MainCam;
    public GameObject FPSCam;
    public GameObject[] perspCameras;

    private GameObject Camera1;
    private GameObject Camera2;
    private GameObject Camera3;
    private GameObject Camera4;

    //Camera Counter/Index (taken from the web)
    private int currentCameraIndex;
    private Camera currentCamera;

    void Start()
    {
        perspCameras = new GameObject[] { Camera1, Camera2, Camera3, Camera4 };
      //  currentCamera = MainCam; //CS0029: Cannot implicitly convert type `UnityEngine.GameObject' to `UnityEngine.Camera'
        ChangeView();
    }

    void Update()
    {

    }

    public void EnableMain() //Main Button, activating MainCamera
    {
        Debug.Log("Main clicked");
        MainCam.SetActive(true);
        FPSCam.SetActive(false);
    }

    public void EnableFPS() //FPS Button, activating FirsPersonControlled-Camera
    {
        Debug.Log("FPS clicked");
        MainCam.SetActive(false);
        FPSCam.SetActive(true);
    }

    public void EnableNext() //Next Button, activates foward cycling through various (here 4) perspective Cameras
    {
        Debug.Log("Next clicked");
        Debug.Log("CamIndex" + currentCameraIndex);
        currentCameraIndex++; //copy&paste from internet, i think it counts up till array end, and starts again?!
        if (currentCameraIndex > perspCameras.Length - 1)
            currentCameraIndex = 0;
        ChangeView();
    }

    public void EnableBack() //Back Button, activates backward cycling through various (here 4) perspective Cameras
    {
        Debug.Log("Back clicked");
        Debug.Log("CamIndex" + currentCameraIndex);
        currentCameraIndex--; //modified:copy&paste from internet, i think it counts up till array end, and starts again?!
        if (currentCameraIndex > perspCameras.Length - 1)
            currentCameraIndex = 0;
        if (currentCameraIndex < 0)
            currentCameraIndex = perspCameras.Length - 1;
        ChangeView();
    }


    //taken from web: pushing the "s"-Key activates camera cycling/ChangeView
    /*  void Update()
      {
          if (Input.GetKeyDown("s"))
          {
              currentCameraIndex++;
              if (currentCameraIndex > perspCameras.Length-1)
                  currentCameraIndex = 0;
              ChangeView();
          }
      } */

    void ChangeView() //tfw: HERE´s a BIG PROBLEM. "SetActive" vs. "enabled" ("GameObject" vs. "camera"?)
    {
         currentCamera.enabled = false;
         currentCamera = perspCameras[currentCameraIndex];//CS0029: Cannot implicitly convert type `UnityEngine.GameObject' to `UnityEngine.Camera'
        currentCamera.enabled = true;
       
    }
}


You have a number of small, but simple issues. Here’s the ones that stood out:

You're trying to access a GameObject as if it were a Camera. **GameObject has ".SetActive"**, while **a camera (like all Components) has ".enabled"**. 

There are two solutions:
1) currentCamera = perspCameras[currentCameraIndex].GetComponent<Camera>();
2) (the better solution) instead of an array of GameObjects, simply make it an array of Camera instead. You'll have to re-drag in your camera references, but it'll be more efficient plus more reliable.

It already is! …but then you make it not be, and it’s the extra “making it not be” that’s actually what is breaking it.

You have an array of objects (which is public, and is assigned in the Inspector), but then in Start you immediately discard that array and replace it with four new GameObjects, which because they’re all private must be null. Simply… don’t. Get rid of Camera1/2/3/4, get rid of that line in Start. And not only is one of your errors fixed, but you also have a customizable number of cameras.

You're probably going to get a null reference error right off the bat because one of the first things you do is try to access currentCamera, before that has been set. You can just make that line:

```
if (currentCamera != null) currentCamera.enabled = false;
```

To guard against that the first time that function is run. (After that, it should be set correctly.)

One more note: If I have to choose between helping someone like you (with slightly wrong English, but provides all the needed context and error messages and screenshots, and uses code tags!), and someone who just pastes their entire script without a word of explanation and expects us to just fix it… I’ll choose to help you 10 times out of 10. :slight_smile:

1 Like

Thanks allot ! This really cleared a confusing Problem, I´m already trying to apply this.
And for a readable, good and complete Documentation I´ll hopefully be back with the solution.
It´s Netiquette, since it´s free I think one must pay the “effort” (peww, falling asleep:smile:).Plus we are although provided with these usefull tools. This mess you linked really is just a horror, and not usefull for other noobs(like myself) who could have learned from it too. It just scares off…
Hope nothing scares you off today!

1 Like

Hi, I´m back.
The big parts are working well. There´s only one anoying thing I really can´t figure out why it happens.
I although made pics again so that a Newbee could recreate it theirselfs…

I´d put “##” in the script, these are parts which are absolutly not clear to me. It´s mainly the Next- & Back-Button Function.
Or the “++” and “”, seems I´m to stupid to find it on the web. But “++” and “–” are really some weird Search-Keywords.^^

I started complet new after I freaked-out yesterday, because with the last one I had some weird Cam problems. The MainCam was skipped and never shown. So, I started Blank… and see the MainCam works again.
But there´s one thing I had yesterday and today again:

When one clicks the Next/Back-Button, nothing happens for the first three clicks. After a fourth click the script works properly and changes the Cameras. I guess it may have to do something with the ominous “-1”.

I again commented everything. So that one can understand what I understand, or it shows what I think I understood.

using UnityEngine;
using System.Collections;

public class newCam : MonoBehaviour
{
    //Fixed Cameras (these should not be Part of the cycling/Array)
    public Camera CamMain;
    public Camera CamFPS;

    //Array of Cameras (these should be controlled by Next- & Back-Button
    public Camera[] perspCams;          //the Camera Array
    private int currentCamIndex = 0;    //this keeps track which ID of the CamArray is active
    private Camera activeCam;           //placeholder for the Arraycameras which needs the ID
    public Camera CamPlaceholder;              //so that this must not be in void-Start: currentCam = MainCam

    void Start()
    {
        //CamMain.enabled = true;
    }

    public void EnableCamMain()         //Button activating only MainCam
    {
        Debug.Log("Main clicked" + currentCamIndex);
        CamMain.enabled = true;
        CamFPS.enabled = false;
    }

    public void EnableCamFPS()          //Button activating only FPSCam
    {
        Debug.Log("FPS clicked" + currentCamIndex);
        CamMain.enabled = false;
        CamFPS.enabled = true;
    }

    public void EnableNext()            //Button to cycle foward through the CamArray
    {
        Debug.Log("Next clicked" + currentCamIndex);
        //## What is "++"?
        currentCamIndex ++;
        //## why "-1"? i know that else it counts up one more as needed, and why not something like this?:
        /*
            if (currentCamIndex > 3)
             currentCamIndex = 0;
             currentCamIndex = currentCamIndex + 1;
        */
        if (currentCamIndex > perspCams.Length - 1)  //compares the cCIndex with the length of the perspCams-Array (-1?)
            currentCamIndex = 0;        //if ccIndex greater than ArrayLength, than cCIndes equals 0 which is the first perspCam
        ChangeView();                   //calls the ChangeView-Method
    }

    public void EnableBack()            //Button to cycle backwards through the CamArray
    {
        Debug.Log("Back clicked" + currentCamIndex);
        //## Same here, what means "--"? minus itself?
        currentCamIndex--;
        //is it that this(following) is static/fix (same as above):
        /*
         if (currentCamIndex < 2)
             = 5;
         currentCamIndex = currentCamIndex - 1;
        */
        //and this(following) is dynamic fitted to the size of the Array?!
        if (currentCamIndex > perspCams.Length - 1)     //keeps cCIndex low(not above Array.Length) and sets it else to zero
            currentCamIndex = 0;
        if (currentCamIndex < 0)                        //keeps cCIndex greater than zero or sets it else to Array.Length?
            currentCamIndex = perspCams.Length - 1;
        ChangeView();
    }

    void ChangeView()       //assignes the activates the ArrayID and its corresponding Camera?!
    {
        if (CamPlaceholder != null) CamPlaceholder.enabled = false; //prevents the Null-Exception Error
        CamPlaceholder.enabled = false;     //## this line really needed?
        CamPlaceholder = perspCams[currentCamIndex];    //Assigns the cCIndex(ID) to PlaceholderCam
        CamPlaceholder.enabled = true;                  //enables the above
    }
}
// i need to click 3x on Next/Back before it changes a Camera, but after the first "three empty" clicks it works as it should. what the heck?


If you’re wondering about special symbols for a programming language, you should search for “[language] operators”. So in your case, “c# operators”. As you’ve noticed, search engines like google don’t like stuff like “++”.

Here’s the page you get from that search.

++ and – and the increment and decrement operators. They’re common in most languages. They simply add one or remove one from the variable in question. It happens after the variable has been read, though:

int myInt = 5;
Debug.Log(myInt); //prints 5

myInt++; //adds 1 to myInt
Debug.Log(myInt); //prints 6

Debug.Log(myInt++); //prints 6 (!); The variable isn't updated before after it's read
Debug.Log(myInt); //prints 7

++myInt; // as myInt++, but increment happens first
Debug.Log(myInt); //prints 8

Debug.Log(++myInt); //prints 9

– is exactly the same, but negative. The ++myInt form is used incredibly rarely.

Don’t have time to help with the other problems, sorry!

1 Like

This is to make sure the index does not get out of range, but you seem to understand this. Getting the Length of an array gives you the number of slots it has in it. Since array position indexes are zero based you will need to subtract one from Length to get the last valid index number. If you have an array with 3 items in it, and you try to access item at index 3 (array[3]) the program will usually throw an IndexOutOfRangeException, because you only have indexes 0 to 2 to work with.
The reasoning for calculating the last index of an array over hardcoding it is that in many cases you work with an array that you don’t know how many slots it has beforehand. You can also resize the array and not have to change any code.

1 Like

It’s worth noting that these days it’s considered pretty poor form to use ++ in a situation where whether it’s before or after matters.

It’s -1 because whoever wrote that used “>” instead of “>=”. Conventionally, code that tests for array bounds would look more like this for clearer readability:

int someIndex = 3;
if (someIndex >= someArray.Length) someIndex = 0;

As you’re aware, if an array has 3 things in it, the indexes of those will be 0, 1, 2. So someArray[3] would throw an error. So you can either use >= Length or you can use > Length - 1; for ints, they are functionally the same.

The reason that line 73 is needed is to disable the old one. If it weren’t there, you’d just enable every camera over time, rendering on top of each other.

2 Likes

This is most likely because all of your cameras are enabled at the start. Cycling through them will disable them each in turn, and when you reach the one that is drawn on top of the others, you will have disabled the others, essentially making the script work like you intend.
You should disable all the cameras in perspCams at start. You should also consider disabling them all, or at least disable the last used one whenever you switch to CamMain or CamFPS.

// Disable all cameras in perspCams
for (int i = 0; i < perspCams.Length; i++)
{
    perspCams[i].enabled = false;
}

All cameras that are enabled will render to the screen, and if they are covering the entire viewport, you will only be able to see what the last camera rendered. There are some cases where rendering several cameras can be useful, like showing a 3D UI over the game camera, or for other special effects, but in your case I don’t think it’s intended.

1 Like

Which reminds me, there’s a line you can use to replace all of that logic.

someIndex++;
someIndex = (someIndex + someArray.Length) % someArray.Length;

% is the “modulus” operator, and it’s basically the remainder after dividing. So 2 % 3 is 2, 3 % 3 is 0, 4 % 3 is 1, etc. It’s fantastic when you’re working with array indexes that are intended to “loop” around. (I add someArray.Length before using modulus in case someIndex is less than 0; that is, -1 % 3 is -1)

You could also do this:

for (int i = 0; i < perspCams.Length; i++) {
perspCams[i].enabled = (i == currentCamIndex);
}

This will make sure that, no matter what has happened before, only one camera will be active.

1 Like

Thank you all so far, for your explanations and helping me make it work.
Next thing for me is to not only apply but to really understand. Arrays are now much clearer to me but I need to practice.
Modulus (“%”) however really strange. So Modulus 1%3 is…? the remainder after dividing…
dividing 1/3=0,333…
but this is obviously not the right way.
2/3=0,666 0,6-3=2,4 not 2.
I think this is something I should look up in motherlanguage. I really don´t get it.

I found an example of which I understand only parts.
100%90 remainder =10, ok no problem with that ´cause 90+10=100.
81%80 remainder =1, k althoug no prob, 80+1=81 and even 1%1 remainder =0 no problem.
But the first Line of this example was: 1000%90 remainder=10 ?Why not 910?
nevertheless, I think I´m going have my next pratice with Modulus and Arrays!

When I use this:

 for (int i = 0; i < perspCams.Length; i++) {
  perspCams[i].enabled = (i == currentCamIndex);
  }

…the CamFPS does weird things. It appears in the array cycle (but only one time, after that it´s not showing up again) and the FPS-Button activates Cam1 sometimes.But only from time to time. So I used ThermalFusion´s one.

For the disabling, I added the following to “EnableCamMain” and “EnableCamFPS” because else the Main-Button was not working properly. CamPlaceholder.enabled=false;

…and finaly, here´s the working script:
(with all it´s Comments and Questions&Answers)

using UnityEngine;
using System.Collections;

public class newCam : MonoBehaviour
{
  //Fixed Cameras (these should not be Part of the cycling/Array)
  public Camera CamMain;
  public Camera CamFPS;

  //Array of Cameras (these should be controlled by Next- & Back-Button
  public Camera[] perspCams;  //the Camera Array
  private int currentCamIndex = 0;  //this keeps track which ID of the CamArray is active
  private Camera activeCam;  //placeholder for the Arraycameras which needs the ID
  public Camera CamPlaceholder;  //so that this must not be in void-Start: currentCam = MainCam

  void Start()
  {
  // Disable all cameras in perspCams
  for (int i = 0; i < perspCams.Length; i++)
  {
  perspCams[i].enabled = false;
  }
  }

  public void EnableCamMain()  //Button activating only MainCam, deactivating every other Cam
  {
  Debug.Log("Main clicked" + currentCamIndex);
  CamMain.enabled = true;
  CamFPS.enabled = false;
  CamPlaceholder.enabled = false;
  }

  public void EnableCamFPS()  //Button activating only FPSCam, deactivating every other Cam
  {
  Debug.Log("FPS clicked" + currentCamIndex);
  CamMain.enabled = false;
  CamFPS.enabled = true;
  CamPlaceholder.enabled = false;
  }

  public void EnableNext()  //Button to cycle foward through the CamArray
  {
  Debug.Log("Next clicked" + currentCamIndex);
  currentCamIndex ++;  //increases by one when clicked
  if (currentCamIndex >= perspCams.Length)  //compares the cCIndex with the length of the perspCams-Array
  currentCamIndex = 0;  //if ccIndex greater than ArrayLength, than cCIndes equals 0 which is the first perspCam
  ChangeView();  //calls the ChangeView-Method
  }

  public void EnableBack()  //Button to cycle backwards through the CamArray
  {
  Debug.Log("Back clicked" + currentCamIndex);
  currentCamIndex --;  //decreases by one when clicked
  if (currentCamIndex <= -1)  //keeps it above 0 (0,1,2) and sets it else to perspCamsArray.Length(3) -1 so that we only get 0, 1 and 2.
  currentCamIndex = perspCams.Length - 1;
  ChangeView();
  }

  void ChangeView()  //assignes the activates the ArrayID and its corresponding Camera?!
  {
  if (CamPlaceholder != null) CamPlaceholder.enabled = false; //prevents the Null-Exception Error
  CamPlaceholder.enabled = false;  //## Q:this line really needed? A:"...needed is to disable the old one. If it weren't there, you'd just enable every camera over time, rendering on top of each other."
  CamPlaceholder = perspCams[currentCamIndex];  //Assigns the cCIndex(ID) to PlaceholderCam
  CamPlaceholder.enabled = true;  //enables the above
  }
}
// Q: i need to click 3x on Next/Back before it changes a Camera, but after the first "three empty" clicks it works as it should. what the heck?
// A: see Line18 (You should disable all the cameras in perspCams at start. You should also consider disabling them all, or at least disable the last used one whenever you switch to CamMain or CamFPS.)


/*
//Next & Back alternative
public void EnableNext()  //Button to cycle foward through the CamArray
  {
  Debug.Log("Next clicked" + currentCamIndex);
  currentCamIndex++;
  if (currentCamIndex > perspCams.Length - 1)
  currentCamIndex = 0;
  ChangeView();
  }

  public void EnableBack()  //Button to cycle backwards through the CamArray
  {
  Debug.Log("Back clicked" + currentCamIndex);
  currentCamIndex--;
  if (currentCamIndex > perspCams.Length - 1)
  currentCamIndex = 0;
  if (currentCamIndex < 0)
  currentCamIndex = perspCams.Length - 1;
  ChangeView();
  }*/

I´m adding again pictures, for others to follow…
For now, I´m going to make beatifull buttons( and finish this up). My head is smoking. But I´ll be back to comabt Modulus as soon as my brains are back. :slight_smile:



If you think way back to when you first learned about division in school (I’m assuming that math teaching where you grew up was similar to mine, though that may not be the case), before you learned how to do non-integer numbers. 3 goes into 1 zero times, but there’s 1 left over. So 1 % 3 = 1. Or, to put it another way: To figure out (a % b), first let (c = a / b) as an int, then (d = a - b * c). d is your result.

The math of it isn’t as important as the pattern. Let’s say x is counting from 0 to 10, and Length is 3.

x  %  L  =  mod
0     3     0
1     3     1
2     3     2
3     3     0
4     3     1
5     3     2
6     3     0
7     3     1
8     3     2
9     3     0
10    3     1

Note how the result of (x % L) is always less than L. That’s the important thing, and that’s why it’s so great for working with arrays.

Alternative explanation:

to find a % b, remove b from a until a is less than b:

int modulo(int a, int b) {
    while (a > b) {
        a = a - b;
    }
    return a;
}

An example of how to use it (that’s not array-related), is to turn a number of seconds into hours, minutes and seconds

Try it yourself!

public class BastesTestScript : MonoBehaviour {

    void Start() {
        PrintHourMinuteSeconds(15); // prints 0:0:15
        PrintHourMinuteSeconds(150); // prints 0:2:30
        PrintHourMinuteSeconds(1500); // prints 0:20:0
        PrintHourMinuteSeconds(15000); // prints 4:10:0
        PrintHourMinuteSeconds(123456); // prints 34:17:36
    }

    void PrintHourMinuteSeconds(int seconds) {
        //Seconds over the last minute:
        int secondsPart = seconds % 60;

        int withoutSeconds = seconds - secondsPart;

        int minutes = withoutSeconds / 60;

        //minutes over the last hour
        int minutesPart = minutes % 60;

        int withoutMinutes = minutes - minutesPart;

        //hours
        int hours = withoutMinutes / 60;

        Debug.Log("Hours:Minutes:Seconds = " + hours + ":" + minutesPart + ":" + secondsPart);
    }

}