How can I create a “see behind walls” effect in Unity?

GDT cover blog

When we want to make a game there is always a challenge like “see behind walls” effect in Unity.

Especially when we want to make a 3D video game. Because in this case the variables are a lot.

So let’s talk about one of the big problems we can face when we create a 3rd person game.

The problem of letting the player see what happens when his avatar disappears behind a wall.

It’s not very nice to have a wall in front of you losing the visual and starting to press randomly the buttons on the pad.

But there should be a way to create something like a “see behind walls” effect in Unity because many games do it.

Let’s investigate and solve this case together.

Shaders

Effect

As many problems in Unity, this one has many solutions too.

The only problem is that most of them expect you to know the usage of shaders and stencils.

What the hell are they? It is too complicated to go into depth but basically the shaders are in charge of a lot of effects in a 2d or 3d model.

But they have their shading language to be created, so it could be quite complicated to make it this way. Obviously if you want to create a particular effect of your “see behind walls” effect the shaders will be the best option.

If you are curious about them please have a look at the official documentation.

We can’t avoid working with them because in some way we need to make our walls transparent, but I want to show you the easiest approach to create our effect and avoid touching the shaders scripts.

An easy solution

Easy solution

Let’s analyze the problem together. We want to create a “see behind walls” effect in Unity. Can we divide it into minor sub-problems? Yes we can!

Let’s make a list of subproblems:

  • Make a wall transparent to let the player see behind it.
  • Apply this special effect only to particular objects, in our case the “walls”.
  • Make a wall transparent only when the avatar is behind it.

Ok great so basically we just need to work out these problems and we’ll solve the case.

I can’t wait to start let’s do it!

Make a wall transparent

Transparent wall

Our first problem to solve is to make our wall object transparent. To do it we need to check and change eventually the configuration of its material.

Depending on the material this configuration could change, so I will show you the configuration with a standard shader. If you can’t follow this solution it is not a problem, just find a way to make your material sensitive in some way to the alpha channel, or to control in some way its transparency.

Find the material of the object like the image below tapping on the material component of the object in the scene (1) and then on its material. (2)

Find material

Now you will see the configuration panel we need to:

  • Set the shader as “Standard”. (1)
  • Set the rendering mode as “Transparent”. (2)
  • Check and set in case the texture with the right texture of the material (Setting the shader the material could lose the reference to its texture so check if it’s there). (3)
  • Set the source as “Albedo Alpha”. (4)

Material configuration

Well, now we can just set the alpha channel clicking on the colour of the albedo map (1) and changing the slider value of the alpha channel (2).

And the magic is done!

Make an object sensible to the “see behind walls” effect

Configuration

Now we know that our material could be transparent. Let’s set its alpha channel to 255 from the channel, so all the walls will start with no transparency effects.

Now we need to configure a “Wall” object. I mean we need to let this kind of object be recognizable and configurable.

To do it let’s use a layer to identify the object (1). With a lot of imagination I’ll call it “Wall”. So every wall in our game should have this layer set.

Set layer

Now it’s time to create a script to set the alpha channel of the material, in this way at a certain point we will be able to change this wall as transparent or not.

Let’s add this script component to all our wall objects.

public class TransparentWall : MonoBehaviour
{
    //Variables
    private Renderer rend;
    Color materialColor;
    private bool transparent = false;

    private void Start()
    {
        //Get the renderer of the object
        rend = GetComponent();
        //Get the material color
        materialColor = rend.material.color;
    }

    public void ChangeTransparency(bool transparent)
    {
        //Avoid to set the same transparency twice
        if (this.transparent == transparent) return;

        //Set the new configuration
        this.transparent = transparent;

        //Check if should be transparent or not
        if (transparent)
        {
            //Change the alpha of the color
            materialColor.a = 0.3f;
        }
        else
        {
            //Change the alpha of the color
            materialColor.a = 1.0f;
        }
        //Set the new Color
        rend.material.color = materialColor;
    }
}

Trigger the right wall

Trigger

Wow we already solved two big subproblems, we just need to work out the last one: apply the “see behind walls” effect only to the wall that covers the player.

If only there was something like a ray that connected the camera and the player and reported everything hit by it… but wait there is!

We need the Raycast! Lucky for you we have also a beautiful article where we talk about the Raycast: Unity Raycast 2D What Is It And How To Use It. I know we are in a 3D world but the concept is similar!

When the player disappears behind a wall it means that the wall will be between the player and the camera. So the ray will hit it then.

As we know the Raycast will report to us the first object hit by the ray, then we can change its transparency.

But we also need to remember to restore the transparency of a wall not hit anymore. I think we should divide this problem too:

  • Create a ray between the user and the camera reporting only wall objects.
  • Change transparency of a wall hit by the ray
  • Restore transparency of a wall not hit anymore

Create a ray between the user and the camera

So our goal is to create a ray between camera and a player (1).

Desired Ray

To do it we need to calculate a vector direction and a length to create our ray.

//Public variable to store a reference to the player game object
    public GameObject player;
