找回密码
 立即注册
查看: 427|回复: 0

[笔记] unity 弹簧-质点模型 布料模拟

[复制链接]
发表于 2022-12-23 10:07 | 显示全部楼层 |阅读模式
牛顿,永远的神!
介绍弹簧质点模型的理论知识的专栏有很多,我这里只写代码。
1.对弹簧-质点进行抽象
首先对质点抽象(也就是建类)。质点有质量,有速度。其位置由unity提供,无须我们用变量存储。加一个受力的方法,来描述其受力时如何运动。好,质点抽象完毕。
public class ClothNode : MonoBehaviour
{
    public float mass=1f;//质点的质量
    public Vector3 v;//质点的速度
    // Start is called before the first frame update
    void Start()
    {
        
    }
    public float dt = 0.007f;//144帧,每帧用时7ms
    // Update is called once per frame
    void Update()
    {
        transform.position += v * dt;//速度产生的位移
        v *= 0.99f;//空气阻力会使速度减小
        AddForce(new Vector3(0,-9.8f,0));//重力。
    }

    public void AddForce(Vector3 force)
    {
        if (mass > 100) return;//若质量大于某一值,不作受力计算直接返回,用于固定的点。
        Vector3 a = force / mass;//此力作用于当前质点上产生的加速度
        v += a * dt;//加速度对质点速度的作用:用来加速度
        transform.position += 0.5f * a * dt * dt;//加速度产生的位移
    }
}
受力的方法是void AddForce(Vector3),其作用写在代码里。
将质点的脚本挂载到随便一个小球上,并将此小球拖为预设体。
然后对弹簧进行抽象。弹簧链接有两个质点,本身有一个劲度系数k,一个自然长度L。方法呢也就一个产生弹力的方法。
public class Spring
    {
        public Transform nodeA, nodeB;//链接的两个质点的Transform
        public float k, L;//劲度系数,自然长度

        public Spring(GameObject a, GameObject b, float k, float L)
        {
            nodeA = a.transform;
            nodeB = b.transform;
            this.k = k;
            this.L = L;
        }

        public void Flex()
        {
            Vector3 dAB = nodeB.transform.position - nodeA.transform.position;//由A指向B的向量
            float scalarF = k * (dAB.magnitude - L);//产生的力的大小
            //对两个质点进行弹力的添加
            nodeA.GetComponent<ClothNode>().AddForce(dAB.normalized * scalarF);
            nodeB.GetComponent<ClothNode>().AddForce(-dAB.normalized * scalarF);
        }
    }
2.生成布料
public : 给布料设定长和宽,并声明质点的预设物体用来实例化。
private : 存储质点和弹簧的List;因为弹簧有结构弹簧、剪切弹簧、弯曲弹簧三类,所以相应地设三组k和L。
public class ClothNode1 : MonoBehaviour
{
    public GameObject node;//质点的预设体
    public int boundLength, height;//布料的底边长和高
   
    List<GameObject> nodes;//质点的List
    List<Spring> springs;//弹簧的List

    float structK, shearK, bendK,//分别是结构弹簧、剪切弹簧、弯曲弹簧的参数
          structL, shearL, bendL;
    // Start is called before the first frame update
    void Start()
    {
        structL = 1f;    shearL = Mathf.Sqrt(2f); bendL = 2f;
        structK = 1000f; shearK = 500f;           bendK = 1000f;
      
        nodes = new List<GameObject>();

        // 生成质点们
        for(int i=0;i<boundLength;i++)
            for(int j=0;j<height;j++)
            {
                GameObject o = Instantiate(node, transform);
                o.transform.position = new Vector3(i, j, 0);//初始位置随便设
                nodes.Add(o);
            }
        //设置固定的质点,用其质量来区分
        nodes[0].GetComponent<ClothNode>().mass = 100000f;
        nodes[boundLength-1].GetComponent<ClothNode>().mass = 100000f;

        // 构建弹簧们
        springs = new List<Spring>();
        //结构弹簧
        for(int i=0;i<boundLength-1;i++)
            for(int j=0;j<height;j++)
                springs.Add(new Spring(
                    nodes[j*boundLength + i],
                    nodes[j * boundLength + i + 1],
                    structK,
                    structL));
        for (int i = 0; i < boundLength; i++)
            for (int j = 0; j < height - 1; j++)
                springs.Add(new Spring(
                    nodes[j * boundLength + i],
                    nodes[(j+1) * boundLength + i ],
                    structK,
                    structL));
        //剪切弹簧
        for(int i=0;i<boundLength-1;i++)
            for(int j=0;j<height-1;j++)
                springs.Add(new Spring(
                    nodes[i * height + j],
                    nodes[(i+1) * height + j +1],
                    shearK,
                    shearL));
            
        for(int i=1;i<boundLength;i++)
            for(int j=0;j<height-1;j++)
                springs.Add(new Spring(
                    nodes[i * height + j],
                    nodes[(i - 1) * height + j + 1],
                    shearK,
                    shearL));
        //弯曲弹簧
        for(int i=0;i<boundLength-2;i++)
            for(int j=0;j<height;j++)
                springs.Add(new Spring(
                    nodes[j * boundLength + i],
                    nodes[j * boundLength + i + 2],
                    bendK,
                    bendL));
            
        for (int i = 0; i < boundLength; i++)
            for (int j = 0; j < height - 2; j++)
                springs.Add(new Spring(
                    nodes[j * boundLength + i],
                    nodes[(j + 2) * boundLength + i],
                    bendK,
                    bendL));
    }

    // Update is called once per frame
    void Update()
    {
        foreach (Spring s in springs) s.Flex();
    }
}
根据各类弹簧的位置来设置它们即可。每帧都遍历调用一次它们的弹力方法,以模拟布料。
3.最终效果
调节各类弹簧的 k 和 L 值,以产生多种多样的布料效果。在Scene视图拖拽固定的点,即可欣赏到模拟的布料效果。



20x20的质点

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-12-22 11:25 , Processed in 0.092721 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表