In certain games, there is a function involved where if there is an object between the camera's view and the player, the object will either become transparent, or the player's silhouette is visible on the object. Would I need some sort of custom diffuse shader, or is there a function that will allow me to change the visibility of an object.
Also, how would I do a raycast/vector check to determine if the object is between the player and camera.
Just do it like Jean said. The Unity script reference on RaycastAll gives you an example for exact your case except it does not revert the transparency. A list would be a solution, but it gets tricky to determine what renderers have to be reverted, which ones should stay transparent and which ones have to set transparent.
I would write a little script that you can attach dynamically to those renderes which should be transparent. In that script you can implement some kind of falloff and when it'sno longer needed it removes itself.
C#:
Here's an example script that gets dynamically attached to the renderer and the script that do the raycast.
using UnityEngine;
using System.Collections;
public class AutoTransparent : MonoBehaviour
{
private Shader m_OldShader = null;
private Color m_OldColor = Color.black;
private float m_Transparency = 0.3f;
private const float m_TargetTransparancy = 0.3f;
private const float m_FallOff = 0.1f; // returns to 100% in 0.1 sec
public void BeTransparent()
{
// reset the transparency;
m_Transparency = m_TargetTransparancy;
if (m_OldShader == null)
{
// Save the current shader
m_OldShader = renderer.material.shader;
m_OldColor = renderer.material.color;
renderer.material.shader = Shader.Find("Transparent/Diffuse");
}
}
void Update()
{
if (m_Transparency < 1.0f)
{
Color C = renderer.material.color;
C.a = m_Transparency;
renderer.material.color = C;
}
else
{
// Reset the shader
renderer.material.shader = m_OldShader;
renderer.material.color = m_OldColor;
// And remove this script
Destroy(this);
}
m_Transparency += ((1.0f-m_TargetTransparancy)*Time.deltaTime) / m_FallOff;
}
}
//And here the script for the camera to cast the ray / capsule ;
using UnityEngine;
using System.Collections;
public class ClearSight : MonoBehaviour
{
public float DistanceToPlayer = 5.0f;
void Update()
{
RaycastHit[] hits;
// you can also use CapsuleCastAll()
// TODO: setup your layermask it improve performance and filter your hits.
hits = Physics.RaycastAll(transform.position, transform.forward, DistanceToPlayer);
foreach(RaycastHit hit in hits)
{
Renderer R = hit.collider.renderer;
if (R == null)
continue; // no renderer attached? go to next hit
// TODO: maybe implement here a check for GOs that should not be affected like the player
AutoTransparent AT = R.GetComponent<AutoTransparent>();
if (AT == null) // if no script is attached, attach one
{
AT = R.gameObject.AddComponent<AutoTransparent>();
}
AT.BeTransparent(); // get called every frame to reset the falloff
}
}
}
The two script files need to be named like the class names (AutoTransparent and ClearSight).
To ignore your player select your player and change the layer on top of the inspector. Just set the layer to “IgnoreRaycast”.
edit
I guess your camera script is also on the camera like my ClearSight script.
JS:
var myDistanceZoomed : float;
GetComponent.<ClearSight>().DistanceToPlayer = myDistanceZoomed;
But don't forget: you need the distance in units. If you have the distance already, great, just pass it to my script ;)
Raycasting would be a start, that is, you raycast from the player position and whatever is hit before the player should be send a message or dealt in some way to become semi transparent. you would need to maintain a list so that you can toggle semi transparent models back when not anymore in front of the player,
Else, you could actually have a box collider starting from the player and pointing at the camera where its size emcopass the player bounds, then anything that collides to that are also in the way.
Otherwise, Physics.CapsuleCast could be also an alternative to building a collider, but very similar in principle, tho the start point would need to be sligtly tweacked and not start right where the player is but slightly closer to the camera to not hit the ground or things to close to the player.
As for the shader, I would replace it with a specific simple one while in front, you might want to have front and backface material for object very close to the camera.
Bunny83 script updated for multiple materials (uses Transparent material of your choice. Good for Unity5)
public class ClearSight : MonoBehaviour {
public float DistanceToPlayer = 5.0f;
public Material TransparentMaterial = null;
public float FadeInTimeout = 0.6f;
public float FadeOutTimeout = 0.2f;
public float TargetTransparency = 0.3f;
private void Update() {
RaycastHit[] hits; // you can also use CapsuleCastAll()
// TODO: setup your layermask it improve performance and filter your hits.
hits = Physics.RaycastAll(transform.position, transform.forward, DistanceToPlayer);
foreach (RaycastHit hit in hits) {
Renderer R = hit.collider.GetComponent<Renderer>();
if (R == null) {
continue;
}
// no renderer attached? go to next hit
// TODO: maybe implement here a check for GOs that should not be affected like the player
AutoTransparent AT = R.GetComponent<AutoTransparent>();
if (AT == null) // if no script is attached, attach one
{
AT = R.gameObject.AddComponent<AutoTransparent>();
AT.TransparentMaterial = TransparentMaterial;
AT.FadeInTimeout = FadeInTimeout;
AT.FadeOutTimeout = FadeOutTimeout;
AT.TargetTransparency = TargetTransparency;
}
AT.BeTransparent(); // get called every frame to reset the falloff
}
}
}
And AutoTransparent class with fade in/out:
public class AutoTransparent : MonoBehaviour {
private Material[] oldMaterials = null;
private float m_Transparency = 1.0f;
public float TargetTransparency { get; set; }
public float FadeInTimeout { get; set; }
public float FadeOutTimeout { get; set; }
public Material TransparentMaterial { get; set; }
private bool shouldBeTransparent = true;
public void BeTransparent() {
shouldBeTransparent = true;
}
private void Start() {
// reset the transparency;
m_Transparency = 1.0f;
if (oldMaterials == null) {
// Save the current materials
oldMaterials = GetComponent<Renderer>().materials;
Material[] materialsList = new Material[oldMaterials.Length];
for (int i = 0; i < materialsList.Length; i++) {
// repalce material with transparent
materialsList *= Object.Instantiate(TransparentMaterial);*
For reference, here’s another implementation. This uses a gameObject attached to the camera with a box collider (as a trigger), a rigid body, and this script:
There’s much simpler way, by having a material for the player, that does double drawing when anything get in-front of the player, without doing the ray-casting each update, here’s an example of such a material I was asking for:
Silly question.
Just how do you assign the scripts? I get errors for hit and stuff.
Does one goto the main camera and the other the main object?
Player
Main Camera
Capsule
I have a variant that works with Triggers. I have a question: is CapsuleCastAll less expensive than using a Trigger attached to the camera?
TransparencyControl is attached to the object being made transparent/opaque. The object has to have the RigidBody attached to it.
using UnityEngine;
using System.Collections;
public class TransparencyControl : MonoBehaviour {
private const float TRANSPARENT = 0.25f;
private const float HYSTERESIS = 0.9f;
private const float INVERSE = 1.0f - HYSTERESIS;
private const float MIN_DELTA = 0.1f;
private float regular, transparent;
private float alpha;
private Color baseColor;
private bool isNormal;
// Use this for initialization
void Start () {
baseColor = transform.renderer.material.GetColor("_Color");
regular = baseColor.a;
alpha = 1.0f;
isNormal = true;
}
void OnTriggerEnter (Collider other) {
if (other != gameObject && isNormal) {
isNormal = false;
}
}
void OnTriggerExit (Collider other) {
if (other != gameObject && !isNormal) {
isNormal = true;
}
}
// Update is called once per frame
void Update () {
if (isNormal)
alpha = HYSTERESIS * alpha + INVERSE;
else
alpha = HYSTERESIS * alpha;
float trans = alpha * regular + (1.0f - alpha) * TRANSPARENT;
transform.renderer.material.SetColor ("_Color",
new Color (baseColor.r, baseColor.g, baseColor.b, trans));
}
}
The Trigger is just a box collider aligned between the Main Camera and the Target Cube.[14842-test+scenario.jpg|14842]
I’m thinking of creating a pyramid, scaling it to the view frustum and using that as the Mesh Object for the Trigger. Not sure whether that will be more expensive or not …