一、数据定义
1.Object2D: 一个有形状、坐标、旋转角的2D对象
2.Shape:包括中心x,y坐标和四角(A,B,C,D)坐标(在这个项目里面碰撞的暂且都是是矩形)
3.MathTOOLS: 辅助计算类,通过传入坐标,可以获得点 线,线 线的相交情况。
二、计算碰撞的原理
我们都知道碰撞是动态的,所以这里请大家用一个动态的心来看以下内容,会事半功倍。下面我们先从点和水平矩形的碰撞说起。
以下碰撞的双方,我们姑且认为是一个子弹和坦克的碰撞。
1.点和水平矩形的碰撞
这里的子弹只是一个点,坐标为(x,y)
坦克则是一个矩形,拥有四对边角位置坐标。
碰撞判定:当子弹从外部进入坦克内部,则判断产生碰撞。这个很容易理解就不细说了。
2.点和非水平矩形的碰撞
子弹是一个坐标为(x,y)
坦克则是旋转一个矩形,拥有四对边角位置坐标,以及旋转方向direction。
碰撞判定:当子弹从外部进入坦克内部,则判断产生碰撞。但由于坦克发生了旋转,则不能单纯的通过四角坐标和点坐标的对比来判断是否产生碰撞,这个时候我们可以通过四个角坐标求出矩形的四个线性方程,通过简单的线性规划的知识(通过线性方程我们可以得出点在线上方还是下方,终而得出点在不在矩形内部),我们便可以判断子弹(点)是否进入坦克(矩形)。
3.矩形和矩形的碰撞
水平矩形之间的碰撞很简单。我们通过遍历子弹的四个点,但凡有一个点在坦克内部,我们就可以说二者发生了碰撞,不过要注意的是,这里遍历的对象形状大小要小于目的对象。要不然就会产生包裹的情况。不过也可以通过双向遍历解决(判断八次,前者判断后者四次,后者判断前者四次)。
4.非水平矩形和非水平矩形的碰撞
情况仿佛一下子就复杂了起来,在讲述这个之前,我们先了解一下线段和线段碰撞的判定方法
如上图,我们很容易通过点C,D的坐标求出L2的线性方程(形如:z=a(x-X)+Y-y)
而且可以发现,由于线段相交,所以A,B坐标代入方程里面的结果是相反的。
所以我们可以得到一个方法,就是通过线性方程来判断两条线段的相交情况,但这里要注意的是,由于是两条线段,而线性方程实际上得到的是,一条线段和一条直线的相交情况,所以这里我们利用双向求解(即改变双方的角色再求一次)来得到最终结果。
当然这里没有考虑端点在线上的情况,不过很容易处理就不细说了。
知道了线段与线段的碰撞处理之后,我们很容易想到用矩形的四条线段与另一个矩形的四条线段去计算碰撞,但凡有一个相交,二者就发生了碰撞。
三、Kotlin版本的代码
代码里面的判断方式和上述唯一不同之处的就是,为了减少计算量,所以先判断点了是不是在max的矩形内部(当紫色矩形的点进入红色矩形的范围之内,才开始判断二者是否碰撞),在内部再进行计算。
//可继承的基础2D对象,包括xy坐标和direction以及一个shape
open class Object2D:Cloneable{
var x:Int
var y:Int
var direction:Int
val shape:Shape
constructor(x: Int,y: Int,direction: Int,length: Int,width:Int){
this.x =x
this.y= y
this.direction = direction
this.shape = Shape(length,width)
}
}
//在这个项目里面主要是矩形控件,所以Shape主要的功能是获取四角的坐标位置
class Shape(private val length: Int,width: Int) {
var centerX = 0//矩形中心x坐标
var centerY = 0//矩形中心y坐标
private val diagonal = (sqrt(((length*length+width*width).toDouble())) /2).toInt() //除半的对角线长度
private val tmpTheta = TOOLS.getDirectionByTan(length, width, 0, 0)
//ABCD分别代表的位置