Multiple Cameras and When to use them… [Unity3D]
If you have worked on unity and been overusing the Main Camera and thinking why even keep the option to add new cameras to the scene… Well, allow me to enlighten you… :P
Disclaimer: I assume you have fair knowledge of Unity basics (and Git)… if not you can find it here… Come back soon…
Ever heard of Portal…?
Yup.. that’s the one…
Shall we try replicating it?
Yeah you heard me… (Disclaimer yet again.. I don’t actually know the internal workings of the game… and am too lazy to look it up… It’s just my take on it, and I don’t expect it to even approach it’s level… It takes way more time and effort to build something that good, atleast more than 1 night… :P)
For this tutorial we will focus only on the visual aspects of the portal, i.e. seeing through them.
- Clone the repository in your desired folder (*whispers* type the below commands in your terminal/command prompt)
git clone git@github.com:Neelarghya/unity-multi-cam.git
- Once cloned in the right folder checkout the commit 576765c
To go back in time to the point from where we can follow this tutorial
The Repo contains the complete solution if you just want that…
cd unity-multi-cam
git checkout 576765c
- You should be greeted with a Project like:
You will find a project with a maze (covered in bathroom tiles) like setup, with a First Person controller. At this point if you play the scene you would be able to move and jump around in the environment.
If you notice carefully you might also find 2 screen like images on the walls. One with a blue border another with the orange border.
These images are actually World Space Canvases with 2 child objects.
An Image which just acts as the border to make it noticeable and
A Raw Image which will be the basis of our portals.
Let’s start Building Portals..!
- Add 2 Render Textures to your Images in the Project Window… And lets call them Portal1Texture and Portal2Texture
- Select the RawImages in each Portal Canvases and assign the respective RawTextures (Portal1Texture and Portal2Texture) to their texture field.
This insures that the RawImages Display the texture that is fed into these RawTextures. Once done the Screens will show Orange and Blue as no texture feed is present as of yet.
[Commit: 10d4765]
- Let’s feed these textures… Add 2 Cameras and place them at the centres of the 2 canvases, such that they look out of the canvases, like so…
- Once you have added both the cameras in the right positions, with the right rotations, Add the opposite RawTextures to the camera’s target texture. i.e. Portal1Camera gets Portal2Texture (RawTexture) and vice versa.
- Once done, hit play… you will see the portals kind of looks like images taken from each other… But that’s exactly what we have done…
They can display your movement from each other’s perspective but they fundamentally lack something… Any guesses?
They are too flat… and don’t visually portray any depth.
[Commit: bf4bd4a] - Let’s add some perspective, add a Scripts folder to your Assets and add a script called PerspectiveCamera to it. Assign the scripts to the 2 Portal cameras we have.
- Remove any unnecessary code and just add 2 Transform properties one for the Main Camera (Which is assigned during start) and another for the Display screen. And a Vector3 Property to store the Initial direction of the camera. Don’t forget to assign the Serialized variable display with the opposite displays. (Canvas2 for Camera1 and vice versa)
[Commit: be23dc1]
using UnityEngine;
public class PerspectiveCamera : MonoBehaviour
{
[SerializeField] private Transform display;
private Transform _camera;
private Vector3 _initialForward;
private void Start()
{
_camera = GameObject
.FindGameObjectWithTag("MainCamera")
.transform;
_initialForward = transform.forward;
}
}
- It’s time to add some logic to it… Add an Update to the script
This might look a bit convoluted at first, but… When we get down to it…
We need to rotate the the camera of the Portal, based on from where the user is viewing the other Display.
private void Update()
{
Vector3 axisOfRotation =
Vector3.Cross((display.position - _camera.position)
, -display.forward)
.normalized;
float angleBetween =
Vector3.Angle( _camera.position - display.position,
-display.forward);
transform.forward =
Quaternion.AngleAxis(angleBetween, axisOfRotation)
* _initialForward;
}
The direction between the user and the display is (display.position — _camera.position) “P” as in player.
Let the the initial forward direction be represented by “I”
And the Display’s Forward direction be “D” (Note that a canvas’ Forward is actually into the plane which is why we have negations in the code)
As we observe the angle between P and D is the same as the Angle between the required view (the out going arrow in Portal2) and I.
Thus the new direction will be the initial direction + the Angle between them (i.e. Theta in the image (medium doesn’t provide special characters, pardon me))
This Angle needs an axis of rotation, that we get via a cross product between the 2 Vectors D and P…
All in all we get
New View Direction = Initial Direction + Theta
Plain and Simple
[Commit: e9c1f40]
- Finally you can add trigger colliders to teleport your player from one portal to another
[Commit: 060b6e7]
There can be so many more applications of multiple cameras… This was just one thought experiment… Hope that was fun… Until next time…