Lập trình game java cơ bản - phát hiện va chạm

Xin chào các bạn :D, hôm nay chúng ta túc tiệp với loạt bài về lập trình game java nhỉ :D.

Bài hôm nay, mình sẽ giới thiệu các bạn cách phát hiện va chạm hết sức đơn giản trong game.

Với những game như mấy game chạy nhảy, bắn súng chẳng hạn, thì việc phát hiện va chạm là rất quan trọng. Vậy hôm nay chúng ta sẽ làm việc này bằng một cách đơn giản nhất: đó là sử dụng luôn lớp Rectangle (mô tả Hình chữ nhật) trong java.awt.

Bây giờ chúng ta bắt đầu luôn nhé :)


Thông thường để bắt va chạm chuẩn xác nhất thì người ta sẽ phải xác định vùng bao của vật thể cần xét va chạm, mà vùng bao này thì muôn hình vạn trạng, có thể là hình tròn, hình đa giác, hay vô số hình phức tạp, đồng thời thuật toán để xét va chạm giữ các hình này thì không phải đơn giản (hiện tại mình mới làm theo kiểu vùng bao hình tròn, hình vuông, chữ nhật mà thôi :v).
Và thật may mắn khi lớp Rectangle trong java.awt đã cung cấp cho ta phương thức intersecters(...) để có thể kiểm tra va chạm giữa 2 hình chữ nhật với nhau, việc chúng ta cần làm là gọi nó ra và dùng mà thôi =)).

Để hiểu rõ hơn mình sẽ đi vào ví dụ cụ thể:
Mình sẽ tạo 2 đối tượng: MyCharacter - tượng trưng cho nhân vật chơi của chúng ta và Enemy - tượng trưng cho quân địch, 2 đối tượng này đơn giản sẽ chỉ là 2 hình chữ nhật, do đó nó sẽ có thuộc tính chung là tọa độ, chiều dài, chiều rộng, để code ngắn gọn ta sẽ tạo 1 lớp Sprite chứa các thuộc tính chung này sau đó cho 2 đối tượng kia kế thừa lại.

Ta có lớp Sprite như sau:

import java.awt.Rectangle;
public class Sprite {
protected int x;
protected int y;
protected int width;
protected int height;

public Sprite(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Rectangle getBound() {
return new Rectangle(x, y, width, height);
}
 }


Phương thức getBound() sẽ dùng để xác định vùng bao của 2 nhân vật, ở đây vì mình làm 2 đối tượng là 2 hình chữ nhật nên vùng bao sẽ chính xác tuyệt đối.

Tiếp theo là lớp MyCharacter:

import java.awt.event.MouseEvent;
public class MyCharacter extends Sprite{
private int mx;
private int my;
public MyCharacter(int x, int y, int width, int height) {
super(x, y, width, height);
}
public void move() {
x = mx;
y = my;
}
public void mouseMoved(MouseEvent e) {
mx = e.getX();
my = e.getY();
}
}


Ta thêm 2 thuộc tính mx và my để biểu diễn tọa độ con trỏ chuột của chúng ta (bài hôm trước mình quên giới thiệu cách điều khiển nhân vật bằng chuột nên giờ mình sẽ dùng chuột điều khiển).
Phương thức mouseMoved(MouseEvent e) đơn giản là lấy tọa độ chuột gán vào cho mx và my.
Phương thức move() sẽ set lại tọa độ nhân vật bằng tọa độ chuột, hiểu đơn giản là di chuột tới đâu nhân vật sẽ chạy theo con trỏ chuột =)),  rất là chuối :)).

Tiếp đến là lớp Enemy:

public class Enemy extends Sprite {

public Enemy(int x, int y, int width, int height) {
super(x, y, width, height);
}

}

Chả có gì hết =)).

Xong, giờ tạo thêm lớp CollisionDetection như sau:

import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JPanel;
import javax.swing.Timer;
public class CollisionDetection extends JPanel implements ActionListener {
private MyCharacter myChar;
private Enemy enemy;
private final int DELAY = 10;
private Timer timer;

public CollisionDetection() {
myChar = new MyCharacter(50, 50, 50, 50);
enemy = new Enemy(100, 100, 50, 50);
addMouseMotionListener(new MyAdapter());
timer = new Timer(DELAY, this);
timer.start();
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(myChar.getX(), myChar.getY(), myChar.getWidth(),
myChar.getHeight());
g.drawRect(enemy.getX(), enemy.getY(), enemy.getWidth(),
enemy.getHeight());
}

@Override
public void actionPerformed(ActionEvent e) {
checkCollision();
myChar.move();
repaint();
}
public void checkCollision() {
Rectangle myCharBound = myChar.getBound();
Rectangle enemyBound = enemy.getBound();
if(myCharBound.intersects(enemyBound)) {
System.out.println("Va cham");
}
}
private class MyAdapter extends MouseMotionAdapter {
@Override
public void mouseMoved(MouseEvent e) {
myChar.mouseMoved(e);
}
}
}

Code này thì các bạn chắc đã hiểu hết vì bài trước mình đã giải thích hết rồi, ngoại trừ chỗ class MyAdapter, bài trước mình tạo class này bên ngoài, nhưng muốn tạo bên ngoài thì cần tham chiếu đối tượng myChar qua hàm tạo nữa, ở đây ta tọa luôn inner class - có nghĩa là class bên trong 1 class, và nó dùng luôn được thuộc tính class chứa nó (ở đây là dùng luôn myChar).
Phương thức checkCollision(): lấy ra 2 hình chữ nhật bao quanh 2 đối tượng, myCharBound.intersects(enemyBound), phương thức intersects(...) nói lên công dụng của nó :), phương thức này trả về true nếu 2 hình chữ nhật giao nhau và false cho trường hợp còn lại.

Các bạn chạy thử demo bằng cách tạo thêm class MainTest như sau:
import java.awt.Color;
import javax.swing.JFrame;
@SuppressWarnings("serial")
public class MainTest extends JFrame {
public MainTest() {
add(new CollisionDetection());
setTitle("Collision detection test");
setSize(500, 500);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setBackground(Color.WHITE);
setVisible(true);
}
public static void main(String[] args) {
new MainTest();
}
}



Vậy là xong bài hôm nay, bây nhiêu đó là hôm sau ta có thể bắt tay vào làm 1 game nhỏ rồi :D. Hẹn gặp lại các bạn vào bài sau.
SHARE

Xuho

  • Image
  • Image
  • Image
  • Image
  • Image

2 comments: