我们在游戏开发中少不了会遇到各种各样的碰撞,例如飞机射击类游戏、坦克大战游戏、推箱子游戏......并且会频繁地去处理这些碰撞,这也是游戏开发中的一种基本算法。常见的碰撞算法有矩形碰撞、像素碰撞、圆形碰撞,其中矩形碰撞使用得最多。
两个矩形发生碰撞的情况有如下几种:
此外还有一种容易被忽视的碰撞情况(重叠):
第1种方法:
我们可以通过判断一个矩形的4个顶点是否在另外一个矩形的内部来完成。下面简单地创建一个Rectangle类:
public class Rectangle {
int x, y, w, h;// 分别是x和y坐标,宽度和高度,构成一个矩形
public Rectangle() {
}
public Rectangle(int x, int y, int w, int h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getRectangleWidth() {
return w;
}
public int getRectangleHeight() {
return h;
}
}
检测是否发生碰撞的部分代码如下:
public boolean isColliding(int px, int py) {
// px和py分别传入的是x坐标和y坐标
// 等号的情况是考虑重叠的情况
// 传入的坐标只要满足下面所有条件就表示传入的坐标在当前矩形范围内,返回true也就是碰撞了
if (px >= getX() && px < getX() + getRectangleWidth() && py >= getY() && py < getY() + getRectangleHeight()) {
return true;
}
return false;
}
// 碰撞检测,发生碰撞返回true,否则返回false
public boolean isColliding(Rectangle two) {
// 判断矩形只要有任何一个点在另一个one所表示的矩形范围内,就表示发生了碰撞,返回true值
if (isColliding(two.getX(), two.getY()) ||
isColliding(two.getX() + two.getRectangleWidth(), two.getY()) ||
isColliding(two.getX(), two.getY() + two.getRectangleHeight()) ||
isColliding(two.getX() + two.getRectangleWidth(), two.getY() + two.getRectangleHeight())) {
return true;
}
return false;
}
完整代码如下:
public class Rectangle {
int x, y, w, h;// 分别是x和y坐标,宽度和高度,构成一个矩形
public Rectangle() {
}
public Rectangle(int x, int y, int w, int h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getRectangleWidth() {
return w;
}
public int getRectangleHeight() {
return h;
}
public boolean isColliding(int px, int py) {
// px和py分别传入的是x坐标和y坐标
// 等号的情况是考虑重叠的情况
// 传入的坐标只要满足下面所有条件就表示传入的坐标在当前矩形范围内,返回true也就是碰撞了
if (px >= getX() && px < getX() + getRectangleWidth() && py >= getY() && py < getY() + getRectangleHeight()) {
return true;
}
return false;
}
// 碰撞检测,发生碰撞返回true,否则返回false
public boolean isColliding(Rectangle r) {
// 判断矩形只要有任何一个点在另一个one所表示的矩形范围内,就表示发生了碰撞,返回true值
if (isColliding(r.getX(), r.getY()) ||
isColliding(r.getX() + r.getRectangleWidth(), r.getY()) ||
isColliding(r.getX(), r.getY() + r.getRectangleHeight()) ||
isColliding(r.getX() + r.getRectangleWidth(), r.getY() + r.getRectangleHeight())) {
return true;
}
return false;
}
public static void main(String[] args) {
Rectangle one = new Rectangle(10, 10, 50, 100);
Rectangle two = new Rectangle(20, 30, 50, 100);
Rectangle three = new Rectangle(10, 30, 50, 100);
Rectangle four = new Rectangle(70, 10, 50, 100);
boolean colliding1 = one.isColliding(two);
boolean colliding2 = one.isColliding(three);
boolean colliding3 = one.isColliding(four);
System.out.println(colliding1);
System.out.println(colliding2);
System.out.println(colliding3);
}
}
输出结果:
true
true
false
解析图如下:
第2种方法:
第1种方法没有考虑到运行速度,而通常游戏中需要大量的碰撞检测,因此要求碰撞检测尽量做到高效率。第1种方法是处理什么时候相交,现在我们换个角度思考什么时候不会相交。可以处理4条边,one矩形的右边界在two矩形的左边界以外,同理,one的上边界需要在two的下边界以外,4条边都判断即可知道one与two是否相交。
方法如下:
/**
* oneX 矩形one的x坐标
* oneY 矩形one的y坐标
* oneW 矩形one的宽度
* oneH 矩形one的高度
* twoX 矩形two的x坐标
* twoY 矩形two的y坐标
* twoW 矩形two的宽度
* twoH 矩形two的高度
*/
public boolean isColliding(int oneX, int oneY, int oneW, int oneH, int twoX, int twoY, int twoW, int twoH) {
if (oneY > twoY + twoH || twoY > oneY + oneH || oneX > twoX + twoW || twoX > oneX + oneW) {
return false;
}
return true;
}
此方法比第1种方法简单且运行快。
第3种方法:
是第2种方法的升级版。我们可以保存两个矩形的左上和右下两个坐标的坐标值,然后对比两个坐标即可得出是否相交。这种方法比第2种方法更优越。推荐使用这种方法。
/**
* one[0]:矩形one左上角x坐标
* one[1]:矩形one左上角y坐标
* one[2]:矩形one右下角x坐标
* one[3]:矩形one右下角y坐标
* two[0]:矩形two左上角x坐标
* two[1]:矩形two左上角y坐标
* two[2]:矩形two右下角x坐标
* two[3]:矩形two右下角y坐标
*
* @param one 第一个矩形的左上角坐标和右下角坐标数组
* @param two 第二个矩形的左上角坐标和右下角坐标数组
* @return 如果发生碰撞则返回true,否则返回false
*/
public static boolean isColliding(int one[], int two[]) {
if (one[0] > two[2]) return false;
if (one[2] < two[0]) return false;
if (one[1] > two[3]) return false;
if (one[3] < two[1]) return false;
return true;
}
public static void main(String[] args) {
int one[] = {10, 10, 60, 110};
int two[] = {20, 30, 70, 130};
System.out.println(isColliding(one, two));
}
输出结果:
true
创作不易,点个关注不迷路~