摆动碰撞检测
Swing collision detection
我刚刚在 java 制作了我的第一款游戏,但碰撞检测非常糟糕。
有没有人知道如何解决它?
我已经尝试了多种方法,但其中 none 行得通。
我的岩石来自玩家上方,它们被检测到,但如果玩家从侧面击中它们,它们就不会击中他。
public void checkRock(){
if((x == rockX && y == rockY) || (x==rockX && y+1 ==rockY) || (x==rockX && y+2 ==rockY)){
running = false;
}
else if((x+1 == rockX && y == rockY) || (x+1==rockX && y+1 ==rockY) || (x+1==rockX && y+2 ==rockY)){
running = false;
}
if(!running){
timer.stop();
}
}
完整代码:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.AttributeSet.ColorAttribute;
import java.util.Random;
import java.util.random.*;
public class GamePanel extends JPanel implements ActionListener{
static final int SCREEN_WIDTH = 600;
static final int SCREEN_HEIGHT = 600;
static final int UNIT_SIZE = 25;
static final int GAME_UNITS = (SCREEN_HEIGHT*SCREEN_WIDTH)/UNIT_SIZE;
static final int DELAY = 75;
int x = 0;
int y = SCREEN_HEIGHT-(UNIT_SIZE*3);
int rocksFallen;
int rockX;
int rockY = 0;
char direction = 'R';
char directionRock = 'D';
boolean running = false;
Timer timer;
Random random;
GamePanel(){
random = new Random();
this.setPreferredSize(new Dimension(SCREEN_WIDTH,SCREEN_HEIGHT));
this.setBackground(Color.BLACK);
this.setFocusable(true);
this.addKeyListener(new MyKeyAdapter());
startGame();
}
public void startGame(){
newRock();
running = true;
timer = new Timer(DELAY,this);
timer.start();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
draw(g);
}
public void draw(Graphics g){
if(running){
for(int i = 0;i<SCREEN_HEIGHT/UNIT_SIZE;i++){
g.drawLine(i*UNIT_SIZE, 0, i*UNIT_SIZE, SCREEN_HEIGHT);
g.drawLine(0, i*UNIT_SIZE, SCREEN_WIDTH, i*UNIT_SIZE);
}
g.setColor(Color.red);
g.fillOval(rockX, rockY, UNIT_SIZE, UNIT_SIZE);
g.setColor(Color.green);
g.fillRect(x, y, UNIT_SIZE*2, UNIT_SIZE*3);
}
else{
gameOver(g);
}
}
public void newRock(){
rockX = random.nextInt((int)(SCREEN_WIDTH/UNIT_SIZE))*UNIT_SIZE;
rockY = 0;
}
public void move(){
switch (direction){
case 'R':
if (x<SCREEN_WIDTH-(UNIT_SIZE*2)){
x = x+UNIT_SIZE;
}
break;
case 'L':
if (x>0){
x = x-UNIT_SIZE;
}
break;
}
if(directionRock=='D'){
rockY = rockY+UNIT_SIZE;
}
}
public void checkRock(){
if((x == rockX && y == rockY) || (x==rockX && y+1 ==rockY) || (x==rockX && y+2 ==rockY)){
running = false;
}
else if((x+1 == rockX && y == rockY) || (x+1==rockX && y+1 ==rockY) || (x+1==rockX && y+2 ==rockY)){
running = false;
}
if(!running){
timer.stop();
}
}
public void checkCollisions(){
if(rockY>SCREEN_HEIGHT){
newRock();
rocksFallen++;
}
}
public void gameOver(Graphics g){
g.setColor(Color.red);
g.setFont(new Font("Ink Free",Font.BOLD,75));
FontMetrics metrics = getFontMetrics(g.getFont());
g.drawString("Game Over", (SCREEN_WIDTH-metrics.stringWidth("Game OVer"))/2, SCREEN_HEIGHT/2);
}
@Override
public void actionPerformed(ActionEvent e){
if(running){
move();
checkRock();
checkCollisions();
}
repaint();
}
public class MyKeyAdapter extends KeyAdapter{
@Override
public void keyPressed(KeyEvent e){
switch (e.getKeyCode()){
case KeyEvent.VK_LEFT:
direction = 'L';
break;
case KeyEvent.VK_RIGHT:
direction = 'R';
break;
}
}
}
}
游戏框架代码:
import javax.swing.*;
import javax.swing.text.AttributeSet.ColorAttribute;
import java.util.Random;
import java.util.random.*;
public class GameFrame extends JFrame{
GameFrame(){
this.add(new GamePanel());
this.setTitle("FlyingRocks");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.pack();
this.setVisible(true);
this.setLocationRelativeTo(null);
}
}
主要代码:
public class Game extends JFrame{
public static void main(String[] args) {
new GameFrame();
}
}
根据 camickr 的建议,使用 Shape
可以轻松检测碰撞。
以下是它的一个基本实现的单文件mre(复制粘贴整个代码到ShapesCollision.java
和运行):
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import java.util.*;
import java.util.List;
import javax.swing.Timer;
import javax.swing.*;
public class ShapesCollision extends JPanel implements ActionListener {
private final static int H = 350, W = 400, RATE = 30;
private final static Color BG = Color.DARK_GRAY;
private final static Color[] SHAPE_COLORS ={Color.RED, Color.BLUE};//to have more balls, add more colors
private final List<Ball> balls;
private final Dimension size;
public ShapesCollision() {
Random rnd = new Random();
balls = new ArrayList<>();
for(Color color : SHAPE_COLORS){
//todo check that added shape does not overlap previous ones
balls.add(new Ball(color,rnd.nextInt(W), rnd.nextInt(H)));
}
Timer timer = new Timer(RATE,this);
timer.start();
size = new Dimension(W,H);
setBackground(BG);
setVisible(true);
}
@Override
public void paintComponent(Graphics g) { //for custom painting override paintComponent, not paint
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g;
for(Ball ball : balls){
g2D.setColor(ball.getColor());
g2D.draw(ball);
g2D.fill(ball);
}
}
public static void main(String[] args) {
JFrame myFrame = new JFrame("Colliding Shapes");
myFrame.setLocationByPlatform(true);;
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.add(new ShapesCollision());
myFrame.pack();
myFrame.setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
checkCollision();
horizontalMove();
verticalMove();
repaint();
}
private void checkCollision() {
for(Ball ball1 : balls){
for(Ball ball2 : balls){
if(ball1 == ball2) {
continue;
}
if(ball1.intersects(ball2.getBounds2D())){
changeHorizontalDirection(ball1);
changeVerticalDirection(ball1);
changeHorizontalDirection(ball2);
changeVerticalDirection(ball2);
repaint();//optional
}
}
}
}
private void horizontalMove() {
for(Ball ball : balls){
double maxX = ball.getBounds().getMaxX();
if(ball.isMovingRight()){
if(maxX < getWidth() || ball.x <= 0){
ball.x += ball.getSpeed(); //keep moving right
continue;
}
}
if(!ball.isMovingRight()){
if(ball.x > 0 || maxX >= getWidth()){
ball.x -= ball.getSpeed();//keep moving left
continue;
}
}
changeHorizontalDirection(ball);
}
}
private void verticalMove() {
for(Ball ball : balls){
double maxY = ball.getBounds().getMaxY();
if(ball.isMovingDown()){
if(maxY<getHeight() || ball.y <=0){
ball.y += ball.getSpeed(); //keep moving down
continue;
}
}
if(!ball.isMovingDown()){
//ball is moving up
if(ball.y > 0 || ball.y >= maxY){
ball.y -= ball.getSpeed();//keep moving up
continue;
}
}
changeVerticalDirection(ball);
}
}
private void changeHorizontalDirection(Ball ball){
ball.setMovingRight(! ball.isMovingRight());
ball.x = ball.isMovingRight() ? ball.x + ball.getSpeed() : ball.x - ball.getSpeed();
}
private void changeVerticalDirection(Ball ball){
ball.setMovingDown(! ball.isMovingDown());
ball.y = ball.isMovingDown() ? ball.y + ball.getSpeed() : ball.y - ball.getSpeed();
}
@Override
public Dimension preferredSize() {
return size;
}
}
class Ball extends Ellipse2D.Double{
private static final int SPEED = 10, SHAPE_SIZE = 40;
private Color color;
private int speed;
private boolean isMovingRight, isMovingDown;
public Ball(Color color, int x, int y) {
super(x,y,SHAPE_SIZE, SHAPE_SIZE);
this.color = color;
Random rnd = new Random();
isMovingRight = rnd.nextBoolean();
isMovingDown = rnd.nextBoolean();
speed = SPEED;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public boolean isMovingRight() {
return isMovingRight;
}
public void setMovingRight(boolean isMovingRight) {
this.isMovingRight = isMovingRight;
}
public boolean isMovingDown() {
return isMovingDown;
}
public void setMovingDown(boolean isMovingDown) {
this.isMovingDown = isMovingDown;
}
}
运行它在线here
我刚刚在 java 制作了我的第一款游戏,但碰撞检测非常糟糕。 有没有人知道如何解决它? 我已经尝试了多种方法,但其中 none 行得通。 我的岩石来自玩家上方,它们被检测到,但如果玩家从侧面击中它们,它们就不会击中他。
public void checkRock(){
if((x == rockX && y == rockY) || (x==rockX && y+1 ==rockY) || (x==rockX && y+2 ==rockY)){
running = false;
}
else if((x+1 == rockX && y == rockY) || (x+1==rockX && y+1 ==rockY) || (x+1==rockX && y+2 ==rockY)){
running = false;
}
if(!running){
timer.stop();
}
}
完整代码:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.AttributeSet.ColorAttribute;
import java.util.Random;
import java.util.random.*;
public class GamePanel extends JPanel implements ActionListener{
static final int SCREEN_WIDTH = 600;
static final int SCREEN_HEIGHT = 600;
static final int UNIT_SIZE = 25;
static final int GAME_UNITS = (SCREEN_HEIGHT*SCREEN_WIDTH)/UNIT_SIZE;
static final int DELAY = 75;
int x = 0;
int y = SCREEN_HEIGHT-(UNIT_SIZE*3);
int rocksFallen;
int rockX;
int rockY = 0;
char direction = 'R';
char directionRock = 'D';
boolean running = false;
Timer timer;
Random random;
GamePanel(){
random = new Random();
this.setPreferredSize(new Dimension(SCREEN_WIDTH,SCREEN_HEIGHT));
this.setBackground(Color.BLACK);
this.setFocusable(true);
this.addKeyListener(new MyKeyAdapter());
startGame();
}
public void startGame(){
newRock();
running = true;
timer = new Timer(DELAY,this);
timer.start();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
draw(g);
}
public void draw(Graphics g){
if(running){
for(int i = 0;i<SCREEN_HEIGHT/UNIT_SIZE;i++){
g.drawLine(i*UNIT_SIZE, 0, i*UNIT_SIZE, SCREEN_HEIGHT);
g.drawLine(0, i*UNIT_SIZE, SCREEN_WIDTH, i*UNIT_SIZE);
}
g.setColor(Color.red);
g.fillOval(rockX, rockY, UNIT_SIZE, UNIT_SIZE);
g.setColor(Color.green);
g.fillRect(x, y, UNIT_SIZE*2, UNIT_SIZE*3);
}
else{
gameOver(g);
}
}
public void newRock(){
rockX = random.nextInt((int)(SCREEN_WIDTH/UNIT_SIZE))*UNIT_SIZE;
rockY = 0;
}
public void move(){
switch (direction){
case 'R':
if (x<SCREEN_WIDTH-(UNIT_SIZE*2)){
x = x+UNIT_SIZE;
}
break;
case 'L':
if (x>0){
x = x-UNIT_SIZE;
}
break;
}
if(directionRock=='D'){
rockY = rockY+UNIT_SIZE;
}
}
public void checkRock(){
if((x == rockX && y == rockY) || (x==rockX && y+1 ==rockY) || (x==rockX && y+2 ==rockY)){
running = false;
}
else if((x+1 == rockX && y == rockY) || (x+1==rockX && y+1 ==rockY) || (x+1==rockX && y+2 ==rockY)){
running = false;
}
if(!running){
timer.stop();
}
}
public void checkCollisions(){
if(rockY>SCREEN_HEIGHT){
newRock();
rocksFallen++;
}
}
public void gameOver(Graphics g){
g.setColor(Color.red);
g.setFont(new Font("Ink Free",Font.BOLD,75));
FontMetrics metrics = getFontMetrics(g.getFont());
g.drawString("Game Over", (SCREEN_WIDTH-metrics.stringWidth("Game OVer"))/2, SCREEN_HEIGHT/2);
}
@Override
public void actionPerformed(ActionEvent e){
if(running){
move();
checkRock();
checkCollisions();
}
repaint();
}
public class MyKeyAdapter extends KeyAdapter{
@Override
public void keyPressed(KeyEvent e){
switch (e.getKeyCode()){
case KeyEvent.VK_LEFT:
direction = 'L';
break;
case KeyEvent.VK_RIGHT:
direction = 'R';
break;
}
}
}
}
游戏框架代码:
import javax.swing.*;
import javax.swing.text.AttributeSet.ColorAttribute;
import java.util.Random;
import java.util.random.*;
public class GameFrame extends JFrame{
GameFrame(){
this.add(new GamePanel());
this.setTitle("FlyingRocks");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.pack();
this.setVisible(true);
this.setLocationRelativeTo(null);
}
}
主要代码:
public class Game extends JFrame{
public static void main(String[] args) {
new GameFrame();
}
}
根据 camickr 的建议,使用 Shape
可以轻松检测碰撞。
以下是它的一个基本实现的单文件mre(复制粘贴整个代码到ShapesCollision.java
和运行):
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import java.util.*;
import java.util.List;
import javax.swing.Timer;
import javax.swing.*;
public class ShapesCollision extends JPanel implements ActionListener {
private final static int H = 350, W = 400, RATE = 30;
private final static Color BG = Color.DARK_GRAY;
private final static Color[] SHAPE_COLORS ={Color.RED, Color.BLUE};//to have more balls, add more colors
private final List<Ball> balls;
private final Dimension size;
public ShapesCollision() {
Random rnd = new Random();
balls = new ArrayList<>();
for(Color color : SHAPE_COLORS){
//todo check that added shape does not overlap previous ones
balls.add(new Ball(color,rnd.nextInt(W), rnd.nextInt(H)));
}
Timer timer = new Timer(RATE,this);
timer.start();
size = new Dimension(W,H);
setBackground(BG);
setVisible(true);
}
@Override
public void paintComponent(Graphics g) { //for custom painting override paintComponent, not paint
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g;
for(Ball ball : balls){
g2D.setColor(ball.getColor());
g2D.draw(ball);
g2D.fill(ball);
}
}
public static void main(String[] args) {
JFrame myFrame = new JFrame("Colliding Shapes");
myFrame.setLocationByPlatform(true);;
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.add(new ShapesCollision());
myFrame.pack();
myFrame.setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
checkCollision();
horizontalMove();
verticalMove();
repaint();
}
private void checkCollision() {
for(Ball ball1 : balls){
for(Ball ball2 : balls){
if(ball1 == ball2) {
continue;
}
if(ball1.intersects(ball2.getBounds2D())){
changeHorizontalDirection(ball1);
changeVerticalDirection(ball1);
changeHorizontalDirection(ball2);
changeVerticalDirection(ball2);
repaint();//optional
}
}
}
}
private void horizontalMove() {
for(Ball ball : balls){
double maxX = ball.getBounds().getMaxX();
if(ball.isMovingRight()){
if(maxX < getWidth() || ball.x <= 0){
ball.x += ball.getSpeed(); //keep moving right
continue;
}
}
if(!ball.isMovingRight()){
if(ball.x > 0 || maxX >= getWidth()){
ball.x -= ball.getSpeed();//keep moving left
continue;
}
}
changeHorizontalDirection(ball);
}
}
private void verticalMove() {
for(Ball ball : balls){
double maxY = ball.getBounds().getMaxY();
if(ball.isMovingDown()){
if(maxY<getHeight() || ball.y <=0){
ball.y += ball.getSpeed(); //keep moving down
continue;
}
}
if(!ball.isMovingDown()){
//ball is moving up
if(ball.y > 0 || ball.y >= maxY){
ball.y -= ball.getSpeed();//keep moving up
continue;
}
}
changeVerticalDirection(ball);
}
}
private void changeHorizontalDirection(Ball ball){
ball.setMovingRight(! ball.isMovingRight());
ball.x = ball.isMovingRight() ? ball.x + ball.getSpeed() : ball.x - ball.getSpeed();
}
private void changeVerticalDirection(Ball ball){
ball.setMovingDown(! ball.isMovingDown());
ball.y = ball.isMovingDown() ? ball.y + ball.getSpeed() : ball.y - ball.getSpeed();
}
@Override
public Dimension preferredSize() {
return size;
}
}
class Ball extends Ellipse2D.Double{
private static final int SPEED = 10, SHAPE_SIZE = 40;
private Color color;
private int speed;
private boolean isMovingRight, isMovingDown;
public Ball(Color color, int x, int y) {
super(x,y,SHAPE_SIZE, SHAPE_SIZE);
this.color = color;
Random rnd = new Random();
isMovingRight = rnd.nextBoolean();
isMovingDown = rnd.nextBoolean();
speed = SPEED;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public boolean isMovingRight() {
return isMovingRight;
}
public void setMovingRight(boolean isMovingRight) {
this.isMovingRight = isMovingRight;
}
public boolean isMovingDown() {
return isMovingDown;
}
public void setMovingDown(boolean isMovingDown) {
this.isMovingDown = isMovingDown;
}
}
运行它在线here