I’m looking for some help scripting this, when my player is behind a wall, I want the wall to appear transparent, so that I can still see my player.
I’ve searched many other scripts, but none of them seemingly relevant to what I need, most seem to ‘fade’ my player character instead of the wall. I should also mention that I am using a custom made shaderforge shader on the wall in question, I’m telling you this because I looked at this unitypackage, Easy Fade Obstruction , but it uses the Unity 5 shader’s rendering mode ( set to fade ), which I don’t have access to within my shaderforge shader.
Ideally, something distance based would be great, so as you get closer, it starts to fade gradually down to a minimum transparency of half ( so that the wall can still be seen, but transparent ), and as you progress back out away from the wall, it regains it’s full appearance again ?
How about writing stencil buffer in circle(or any shape you need) around player
Then when drawing wall first check stencil and if it passes, then write transparent wall, otherwise write usual wall?
Another way is if you have only one player, before drawing walls, set player position in walls shader then in shader compare distance to player and either make wall transparent or not depending on that.
Thanks for your suggestions, sounds quite complicated for my feeble scripting abilities.
I’ve come up with another way, using box collider with IsTrigger enabled and simply by swapping the material OnTriggerEnter. I’m going to ‘try’ and extend this further though, as I’d like this to happen over a period of time that I set, so the transition is smooth rather than an immediate ‘pop’.
using UnityEngine;
using System.Collections;
public class triggerChangeMaterial : MonoBehaviour {
public Material baseMaterial;
public Material fadeMaterial;
void OnTriggerEnter (Collider other)
{
Debug.Log("Player has entered the trigger");
if (other.gameObject.tag == "Player")
{
GetComponent<Renderer>().material = fadeMaterial;
}
}
void OnTriggerExit (Collider other)
{
Debug.Log("Player has exited the trigger");
if (other.gameObject.tag == "Player")
{
GetComponent<Renderer>().material = baseMaterial;
}
}
}
Well, if you want very simple solutions, you could use raycasting. If raycast from camera to player hits wall, then hide wall. If it didn’t, reactivate wall… It won’t look very beautiful though.
For the type of game I’m putting together, it doesn’t need to be complicated, imagine a square arena with a fixed camera view, the wall at the front of the arena is the only wall I need to hide, so in this case, it’s simpler to just go with the material replace option when triggered.
Works great in my test scene, however, my actual ‘ingame’ wall has 3 different materials on it, I now need to find a way to replace all 3 mats at once, or alternatively just change their shader, as they all use the same ‘fade’ shader, I’ll post back here if I’m successful or not.
EDIT : Well, it worked, not sure if I’m calling the material.shader correctly ?, but it works, that’s the main thing ! ```csharp
**using UnityEngine;
using System.Collections;
public class triggerChangeShader : MonoBehaviour {
public Shader baseShader;
public Shader fadeShader;
void OnTriggerEnter (Collider other)
{
Debug.Log("Player has entered the trigger");
if (other.gameObject.tag == "Player")
{
GetComponent<Renderer>().material.shader = fadeShader;
}
}
void OnTriggerExit (Collider other)
{
Debug.Log("Player has exited the trigger");
if (other.gameObject.tag == "Player")
{
GetComponent<Renderer>().material.shader = baseShader;
}
}
This is the code for a simple camera which is following a player. It can actually manage more than one object obstructing the view.
Also I pass it a mask which I target with its name instead of checking for the collider name tag. Have fun.
using UnityEngine;
public class followPlayer : MonoBehaviour
{
public Transform player;
public Vector3 offset;
public Transform[] obstructions;
private int oldHitsNumber;
void Start()
{
oldHitsNumber = 0;
}
private void LateUpdate()
{
viewObstructed();
}
void Update()
{
transform.position = player.TransformPoint(offset);
transform.LookAt(player);
}
void viewObstructed()
{
float characterDistance = Vector3.Distance(transform.position, player.transform.position);
int layerNumber = LayerMask.NameToLayer("Walls");
int layerMask = 1 << layerNumber;
RaycastHit[] hits = Physics.RaycastAll(transform.position, player.position - transform.position, characterDistance, layerMask);
if (hits.Length > 0)
{
// Means that some stuff is blocking the view
int newHits = hits.Length - oldHitsNumber;
if (obstructions != null && obstructions.Length > 0 && newHits < 0)
{
// Repaint all the previous obstructions. Because some of the stuff might be not blocking anymore
for (int i = 0; i < obstructions.Length; i++)
{
obstructions[i].gameObject.GetComponent<MeshRenderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
}
}
obstructions = new Transform[hits.Length];
// Hide the current obstructions
for (int i = 0; i < hits.Length; i++)
{
Transform obstruction = hits[i].transform;
obstruction.gameObject.GetComponent<MeshRenderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.ShadowsOnly;
obstructions[i] = obstruction;
}
oldHitsNumber = hits.Length;
}
else
{
// Mean that no more stuff is blocking the view and sometimes all the stuff is not blocking as the same time
if (obstructions != null && obstructions.Length > 0)
{
for (int i = 0; i < obstructions.Length; i++)
{
obstructions[i].gameObject.GetComponent<MeshRenderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
}
oldHitsNumber = 0;
obstructions = null;
}
}
}
}
Hi If your still looking for a solution check our Asset as this is custom made shader for this use case. It can handle multi user multi buildings ( obstructions ) and is available for Standard URP and HDRP .
Greetings!
I slightly modified the @robinloul solution to support the opaque surface type and change the transparency over time for easing fading in/ fading out.
You just need to set it up on your camera, change the layer tag to your liking and set up somewhere material with surface type = transparent.
I hope you find it usefuil.
using System.Collections.Generic;
using UnityEngine;
public class MakeWallsTransparent : MonoBehaviour
{
public Transform player;
public Vector3 offest;
[SerializeField]
private List<Transform> ObjectToHide = new List<Transform>();
private List<Transform> ObjectToShow = new List<Transform>();
private Dictionary<Transform, Material> originalMaterials = new Dictionary<Transform, Material>();
void Start()
{
}
private void LateUpdate()
{
ManageBlockingView();
foreach (var obstruction in ObjectToHide)
{
HideObstruction(obstruction);
}
foreach (var obstruction in ObjectToShow)
{
ShowObstruction(obstruction);
}
}
void Update()
{
}
void ManageBlockingView()
{
Vector3 playerPosition = player.transform.position + offest;
float characterDistance = Vector3.Distance(transform.position, playerPosition);
int layerNumber = LayerMask.NameToLayer("floor");
int layerMask = 1 << layerNumber;
RaycastHit[] hits = Physics.RaycastAll(transform.position, playerPosition - transform.position, characterDistance, layerMask);
if (hits.Length > 0)
{
// Repaint all the previous obstructions. Because some of the stuff might be not blocking anymore
foreach (var obstruction in ObjectToHide)
{
ObjectToShow.Add(obstruction);
}
ObjectToHide.Clear();
// Hide the current obstructions
foreach (var hit in hits)
{
Transform obstruction = hit.transform;
ObjectToHide.Add(obstruction);
ObjectToShow.Remove(obstruction);
SetModeTransparent(obstruction);
}
}
else
{
// Mean that no more stuff is blocking the view and sometimes all the stuff is not blocking as the same time
foreach (var obstruction in ObjectToHide)
{
ObjectToShow.Add(obstruction);
}
ObjectToHide.Clear();
}
}
private void HideObstruction(Transform obj)
{
//obj.GetComponent<MeshRenderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.ShadowsOnly;
var color = obj.GetComponent<Renderer>().material.color;
color.a = Mathf.Max(0, color.a - WorldConfigurator.Instance.ObstructionFadingSpeed * Time.deltaTime);
obj.GetComponent<Renderer>().material.color = color;
}
private void SetModeTransparent(Transform tr)
{
MeshRenderer renderer = tr.GetComponent<MeshRenderer>();
Material originalMat = renderer.sharedMaterial;
if (!originalMaterials.ContainsKey(tr))
{
originalMaterials.Add(tr, originalMat);
}
else
{
return;
}
Material materialTrans = new Material(WorldConfigurator.Instance.transparentMaterial);
//materialTrans.CopyPropertiesFromMaterial(originalMat);
renderer.material = materialTrans;
renderer.material.mainTexture = originalMat.mainTexture;
}
private void SetModeOpaque(Transform tr)
{
if (originalMaterials.ContainsKey(tr))
{
tr.GetComponent<MeshRenderer>().material = originalMaterials[tr];
originalMaterials.Remove(tr);
}
}
private void ShowObstruction(Transform obj)
{
var color = obj.GetComponent<Renderer>().material.color;
color.a = Mathf.Min(1, color.a + WorldConfigurator.Instance.ObstructionFadingSpeed * Time.deltaTime);
obj.GetComponent<Renderer>().material.color = color;
if (Mathf.Approximately(color.a, 1f))
{
SetModeOpaque(obj);
}
}
}
It is not an issue with this. The WorldConfigurator is their own “personal” config-class. You don’t need it, replace the entire WorldConfigurator.Instance.ObstructionFadingSpeed with a simple float number (or make your own config entry in your own config solution and plug it in. Start with value 1 and change it as needed.