【Unity】如何优雅地实现任何线性效果?

在游戏中,我们经常想实现一些动画效果。比如:物体缓慢转动、沿不规则路线运动、UI淡入淡出、颜色变化、等等。
其实这些效果的背后,都跟一个函数有关系–Lerp
当你了解并掌握这个函数,实现上面的效果对你来说都非常简单。
在本文中,我将分享一下:

  • 使用经验
  • 使用误区
  • 不规则路线的移动

什么是线性插值 ?

两个向量之间的任意值。如图:
向量A,向量B。他们之间的线段AB。在线段AB之间的值,就是线性插值。
image.pngimage.png

对应的 Unity 的 Lerp 函数 Lerp(A, B, 比率t);
比率范围时 0~1 的情况下:
当比率是0时,那插值等于A点。
当比率是1时,插值等于B点。

Unity Lerp 相关函数有哪些?

  • Material.Lerp : 材质之间的插值
  • Vector2.Lerp : 向量之间的插值
  • Vector3.Lerp : 向量之间的插值
  • Mathf.Lerp : 浮点数之间的插值
  • Color.Lerp : 颜色之间的插值

到此为止,如果你理解什么是线性插值。那你应该知道怎么在 Unity 里使用它了。
但是,如果你就此满足。你可能会在漏掉一些细节,从而导致不理想的效果。

使用经验

移动

我们先简单用 Lerp 函数实现物体的 在 1S 内移动。
代码如下:

public class Lerp : MonoBehaviour
{
    // 终点
    public Vector3 targetPosition;
    // 开始位置
    public Vector3 startPosition;
    // 持续时间
    public float lerpDuration = 1f;
    // 记录运行时间
    private float _timeElapsed = 0;

    private void Start()
    {
        transform.position = startPosition;
    }

    void Update()
    {
        // 记录这一帧移动的位置
        Vector3 valueToLerp;
        _timeElapsed += Time.deltaTime;
        if ((_timeElapsed < lerpDuration))
        {
            valueToLerp  = Vector3.Lerp(startPosition, targetPosition, _timeElapsed / lerpDuration);
            transform.position = valueToLerp;
        }
        else
        {
            valueToLerp = targetPosition;
        }
        transform.position = valueToLerp;
    }
}

效果:
Lerp.gif

好的,这么一个移动效果就完成了。

取线段的任意一点

换个角度,我们也可以取得 AB 线段上的任意一点的坐标。

代码如下:

public class GetPoint : MonoBehaviour
{
    // 坐标点A
    public Vector2 a;

    //  坐标点B
    public Vector2 b;

    // 比值
    public float ratio;

    private void Update()
    {
        var position = Vector3.Lerp(a, b, ratio);
        transform.position = position;
    }
}

效果如下:
取线段任意一点.gif

其他的线性变化效果其实也是同理,只不过你使用的函数方法可能不一样。

文字淡入淡出

![淡入淡出.gif](https://cdn.nlark.com/yuque/0/2022/gif/1256055/1664379245737-f3395f14-280e-47d8-a7f4-3d1f62348c5a.gif

public class FadeToBlack : MonoBehaviour
{
    public float targetValue = 0;
    CanvasRenderer elementToFade;
    void Start()
    {
        elementToFade = gameObject.GetComponent<CanvasRenderer>();
        StartCoroutine(LerpFunction(targetValue, 0.8f));
    }
    IEnumerator LerpFunction(float endValue, float duration)
    {
        while (true)
        {
            float time = 0;
            float startValue = elementToFade.GetAlpha();
            // 淡出
            while (time < duration)
            {
                elementToFade.SetAlpha(Mathf.Lerp(startValue, endValue, time / duration));
                time += Time.deltaTime;
                yield return null;
            }
            elementToFade.SetAlpha(endValue);
        
            // 淡入
            time = 0;
            while (time < duration)
            {
                elementToFade.SetAlpha(Mathf.Lerp(endValue, startValue, time / duration));
                time += Time.deltaTime;
                yield return null;
            }
           elementToFade.SetAlpha(startValue);
        }
    }

}

渐变

渐变.gif

public class LerpMaterialColour : MonoBehaviour
{
    
    private Text text;
    public Color targetColor = new Color(0, 1, 0, 1); 
    void Start()
    {
        text = gameObject.GetComponent<Text>();
        StartCoroutine(LerpFunction(targetColor, 5));
    }
    IEnumerator LerpFunction(Color endValue, float duration)
    {
        float time = 0;
        Color startValue = text.color;
        while (time < duration)
        {
            text.color = Color.Lerp(startValue, endValue, time / duration);
            time += Time.deltaTime;
            yield return null;
        }
        text.color = endValue;
    }
}

使用误区

我们平时可能会遇到过,有使用 Lerp 函数实现平滑移动的效果。
效果如下:
平滑移动-Lerp.gif
代码如下:

public class Lerp_Error1 : MonoBehaviour
{
    // 终点
    public Vector3 targetPosition;
    // 开始位置
    public Vector3 startPosition;
    
    private void Start()
    {
        transform.position = startPosition;
    }

    void Update()
    {
        transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime);
    }

❎ 这使用其实不正确的。因为它实际在改变初始值,通过改变AB之间的距离来得出“插值”。这已经不属于线性插值计算了。
image.png

况且,想实现平滑移动的效果,我们其实可以使用 SmoothDamp() 方法。具体使用可以参考这篇文章 TODO。

怎么实现不规则运动?

不规则运动.gif

刚刚我们很容易实现了直线运动,那如果我们能把不规则的路线用直线概括指出来,看起来物体只不过是在进行多段直线运动。
不规则运动拆分.gif

如果觉得还不错,可以点个赞。你的喜欢,是我最大的动力。

猜你喜欢

转载自blog.csdn.net/GG_and_DD/article/details/127195067