我正在编写一个基本的乒乓球游戏,球从由玩家控制的两个飞行垫反弹回来。我的问题开始于球与“右垫”碰撞的地方。我有两个垫,与左垫的碰撞效果很好,但球与右垫的碰撞没有按我想要的方式工作。
球穿过右侧的垫子(我不知道为什么,它不应该这样做)并与其碰撞多次,这会导致游戏中令人不愉快的视觉和声音。左垫不会发生这种情况。我添加了一个视频以便更好地理解:
我的代码有点长,所以为了让你更容易,我会提到我怀疑的内容。
public void checkCollision() {
for (ColoredShape tile : tiles) {
if (ball.getShape().getBounds().intersects(tile.getShape().getBounds())) {
playSound("sounds\\collisionsound1.wav");
// Calculating the angle of incidence
double tileCenterX = tile.getShape().getBounds2D().getCenterX();
double tileCenterY = tile.getShape().getBounds2D().getCenterY();
double ballCenterX = ball.getShape().getCenterX();
double ballCenterY = ball.getShape().getCenterY();
double angle = Math.atan2(ballCenterY - tileCenterY, ballCenterX - tileCenterX);
if (Math.abs(angle) < Math.PI / 2) {
ball.setVx(-ball.getVx());
} else {
ball.setVy(-ball.getVy());
}
}
}
}
public void moveBalls() {
if (!gameOver && ballShouldMove) {
ball.setX(ball.getX() + ball.getVx());
ball.setY(ball.getY() + ball.getVy());
}
if (ball.getX() > getWidth() - ball.getShape().width || ball.getX() < 0) {
playSound("sounds\\collisionsound3.wav");
if (ball.getX() > getWidth() - ball.getShape().width) {
ball.setVx(-Math.abs(ball.getVx()));
leftScore += 1;
} else if (ball.getX() < 0) {
ball.setVx(Math.abs(ball.getVx()));
rightScore += 1;
}
resetBallLocation();
ballShouldMove = false;
}
if (ball.getY() > getHeight() - ball.getShape().height || ball.getY() < 0) {
playSound("sounds\\collisionsound2.wav");
ball.setVy(-ball.getVy());
}
}
public void moveTiles() {
if (moveTile1Up) {
Rectangle bounds = (Rectangle) tiles.get(0).getShape().getBounds2D();
int newY = (int) (bounds.getY() - 5);
if (newY >= 0) {
tiles.get(0).setShape(new Rectangle((int) bounds.getX(), newY, tileWidth, tileHeight));
}
} else if (moveTile1Down) {
Rectangle bounds = (Rectangle) tiles.get(0).getShape().getBounds2D();
int newY = (int) (bounds.getY() + 5);
if (newY + tileHeight <= getHeight()) { // Check if the new position is within the frame
tiles.get(0).setShape(new Rectangle((int) bounds.getX(), newY, tileWidth, tileHeight));
}
}
if (moveTile2Up) {
Rectangle bounds = (Rectangle) tiles.get(1).getShape().getBounds2D();
int newY = (int) (bounds.getY() - 5);
if (newY >= 0) { // Check if the new position is within the frame
tiles.get(1).setShape(new Rectangle((int) bounds.getX(), newY, tileWidth, tileHeight));
}
} else if (moveTile2Down) {
Rectangle bounds = (Rectangle) tiles.get(1).getShape().getBounds2D();
int newY = (int) (bounds.getY() + 5);
if (newY + tileHeight <= getHeight()) { // Check if the new position is within the frame
tiles.get(1).setShape(new Rectangle((int) bounds.getX(), newY, tileWidth, tileHeight));
}
}
}
Shape tile1 = new Rectangle(tileWidth / 2, Interface.frameHeight / 3, tileWidth, tileHeight);
Shape tile2 = new Rectangle(Interface.frameWidth - tileWidth*2, Interface.frameHeight / 3, tileWidth, tileHeight);
ColoredShape tileLeft = new ColoredShape(tile1, Color.white);
ColoredShape tileRight = new ColoredShape(tile2, Color.white);
tiles.add(0, tileLeft);
tiles.add(1, tileRight);
--源代码--
接口.java
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Ellipse2D;
public class Interface extends JFrame {
protected static int frameHeight = 637;
protected static int frameWidth = 1214;
protected int tileWidth = 20;
protected int tileHeight = 200;
public Interface() {
setSize(frameWidth, frameHeight);
getContentPane().setBackground(Color.BLACK);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
setTitle("Ping Pong");
setResizable(false);
add(new Core(tileWidth, tileHeight));
setVisible(true);
}
public static void main(String[] args) {
new Interface();
}
}
class ColoredShape {
private Shape shape;
private Color color;
public ColoredShape(Shape shape, Color color) {
this.shape = shape;
this.color = color;
}
public Shape getShape() {
return shape;
}
public void setShape(Shape shape) {
this.shape = shape;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
}
class Ball {
private Ellipse2D.Double shape;
private Color color;
private int vx;
private int vy;
public Ball(int x, int y, int diameter, Color color, int vx, int vy) {
this.shape = new Ellipse2D.Double(x, y, diameter, diameter);
this.color = color;
this.vx = vx;
this.vy = vy;
}
public Ellipse2D.Double getShape() {
return shape;
}
public Color getColor() {
return color;
}
public int getVx() {
return vx;
}
public void setVx(int vx) {
this.vx = vx;
}
public int getVy() {
return vy;
}
public void setVy(int vy) {
this.vy = vy;
}
public int getX() {
return (int) shape.x;
}
public void setX(int x) {
shape.x = x;
}
public int getY() {
return (int) shape.y;
}
public void setY(int y) {
shape.y = y;
}
}
核心.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.lang.Math;
import javax.sound.sampled.*;
import java.io.*;
class Core extends JPanel {
private String winner = "";
protected int x;
protected int y;
protected int diameter = 10;
private int vx = 10, vy = 3;
private int rightScore = 0, leftScore = 0;
private int tileWidth, tileHeight;
private boolean moveTile1Up, moveTile1Down, moveTile2Up, moveTile2Down;
private boolean ballShouldMove = false;
private boolean gameOver = false;
private Timer speedIncreaseTimer;
private ArrayList<ColoredShape> tiles;
protected Ball ball;
public Core(int tileWidth, int tileHeight) {
setOpaque(false);
this.tileWidth = tileWidth;
this.tileHeight = tileHeight;
this.tiles = new ArrayList<>();
x = (int) Interface.frameWidth / 2;
y = (int) Interface.frameHeight / 2;
vx = (Math.random() < 0.5) ? vx : -1*vx;
vy = (Math.random() < 0.5) ? vy : -1*vy;
Shape tile1 = new Rectangle(tileWidth / 2, Interface.frameHeight / 3, tileWidth, tileHeight);
Shape tile2 = new Rectangle(Interface.frameWidth - tileWidth*2, Interface.frameHeight / 3, tileWidth, tileHeight);
ColoredShape tileLeft = new ColoredShape(tile1, Color.white);
ColoredShape tileRight = new ColoredShape(tile2, Color.white);
tiles.add(0, tileLeft);
tiles.add(1, tileRight);
ball = new Ball(x, y, diameter, Color.white, vx, vy);
// Add KeyListener to detect key presses
setFocusable(true);
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_W) {
moveTile1Up = true;
} else if (keyCode == KeyEvent.VK_S) {
moveTile1Down = true;
} else if (keyCode == KeyEvent.VK_UP) {
moveTile2Up = true;
} else if (keyCode == KeyEvent.VK_DOWN) {
moveTile2Down = true;
}
if (keyCode == KeyEvent.VK_SPACE) {
if (gameOver) {
restartGame();
} else {
ballShouldMove = true;
}
}
if (keyCode == KeyEvent.VK_ESCAPE) {
restartGame();
}
}
@Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_W) {
moveTile1Up = false;
} else if (keyCode == KeyEvent.VK_S) {
moveTile1Down = false;
} else if (keyCode == KeyEvent.VK_UP) {
moveTile2Up = false;
} else if (keyCode == KeyEvent.VK_DOWN) {
moveTile2Down = false;
}
}
});
ImageIcon originalIcon = new ImageIcon("images\\restartButton.jpg");
int scaledWidth = 50;
int scaledHeight = -1; // Automatically calculate the height to preserve aspect ratio
Image scaledImage = originalIcon.getImage().getScaledInstance(scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
ImageIcon scaledIcon = new ImageIcon(scaledImage);
JLabel restartLabel = new JLabel(scaledIcon);
add(restartLabel);
restartLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
restartGame();
}
});
Timer timer = new Timer(10, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
moveBalls();
checkCollision();
moveTiles();
gameOver();
repaint();
}
});
timer.start();
speedIncreaseTimer = new Timer(30000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (ballShouldMove) {
ball.setVx((int) (ball.getVx() * 1.25));
ball.setVy((int) (ball.getVy() * 1.25));
}
}
});
speedIncreaseTimer.start();
}
public void moveBalls() {
if (!gameOver && ballShouldMove) {
ball.setX(ball.getX() + ball.getVx());
ball.setY(ball.getY() + ball.getVy());
}
if (ball.getX() > getWidth() - ball.getShape().width || ball.getX() < 0) {
playSound("sounds\\collisionsound3.wav");
if (ball.getX() > getWidth() - ball.getShape().width) {
ball.setVx(-Math.abs(ball.getVx()));
leftScore += 1;
} else if (ball.getX() < 0) {
ball.setVx(Math.abs(ball.getVx()));
rightScore += 1;
}
resetBallLocation();
ballShouldMove = false;
}
if (ball.getY() > getHeight() - ball.getShape().height || ball.getY() < 0) {
playSound("sounds\\collisionsound2.wav");
ball.setVy(-ball.getVy());
}
}
public void gameOver() {
if (rightScore >= 10 || leftScore >= 10) {
playSound("sounds\\gameoversound1.wav");
gameOver = true;
if (rightScore >= 10) {
winner = "Right Player";
} else {
winner = "Left Player";
}
rightScore = 0;
leftScore = 0;
}
}
private void restartGame() {
gameOver = false;
winner = "";
resetBallLocation();
ballShouldMove = false;
rightScore = 0;
leftScore = 0;
}
public void resetBallLocation() {
ball.setX((int) (Interface.frameWidth / 2));
ball.setY((int) (Interface.frameHeight / 2));
ball.setVx(10);
ball.setVy(3);
vy = (Math.random() < 0.5) ? vy : -1*vy;
}
public void checkCollision() {
for (ColoredShape tile : tiles) {
if (ball.getShape().getBounds().intersects(tile.getShape().getBounds())) {
playSound("sounds\\collisionsound1.wav");
// Calculating the angle of incidence
double tileCenterX = tile.getShape().getBounds2D().getCenterX();
double tileCenterY = tile.getShape().getBounds2D().getCenterY();
double ballCenterX = ball.getShape().getCenterX();
double ballCenterY = ball.getShape().getCenterY();
double angle = Math.atan2(ballCenterY - tileCenterY, ballCenterX - tileCenterX);
if (Math.abs(angle) < Math.PI / 2) {
ball.setVx(-ball.getVx());
} else {
ball.setVy(-ball.getVy());
}
}
}
}
public void moveTiles() {
if (moveTile1Up) {
Rectangle bounds = (Rectangle) tiles.get(0).getShape().getBounds2D();
int newY = (int) (bounds.getY() - 5);
if (newY >= 0) {
tiles.get(0).setShape(new Rectangle((int) bounds.getX(), newY, tileWidth, tileHeight));
}
} else if (moveTile1Down) {
Rectangle bounds = (Rectangle) tiles.get(0).getShape().getBounds2D();
int newY = (int) (bounds.getY() + 5);
if (newY + tileHeight <= getHeight()) { // Check if the new position is within the frame
tiles.get(0).setShape(new Rectangle((int) bounds.getX(), newY, tileWidth, tileHeight));
}
}
if (moveTile2Up) {
Rectangle bounds = (Rectangle) tiles.get(1).getShape().getBounds2D();
int newY = (int) (bounds.getY() - 5);
if (newY >= 0) { // Check if the new position is within the frame
tiles.get(1).setShape(new Rectangle((int) bounds.getX(), newY, tileWidth, tileHeight));
}
} else if (moveTile2Down) {
Rectangle bounds = (Rectangle) tiles.get(1).getShape().getBounds2D();
int newY = (int) (bounds.getY() + 5);
if (newY + tileHeight <= getHeight()) { // Check if the new position is within the frame
tiles.get(1).setShape(new Rectangle((int) bounds.getX(), newY, tileWidth, tileHeight));
}
}
}
public void playSound(String filePath) {
try {
File sound = new File(filePath);
AudioInputStream audioInput = AudioSystem.getAudioInputStream(sound);
Clip clip = AudioSystem.getClip();
clip.open(audioInput);
clip.start();
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
e.printStackTrace();
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (ColoredShape tile : tiles) {
g2d.setColor(tile.getColor());
g2d.fill(tile.getShape());
}
g2d.setColor(ball.getColor());
g2d.fill(ball.getShape());
String strLeftScore = String.valueOf(leftScore);
String strRightScore = String.valueOf(rightScore);
Font scoreFont = new Font("Arial", Font.BOLD, 36);
g2d.setFont(scoreFont);
FontMetrics fmLeft = g.getFontMetrics(scoreFont);
FontMetrics fmRight = g.getFontMetrics(scoreFont);
int xLeft = (getWidth() - fmLeft.stringWidth(strLeftScore)) / 2 - 80;
int xRight = (getWidth() - fmRight.stringWidth(strRightScore)) / 2 + 80;
int yLeft = 40;
int yRight = 40;
g2d.drawString(strLeftScore, xLeft, yLeft);
g2d.drawString(strRightScore, xRight, yRight);
if (gameOver) {
String gameOverMessage = "Game Over, " + winner + " won.";
Font font = new Font("Arial", Font.BOLD, 36);
g2d.setFont(font);
FontMetrics fm = g.getFontMetrics(font);
int x = (getWidth() - fm.stringWidth(gameOverMessage)) / 2;
int y = getHeight() / 2;
g2d.drawString(gameOverMessage, x, y);
}
}
public int getRightScore() {
return rightScore;
}
public int getLeftScore() {
return leftScore;
}
}
您的碰撞检测算法不足。如果球的速度增加到在帧之间移动显着距离的程度,或者如果垫对象的方向垂直于运动方向并且球以较小的角度接近,则球可能会穿过对象而不被检测到(至少在正确的时间)通过碰撞检测机制。这就是所谓的
tunneling
。
新的 checkCollision() 方法:
public void checkCollision() {
for (ColoredShape tile : tiles) {
double ballX = ball.getX();
double ballY = ball.getY();
double ballRadius = ball.getRadius();
double ballVx = ball.getVx();
double ballVy = ball.getVy();
Shape tileShape = tile.getShape();
Rectangle2D tileBounds = tileShape.getBounds2D();
double tileLeft = tileBounds.getMinX();
double tileRight = tileBounds.getMaxX();
double tileTop = tileBounds.getMinY();
double tileBottom = tileBounds.getMaxY();
// Calculate the number of steps based on the maximum velocity component
double maxSpeed = Math.max(Math.abs(ballVx), Math.abs(ballVy));
int numSteps = (int) Math.ceil(maxSpeed / ballRadius);
// Perform collision detection for each step
for (int i = 0; i < numSteps; i++) {
double stepX = ballX + ballVx * (i + 1) / numSteps;
double stepY = ballY + ballVy * (i + 1) / numSteps;
if (stepX + ballRadius >= tileLeft && stepX - ballRadius <= tileRight &&
stepY + ballRadius >= tileTop && stepY - ballRadius <= tileBottom) {
playSound("sounds\\collisionsound1.wav");
// Determine the side of the collision
double overlapX = Math.max(0, Math.min(stepX + ballRadius, tileRight) - Math.max(stepX - ballRadius, tileLeft));
double overlapY = Math.max(0, Math.min(stepY + ballRadius, tileBottom) - Math.max(stepY - ballRadius, tileTop));
boolean collisionFromLeftOrRight = overlapX < overlapY;
if (collisionFromLeftOrRight) {
// Adjust x velocity
ball.setVx((int) -ballVx);
} else {
// Adjust y velocity
ball.setVy((int) -ballVy);
}
break; // Exit the loop after handling the collision
}
}
}
}