欢迎光临散文网 会员登陆 & 注册

来,我们用Unity做一个大炮

2022-06-20 16:58 作者:皮皮关做游戏  | 我要投稿

(本文作者 @沈琰 )

前言

如标题所述,这次的目标是在unity里实现一门较为贴近现实情况的大炮,操作方式参考坦克世界的火炮视角。主要涉及的知识点是坐标系,向量,四元数的应用,难度适中。

话不多说,开搞。


偏转轴与俯仰轴

现实中的火炮进行瞄准的时候,一般在机械结构上驱动的部分为两个轴:偏转轴(yaw)与俯仰轴(pitch),并且是偏转轴带动俯仰轴旋转。


换句话说就是在unity物体结构中,俯仰轴是偏转轴的子物体:


用unity里默认的物体拼一个简单的火炮模型,外形长什么样无所谓,有那意思就行。关键点是确定旋转轴,使其在两个轴上的旋转相互独立:



旋转

按照功能需求,应该是鼠标指向场景中的一个点,然后火炮依据目标点的位置按一个设定的速度旋转到瞄准指向的目标。



偏转角的计算很好写,获取落点的xz平面坐标到位置的向量,与火炮的朝向计算夹角,然后让偏转轴按设定速度转到目标方向。



俯仰角的计算要用到一些数学和物理知识,稍微棘手一点。

如果把大炮当时朝向的yz平面看作一个坐标系,那么炮弹飞出去形成的轨迹是这个坐标系上的一条抛物线。


这部分内容,包括后面要用到的火炮射程计算,都是关于弹道的数学问题,这部分内容不难但比较繁琐。为不影响文章节奏,这里会略过涉及到弹道计算的数学推导部分,在代码里这些计算部分会抽象到一个类中,感兴趣的同学可以在工程中自行查阅,如有必要以后会把数学推导部分单独写出来。



根据计算得到的目标角去旋转俯仰轴部分,与偏转轴部分基本一样:



这里会遇到一个问题,如果我们把抛物线的二维坐标系的横轴视为火炮出射角的0度角,仰角为正俯角为负,那么旋转轴就是垂直于坐标系且朝外,而加入这根转轴的三维坐标系是一个左手坐标系,unity使用的则是右手坐标系,这就使计算出来的值与实际的旋转表现是一个相反的关系。


编辑

解决方法有很多,这里为了后续设置与计算的方便,选择颠倒俯仰轴节点的本地坐标系。随之而来的改变是:当后续要使用pitch.forward的值的时候都要取负,这点需要特别注意。


forward的方向与炮口方向颠倒,使得仰角为正,俯角为负


显示弹道

现在要验证一下反向计算出的弹道落点是否与鼠标射线在场景中的坐标相吻合,需要把弹道显示出来。先暂时用Gizmos画出弹道线。


计算出的落点与鼠标位置应该完全吻合,否则可能哪里计算错误




限制俯仰角与最大射界

按我们现在的火炮外形设计,在偏转轴上没有角度限制,可以360度无死角旋转,但是在现实中绝大部分火炮肯定都有俯仰角的限制,然后加上火炮出膛最大速度与射击高度,进一步还能计算出火炮的最大射界。



这里有个有趣的数学问题:中学的时候我们都学过,沿45度抛出的石头能丢的最远,但这个结论有一个限制条件:出射点和落点的高度必须相同。

但现在明显我们的火炮出射点要略高于落点,这会使得最大射程的倾角发生改变。具体的推导和计算也属于弹道数学的部分,暂且略过,直接说结论:若出射高度大于落点高度,会使最大射程的倾角小于45度,反之亦然。

所以只要火炮的出射高度不低于落点,且最大仰角不小于45度的情况下,火炮的最大射程其实就与角度限制无关了,只与出射高度和炮弹的出膛速度有关。

根据这个结论我们可以分别计算出火炮的最远、最近和零度角的射界范围,依然用Gizmos显示在场景中。


  

最近(蓝)最远(红)0度(灰)


编辑切换为居中

再把限制角的范围也用Gizmos显示在场景中,调整不同的参数看下逻辑是否正确:

最大射界的范围会随着参数调整实时变化




精度变化

下一步工作是模拟火炮在瞄准时,由于旋转炮管产生的抖动造成的角度偏差,使得实际出射角变化产生的落点散布问题,也就是原版游戏中的“缩圈”功能。

按照原版表现,需要以落点为圆心显示一个“圈”,表示火炮落点可能的散布范围。当火炮旋转瞄准时散布圈会变大,当静止时,散布圈会变小。

那么第一步是记录火炮的旋转状态,这里简单点写,把偏转轴和俯仰轴合在一起计算状态,也就是不管哪个轴在旋转,都会实时影响精度。



接着根据旋转状态来更新精度因子。先以落点为圆心,精度乘以一个常数作为半径画一个圈来观察下逻辑是否正确。




落点散布

但是我们应该能猜到,实际的落点散布形状肯定不会是个圆,因为偏转角和俯仰角分别对落点位置的影响系数不同,那么投影到地面上的形状肯定不会是一个规则图形。但是直接这么想有点太过抽象,所以干脆试着让不同的炮口扰动形状投影到地面的形状显示在场景中。

以四个最大偏转角度为端点连接起来会形成一个矩形,我们假设偏转和俯仰的扰动角相等并且实时精度系数也相等,那么就相当于用炮管画出来一个正方形。


然后遍历这个“框”计算弹道落点,把落点形成的形状显示在场景中。


这个形状有点像汽车的前车窗,上下两个边是弧形,并且随着落点远近,长宽都会发生变化。

但是在实际情况中,炮口的随机抖动在两个轴的角度上都取到极值的几率很低,两个扰动角都取更接近实际的正态分布的随机值的话,炮口出射角的分布形状应该更接近一个圆。所以用同样的方法再来计算一下圆形炮口扰动的落点散布形状。


此时散布形状就变成了一个上面较宽下面较窄的近似椭圆,与原型功能的散布形状相似,这也从侧面也解释了游戏里散布形状的由来。

坦克世界里火炮投影到地面的散布圈接近一个椭圆



收尾与完善

到这一步基本上要实现的功能都完成了,剩下的就是补齐之前用Gizmos替代的功能,如弹道线、散布圈等,还有炮弹的发射、爆炸特效,摄像机移动等一些琐碎功能。这些东西比较简单,就不展开细说了,对实现方式感兴趣的同学可以直接查看工程。

最后的效果如下图:


工程链接: https://pan.baidu.com/s/1u3Z94yD8UzSmvBaz9wkWQg?pwd=6g2a



皮皮关与网易联合开发了完备的游戏开发线上课程。想要进军游戏开发领域的童鞋,可戳下面链接了解课程详情:

https://ke.study.163.com/course/detail/100103487

如果有任何问题,还可以通过以下入口与网易小伙伴取得联系,有什么问题直接正面怼他(战术后退

同时,欢迎加入游戏开发群欢乐搅基:1082025059


来,我们用Unity做一个大炮的评论 (共 条)

分享到微博请遵守国家法律