Unity模拟榴弹运动轨迹

5 篇文章 0 订阅

前言

最近迷上了《最高指挥官2》这个游戏,里面的远程火炮的轰击很让我着迷,于是本着闲着也是闲着的原则,就写一篇关于实现这种类似榴弹炮效果的代码。

斜抛运动

先回忆在高中物理中,是否算过这么一个问题,给了一个物体的斜抛角度,初速度,重力加速度,然后让你计算这个物体抛出后,会落在什么地方。这里假设斜抛角为60度,初速度为100m/s,重力加速度为10m/s^2;(为了方便,下面默认单位为m)
很明显这是一个简单的物理题,处理这个问题的主要解决思想为求出物体竖直方向的运动时间,这个时间就是物体整个斜抛运动的运动时间,乘以水平方向速度,就可以得到位置。先可以把初速度分解为两个方向的速度,分别是水平方向和竖直方向(重力加速度的反方向),这里要特别注意的是被分解的水平速度和竖直速度是两个互不影响速度,也就是说,可以把整斜抛过程分解为水平方向的匀速直线运动,竖直方向为加速度为-10的匀加速直线运动。这点很重要!
如图中所示,设Vy = sinθ * V, Vx = cosθ * V,得到了两个速度,然后开始独立分解这两个运动。
首先竖直方向,当物体克服重力做功到达最高点时,物体在竖直方向的速度就为0,又知道物体在竖直方向做的是匀减速直线运动,所以:
Vc = V0 + at;
Vc为物体当前的竖直方向的速度,V0为Vy,加速度a就是重力加速度g;
Vc = 0m/s;
便可以得到:
V0 = -at;(注意这里的a = g = - 10m/s^2)
就可以求出
t = Vy / -g = 100 * sin(60°) / 10 = 5s;
所以求出了物体向上运动的时间为5s。
接下来物体要做的就是自由落体运动,可以用同样的方法计算出下落时间为5s,当然也可以省略这一步骤直接得出下落时间为5s,因为物体做自由落体运动的高度与加速度都不变,所以其花费的时间也不变,等于上抛时间。
这样计算出了整个斜抛运动的时间花费为10s,所以就可以直接得出物体落下的位置为:
距离自身 Vx * t 处的位置,(带入的计算我就不说了,读者可以自行带入计算)。
对于这个题还需要加众多的限制条件,例如斜抛过程忽略空气阻力的影响、斜抛前方没有障碍物等。

逆向的推导

在已知重力加速度,物体初速度和斜抛仰角的情况下计算出了,物体将要击中的位置。所以当给出重力加速度,物体初速度,以及击中位置,要求计算斜抛仰角,也是可以的。
设斜抛仰角为θ,物体初速度与重力加速度和上一节一样,不同的是这里多了一个击中位置,直接取得击中位置与发射位置的距离为S。还是对这个速度进行分解,如下图所示:
在这里插入图片描述
Vx = cosθ * V,Vy = sinθ * V,如果将θ当作已知量处理,就会得到和上一节相同的公式:
即:Vy = -gt1;(a)
又因为,已知道了水平方向的运动距离,所以就有:
S = Vx * t2;
可以得到:
t2 = S / Vx;(b)
联立a,b两个式子来看,都有未知量时间t,基于上一节的理论,可以知道,t2 = 2 * t1(因为式子a只表示物体克服重力做功速度的变化量,不包括后阶段的自由落体运动),所以可以将a,b联立消除未知量t,所以就得到
S / Vx = -2Vy / g;
接下来为读者演示推到过程:
Vy = V * sinθ;
Vx = V * cosθ;
Vy = -g * t1;
t1 = -Vy / g;
S / Vx = t2;
T2 = 2 * t1;
S / Vx = -2 * Vy / g;
Vx * Vy = -S * g / 2;
sinθ * cosθ = -S * g / 2 * V^2;
sin(2θ) = - S * g / V^2;
因为要求的是θ,所以要将带有θ的项移到一旁,至于cosθ * sinθ = 0.5 * sin(2θ),这是高中数学由涉及到的只是,而且很重要,不理解的读者可以补补高中的知识,或者你就知道这么用就可以了,因为这已经是被数学家所证明过的公式了。就像知道 平均速度V = X / t。
得到了sin(2θ)的值,利用Asin(sin(2θ))就可以计算出发射仰角θ(众多游戏引擎中的数学库已经包含了对 Asin 的实现,所以不必自己手动实现一遍了)。

运用在游戏中

