- Home /
Changing and resetting materials dynamically?
Hi
I want to apply a given material to any object being directly looked at, and then have it reset back to it's original material when it is no longer being directly looked at.
I can handle the change to new material OK by doing a Physics.Raycast forward from the player camera, and setting rayHit.transform.renderer.material.
The problem is in reverting back to the original material. I have hundreds of game objects using any one of 10 materials. Obviously I want to avoid having to keep arrays of these objects to check they're no longer being looked at.
Is there a way to somehow know what just the penultimate object looked at was, so I only have to revert that single object's material?
Thanks
Answer by Jessy · Jan 22, 2011 at 11:30 PM
using UnityEngine;
public class MaterialEyes : MonoBehaviour {
[SerializeField] Material material;
[HideInInspector] [SerializeField] new Camera camera; [HideInInspector] [SerializeField] Vector3 screenCenter;
void Reset () { camera = GetComponent<Camera>(); screenCenter = new Vector3(.5F, .5F, 0); }
class ChangedObject { public Renderer renderer; public Material originalMaterial;
public ChangedObject (Renderer renderer, Material material) {
this.renderer = renderer;
originalMaterial = renderer.sharedMaterial;
renderer.material = material;
}
}
ChangedObject changedObject; RaycastHit raycastHit; void Update () { Ray ray = camera.ViewportPointToRay(screenCenter); if (Physics.Raycast(ray, out raycastHit)) { Renderer hitRenderer = raycastHit.transform.renderer; if (hitRenderer) { if (changedObject != null) if (changedObject.renderer == hitRenderer) return; else changedObject.renderer.material = changedObject.originalMaterial; changedObject = new ChangedObject(hitRenderer, material); } } else if (changedObject != null) { changedObject.renderer.material = changedObject.originalMaterial; changedObject = null; } }
}
$$anonymous$$any thanks for this example Jessy, I was tying myself in knots with this :)
I think you need to do this before line #40 to avoid a memory leak : Destroy(changedObject.renderer.material);
as the instanced $$anonymous$$aterial is yours to free.
Brilliant!
A little update for later Unity versions:
Renderer hitRenderer = raycastHit.transform.GetComponentInChildren<Renderer>();
Answer by Jason B · Jan 22, 2011 at 10:50 PM
Before you change the material, have a GameObject variable and Material variable set up to "capture" the material and game object you hit with a ray before you do anything else to it. Then when you leave it, you can tell the game object to revert back to the stored material value.
Right, but you only need to store the Renderer, not the whole Game Object. $$anonymous$$y code example does this.
Answer by Steve Edwards · Jan 23, 2011 at 09:39 AM
This is Jessy's C# code changed to Javascript. (No advantage, I just needed to integrate it within an existing .js)
To recap, attach this script to a camera and it will temporarily change the material of the object you're looking at (in the centre of screen) to the one you assign to newMaterial
(Thanks again Jessy, as a C# newbie I learned a lot from studying your code)
var newMaterial : Material; private var screenCenter : Vector3; private var raycastHit : RaycastHit; private var theRenderer : Renderer; private var originalMaterial : Material;
function Start () { screenCenter = Vector3(.5F, .5F, 0); } function Update () { var ray : Ray = camera.ViewportPointToRay(screenCenter); // creates a ray going from camera to viewport center if (Physics.Raycast(ray, raycastHit)) { // get what that ray hits var hitRenderer : Renderer = raycastHit.transform.renderer; //get the renderer of the hit object if (hitRenderer) { if (theRenderer != null){ //if we've already assigned a renderer if (theRenderer == hitRenderer) return; //if this the same renderer do nothing else theRenderer.material = originalMaterial; // if it's a different one, put the original material back } theRenderer = hitRenderer; originalMaterial = hitRenderer.sharedMaterial; hitRenderer.material = newMaterial; } } else if (theRenderer != null) { // if we didn't hit anything and there exists a changed renderer, put the original material back theRenderer.material = originalMaterial; theRenderer = null; }
}