private void FixedUpdate()
    {
        //Calculate the Vector direction 
        Vector3 direction = player.transform.position - transform.position;
        //Calculate the length
        float length = Vector3.Distance(player.transform.position, transform.position);
        //Draw the ray in the debug
        Debug.DrawRay(transform.position, direction.normalized * length, Color.red);
    }

We can try it by adding a script to the camera with this FixedUpdate().

Draw Ray

Change transparency of a wall hit by the ray

Now we want to report only the walls objects, lucky for us we set the layer before and we can set a layerMask as a parameter to a raycast.

The layerMask allows the ray to report only the layers in the mask ignoring the others.

Once we reported the first desired object (Wall) hit by the ray we want just to set its transparency through the script we created below:

private void FixedUpdate()
    {
        //Calculate the Vector direction 
        Vector3 direction = player.transform.position - transform.position;
        //Calculate the length
        float length = Vector3.Distance(player.transform.position, transform.position);
        //Draw the ray in the debug
        Debug.DrawRay(transform.position, direction * length, Color.red);
        //The first object hit reference
        RaycastHit currentHit;
        //Cast the ray and report the firt object hit filtering by "Wall" layer mask
        if (Physics.Raycast(transform.position, direction, out currentHit, length, LayerMask.GetMask("Wall")))
        {
            //Getting the script to change transparency of the hit object
            TransparentWall transparentWall = currentHit.transform.GetComponent();
            //If the object is not null
            if (transparentWall)
            {
                //Change the object transparency in transparent.
                transparentWall.ChangeTransparency(true);
            }
        }
    }

Restore transparency of a wall not hit anymore

Only one problem remains, how can we restore a wall not hit anymore?

We just need to save the reference of the currentTransparentWall and check when the ray hits something whether the wall hit is the current one, a new one or no-one.

In any case we will need to set the new transparency or restore the old one. Let’s change the previous script to handle this:

public class SeeBehindWall : MonoBehaviour
{
    //Public variable to store a reference to the player game object
    public GameObject player;
    //The current wall
    private TransparentWall currentTransparentWall;

    private void FixedUpdate()
    {
        //Calculate the Vector direction 
        Vector3 direction = player.transform.position - transform.position;
        //Calculate the length
        float length = Vector3.Distance(player.transform.position, transform.position);
        //Draw the ray in the debug
        Debug.DrawRay(transform.position, direction * length, Color.red);
        //The first object hit reference
        RaycastHit currentHit;
        //Cast the ray and report the firt object hit filtering by "Wall" layer mask
        if (Physics.Raycast(transform.position, direction, out currentHit, length, LayerMask.GetMask("Wall")))
        {
            //Getting the script to change transparency of the hit object
            TransparentWall transparentWall = currentHit.transform.GetComponent();
            //If the object is not null
            if (transparentWall)
            {
                //If there is a previous wall hit and it's different from this one
                if (currentTransparentWall && currentTransparentWall.gameObject != transparentWall.gameObject)
                {
                    //Restore its transparency setting it not transparent
                    currentTransparentWall.ChangeTransparency(false);
                }
                //Change the object transparency in transparent.
                transparentWall.ChangeTransparency(true);
                currentTransparentWall = transparentWall;
            }            
        }
        else
        {
            //If nothing is hit and there is a previous object hit
            if (currentTransparentWall)
            {
                //Restore its transparency setting it not transparent
                currentTransparentWall.ChangeTransparency(false);
            }
        }
    }
}

Follow the player

Added the previous script to the camera, the only thing which remains is to let the camera to follow the player! Here is the snippet to do it:

public class FollowPlayer : MonoBehaviour
{
    //Public variable to store a reference to the player game object
    public GameObject player;
    //Private variable to store the offset distance between the player and camera
    private Vector3 offset;

    void Start()
    {
        //Calculate and store the offset value by getting the distance between the player's position and camera's position.
        offset = transform.position - player.transform.position;
    }

    private void LateUpdate()
    {
        // Set the position of the camera's transform to be the same as the player's, but offset by the calculated offset distance.
        transform.position = player.transform.position + offset;
    }
}

Hey! Don’t forget to assign the player to both the scripts components.

Assign player to script component
Now adding this script to the camera we will be able to handle the “see behind walls” effect in Unity! Let’s see it in action.

Conclusion

We did it again! It’s amazing how this brainstorming works!

In the beginning it seemed impossible, but we worked it out, we created a cool “see behind walls” effect in Unity!

Let’s do a quick recap:

  • Set the material configuration of our walls to handle the transparency.
  • Create a configuration of a transparent wall with a combination of script and layer.
  • Generate a ray between the player and the camera to trigger the transparency of a wall.

Easy peasy!

I hope you enjoyed it, if you have some interesting ways to do the same result please share with us in a comment!

See you for the next case.

More from Romeo Violini

UnityEngine.Random VS System.Random

“To be or not be, UnityEngine.Random vs System.Random, that is the question!”....
Read More

Leave a Reply

Your email address will not be published. Required fields are marked *