炮弹运动
当然,的目的是把代码运用在游戏中,所以在真正编写代码的过程中没办法去推导这些公式。
我以Unity为例来实现这个过程,但是在计算仰角之前必须先搞定炮弹的运动算法。
1.炮弹的初速度方向与炮口方向保持一致;
2.炮弹运动为竖直方向的匀加/减速的直线运动,水平方向为匀速直线运动。
因为游戏中的时间是以帧来表示的,且这些游戏中的时间都是离散的,而不像生活中所处世界的时间是连续的。所以要用微积分的思想解决炮弹的运动问题。
竖直方向的运动
对于速度的表达式
V = V0 + dv;
这里的V是当前速度,V0是上一帧的速度。
又因为速度关于时间的导数是加速度,所以可以得到:
dv = a * dt;
a为重力加速度g,dt为一个很短的时间段,在unity中为0.02s,或者Unity中的

Time.deltaTime

所以就可以得到在竖直方向运动的速度表达式:
V += g * dt;
同理得到位移的表达式:
H = H0 + dH;
相同的可以得到:
dH = V * dt;
所以最终所需要的表达式为:
H += V * dt;
水平方向的运动
解决了上面的问题这个久相对简单了,直接通过S = S0 + V * dt,直接得到其表达式:
S += V * dt ;
所以最终的抛体运动公式为:
Sc = S + H;
Sc为炮弹的位置。
下面为在Unity中的C#实现代码:

float h;//竖直方向的位移量
float s;//水平方向的位移量
float g;//重力加速度大小
float speed;//炮弹的初速度大小

Transform bullet;//炮弹
Transform cannon;//加农炮
void Bullet()
{
	h += g * Time.deltaTime;  //竖直方向速度变化量:dV = a * dt
	s = speed * Time.deltaTime;//水平方向位移变化量:dS = V * dt
	bullet.position += Cannon.forward * s + Vector3.up * h * Time.deltaTime;
}

这里要注意的是,对于速度,加速度都是矢量,有大小有方向,所以在最后一行要给他们添加上相应的方向。
当然你还要再初始化的时候将炮弹的位置更新到炮口位置,这样运行后你就会看到一个炮弹的完美斜抛运动轨迹。
计算发射仰角
在最开始已经将计算发射仰角的方法给出,但是在真正写代码的时候,不可能去在CPU中一步步的化简式子,所以应该求出所需要结果的代数式。
在计算方法中最终化简了这么一个式子:
sinθ * cosθ = XXX;
然后一进步化简得到:
sin(2θ) * 0.5 = XXX;
所以:
sin(2θ) = 2 * XXX;
这样就很明了了,只需要求出XXX的结果就好,当然不要计算,知识他的代数式,在开头已经得到其代数式,所以:
θ = Asin(- S * g / V^2);
代码形式为:

float distance;//大炮与目标点的水平距离
float g;//重力加速度大小
float speed;//炮弹初速度大小
float theta;//发射仰角

theta = -Mathf.Asin(distance * g / Mathf.Pow(speed, 2)) * 0.5f * Mathf.Rad2Deg;

注意Mathf.Asin(float f),计算出来的结果为弧度制,所以根据需要将它转化为角度或者什么都不做。
有了发射仰角就可以求出炮管的朝向或者改变炮管的欧拉角,当然前者可能需要使用到勾股定理,而后者则需要注意自身空间与世界空间的问题。这里不在给出实现,读者可以自己尝试。当然炮弹是无法击中范围外的目标的,所以这个也要控制好。

总结

这是一个模拟迫击炮的标准目标的代码,但是这是一个最理想的状态下的模拟,他要求大炮与目标同处于同一水平面,而且计算过程中只考虑了重力的影响。我也希望在以后的学习中可以突破自己。其实这么久以来我以为大家都有一个误区,就是有现成的插件用就好了,不需要知道它的实现是怎么样的,也不考虑这个插件内在到底是怎么实现的。我认为这是非常不好的,如果你只是在工作项目的需要,那么你大可去学习怎么使用,例如刚体,当然使用官方自带的组件要方便,而因为工期需要,也不可能自己去实现一个刚体,这种情况我觉得可以理解。但如果是为了自己学习那就很不好,对于一个刚体的实现其所需要具备的知识不仅包括了物理,还有高数,线性代数等知识,也就是说,如果是正在学习的你,有这种拿来主义的思想,那么就等于你直接放弃了一个使你自身提高的机会,当你你真的花时间学习这些知识,你所制作出来的游戏的品质也会不断的提升,这才是游戏开发的乐趣所在!

  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值