Java Hit Detection/Collision between objects - java

Being new to coding, I watched a video on how to create Pong. That went well, and as a result I wanted to try and recreate Brick Breaker using some of the coding techniques that were used for Pong. So far I have the ball, paddle, and the base of the game. However, the ball does not collide properly with the paddle, instead of bouncing of it passes through.
Code for base game:
import java.applet.Applet;
import java.awt.Color;
import java.awt.Image;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Breaker extends Applet implements Runnable, KeyListener{
final int WIDTH = 600, HEIGHT= 400;
Thread thread;
Graphics gfx;
Image img;
Ball b1;
Player p1;
public void init() {
this.resize(WIDTH, HEIGHT);
this.addKeyListener(this);
b1 = new Ball();
p1 = new Player(1);
img = createImage(WIDTH,HEIGHT);
gfx = img.getGraphics();
thread = new Thread(this);
thread.start();
}
public void paint(Graphics g) {
gfx.setColor(Color.black);
gfx.fillRect(0, 0, WIDTH, HEIGHT);
b1.draw(gfx);
p1.draw(gfx);
g.drawImage(img, 0, 0, this);
}
public void update (Graphics g) {
paint(g);
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_LEFT){
p1.setLeftAccel(true);
}
else if(e.getKeyCode() == KeyEvent.VK_RIGHT){
p1.setRightAccel(true);
}
}
#Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_LEFT){
p1.setLeftAccel(false);
}
else if(e.getKeyCode() == KeyEvent.VK_RIGHT){
p1.setRightAccel(false);
}
}
#Override
public void run() {
for(;;) {
p1.move();
b1.move();
b1.checkPlayerCollision(p1);
repaint();
getToolkit().sync();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void keyTyped(KeyEvent arg0) {
}
}
Ball:
import java.awt.Graphics;
import java.awt.Color;
public class Ball {
int score;
double xVel, yVel, x, y;
public Ball() {
x= 350;
y = 250;
xVel= 0;
yVel = 1;
}
public void draw(Graphics g) {
g.setColor(Color.white);
g.fillOval((int)x - 10, (int)y - 10, 20, 20);
}
public void checkPlayerCollision(Player p1) {
if (x <= 50) {
if(x >= p1.getX() && x <= p1.getX() + 10)
xVel = -xVel;
}
else if (x >= 680) {
xVel = -xVel;
}
}
public void move() {
x += xVel;
y += yVel;
if (y < 10) //Bounce at top side of screen
yVel = -yVel;
if (x < 10) // Bounce at left side of screen
xVel = -xVel;
}
public int getX() {
return (int)x;
}
public int getY() {
return (int)y;
}
}
Player:
import java.awt.Color;
import java.awt.Graphics;
public class Player implements Paddle {
double x, xVel;
boolean leftAccel, rightAccel;
int player, y;
public Player(int player) {
leftAccel = false; rightAccel = false;
x=260; xVel=0;
}
#Override
public void draw(Graphics g) {
g.setColor(Color.white);
g.fillRect((int)x, 380, 80, 10);
}
#Override
public void move() {
if(leftAccel){
xVel -= 2;
}
else if (rightAccel) {
xVel += 2;
}
else if (!leftAccel && !rightAccel) {
xVel = 0;
}
if(xVel >= 5) {
xVel = 5;
}
else if (xVel <= -5) {
xVel = -5;
}
x += xVel;
if(x < 0)
x=0; //Collision with left wall
if(x > 520)
x = 520; //Collision with right wall
}
public void setLeftAccel(boolean input) {
leftAccel = input;
}
public void setRightAccel(boolean input) {
rightAccel = input;
}
#Override
public int getX() {
return (int)x;
}
}
Paddle:
import java.awt.Graphics;
public interface Paddle {
public void draw(Graphics g);
public void move();
public int getX();
}
I don't completely understand hit detection yet, so I just messed with what was used for pong, however, that doesn't work (and I'm sure it might look weird).

Collision detection can be hard to wrap your head around sometimes. What really helps me is to draw on a piece of paper the things I need to check for and write the checks in code one by one. It's also a good idea to keep your axes in mind, it's really easy to mix up an x and a y, but they will have an important effect on gameplay.
My eye was drawn to your checkPlayerCollision function as it seemed a little off, I have changed some checks around and commented what each check/function is doing.
public void checkPlayerCollision(Player p1) {
if (
// Is the ball at or below paddle height?
y <= 50
// AND Is the ball right of the left side of the paddle?
&& x >= p1.getX()
// AND Is the ball left of the right side of the paddle?
&& x <= p1.getX() + 10
) {
// Collision with the paddle!
// (Ball is lower than y=50 and between left and right side of the paddle!)
// As we want the ball to go back up, we will be changing its velocity
// along the Y-axis.
yVel -= yVel;
} else if (x >= 680) {
// This checks whether the ball went too far to the right..?
xVel -= xVel;
}
}
Best tip I can give you: grab a piece of paper and just draw a simple grid or graph and use that to draw out different things you need to check. Be sure to label your (0,0) point so when you work with the different borders, you know which ones are 0 (usually top and left borders) and which ones are equal to width and height of the canvas.

Related

How can I change the vector of a ball in breakout game made using Java?

I'm using Java to make a breakout game. The independent parts are functioning: the paddle, the ball, the bricks.
However, the ball hits a wall and then instead of changing it's vector, the ball just travels up the x-axis in a straight line at the edge of the JFrame window until it hits the top of the window and bounces back down from this corner.
The ball then gets stuck in an infinite back and forth line from the top left corner until it touches the paddle (back and forth) and will never break any of the other bricks.
How can I change my code to fix this problem?
import java.awt.Graphics;
public class Ball extends Sprite {
private int xVelocity = 1, yVelocity = -1;
// Constructor
public Ball() {
setWidth(Settings.BALL_WIDTH);
setHeight(Settings.BALL_HEIGHT);
resetPosition();
public void resetPosition() {
setX(Settings.INITIAL_BALL_X);
setY(Settings.INITIAL_BALL_Y);
}
public void update() {
x += yVelocity;
y += yVelocity;
// Bounce off left side of screen
if(x <= 0) {
x = 0;
setXVelocity(-1);
}
// Bounce off right side of screen
if(x >= Settings.WINDOW_WIDTH - Settings.BALL_WIDTH) {
x =Settings.WINDOW_WIDTH;
setXVelocity(-1);
}
// Bounce off top of screen
if(y <= 0) {
y = 0;
setYVelocity(1);
}
}
public void setXVelocity(int x) {
xVelocity = x;
}
public void setYVelocity(int y) {
yVelocity = y;
}
public int getXVelocity() {
return xVelocity;
}
public int getYVelocity() {
return yVelocity;
}
public void paint(Graphics g) {
g.fillOval(x, y, Settings.BALL_WIDTH, Settings.BALL_HEIGHT);
}
}
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Paint;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class BreakoutPanel extends JPanel implements ActionListener, KeyListener {
static final long serialVersionUID = 2L;
private boolean gameRunning = true;
private int livesLeft = 3;
private String screenMessage = "";
private Ball ball;
private Paddle paddle;
private Brick bricks[];
public BreakoutPanel(Breakout game) {
addKeyListener(this);
setFocusable(true);
Timer timer = new Timer(5, this);
timer.start();
ball = new Ball();
paddle = new Paddle();
bricks = new Brick[Settings.TOTAL_BRICKS];
createBricks();
}
private void createBricks() {
int counter = 0;
int x_space = 0;
int y_space = 0;
for(int x = 0; x < 4; x++) {
for(int y = 0; y < 5; y++) {
bricks[counter] = new Brick((x * Settings.BRICK_WIDTH) + Settings.BRICK_HORI_PADDING + x_space, (y * Settings.BRICK_HEIGHT) + Settings.BRICK_VERT_PADDING + y_space);
counter++;
y_space++;
}
x_space++;
y_space = 0;
}
}
private void paintBricks(Graphics g) {
for(int x = 0; x < Settings.TOTAL_BRICKS; x++) {
for(int y = 0; y < Settings.TOTAL_BRICKS; y++) {
bricks[y].paint(g);
}
}
}
private void update() {
if(gameRunning) {
// TODO: Update the ball and paddle
ball.update();
paddle.update();
collisions();
repaint();
}
}
private void gameOver() {
stopGame();
screenMessage = "Game Over!";
}
private void gameWon() {
stopGame();
screenMessage = "Congratulations! You have won!";
}
private void stopGame() {
gameRunning = false;
}
private void collisions() {
// Check for loss
if(ball.y > 450) {
// Game over
livesLeft--;
if(livesLeft <= 0) {
gameOver();
return;
} else {
ball.resetPosition();
ball.setYVelocity(-1);
}
}
// Check for win
boolean bricksLeft = false;
for(int i = 0; i < bricks.length; i++) {
// Check if there are any bricks left
if(!bricks[i].isBroken()) {
// Brick was found, close loop
bricksLeft = true;
break;
}
}
if(!bricksLeft) {
gameWon();
return;
}
// Check collisions
if(ball.getRectangle().intersects(paddle.getRectangle())) {
// Simplified touching of paddle
ball.setYVelocity(-1);
}
for(int i = 0; i < bricks.length; i++) {
if (ball.getRectangle().intersects(bricks[i].getRectangle())) {
int ballLeft = (int) ball.getRectangle().getMinX();
int ballHeight = (int) ball.getRectangle().getHeight();
int ballWidth = (int) ball.getRectangle().getWidth();
int ballTop = (int) ball.getRectangle().getMinY();
Point pointRight = new Point(ballLeft + ballWidth + 1, ballTop);
Point pointLeft = new Point(ballLeft - 1, ballTop);
Point pointTop = new Point(ballLeft, ballTop - 1);
Point pointBottom = new Point(ballLeft, ballTop + ballHeight + 1);
if (!bricks[i].isBroken()) {
if (bricks[i].getRectangle().contains(pointRight)) {
ball.setXVelocity(-1);
} else if (bricks[i].getRectangle().contains(pointLeft)) {
ball.setXVelocity(1);
}
if (bricks[i].getRectangle().contains(pointTop)) {
ball.setYVelocity(1);
} else if (bricks[i].getRectangle().contains(pointBottom)) {
ball.setYVelocity(-1);
}
bricks[i].setBroken(true);
}
}
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
ball.paint(g);
paddle.paint(g);
paintBricks(g);
// Draw lives left
// TODO: Draw lives left in the top left hand corner**
if(livesLeft != 0) {
String displayLives = Integer.toString(livesLeft);
g.setFont(new Font("Arial", Font.BOLD, 18));
g.drawString(displayLives, Settings.LIVES_POSITION_X, Settings.LIVES_POSITION_Y);
}
// Draw screen message*
if(screenMessage != null) {
g.setFont(new Font("Arial", Font.BOLD, 18));
int messageWidth = g.getFontMetrics().stringWidth(screenMessage);
g.drawString(screenMessage, (Settings.WINDOW_WIDTH / 2) - (messageWidth / 2),
Settings.MESSAGE_POSITION);
}
}
#Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) {
paddle.setXVelocity(-1);
}
if (key == KeyEvent.VK_RIGHT) {
paddle.setXVelocity(1);
}
}
#Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_RIGHT) {
paddle.setXVelocity(0);
}
}
#Override
public void keyTyped(KeyEvent arg0) {
}
#Override
public void actionPerformed(ActionEvent arg0) {
update();
}
}
I made a similar game in python years ago. What I did to solve this problem was allow the player to control the direction of the ball by hitting it with different parts of the paddle.
If the player hit the ball with the absolute center of the paddle, then I would just reverse the vector as if it had hit off a wall. But if the absolute edge of the paddle hit the ball, then I would increase the x velocity in the same direction of the edge that it hit. You can make the effect continuous, so if the the ball hits the paddle halfway between the edge and the center, it increases the x velocity half as much as it would have if it had hit the absolute edge.
This way the vector will continuously change and the ball will bounce off of things in a more interesting way, as well as give the player more control so they don't have to just wait until the ball eventually hits every brick in a predefined pattern.
You can use a coefficient, or more complex function if you don't want the effect of where the ball hits the paddle to effect the x velocity in such a linear manner.
You can use a similar strategy for handling collisions with the bricks. For instance, if the ball hits the corner of a brick, you probably don't want it to react as if it had hit the bottom, or the side, but have a reaction that is somewhere in between.

