为什么会想做一个碰撞测试呢?这其实是一个很懊恼很无奈的决定,这个想法最初源于暑假做的一个小游戏。小游戏中有很多个独立线程的个体,这些个体的行为控制并不难,但把这些个体联系起来,做到运动中不相互重叠在当时的我看来相当有难度,开始的想法直接在类的run()方法里直接用if语句判断。
这种方法最难的是判断方法的推理找寻,特别是坐标间的关系。
下面是我的推算过程。
如图所示的两个矩形,方别是黑矩形和红矩形,我们用p1.x代表p1点的x坐标,p1.y代表p2点的y坐标(诸如此类)。
图中的两个矩形关系是相交,为了便于推理,我们先假定这两个矩形是不相交的,通过观察,我们可以得出一个关系式:
A:(p2.y≤p3.y)∨(p1.y≥p4.y)∨(p2.x≤p3.x)∨(p1.x≥p4.x)
当A==true的时候,两个矩形是不相交的,而只要使用一次德摩根律就可以得到我们想要的相交式B。
B=﹁A=﹁[(p2.y≤p3.y)∨(p1.y≥p4.y)∨(p2.x≤p3.x)∨(p1.x≥p4.x)]=(p2.y>p3.y)∧(p1.y<p4.y)∧(p2.x>p3.x)∧(p1.x<p4.x)
看似简单的式子在实际编程的过程中,用的却不省心,因为游戏中的对象具有上下左右的方向,为了不使停止一刻显得过于突兀
我们还得为上下左右四种状态分别编写上下左右四种B式,也就是说需要我们推导的B式共有16条。这对于开始初学者来说过于
苛刻,并且臃肿的代码行会产生很多难以发现的小错误,以至于游戏不能够按我们的想法进行。
这是其中一个方向的判断源代码:
if(et!=this)
{
//如果敌人的方向是向下或者向上
if(et.direct==0||et.direct==2)
{
//上点
if(this.x+30>=et.x&&this.x+30<=et.x+20&&this.y>=et.y&&this.y<=et.y+30)
{
return true;
}
//下点
if(this.x+30>=et.x&&this.x+30<=et.x+20&&this.y+20>=et.y&&this.y+20<=et.y+30)
{
return true;
}
}
if(et.direct==3||et.direct==1)
{
if(this.x+30>=et.x&&this.x+30<=et.x+30&&this.y>=et.y&&this.y<=et.y+20)
{
return true;
}
if(this.x+30>=et.x&&this.x+30<=et.x+30&&this.y+20>=et.y&&this.y+20<=et.y+20)
{
return true;
}
}
}
在一番折腾之后,我思来想去,终究还是放弃了这个判断方法,Java的包那么丰富,这几天我就一直在找关于矩形碰撞的类方法。终于,我发现了intersects方法可以解决我的问题。于是,这个简单的矩形碰撞测试边在我的脑海里应运而生。这个方法并不难实现,基本的思想就是将两个Rectangle对象依附在既定好的两个矩形上,通过线程时时判断是否相交。
public boolean isHitA() {
Rectangle rA=new Rectangle(A.getX(),A.getY(),100,100);
Rectangle rB=new Rectangle(B.getX(),B.getY(),100,100);
if(rA.intersects(rB)) {
return false;
}else {
return true;
}
}
//碰撞函数
public boolean isHitB() {
Rectangle rA=new Rectangle(A.getX(),A.getY(),105,105);
Rectangle rB=new Rectangle(B.getX(),B.getY(),105,105);
if(rB.intersects(rA)) {
//System.out.println("sdas");
return false;
}else {
return true;
}
}
虽然还没精确到像素单位,但效果也基本达到了预期,在这基础上,我会尝试着继续改进。
源代码:
/*
* 目的:学会使用intersects方法进行游戏碰撞测试
* 方法:两个移动的正方形间的碰撞测试
*/
package First;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.Graphics;
public class Two extends JFrame {
Mypanel mp=null;
public static void main(String[] args) {
// TODO Auto-generated method stub
Two two=new Two();
}
public Two() {
mp=new Mypanel();
Thread tM=new Thread();
tM.start();
this.add(mp);
this.setTitle("碰撞测试");
this.setSize(400, 400);
this.setLocation(100, 100);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
}
class Mypanel extends JPanel implements Runnable{
RectangleA A=null;
RectangleA B=null;
public void paint(Graphics g)
{
super.paint(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, 400, 400);
g.setColor(Color.cyan);
this.drawRactangle(A.x, A.y, g);
this.drawRactangle(B.x, B.y, g);
A.setBump(this.isHitA());
B.setBump(this.isHitB());
this.repaint();
}
public Mypanel(){
A=new RectangleA(30,30);
A.setSpeed(15);
Thread tA=new Thread(A);
tA.start();
B=new RectangleA(190,190);
B.setSpeed(10);
Thread tB=new Thread(B);
tB.start();
}
public void drawRactangle(int x,int y,Graphics g) {
g.setColor(Color.cyan);
g.fill3DRect(x, y, 100, 100, true);
}
public boolean isHitA() {
Rectangle rA=new Rectangle(A.getX(),A.getY(),100,100);
Rectangle rB=new Rectangle(B.getX(),B.getY(),100,100);
if(rA.intersects(rB)) {
return false;
}else {
return true;
}
}
//碰撞函数
public boolean isHitB() {
Rectangle rA=new Rectangle(A.getX(),A.getY(),105,105);
Rectangle rB=new Rectangle(B.getX(),B.getY(),105,105);
if(rB.intersects(rA)) {
//System.out.println("sdas");
return false;
}else {
return true;
}
}
@Override
public void run() {
// TODO Auto-generated method stu;
try {
Thread.sleep(50);
}catch(Exception e) {
e.printStackTrace();
}
//重绘
this.repaint();
}
}
class RectangleX{
boolean bump=true;
//碰撞值输入输出方法组
public boolean isBump() {
return bump;
}
public void setBump(boolean bump) {
this.bump = bump;
}
int x,y;
int direct=1;
int times=0;
int speed;
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
RectangleX(int x,int y){
this.x=x;
this.y=y;
}
}
class RectangleA extends RectangleX implements Runnable{
RectangleA(int x, int y) {
super(x, y);
// TODO Auto-generated constructor stub
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
//System.out.println("saa");
this.direct=(int)(Math.random()*4);
switch(this.direct){
case 0:
for(int i=0;i<15;i++){
if(this.y>0&&this.bump){
y-=speed;
}else if(this.y>0&&this.bump==false){
y+=speed;
this.setBump(true);
break;
}
try {
Thread.sleep(50);}
catch(Exception e){
e.printStackTrace();
}
}
break;
case 1:
for(int i=0;i<15;i++){
if(this.x<275&&this.bump){
x+=speed;
}else if(this.x<275&&this.bump==false) {
x-=speed;
i=15;
this.setBump(true);
break;
}
try {
Thread.sleep(50);}
catch(Exception e){
e.printStackTrace();
}
}
break;
case 2:
for(int i=0;i<15;i++){
if(this.y<253&&this.bump){
y+=speed;
}else if(this.y<253&&this.bump==false) {
y-=speed;
i=15;
this.setBump(true);
break;
}
try {
Thread.sleep(50);}
catch(Exception e){
e.printStackTrace();
}
}
break;
case 3:for(int i=0;i<15;i++){
if(this.x>0&&this.bump){
x-=speed;
}else if(this.x>0&&this.bump==false) {
x+=speed;
i=15;
this.setBump(true);
break;
}
try {
Thread.sleep(50);}
catch(Exception e){
e.printStackTrace();
}
}
break;
}
this.direct=(int)(Math.random()*4);
}
}
}