Posts
Search
Contact
Cookies
About
RSS

Rearview mirror with Unity 3D

Added 1 Jan 2021, 11:18 p.m. edited 18 Jun 2023, 1:12 a.m.
https://youtu.be/nCEqli2gtUA

Its not at all uncommon to have multiple cameras in a game. In my case I decided that a rearview mirror would help the player with situational awareness.

Handily its no surprise that Unity has a Render Texture asset which is just the thing we need for this job. In the example video there is a canvas providing us with what is in effect a HUD (heads-up display) The render texture (which is rectangular) is overlaid with a mirror frame, you might just guess I'm no real artist, but it does the job of disguising the fact that the mirror has sharp corners, that said you might decide that a frameless mirror works well with your style of game. Its worth noting that the order of canvas children is important, as the mirror frame appears above the render texture in the project hierarchy, its rendered over the top of the render texture.

In order to render to the render texture you will need a second camera, both of the camera's in the scene are attached to the player GameObject, with the second camera mounted just above the players mesh, facing backwards. As you can only have one Audio Listener in a scene you need to delete the one that comes with your new camera, the only other setting that needs changing is Target Texture, clicking the texture selection widget and you should see your render texture, once its selected it should change from black to the view from your rear view camera. In your HUD UI add a raw image (you can't use the normal image as its expecting a sprite). Also ensure that the raw image isn't using a material, you can however tint the mirror with a little blue, by changing the colour setting just above material.

Bonus material

You probably noticed that the cube player object can walk up the stairs, but it also happens to collide "properly" with the cubes, this is an easy trick, the player has two colliders, one is either a capsule or sphere at the bottom of the player, with the cube collider starting just above the height of a stair riser.

You probably also noticed that the scene starts with no cubes in it, while normally people will instance a prefab from a prefab that's attached to a GameObject, you can however also load it from the Resources directory in Assets. In the cubes behaviour script I added a static method, so when the player script starts it can call this method.

// create all the targets (called once by player.start)
static public void createTargets() 
{
    // prefab is loaded from Resources directory
    GameObject targetPrefab = Resources.Load<GameObject>("targetCube");

    for (int i=0; i < 100; i++) Instantiate(targetPrefab); // prefabs.start, positions etc the prefab

    allTargets = GameObject.FindGameObjectsWithTag("tractorTarget");
}

Lets look at boxes behaviour script start method.

// a list of all the targetable objects
private static GameObject[] allTargets;

private Rigidbody rb = null;

// Start is called before the first frame update
void Start()
{
    rb =  gameObject.GetComponent<Rigidbody>();
    
    teleport();
}

The teleport method is a tiny bit involved, as all the prefabs are created initially in the same place (I should have instance them all in a line under the ground plane really...)

public void teleport() 
{
    // find a spot not too near and not too far...
    Vector3 pos = new Vector3(0,0,0);
    while(pos.magnitude < 5) {
 pos.Set(Random.Range(-40, 40), 1.0f, Random.Range(-40, 40));
  if (getNearest(pos)) pos.Set(0,0,0); // don't let them be too close to each other
    }
  
    pos.y = 2f;
    rb.position = pos;
  
    // because all the prefabs are instanced in the same place they will "explode" apart
    // leave the angular velocity alone as it gives them a random seeming rotation
    rb.velocity = new Vector3(0,0,0);

    gameObject.GetComponent<Renderer>().material.color = Random.ColorHSV(0f, 1f, 1f, 1f, 0.5f, 1f);
}

Because all the prefabs are instanced by the time we get to the teleport method, we know where all boxes are. The while loop should be used with care as add in too many cube to fit in the space and it will lock up! (you could count the iterations of the while look an give up adding new cubes after a specific number of failures) The material colour setting was lifted directly from the Unity online manual.

private GameObject getNearest(Vector3 ppos) {
    float dist = 1000f;
    GameObject nearest = null;
    foreach( GameObject go in allTargets) {
        Rigidbody rb = go.GetComponent<Rigidbody>();
        float d = Vector3.Distance(rb.position, ppos);
 if ( d < dist) {
            dist = d;
            nearest = go;
        }
    }

    if (dist > 6) nearest = null;
      
    return nearest;   
}

Changing the line that nulls the result if the distance is greater than six to say greater than two will allow you to squeeze more boxes into the scene should you wish to.

I've included the frame texture above, to save you the bother of creating it for yourself, its not great but it will do as a stand in.

I hope you have found some of the techniques use here of some use, and should you have any ideas for a tutorial you'd like to see, please do use the contact form in the menu above.

Enjoy!