Search code examples
c#unity-game-engineshader

Setting a shader property (Propertyblock) twice (Fading out a color with Lerp and resetting it to the original color)


I am trying to make "wet footprints", but am encountering some issues with fading out the color and resetting it to it's starting color when the step is reused. I have a gameObject array with a couple of footstep gameobject and they are placed on the position of the players foot when it touches the ground. It is currently pooling those steps, which is all working fine. Since I want to keep it as lightweight possible I am using a decal shader with a propertyblock and I need to fade out the _MainColor property when the object is placed, and when that same object is used again on a different position it needs to be reset. The problem is however the fading isn't working in it's current setup. What currently happens is the color is reset to black when it is reused and it's suppose to do the fade out again, but instead it just instantly transparent again.

void Update()
    {
        if (lastPos != transform.position)
        {
            stepPositioner = true;
            lastPos = transform.position;
            //Debug.Log("This Go changed position: ", this);
        }

        if (stepPositioner)
        {
            StartCoroutine(FadeTimer());
        }
    }

    IEnumerator FadeTimer()
    {
        _renderer.GetPropertyBlock(_propertyBlock);
        _propertyBlock.SetColor("_MainColor", startColor);
        _renderer.SetPropertyBlock(_propertyBlock);

        yield return new WaitForSeconds(1);

        _renderer.GetPropertyBlock(_propertyBlock);
        _propertyBlock.SetColor("_MainColor", Color.Lerp(startColor, endColor, Time.time * .5f));
        _renderer.SetPropertyBlock(_propertyBlock);
        stepPositioner = false;
    }

Solution

  • That's not how Lerp works.

    Linearly interpolates between colors a and b by t.

    t is clamped between 0 and 1. When t is 0 returns a. When t is 1 returns b.

    You are calling it only once with a pritty small factor of about 0.5/60 so around 0.008333 resulting in almost the start value.

    Additionally you wait one second before resetting the stepPositiontimet so for one second you Update method starts a few hundred concurrent Coroutines!

    For fading out the color over one second use something like

    IEnumerator FadeTimer(float duration)
    {
        // This has to be done right away!
        // Otherwise you get concurrent routines!
        stepPositioner = false;
    
        _renderer.GetPropertyBlock(_propertyBlock);
        _propertyBlock.SetColor("_MainColor", startColor);
        _renderer.SetPropertyBlock(_propertyBlock);
    
        var timePassed = 0f;
        while (timePassed < duration)
        {
            _renderer.GetPropertyBlock(_propertyBlock);
            _propertyBlock.SetColor("_MainColor", Color.Lerp(startColor, endColor, timePassed / duration));
            _renderer.SetPropertyBlock(_propertyBlock);
    
            // Add the time since last frame
            timePassed += Time.deltaTime;
    
            // Tells Unity to "pause" the routine, render this frame
            // and continue from here in the next frame
            yield return null;
        }
    
        // Just to be sure set the final value in the end
        _renderer.GetPropertyBlock(_propertyBlock);
        _propertyBlock.SetColor("_MainColor", endColor, );
        _renderer.SetPropertyBlock(_propertyBlock);
    }
    

    Just added the duration as parameter to make it clearer and more flexible. So just call it as

    StarCoroutine(FadeTimer(2)); 
    

    e.g. to fade within 2 seconds


    Note: Typed on smartphone but I hope the idea gets clear