unity 模拟绳子的物理特性

RoadLun原创(包括图片),转载请声明


演示:

效果如上图,具有绳子一般的柔软


对绳子发射一颗Cube,绳子会做出被外力击中的反应,模拟绳子的柔软的物理特性。

思路

思路如下:将绳子看作数个节点组成的链,控制这些节点从而控制整个绳子的运动情况。

如下图


单个节点的受力情况:

所以,模拟单个节点的受力情况,需要模拟此节点受到的重力和绳子的拉力,此处我用添加刚体的方法来模拟重力,接下来就是模拟拉力了。

通常认为的绳子并不是一个有弹性的绳子(橡皮筋),所以绳子上两个点的最大距离不超过绳子绷直时两点的距离,所以我通过时刻修正节点的位置(确保两个节点之间的距离不超过最大值),来模拟绳子的拉力。

所以,如何修正节点的位置呢?

因为节点添加了刚体,所以节点会收到重力下落,一旦超过两节点最大值,就要被修正,如下图:


代码如下:

        realDistance = Vector3.Magnitude(lastNodeTran.position-transform.position);  //求两点实际距离
        if (realDistance>nodeDistance*nodeDistance||realDistance<nodeDistance*nodeDistance-allowValue) //如果两节点距离超过规定距离,则进行修正。
        {
            //修正
            transform.position = Vector3.Lerp(lastNodeTran.position, transform.position, nodeDistance / realDistance);

        }

注:这个脚本赋给要修正的子节点上

按照上面的思路写,就ok了,

但是在实际运用中,发现虽然子节点的位置被修正,但是子节点一直处于下落状态(重力势能转化为动能,所以子节点会抖动并且抖得越来越快),所以我加上了一个修正速度的方法,没帧判断当前节点的速度,如果超过一个定值,则进行修正。这个方法可以缓解抖动问题,但是没法彻底解决。方法如下:

    void Deceleration() //减速
    {
        if (rig.velocity.sqrMagnitude>25)   //检测速度是否超过 5 m/s
        {
            rig.velocity /= 3;      //修正为3 m/s
        }
    }

如何显示:

思路时用线性渲染(LineRender)组件,实时修改LineRender的节点位置为绳子节点位置。

代码:


 修正节点的脚本。(需要附在节点上,并且头节点不要放Rigbody组件,也不要放此脚本)

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

public class NodePosition : MonoBehaviour {

    //绳子核心算法:
    //判断两个节点的距离,如果超过节点定长,则当前距离变为定常距离(改变子节点位置,用插值来计算)
    private float nodeDistance = 1.6f;  //节点定长
    public Transform lastNodeTran;   //上一个节点的位置
    float realDistance;      //真实距离
    Rigidbody rig;
    float allowValue = 0.02f;  //允许误差的值

    private void Start()
    {
        rig = GetComponent<Rigidbody>();
    }
    private void Update()
    {
        Judge();
        Deceleration();
    }
    void Judge()
    {
        //求两点距离时,用平方会比开方好
        realDistance = Vector3.Magnitude(lastNodeTran.position-transform.position);
        if (realDistance>nodeDistance*nodeDistance||realDistance<nodeDistance*nodeDistance-allowValue) //用平方来比较
        {
            //已经超过
            transform.position = Vector3.Lerp(lastNodeTran.position, transform.position, nodeDistance / realDistance);
        }
    }
    void Deceleration() //减速
    {
        if (rig.velocity.sqrMagnitude>25)
        {
            rig.velocity /= 3;
        }
    }
}

控制LineRender的脚本,需要LineRender组件

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

public class SetNode : MonoBehaviour {

    //控制绳子的显示
    //获得LineRender组件,设置Size等于节点数,设置节点的位置
    public Transform node0;
    public Transform node1;
    public Transform node2;
    public Transform node3;
    public Transform node4;
    public Transform node5;

    Vector3[] allNodes;

    LineRenderer lineRen;

    private void Start()
    {
        lineRen = GetComponent<LineRenderer>();
        Debug.Log(lineRen);
        //lineRen.positionCount = 6;
        //新建一个数组
        lineRen.positionCount = 6;
        
        
        Debug.Log(lineRen.positionCount);
    }
    private void Update()
    {
        allNodes = new Vector3[6] { node0.position, node1.position, node2.position, node3.position, node4.position, node5.position };
        lineRen.SetPositions(allNodes);
    }
}
ok~ 就是这个思路~


猜你喜欢

转载自blog.csdn.net/roadlun/article/details/80200304