Java实现模拟小球二维完全弹性斜碰
QQ的桌球游戏初中那会很流行,当时就想着是怎么做到的呢?游戏开发其中也有很多门道,很多方面。这里我也不懂,只是做了一个桌球游戏的简单实现,实现的过程可能并不完美,不过还是尽量图文并茂、易于理解的写出来。如果有不正确的地方欢迎指正。
功能简述
1、桌面是二维光滑平面,带有边缘(墙壁),没有阻力。
2、球与球之间能实现完全弹性斜碰,即实现真正的物理上桌球的击球效果。
软件架构
绘图使用JFrame嵌套JPanel,JPanel自带双缓冲,能够做到绘图减少闪烁。JPanel内使用Canvas、Graphics、Image绘图,Image也自带双缓冲。
绘图功能由CanvasThread实现,球的运动和计算由CircleThread实现,球的数量控制由ServeThread实现。
原理详解
注意:在这里的实现中,球和圆是同一个意思,即球可以等价看出圆。
这里的球碰撞主要是要解决两个问题:碰墙、碰球。
球的运动比较简单,球的运动速度矢量V可以分解在xy轴上,即向量Vx和Vy(有方向和大小),单位时间可以通过加上Vx和Vy的改变球的位置坐标Lx、Ly,从而实现球的运动。不过这里有一个小细节需要注意一下,不然会出现球有速度却静止不动的情况。
碰墙比较好解决,只要球超出边界就让它对应的Vx或Vy取反就行,不过这里还有几个小细节需要注意一下,不然会出现球嵌在墙里出不来的情况。
碰球的问题就比较复杂了,高中物理学过一维上两个小球的正面弹性碰撞公式,这里是小球在二维平面上的弹性斜碰情况。(均是光滑、无阻力的平面)
不过弹性斜碰仍然是可以分解转换成正面弹性碰撞的,下面进行分步骤详细解说一下吧。
数据准备:
球内存储的数据为:
这些数据共同参与运算构成了整个运行流程。
第零步:先判断两个圆的位置
只有满足两个圆心距离dn小于或等于两个圆的半径和dc的情况才叫碰撞,即两圆相切或相交时,需要做进一步处理。否则两圆相离无需做处理。
相切时的示意图:
第一步:计算合速度的大小和方向
球内存储的数据只有Vx和Vy这两个矢量,没有球运动的合速度矢量。不过解决球的斜碰还是需要合速度矢量的,需要计算出来。
分解速度矢量和合速度矢量:
第二步:计算共同(相对)坐标系s,解决粘连问题,计算ab在s中的速度的大小和方向
这个步骤比较复杂,需要分成三步来解决。
1)计算共同(相对)坐标系s。
为了将斜碰转化为正碰,需要将球的运动坐标系进行转化,共同坐标系s的x轴是以两个圆心所在直线为基础,因为是球b相对于球a的坐标系,所以坐标系的原点即球a的圆心。
共同(相对)坐标系s:
2)解决粘连问题,或者碰撞时已经相交的情况,让两个圆后退到相切状态。
让两个圆由相交状态回退到相切状态,需要将两个圆沿着运动方向的反方向移动,移动到相切(或略微有相离)为止。移动的距离就是中间的重叠部分,更详细点就是分别修改圆的x轴和y轴数值,数值大小就是重叠部分的数值,因为是重叠所以要除以2,即 (dn-dc)/2。
移动的加减运算的公式是一致的,这是可以推导的。
解决粘连问题:
3)将ab的速度大小和方向由原坐标系转化为在共同坐标系s中的速度大小和方向。
将ab的合速度投影到共同坐标系s中,便于接下来计算正碰。
坐标转化:
第三步:计算完全弹性斜碰
ab球的速度映射到s坐标系后,处在y轴的分速度发生弹性正碰时不受影响。处在x轴的分速度就等价于弹性正碰。
两个小球弹性正碰的公式推导(百度百科):
发生弹性正碰之后,原来的y轴的分速度没有变化,x轴的分速度按公式进行“变换”。
第四步:计算两球发生碰撞后在s中的各自新合速度大小和运动方向
新的合速度和运动方向很容易就可以得出。
发生弹性正碰之后的两个小球的分速度变化和新合速度:
第五步:将两球速度转化为原坐标系中的速度
只需要将合速度进行三角运算即可得到映射的分速度。
两球新的合速度矢量转化为原坐标系中的分速度:
第六步:更新
将碰撞后的新数据更新到圆的存储中即可。
碰撞后的新方向:
补充:整个过程中因为桌面光滑,完全无损失碰撞,所以所有的球的动能和应该是恒定不变的。
关键代码
球的运动和碰墙检测代码:
1 | /** |
球的斜碰:
1 | /** |
测试结果
运行过程中某一个时间段的动图(动图帧率比较低,会显示的有些卡顿):
运行过程中某一刻的球运动数据输出:
从结果可以看出系统总能是守恒的。
总结思考
游戏编程主要是将数学和物理(也有其他学科)的知识结合图像显示出来,编写一些小游戏代码,能够锻炼代码的逻辑能力,也能遇到到一些编程中的细节问题,同时也是将数学转为代码,锻炼思维能力。游戏编写过程中,也能体会到代码加数学的魔性,增强编程的兴趣。