预计算刚体运动轨迹

发表于2018-11-30
评论0 4.5k浏览
像以前玩过的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碰撞

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引