- Home /
Material transition through animation
I have this script that ping pongs back and forth between two materials (doesn't transition textures but that's fine with me for what I'm doing). How would I change it so that instead of ping ponging I can cause the transition to happen through using animation events? So, I could have an animation that on frame 60 I trigger a transition to material 2 and on frame 120 transition back to material 3.
using UnityEngine;
using System.Collections;
public class swap_mats : MonoBehaviour {
// Blends between two materials
public Material material1;
public Material material2;
public float duration = 2.0F;
public Renderer rend;
void Start() {
// At start, use the first material
rend = GetComponent<Renderer>();
rend.material = material1;
}
void Update() {
// Ping-pong between the materials over the duration
float lerp = Mathf.PingPong(Time.time, duration) / duration;
rend.material.Lerp(material1, material2, lerp);
}
}
Okay, thank you. However, what would that public method look like exactly? (to transition from material 1 to material 2) Sorry, I'm quite new to scripting.
Answer by Zodiarc · Oct 18, 2016 at 12:33 PM
As far as I understand the animation events, you need two public methods. One to transition from material 1 to 2 and one form 2 to 1. Then you select your animation in the editor and in the timeline you can create an animation event (through the right click context menu I guess) where you need to pull in the game object that has this script attached and select the corresponding function.
Okay, thank you. However, what would that public method look like exactly? (to transition from material 1 to material 2) Sorry, I'm quite new to scripting.
Exactly the same as you already do it in Update, but without the pingpong. $$anonymous$$ethod 1 would be renderer.material.Lerp(material1, material2, speed Time.deltaTime); and method 2 renderer.material.Lerp(material2, material1, speed Time.deltaTime);
Zodiarc, thank you so much!
However... "speed Time.deltaTime" as you wrote it, did not work for me. Is it because I need to declare "speed" somewhere? And is there supposed to be anything between "speed" and "Time.deltaTime"?
But I got it to work by simply removing "speed". It works now, however, the material switches immediately. I assume this is because the third spot (after material1, material2) is the spot that deter$$anonymous$$es the speed of the transition and by me putting just "Time.deltaTime" in there, the speed is the time it took to render the last frame, so essentially, it switches the material in about the time to render the following frame, which is why it appears to switch immediately. Am I understanding this right? If I wanted the transition to take longer than one frame, how would I do that?
Yes it was a formatting error in the comment. There should be a multiplication sign between speed and Time.deltaTime. You can leave the speed parameter out, but then the transition will always take 1 second. You can define the parameter as a class variable so you can adjust the transition speed to your needs. The third value of the lerp function is how long the transition will take. Setting it to Time.deltaTime will mean, that the transition should be done after 1 second. If it swaps immediatelly then the problem is somwhere else. $$anonymous$$aybe you need a wrapper method which starts the transition as a coroutine https://docs.unity3d.com/$$anonymous$$anual/Coroutines.html
Does the animation stutter at the point where the transition is happening?
(thanks for continuing to help me with this) The animation does not stutter. And when I put a multiplication sign between speed and Time.deltaTime, and then define speed up above I don't get any errors but the transition still happens immediately. First of all, am I defining speed right? I changed the following line in my script ...
public float duration = 2.0F;
... to ...
public float speed = 2.0F;
Answer by tylerlybb · Oct 19, 2016 at 12:23 PM
I read the documentation on coroutines then came up with this (down below) for the code to use. It doesn't work: I now don't even get the immediate transitions I was seeing before. Now it just does a one frame flash at the end of the animation (neither of my animation events are at the end of the animation).
Also, I'm pretty sure what I have in update isn't right. I don't know how to do this because I think it's more complicated than the example used in the documentation. I only say this because, for mine, I only want to restart the coroutine "SwitchToMat1", for example, if that coroutine is not finished yet. Or do I misunderstand how coroutines work?
using UnityEngine;
using System.Collections;
public class swap_mats : MonoBehaviour {
public Material material1;
public Material material2;
public Renderer rend;
void Start() {
rend = GetComponent<Renderer>();
rend.material = material1;
}
void Update() {
StartCoroutine("SwitchToMat1");
StartCoroutine("SwitchToMat2");
}
public IEnumerator SwitchToMat1() {
GetComponent<Renderer>().material.Lerp(material2, material1, Time.deltaTime);
yield return null;
}
public IEnumerator SwitchToMat2() {
GetComponent<Renderer>().material.Lerp(material1, material2, Time.deltaTime);
yield return null;
}
}
Side note: I decided to remove the "speed" multiplier since without it you said I should still be seeing a one second transition, which should work just fine for what I'm doing.
public IEnumerator switchTo$$anonymous$$aterial() {
// your material lerp here
}
public void switchTo$$anonymous$$aterialWrapper() {
StartCoroutine("switchTo$$anonymous$$aterial");
}
Then assign the wrapper function to the animation event.
If this doesn't work try this: https://forum.unity3d.com/threads/cant-get-material-lerp-to-work.8936/
By the way the documentation states "$$anonymous$$ost often you want the materials that are interpolated between to be the same (use the same shaders and textures) except for colors and floats. Then you use Lerp to blend between them." https://docs.unity3d.com/ScriptReference/$$anonymous$$aterial.Lerp.html
If you actually want to blend between textures, I would recommend a custom shader.
First of all, thanks again for your help. I really appreciate it.
I still get an error. I am also getting errors when I use the script from the forum that you suggested but I'll tell you more about that in a separate reply.
Here's the error I'm getting:
Assets/$$anonymous$$atTransition.cs(12,28): error CS0161: `$$anonymous$$atTransition.switchTo$$anonymous$$aterial()': not all code paths return a value
Here is my complete updated script:
using UnityEngine;
using System.Collections;
public class $$anonymous$$atTransition : $$anonymous$$onoBehaviour {
public $$anonymous$$aterial material1;
public $$anonymous$$aterial material2;
public Renderer rend;
void Start() {
rend = GetComponent<Renderer>();
rend.material = material1;
}
public IEnumerator switchTo$$anonymous$$aterial() {
GetComponent<Renderer>().material.Lerp(material1, material2, Time.deltaTime);
}
public void switchTo$$anonymous$$aterialWrapper() {
StartCoroutine("switchTo$$anonymous$$aterial");
}
}
FYI - I resolved the errors I was getting from the forum thread you suggested, but just like we learned, the gradual transition only works when it's a ping pong being called through update on every frame. As soon as I try and trigger it through an animation event, the transition happens immediately over one frame. So that brings me back to your suggestion to handle this through a coroutine. So if you have any more ideas on getting that to work, again, I would really appreciate it.