LiteralliJeff 发表于 2022-12-23 10:07

unity 弹簧-质点模型 布料模拟

牛顿,永远的神!
介绍弹簧质点模型的理论知识的专栏有很多,我这里只写代码。
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.GetComponent<ClothNode>().mass = 100000f;
      nodes.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,
                  nodes,
                  structK,
                  structL));
      for (int i = 0; i < boundLength; i++)
            for (int j = 0; j < height - 1; j++)
                springs.Add(new Spring(
                  nodes,
                  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,
                  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,
                  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,
                  nodes,
                  bendK,
                  bendL));
            
      for (int i = 0; i < boundLength; i++)
            for (int j = 0; j < height - 2; j++)
                springs.Add(new Spring(
                  nodes,
                  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的质点
页: [1]
查看完整版本: unity 弹簧-质点模型 布料模拟