- Home /
How to make an image transparent with color.lerp after delay, in a coroutine
So I am trying to do as the title says. I am swapping weapons and the image icons in the UI should have the color become transparent over time after swapping, but then go back to opaque if swapped again. This gives the effect of swapping making the icons show up on the screen and then fading after a couple seconds. This ALMOST is working. However, with my current implementation, since there is a waitForSeconds call made, so there is a delay and the image actually appears on screen, the coroutine lerping still makes the icon invisible without resetting the 5 second delay. I want the behavior that if u swap back and forth over 4 seconds, the color should still not go transparent for another 5 seconds, but currently it goes invisible after 1 with that test case.
Here is my current implementation of the 3 relevant functions:
void SwitchWeaponIcons()
{
justSwapped = true;
Color tempInvis = selectedIconObj.GetComponent<Image>().color;
tempInvis.a = 1;
selectedIconObj.GetComponent<Image>().color = tempInvis;
tempInvis = backbarIconObj.GetComponent<Image>().color;
tempInvis.a = 1;
backbarIconObj.GetComponent<Image>().color = tempInvis;
selectedIconObj.GetComponent<Image>().sprite = weaponIconList[selectedWeapon];
backbarIconObj.GetComponent<Image>().sprite = weaponIconList[backbarWeapon];
StartCoroutine(MakeVisible());
}
IEnumerator MakeVisible()
{
Color tempColor1 = selectedIconObj.GetComponent<Image>().color;
tempColor1.a = 1f;
selectedIconObj.GetComponent<Image>().color = tempColor1;
Color tempColor2 = backbarIconObj.GetComponent<Image>().color;
tempColor2.a = 1f;
backbarIconObj.GetComponent<Image>().color = tempColor2;
yield return new WaitForSeconds(5f);
justSwapped = false;
StartCoroutine(ColorLerpOut());
}
IEnumerator ColorLerpOut()
{
Color invisColor = Color.white;
invisColor.a = 0f;
for (float t = 0.01f; t < 3f; t+= 0.1f)
{
if (justSwapped) break;
selectedIconObj.GetComponent<Image>().color = Color.Lerp(selectedIconObj.GetComponent<Image>().color,invisColor,t/3);
backbarIconObj.GetComponent<Image>().color = Color.Lerp(backbarIconObj.GetComponent<Image>().color, invisColor, t / 30);
yield return null;
}
if (justSwapped)
{
Color tempColor1 = selectedIconObj.GetComponent<Image>().color;
tempColor1.a = 1f;
selectedIconObj.GetComponent<Image>().color = tempColor1;
Color tempColor2 = backbarIconObj.GetComponent<Image>().color;
tempColor2.a = 1f;
selectedIconObj.GetComponent<Image>().color = tempColor2;
yield return null;
}
}
Answer by Fulcrum_Games · Mar 05 at 12:50 AM
Figured it out. Rather than worrying about whether the coroutine is still running or not, which is evidently very hard since its not built in, I know there should only be one coroutine of this running at a time and i want to restart it if its running. Luckily, saying stopcoroutine doesnt give you an error if its not running. So here is the finished code block that makes the weapon icons fade after 3 seconds.
void SwitchWeaponIcons()
{
selectedIconObj.GetComponent<Image>().sprite = weaponIconList[selectedWeapon];
backbarIconObj.GetComponent<Image>().sprite = weaponIconList[backbarWeapon];
StartCoroutine(MakeVisible());
}
IEnumerator MakeVisible()
{
Color tempColor1 = selectedIconObj.GetComponent<Image>().color;
tempColor1.a = 1f;
selectedIconObj.GetComponent<Image>().color = tempColor1;
Color tempColor2 = backbarIconObj.GetComponent<Image>().color;
tempColor2.a = 1f;
backbarIconObj.GetComponent<Image>().color = tempColor2;
StopCoroutine("ColorLerpOut");
StartCoroutine("ColorLerpOut");
yield return null;
}
IEnumerator ColorLerpOut()
{
Color invisColor = Color.black;
invisColor.a = 0f;
yield return new WaitForSeconds(2);
for (float t = 0.01f; t < 30f; t += Time.deltaTime / 15)
{
//if (justSwapped) break;
selectedIconObj.GetComponent<Image>().color = Color.Lerp(selectedIconObj.GetComponent<Image>().color, invisColor, t / 15);
backbarIconObj.GetComponent<Image>().color = Color.Lerp(backbarIconObj.GetComponent<Image>().color, invisColor, t / 15);
yield return null;
}
}
Checking whether the coroutine is still running is fairly straightforward, and handy in lots of situations. I put an answer below, if you're interested.
Answer by Cakebox · Mar 05 at 01:00 AM
When you call StartCoroutine(MakeVisible())
, you can store a reference to that coroutine like this:
public class YourClass
{
private Coroutine runningCoroutine;
private void Foo()
{
runningCoroutine = StartCoroutine(MakeVisible());
}
[...]
}
That enables you to later do things like StopCoroutine(runningCoroutine)
.
If you also set runningCoroutine = null
at the end of MakeVisible, then you can have other parts of your code effectively check whether MakeVisible is running by checking whether runningCoroutine is null.
Putting it together, you can replace your current line StartCoroutine(MakeVisible())
with this:
if (runningCoroutine != null)
{
StopCoroutine(runningCoroutine);
}
runningCoroutine = StartCoroutine(MakeVisible());
This basically says, 'If there's a five-second timer already running, scrap it, and replace it with this fresh one.' Hope it helps.
Thanks for the answer. I believe this would work too. Last night I did something similar but more simple and I would just like confirmation that my solution is okay. Im basically doing the same thing you are but just not caring if the coroutine is running and just code to stop the coroutine always and start again. Is this acceptable or is something wrong on the backend im not seeing when you say stopcoroutine on one thats already stopped? (My solution is currently what i have as accepted answer.)
You're welcome, of course. Yes, what you've described above sounds good too. Glad you worked it out!
Answer by FlofYT · Mar 04 at 11:37 AM
Mhh, did you search on stackoverflow yet? I belive someone allready asked that. but sorry i dont know either..
I saw a couple similar issues on stackoverflow but none fully solved the issue im currently trying to solve.