How Do I Make my 2D Pong Racquets Move Up and Down, Rather than Side to Side?

I saw a lot of results on how do move a sprite in Java, but I can't find any that suite my code. I'm following a tutorial on how to make a Pong style game.
You can find the tutorial here. (That's the part I encountered the problem).
Here's my code for the Racquet class:
package com.tennis;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
public class Racquet {
private static final int Y = 330;
private static final int WIDTH = 60;
private static final int HEIGHT = 10;
int x = 0;
int xa = 0;
private Game game;
public Racquet(Game game) {
this.game = game;
}
public void move() {
if(x + xa > 0 && x + xa < game.getHeight()-60)
x = x + xa;
}
public void paint(Graphics2D g) {
g.fillRect(x, 50, 10, 70);
}
public void keyReleased(KeyEvent e) {
xa = 0;
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP)
xa = -1;
if(e.getKeyCode() == KeyEvent.VK_DOWN)
xa = 1;
}
public Rectangle getBounds() {
return new Rectangle(x, Y, WIDTH, HEIGHT);
}
public int getTopY() {
return Y;
}
}
Now the part I'm looking at that I need help changing is this:
public void keyReleased(KeyEvent e) {
xa = 0;
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP)
xa = -1;
if(e.getKeyCode() == KeyEvent.VK_DOWN)
xa = 1;
}
public Rectangle getBounds() {
return new Rectangle(x, Y, WIDTH, HEIGHT);
}
Alrighty, so the actual problem I have is that I need the Racquet to move up and down along the Y axis, at the current moment it's moving along the X axis.
In your move() function replace x = x + xa to:
y = y + xa;
x variable supposed to be y, and pass to the 2nd param for fillRect
public void paint(Graphics2D g) {
g.fillRect(50, x, 10, 70);
}
public abstract void fillRect(int x,
int y,
int width,
int height)
x - the x coordinate of the rectangle to be filled.
y - the y coordinate of the rectangle to be filled.
width - the width of the rectangle to be filled.
height - the height of the rectangle to be filled.
I was overcomplicating things by trying to make the ping pong racquet move on the Y axis instead of the X axis, so I switched it. Problem solved-ish. Now I can finish the game and continue onto making funner Java games.
So I messed around with these integers,
public void paint(Graphics2D g) {
g.fillRect(x, 250, 70, 10);
}
Then I made the keys as so:
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT)
xa = -1;
if(e.getKeyCode() == KeyEvent.VK_RIGHT)
xa = 1;
}

