【Unity】LineRenderer模拟橡皮筋弹跳效果

LineRenderer模拟橡皮筋弹跳的物理现象,手拖拽橡皮筋然后松开,橡皮筋会来回跳动,并最终静止在原位置,主要思路就是实时计算点的位置,给LineRenderer的点赋值,实现动态弹跳效果
【Unity】LineRenderer模拟橡皮筋弹跳效果_第1张图片
代码里主要分了两步:

  1. 从松开手的位置,回弹到原来位置
  2. 然后开始来回跳动,调动幅度越来越小,直到停止不动

该Demo里没有添加手拖拽皮筋的代码,如果有需要可自行实现,仅需要在LineRenderer 点位的起点与终点之间,添加一个中点,然后实时修改中点位置与屏幕鼠标位置一致即可(注意坐标转换~)

还有一个待实现功能,因为实际项目中没有这个需求,原谅楼主太懒没有写出代码··· 目前line从手松开点回弹时,仅用了三个点位,line显示就是两条线段,会有一个明显的夹角,如果要更加自然一点,可以多增加一些点位,利用正弦曲线值乘一个系数,原理与弹跳原理一样

在场景中创建一个空物体,添加LineRenderer 组件,设置合适的参数(材质、宽度等),然后挂载该脚本就OK啦~

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Line : MonoBehaviour
{
    //取样点的数量,数量越多,线条越圆滑
    [SerializeField] int pointCount = 20;
    //回弹速度
    [SerializeField, Range(0, 1)] float lineGoBackSpeed = 0.3f;
    //弹跳衰减参数
    [SerializeField, Range(0, 0.2f)] float bounceReduceParame = 0.1f;
    //线条弹跳频率
    [SerializeField, Range(0, 2.0f)] float bounceFrequencyParame = 0.8f;
    //弹跳高度参数,最大弹跳高度
    [SerializeField] float bounceHighParame = 0.05f;

    //LineRenderer组件
    LineRenderer lineRenderer;
    //line起点终点
    Vector2 startPoint, endPoint;
    //回弹线条取样点,取正弦曲线上半部分,作为线条弯曲最大曲线,在此基础上,乘以动态系数,改变回弹幅度
    List pointList;

    void Start()
    {
        //测试
        Init(Vector2.zero, Vector2.one * 2);
        BackToFormerPosition(new Vector2(1, 0));
    }

    void Init(Vector2 startPos, Vector2 endPos)
    {
        //获取组件
        lineRenderer = GetComponent();

        //设置向量起点与终点,保证向量在一二象限,即方向与Y轴正方向夹角不大于90度
        if (startPos.y > endPos.y)
        {
            startPoint = endPos;
            endPoint = startPos;
        }
        else
        {
            startPoint = startPos;
            endPoint = endPos;
        }

        //获取0--π的sin值,即正弦函数上半部分,按指定数量均分,获取取样点,作为弹跳曲线的原型
        if (pointList == null)
        {
            pointList = new List();
            for (float i = 0; i <= Mathf.PI; i += Mathf.PI / (pointCount - 1))
            {
                pointList.Add(Mathf.Sin(i));
            }
            pointList.Add(0);
        }
    }

    //从给定点,回弹到原位置
    public void BackToFormerPosition(Vector2 nowPoint)
    {
        //计算给定点,到原位置所在直线的距离,正负表示在向量的左右侧
        float middlePointHigh = (nowPoint.y - startPoint.y) * (nowPoint.x - endPoint.x) - (nowPoint.y - endPoint.y) * (nowPoint.x - startPoint.x);

        //计算弹跳高度,设置最大、最小幅度,避免高度太大或太小
        float bounceHigh = middlePointHigh * bounceHighParame;
        if (bounceHigh > 1)
            bounceHigh = 1;
        else if (bounceHigh < -1)
            bounceHigh = -1;
        else if (bounceHigh > -0.2f & bounceHigh < 0f)
            bounceHigh = -0.2f;
        else if (bounceHigh < 0.2f & bounceHigh >= 0)
            bounceHigh = 0.2f;
        //
        StartCoroutine(SetLineOriginal(nowPoint, bounceHigh));
    }

    //从给定点回弹到原位置
    IEnumerator SetLineOriginal(Vector2 middlePoint, float bounceHigh)
    {
        //目标点为原位置的中点,中间点从给定位置移动到目标点
        Vector2 targetPoint = (startPoint + endPoint) / 2;
        //设置lineRenderer参数
        lineRenderer.positionCount = 3;
        lineRenderer.SetPosition(0, startPoint);
        lineRenderer.SetPosition(2, endPoint);
        lineRenderer.SetPosition(1, middlePoint);

        float distance = Vector2.Distance(middlePoint, targetPoint);
        float speed = distance * lineGoBackSpeed;

        //持续移动lineRenderer中点,直到与很接近原位置
        while (distance > 0.05f)
        {
            middlePoint = Vector2.MoveTowards(middlePoint, targetPoint, speed);
            lineRenderer.SetPosition(1, middlePoint);
            distance = Vector2.Distance(middlePoint, targetPoint);
            yield return new WaitForSeconds(0.02f);
        }
        //恢复直线状态
        lineRenderer.positionCount = 2;
        lineRenderer.SetPosition(0, startPoint);
        lineRenderer.SetPosition(1, endPoint);

        //回弹到原位置后,开始线条跳动效果
        StartCoroutine(SetLineBounce(bounceHigh));
    }

    //线条跳动效果
    IEnumerator SetLineBounce(float bounceHigh)
    {
        //起点与终点在X/Y方向的距离
        float distanceX = endPoint.x - startPoint.x;
        float distanceY = endPoint.y - startPoint.y;
        //向量与水平方向的夹角,单位为弧度
        float vectorAngle = Mathf.Acos(Vector2.Dot((endPoint - startPoint).normalized, Vector2.right));

        //设置lineRenderer点数量
        lineRenderer.positionCount = pointCount;

        //angle累加,计算其正弦值,得到正弦曲线,0 -> 1 -> 0 -> -1 -> 0 ,依次作为line弹跳幅度系数,正负表示方向
        float angle = 0;
        //衰减数值,影响弹跳幅度,数值越来越小,弹跳幅度越来越小,直到弹跳停止
        float reduce = 1;
        while (reduce >= 0.02f)
        {
            //angle累加,bounceFrequencyParame会决定弹跳幅度的变化速度
            angle += bounceFrequencyParame;
            //插值计算reduce值
            reduce = Mathf.Lerp(reduce, 0, bounceReduceParame);
            //设置lineRenderer点位,线条弹起高度 = 幅度系数 * 衰减系数 * 高度系数
            SetBounceLine(Mathf.Sin(angle) * reduce * bounceHigh, distanceX, distanceY, vectorAngle);
            yield return new WaitForSeconds(0.02f);
        }
        //恢复直线状态
        lineRenderer.positionCount = 2;
        lineRenderer.SetPosition(0, startPoint);
        lineRenderer.SetPosition(1, endPoint);
    }
    //偏移line的点
    void SetBounceLine(float highParam, float distanceX, float distanceY, float vectorAngle)
    {
        //计算每个点的坐标位置
        //计算公式:
        //偏移点(x1,y1),向量起点(x0,y0),起点到终点的向量与X轴夹角 a
        //在一二象限
        //x1 = x0 - sin(a), y1 = y0 + cos(a)
        //在三四象限
        //x1 = x0 + sin(a), y1 = y0 + cos(a)
        //所以在初始化的时候,互换起点与终点,保证向量在一二象限,可以方便计算
        float xx, yy;
        for (int i = 0; i < pointCount; i++)
        {
            xx = startPoint.x + distanceX * i / (pointCount - 1) - pointList[i] * highParam * Mathf.Sin(vectorAngle);
            yy = startPoint.y + distanceY * i / (pointCount - 1) + pointList[i] * highParam * Mathf.Cos(vectorAngle);
            lineRenderer.SetPosition(i, new Vector2(xx, yy));
        }
    }

}

你可能感兴趣的:(物理,数学)