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]