Player and Blocks collision (Terraria) [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I am creating a game like Terraria. I already have a player and I can place blocks and delete them by clicking on an existing block. But I canĀ“t figure out how to make player and block collision. I want to create physics like in terraria. Can you help me somehow?
Here are my 3 classes (I have 4 but the 4. class is only JFrame class):
Here is my Main Class (gameloop):
package Package;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Game extends JPanel implements ActionListener, MouseListener, KeyListener {
Timer gameLoop = new Timer(5, this);
private static final long serialVersionUID = 1L;
public static final int WIDTH = 1500;
public static final int HEIGHT = 900;
public static final Dimension windowSize = new Dimension(WIDTH, HEIGHT);
Player player = new Player();
public static ArrayList<Block> blocks = new ArrayList<Block>();
private int xDistance;
private int yDistance;
public Game() {
setPreferredSize(windowSize);
setFocusable(true);
addMouseListener(this);
addKeyListener(this);
setBackground(Color.WHITE);
for (int i = 0; i < Game.WIDTH; i += 50) {
for (int j = Game.HEIGHT - 150; j < Game.HEIGHT; j += 50) {
blocks.add(new Block(i, j));
}
}
start();
}
public void start() {
gameLoop.start();
}
public void stop() {
gameLoop.stop();
}
public void paint(Graphics g) {
for (Block b : blocks) {
b.render(g);
}
player.render(g);
}
public void actionPerformed(ActionEvent e) {
player.move();
player.offScreen();
repaint();
}
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {
int mouseX = e.getX();
int mouseY = e.getY();
boolean foundBlock = false;
xDistance = mouseX % 50;
yDistance = mouseY % 50;
for (Block b : blocks) {
if (b.x == mouseX - xDistance && b.y == mouseY - yDistance) {
if (mouseX >= player.x - 150 && mouseX <= player.x + 200 && mouseY >= player.y - 150 && mouseY <= player.y + 250) {
blocks.remove(b);
foundBlock = true;
break;
}
}
}
if (foundBlock == false) {
if (mouseX >= player.x - 150 && mouseX <= player.x + 200 && mouseY >= player.y - 150 && mouseY <= player.y + 250) {
blocks.add(new Block(mouseX - xDistance, mouseY - yDistance));
}
}
}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void keyTyped(KeyEvent e) {}
public void keyPressed(KeyEvent e) {
int arrows = e.getKeyCode();
if (arrows == KeyEvent.VK_D) {
player.xSpeed = 2;
}
if (arrows == KeyEvent.VK_A) {
player.xSpeed = -2;
}
}
public void keyReleased(KeyEvent e) {
int arrows = e.getKeyCode();
if (arrows == KeyEvent.VK_D) {
player.xSpeed = 0;
}
if (arrows == KeyEvent.VK_A) {
player.xSpeed = 0;
}
}
}
Here is my Player class:
package Package;
import java.awt.Color;
import java.awt.Graphics;
public class Player {
public int x = 14 * 50;
public int y = Game.HEIGHT - (5 * 50);
public int xSpeed = 0;
public int ySpeed = 0;
public boolean jump = false;
public int ticks = 6;
public void render(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(x, y, 50, 100);
}
public void move() {
x += xSpeed;
y += ySpeed;
}
public void offScreen() {
if (x <= 0) {
xSpeed = 0;
} else if (x + 50 >= Game.WIDTH) {
xSpeed = 0;
}
}
}
Here is my Block Class:
package Package;
import java.awt.Color;
import java.awt.Graphics;
public class Block {
public int x;
public int y;
public Block(int x, int y) {
this.x = x;
this.y = y;
}
public void render(Graphics g) {
g.setColor(Color.GRAY);
g.fillRect(x, y, 50, 50);
g.setColor(Color.BLACK);
g.drawRect(x, y, 50, 50);
}
}
You don't want to store you blocks this way:
public static ArrayList<Block> blocks = new ArrayList<Block>();
Instead, create a structure that easily allows you to access blocks by their coordinates. Maybe:
public static HashMap<Integer, HashMap<Integer, Block>>()
... where the 1st Integer is the X coordinate and the 2nd is the Y. That way, you can quickly find the blocks that have any chance of colliding with the player, instead of checking every block in the world every iteration.
Also, why is this static? It shouldn't be, I think.
To check for collisions (I assume to prevent players from moving through blocks), just figure out the coordinates of where the player would wind up after the proposed move and see if the players bounding rectangle (the rectangle from the upper left of the player's sprite to the lower right) overlaps the bounding rectangle of any block.
But it's really important: arrange the blocks so you don't have to check them all! Only the blocks in a very narrow coordinate range can possibly be collisions, so make sure you can access blocks by range quickly.

creating many objects with collision properties. JAVA

I have a simple program with three rectangles: one that can move with the push of the arrow keys, and two that are already moving back and forth on their own.
When the 'player' rectangle and top red collide, the player driven rectangle gets put back to (0,0). When I try to collide the player rectangle with the bottom red rectangle, it does not have those collision properties and I have no idea why.
What am I missing?
import java.awt.*;//needed for graphics
import javax.swing.*;//needed for JFrame window
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class javaapplication23 extends JFrame implements KeyListener, ActionListener {
public static int x = 0;
public static int y = 0;
public static int x2 = 100;
public static int y2 = 100;
public javaapplication23() {//constructor for JPanel
add(new JP());
}//close Jpanel Contructor
public static void main(String[] args) {
javaapplication23 w = new javaapplication23();
w.setTitle("MIKE IS AWESOME");
w.setSize(Toolkit.getDefaultToolkit().getScreenSize());
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
w.setVisible(true);
w.addKeyListener(w);
}
public class JP extends JPanel {//start JPanel CLass
public JP() {
Container c = getContentPane();
c.setBackground(Color.white);//backgraund color can be changed
}
public void paint(Graphics g) {//opens paint method
super.paint(g);
player(g, x, y);
g.setColor(Color.RED);
enemylevel1(g, x2, y2);
Rectangle enemyblocks = new Rectangle(x2, y2, 25, 25);
Rectangle player = new Rectangle(x, y, 25, 25);
enemyblocks.contains(x2, y2);
player.contains(x, y);
if (player.getBounds().intersects(enemyblocks.getBounds())) {
x = 0;
y = 0;
}
pause(1);
repaint();
}//close paint method
}//close JPanel Class
public static void pause(int time) {
try //opens an exception handling statement
{
Thread.sleep(time);
} catch (InterruptedException e) {
} //captures the exception
}
public void actionPerformed(ActionEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == e.VK_RIGHT) {
x += 20;//global variable controlling right movement
repaint();
}
if (e.getKeyCode() == e.VK_LEFT) {
x -= 20;//global variable controlling left movement
repaint();
}
if (e.getKeyCode() == e.VK_UP) {
y -= 20;//global variable controlling up movement
repaint();
}
if (e.getKeyCode() == e.VK_DOWN) {
y += 20;//global variable controlling down movement
repaint();
}
}
public void player(Graphics g, int x, int y) {
g.fillRect(x, y, 30, 30);
}
public void enemylevel1(Graphics g, int x, int y) {
g.fillRect(x2, y2, 25, 25);
g.fillRect(x2, y2 + 100, 25, 25);
if (x2 < 200 && y2 == 100) {
x2 += 1;
}
if (x2 == 200 && y2 >= 100) {
y2 += 1;
}
if (x2 <= 200 && y2 >= 101) {
x2 -= 1;
}
if (x2 == 100 && y2 <= 101) {
y2 -= 1;
}
pause(10);
repaint();
}
}
Start by having a look at Working with Geometry, this will allow you to reduce much of the code complexity.
Basically, a enemy is just a Rectangle, Graphics2D can paint these without to much of an issue. What you need to do is create an instance which can also update it's position based on your needs
public class Enemy extends Rectangle {
private int xDelta;
public Enemy(int x, int y) {
super(x, y, 20, 20);
if (x == 0) {
xDelta = 1;
} else {
xDelta = -1;
}
}
public void update(Rectangle bounds) {
x += xDelta;
if (x < bounds.x) {
x = bounds.x;
xDelta *= -1;
} else if (x > bounds.x + bounds.width - width) {
x = bounds.x + bounds.width - width;
xDelta *= -1;
}
}
}
So, this creates a single unit of work, which is isolated from everything else and carries it's own logic with it. This makes updating it, painting and generally working with much simpler.
Next, you need to create a List of these
public class Bounce extends JPanel implements KeyListener, ActionListener {
private List<Enemy> enemies;
//...
public Bounce() {
enemies = new ArrayList<>(5);
int y = 100;
for (int index = 0; index < 5; index++) {
int x = (index % 2 == 0) ? 0 : 200;
Enemy enemy = new Enemy(x, y);
enemies.add(enemy);
y += 60;
}
This creates a List of Enemys which are distributed evenly within the container.
Now, we need to paint them....
#Override
protected void paintComponent(Graphics g) {//opens paint method
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
g2d.setColor(Color.RED);
for (Enemy enemy : enemies) {
g2d.fill(enemy);
}
}//close paint method
nb: General convention suggests that you should override paintComponent when you want to perform custom painting
But they don't move, that kind of sucks. So we need a way to, on a regular bases, update the position of the enemies...
First, we create a simple method which we can call to update the enemies, remember, they are capable of updating themselves, we just need to tell them when
public void updateState() {
Rectangle bounds = new Rectangle(20, 20, 200, 200);
for (Enemy enemy : enemies) {
enemy.update(bounds);
}
}
Remember, the Enemy is self contained, it knows how to update itself based on the constraints you have provided.
And now, we need to call this method on a regular bases...
javax.swing.Timer timer = new javax.swing.Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateState();
repaint();
}
});
timer.start();
Okay, this will schedule a callback every 40 milliseconds which will allow us to call the updateState method and repaint the component. This is neat because it won't block the Event Dispatching Thread (making our program look like it's hung) but which notifies us within the context of the EDT, making it safe to update the UI from within - WIN/WIN :)
Take a look at Concurrency in Swing and How to use Swing Timers for more details.
Okay, but that doesn't solve the collision...
The player is also a Rectangle, so why not use the same concept we have with the enemies...
public class Bounce extends JPanel implements KeyListener, ActionListener {
private List<Enemy> enemies;
private Rectangle player;
//...
public Bounce() {
player = new Rectangle(0, 0, 30, 30);
enemies = new ArrayList<>(5);
//...
}
#Override
protected void paintComponent(Graphics g) {//opens paint method
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
drawPlayer(g2d);
g2d.setColor(Color.RED);
for (Enemy enemy : enemies) {
g2d.fill(enemy);
if (player.intersects(enemy)) {
player.x = 0;
player.y = 0;
}
}
}//close paint method
public void drawPlayer(Graphics2D g) {
g.fill(player);
}
Which ends up with something like...
This allows you to add/remove enemies as you want and also change the way in which the enemies move, simply and easily
An my "awesome" test code...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Bounce extends JPanel implements KeyListener, ActionListener {
private List<Enemy> enemies;
private Rectangle player;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Bounce());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public Bounce() {
player = new Rectangle(0, 0, 30, 30);
enemies = new ArrayList<>(5);
int y = 100;
for (int index = 0; index < 5; index++) {
int x = (index % 2 == 0) ? 0 : 200;
Enemy enemy = new Enemy(x, y);
enemies.add(enemy);
y += 60;
}
setBackground(Color.white);//backgraund color can be changed
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateState();
repaint();
}
});
timer.start();
setFocusable(true);
requestFocusInWindow();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
requestFocusInWindow();
}
});
addKeyListener(this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(240, 400);
}
#Override
protected void paintComponent(Graphics g) {//opens paint method
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
drawPlayer(g2d);
g2d.setColor(Color.RED);
for (Enemy enemy : enemies) {
g2d.fill(enemy);
if (player.intersects(enemy)) {
player.x = 0;
player.y = 0;
}
}
}//close paint method
public void actionPerformed(ActionEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == e.VK_RIGHT) {
player.x += 20;//global variable controlling right movement
}
if (e.getKeyCode() == e.VK_LEFT) {
player.x -= 20;//global variable controlling left movement
}
if (e.getKeyCode() == e.VK_UP) {
player.y -= 20;//global variable controlling up movement
}
if (e.getKeyCode() == e.VK_DOWN) {
player.y += 20;//global variable controlling down movement
}
}
public void drawPlayer(Graphics2D g) {
g.fill(player);
}
public void updateState() {
Rectangle bounds = new Rectangle(20, 20, 200, 200);
for (Enemy enemy : enemies) {
enemy.update(bounds);
}
}
public class Enemy extends Rectangle {
private int xDelta;
public Enemy(int x, int y) {
super(x, y, 20, 20);
if (x == 0) {
xDelta = 1;
} else {
xDelta = -1;
}
}
public void update(Rectangle bounds) {
x += xDelta;
if (x < bounds.x) {
x = bounds.x;
xDelta *= -1;
} else if (x > bounds.x + bounds.width - width) {
x = bounds.x + bounds.width - width;
xDelta *= -1;
}
}
}
}

