预计算刚体运动轨迹
发表于2018-11-30
像以前玩过的TNT、弹弹岛、百战天虫这样的游戏,都会有一个弹道路线提示,而通过施加力或速度,可以控制刚体移动,刚好前段时间在商店看到一个插件(脚本),可以通过刚体信息、速度、受力等信息,计算出刚提的运动轨迹,与刚体实际运动轨迹完全相符(计算方式可能与Unity物理引擎计算方式类似或相同),可以实现预计算弹道轨迹等功能。
免费插件链接:Calculate Trajectory https://assetstore.unity.com/packages/tools/physics/calculate-trajectory-84341
下面解析模拟物理计算代码,做了一些修改(其中有两处公式不明白,欢迎大佬告知):
public static class RigidbodyExtension
{
//returns返回值:运动轨迹点位位置及该点的速度
//stepCount:运动轨迹点位数量,数量越多,得到的轨迹越远,计算量也就越多
//calculateCountPerStep:每相邻点位之间,模拟的次数,次数越多,相邻点位距离越远(轨迹也会随之变远),但精度会降低
//addedSpeed:要增加的速度
//addedForce:要施加的力
public static Vector3[][] CalculateMovement(this Rigidbody that, int stepCount, int calculateCountPerStep, Vector3 addedSpeed, Vector3 addedForce)
{
//计算刚体现有速度
Vector3 vel = !that.isKinematic ? that.velocity : Vector3.zero;
//计算刚体重力
Vector3 gra = (that.useGravity && !that.isKinematic) ? Physics.gravity : Vector3.zero;
//根据所需数据计算轨迹
return CalculateMovement(that.transform.position, vel, gra, stepCount, calculateCountPerStep, addedSpeed, addedForce, that.mass, that.drag);
}
//returns返回值:运动轨迹点位的位置与速度
//position:刚体起始位置
//velocity:刚体原有速度
//gravity:刚体重力
//stepCount:点位数量
//calculateCountPerStep:模拟精度
//addedSpeedparam:增加速度
//addedForceparam:增加受力
//mass:刚体质量
//drag:所受阻力
public static Vector3[][] CalculateMovement(Vector3 position, Vector3 velocity, Vector3 gravity, int stepCount, int calculateCountPerStep, Vector3 addedSpeed, Vector3 addedForce, float mass, float drag)
{
Vector3[][] movePath = new Vector3[stepCount][];
//将受力转化为速度(动量公式 F*t = M*V)
Vector3 addedVel = addedForce / mass * Time.fixedDeltaTime;
//速度和
Vector3 vel = velocity + addedSpeed + addedVel;
//点位的位置和该位置下的速度
Vector3[] calc = new Vector3[] { position, vel };
//依次计算所有点位
for (var i = 0; i < stepCount; ++i)
{
//使用上一点位的位置和速度信息,计算下一点位
calc = CalculateNewPos(calc[0], calc[1], gravity, drag, calculateCountPerStep);
//记录点位位置速度信息
movePath[i] = new Vector3[2] { calc[0], calc[1] };
}
//返回值
return movePath;
}
static Vector3[] CalculateNewPos(Vector3 pos, Vector3 vel, Vector3 gra, float drag, int count)
{
//数据转化
//物理刷新时间间隔
var dt = Time.fixedDeltaTime;
//所受重力转化为速度
var graVel = gra * dt;
//抵消掉阻力之后的受力系数(drag * dt:所受阻力)(不懂这个计算方式)
var dragDt = 1 - drag * dt;
//确保系数不为负
dragDt = dragDt < 0 ? 0 : dragDt;
//模拟速度位置
for (int i = 0; i < count; i++)
{
//新速度 = (原速度 + 重力产生的速度) * 系数
vel = (vel + graVel) * dragDt;
//新位置 = 原位置 + 速度产生的位移 + 重力产生的位移(不懂0.5的作用)
pos = pos + vel * dt + 0.5f * graVel * dt;
}
return new Vector3[] { pos, vel };
}
}
根据Rigidbody对Rigidbody2D也做了扩展,计算方式与上面代码相差不大,就不重复粘贴了,Rigidbody2D多了一个bodyType属性:
Rigidbody2D.bodyType:
Dynamic 动态
对重力和施加力做出反应,包括其他Rigidbody2D接触产生的力
动态Rigidbody2D将与所有其他Rigidbody2D产生碰撞
Kinematic运动学
对重力和施加力无反应,包括其他Rigidbody2D接触产生的力(也无反应)
可以通过设置Rigidbody2D.velocity或Rigidbody2D.angularVelocity或显式重新定位来移动
运动Rigidbody2D只会与动态Rigidbody2D碰撞
如果Rigidbody2D.useFullKinematicContacts设置为true,在这种情况下它将与所有Rigidbody2D碰撞
Static静态
对重力或施加力无反应,包括与任何其他Rigidbody2D的接触(也无反应)
永远不会移动
静态Rigidbody2D只会与动态Rigidbody2D碰撞
如果Rigidbody2D.useFullKinematicContacts设置为true,在这种情况下它将与所有Rigidbody2D碰撞