How Do I Occlude Objects Between the Player and a 3rd-Person Camera?
I've been trying to figure this out for days, trying out various techniques to no success. Note that I am not using a Third Person Character Controller because my object uses ball movement.
Here's what I currently have for my Third Person Camera. This creates a ray that always points directly at the player. It gets an array of all objects it points at. If these are not player, and they have an OccludeWhenPointed script, it calls the TogglePointed function :
float m_dist;
void Start ()
{
//Create a ray at the object's position and rotation
m_dist = GetComponent<ThirdPersonCamera>().GetDistance();
}
// Update is called once per frame
void Update ()
{
int x = Screen.width / 2;
int y = Screen.height / 2;
Ray ray = Camera.main.ScreenPointToRay(new Vector3(x, y, 0));
RaycastHit hit;
RaycastHit[] allHits = Physics.RaycastAll(ray, m_dist);
foreach (var rayHit in allHits)
{
if (rayHit.collider.gameObject.CompareTag("Player"))
{
}
else if (!rayHit.collider.gameObject.layer.Equals("EditorCameraIgnore") && !rayHit.collider.gameObject.CompareTag("Ground"))
{
//Call HitByRay function
if(rayHit.collider.gameObject.GetComponent<OccludeWhenPointed>() != null)
{
//print("Ready to point");
rayHit.collider.gameObject.GetComponent<OccludeWhenPointed>().TogglePointed();
}
//Material e_mat = rayHit.collider.gameObject.GetComponent<MeshRenderer>().material;
}
}
Here's the pseudocode I've been trying to follow:
//keep the ray in the camera's position and rotation
//if the ray is colliding with something other than the player ball,
//If there is no transparency material of the object
//Add a transparency material to it
//Else
//Set object's alpha to a lower number
//if the ray is not colliding with it
//Set object's alpha to full
}
}
Meanwhile, each object that I want to be transparent has both a material in Transparent rendering mode and this script. When it's pointed at, it reduces its alpha to 0.5: using UnityEngine; using System.Collections;
public class OccludeWhenPointed : MonoBehaviour {
[SerializeField]
bool m_PointedAtByRay = false;
bool m_Switched = false;
float m_OcclusionTime = 1.0f;
float m_Timer;
Color newColor;
Material mat;
void Start()
{
mat = GetComponent<MeshRenderer>().material;
m_Timer = m_OcclusionTime;
}
public void TogglePointed()
{
if (m_PointedAtByRay == false)
m_PointedAtByRay = true;
else
m_PointedAtByRay = false;
m_Switched = false;
}
// Update is called once per frame
void Update ()
{
if(m_PointedAtByRay)
{
print("Pointing is " + m_PointedAtByRay);
print("Switched is " + m_Switched);
if (!m_Switched)
{
print("Turning Transparent");
newColor.a = 0.5f;
mat.color = newColor;
m_Switched = true;
}
if (m_Timer > 0)
{
m_Timer -= Time.deltaTime;
}
else
{
TogglePointed();
m_Timer = m_OcclusionTime;
}
}
else
{
if (m_Switched)
{
print("Un-Turning Transparent");
newColor.a = 1.0f;
mat.color = newColor;
m_Switched = false;
}
}
}
}
The result I do get is that when my camera points at objects, they still look solid.
If anyone knows how to solve this problem, or more efficient alternatives that can be instructed step-by-step, your help would be greatly appreciated.
Answer by cjake2299 · Jul 11, 2018 at 08:28 PM
It's a few years late and you probably found a solution already, but your question helped me create a solution to my issue and I wanted to share my results with you and says thanks for asking the question:
In order for these 2 scripts to work you need a few things:
A "Resources" folder with a material inside called "Transparent"
The "Transparent" material should be standard > fade, with the color set to a low alpha level in the inspector windows color selector (like 5 or 10) and white.
The objects with the material you want to occlude must have colliders, the OccludeWhenPointed script and it's material all on the same object
Here is the script to go on the main camera: using UnityEngine;
public class CameraOcclude : MonoBehaviour
{
public Transform m_player = null; //inspector assigned variable
public LayerMask mask = -1; //inspecotr assigned variable, defalut is "Everything"
public float m_radiusOffset = 1.5f;
float m_dist = 1.0f;
float m_radius = 3.0f; //the radius of the sphere being cast
/// <summary>
/// Set the Layer Mask in the inspector window
/// members of this layer mask will need to have a collider and the OccludeWhenPointed script
/// otherwise they will not change
///
/// m_sphereCastTarget should be a sphere attached to the player, the size of the thickness
/// and offset behind the player between the player and camera.
///
/// this is because the thickness of the sphere will extend beyon the player, causing objects infront of the
/// player to become transparent.
/// </summary>
void Update()
{
m_dist = Vector3.Distance(m_player.position, transform.position); //Camera distance may change in the future
m_dist -= (m_radius + m_radiusOffset); //reduce the distance of the SphereCast so objects infront of the player do not become transparent
m_dist = Mathf.Max(0.1f, m_dist); //ensures that m_dist is never less than 0.1f
Vector3 fwd = transform.TransformDirection(Vector3.forward);
RaycastHit[] hits = Physics.SphereCastAll(transform.position, m_radius, fwd, m_dist, mask);
for (int i = 0; i < hits.Length; i++)
{
RaycastHit hit = hits[i];
OccludeWhenPointed occlude = hit.collider.gameObject.GetComponent<OccludeWhenPointed>();
if (occlude != null) //make sure what we collided with has the script
{
occlude.ChangeMatAlpha();
}
}
}
}
and then the OccludeWhenPointed script:
using UnityEngine;
public class OccludeWhenPointed : MonoBehaviour
{
public bool m_Switched = false; //incase we want to set this from an external function later
private float m_OcclusionTime = 0.1f;
private float _Timer;
private Material m_StartMat, m_Transparent;
public Renderer m_Renderer;
void Start()
{
m_Renderer = GetComponent<Renderer>();
m_StartMat = m_Renderer.material; //this is the game object original material
m_Transparent = Resources.Load<Material>("Transparent");
/*Transparent material must be located inside of the resources folder
* ./Resources/Transparent.mat
* or it cannot be loaded and used. Also, the transparent material should be "fade" and not "transparent"
* or it will how an outline.
* */
}
/// <summary>
/// Camera will trigger
/// Function will change alpha and set timer to "m_OcclusiongTime"
/// This keeps the object transparent as long as the camera ray hits it
/// Update function will handle transition back
/// </summary>
public void ChangeMatAlpha()
{
m_Switched = true;
m_Renderer.material = m_Transparent;
_Timer = m_OcclusionTime;
}
/// <summary>
/// This function will return the object to it's original material once
/// time has expired. It also sets the bool m_Switched back to false
/// </summary>
private void ReturnMat()
{
m_Switched = false;
m_Renderer.material = m_StartMat;
}
// Update is called once per frame
void Update()
{
if(m_Switched)
{
//now we check the timer
if (_Timer > 0.0f)
_Timer = Mathf.Max(0.0f, _Timer - (1.0f * Time.deltaTime)); //1.0f * Time.deltaTime to get transition over 1 second
else
{
ReturnMat();
}
}
}
}
Your answer
Follow this Question
Related Questions
Camera rotate X degree based on player rotate 0 Answers
Camera Rotation Switching Problem 0 Answers
How do i make so the player rotates when the camera does? 2 Answers
Rotate Camera with player vehicle 0 Answers