Cant understand how to get this collision detection to work what am i doing wrong?

I need help trying to sort how this collision detection in my code.
Here is my code and I have had a little assistance from various sources online but nothing with a definite explanation to it yet, but all in all I have made a lot of progress. So basically a quick thank you internet.
What I have to do is make a game where I control an object, in this case a blue circle and then touch another on screen object, in this case a yellow circle and when they touch the yellow circle will dissapear and the word "YUM" will take its place then the yellow circle will move randomly to a new location.
This is a lot like the game snake but I can't get the collision to work right and also if I did have it working I have no idea how I would update the location to a new location randomly on the screen.
here is the code:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Random;
import javax.swing.JFrame;
import org.w3c.dom.css.Rect;
public class javagame2 extends JFrame {
private Image dbimage;
private Graphics dbg;
Font font = new Font("Arial", Font.BOLD, 30);
static Random generator = new Random();
static int randomIndex0 = generator.nextInt(900);
static int randomIndex1 = generator.nextInt(400);
public static class player
{
static int x = 150;
static int y = 150;
static final int Width = 50;
static final int Height = 50;
}
public static class pizza
{
static final int x1 = randomIndex0;
static final int y1 = randomIndex1;
int Width = 50;
int Height = 50;
public boolean intercepts(player p) {
// TODO Auto-generated method stub
return false;
}
}
public class AL extends KeyAdapter
{
public void keyPressed(KeyEvent e)
{
int keycode = e.getKeyCode();
if(keycode == e.VK_LEFT){
if(player.x <= 0)
{
player.x = 0;
}
else
{
player.x += -10;
}
}
if(keycode == e.VK_RIGHT){
if(player.x >= 950)
{
player.x = 950;
}
else
{
player.x += 10;
}
}
if(keycode == e.VK_UP){
if(player.y <= 20)
{
player.y = 20;
}
else
{
player.y += -10;
}
}
if(keycode == e.VK_DOWN){
if(player.y >= 450)
{
player.y = 450;
}
else
{
player.y += 10;
}
}
}
public void keyReleased(KeyEvent e){}
}
public int getX()
{
return player.x;
}
public int getY()
{
return player.y;
}
/*
public boolean collide(pizza f)
{
if(f.X <= (player.x+40) && f.Y >= (player.y+40))
{
System.out.println("they collided");
return true;
}
else
{
return false;
}
}
*/
public boolean collide(pizza f, player p)
{
if(f.x1 <= p.x && f.y1 >= p.y)
{
System.out.println("they collided");
return true;
}
else
{
return false;
}
}
public javagame2()
{
addKeyListener(new AL());
setTitle("Ballon Popper");
setSize(1000, 500);
setResizable(false);
setVisible(true);
setBackground(Color.BLACK);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void paintcomponent(Graphics g)
{
g.setColor(Color.WHITE);
g.drawRect(pizza.x1, pizza.y1, 50, 50);
g.setFont(font);
g.setColor(Color.RED);
g.drawString("Eat the Pizza!", 350, 50);
g.setColor(Color.BLUE);
g.drawRect(getX(), getY(), 50, 50);
g.fillOval(getX(), getY(), 50, 50);
g.setColor(Color.YELLOW);
g.fillOval(pizza.x1, pizza.y1, 50, 50);
repaint();
}
public void paint(Graphics g)
{
dbimage = createImage(getWidth(), getHeight());
dbg = dbimage.getGraphics();
paintcomponent(dbg);
g.drawImage(dbimage, 0, 0, this);
}
public static void main(String arg[])
{
new javagame2();
}
}
Here is the updated code I just worked out a little bit, it does run but I'm a bit confused on how to go about removing the yellow circle once I have touched or even what to do, I tried using .intersects but that didn't seem to do anything.
I don't know java, but this seems straightforward.
Two circles collide when the distance between their centers is less than the sum of their radii. (And if the circles are the same size, we needn't worry about whether their "locations" are actually at their centers or not.)
public boolean collide(pizza f, player p)
{
if(Math.pow(f.x1-p.x, 2) + Math.pow(f.y1-p.y, 2) <= Math.pow(f.Width/2 + p.Width/2, 2))
{
System.out.println("they collided");
return true;
}
else
{
return false;
}
}

Categories

Resources