I made Breakout and it works fine but its a bit laggy due to tons of checks being made for hits. Does anyone know how I can redo my hit checking to improve the speed/efficiency? (FYI the didCollide[Direction] methods check if it hit on that side - didCollideTop() checks if it hit the top of the block)
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Canvas;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import static java.lang.Character.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
public class Breakout extends Canvas implements KeyListener, Runnable, Frame
{
private Ball ball;
private Paddle paddle;
private boolean[] keys;
private BufferedImage back;
private ArrayList<Wall> walls = new ArrayList<Wall>();
private ArrayList<Square> squares = new ArrayList<Square>();
private final int SIDEBORDER = 20;
private final int BOTTOMBORDER = 60;
private final int BALL_SPEED = 20;
private final int WALL_SIZE = 20;
public Breakout()
{
ball = new Ball((int) (Frame.WIDTH / 2.1), (int) (Frame.HEIGHT / 1.15), 20, 20, Color.green, BALL_SPEED, -BALL_SPEED); //instantiate a Ball
paddle = new Paddle((int) (Frame.WIDTH / 2.2), Frame.HEIGHT - 80, 100, 25, Color.blue, 20); //instantiate a Paddle
//makes the left walls
for (int i = 20; i < Frame.HEIGHT - BOTTOMBORDER; i += 20)
walls.add(new Wall(0, i, WALL_SIZE, WALL_SIZE));
//makes the top walls
for (int i = 0; i < Frame.WIDTH - SIDEBORDER; i += 20)
walls.add(new Wall(i, 0, WALL_SIZE, WALL_SIZE));
//makes the right walls
for (int i = 20; i < Frame.HEIGHT - BOTTOMBORDER; i += 20)
walls.add(new Wall(Frame.WIDTH - 40, i, WALL_SIZE, WALL_SIZE));
//makes the squares (the ones you hit)
for (int i = 80; i < Frame.HEIGHT / 2; i += 40)
for (int j = 80; j < Frame.WIDTH / 1.1; j += 70)
squares.add(new Square(j, i, 60, 30));
keys = new boolean[2];
setBackground(Color.WHITE);
setVisible(true);
addKeyListener(this);
new Thread(this).start();
}
public void update(Graphics window)
{paint(window);}
public void paint(Graphics window)
{
Graphics2D twoDGraph = (Graphics2D) window;
back = null;
//take a snap shop of the current screen and save it as an image
if (back == null)
back = (BufferedImage)(createImage(getWidth(), getHeight()));
//create a graphics reference to the back ground image
//draw all changes on the background image
Graphics graphToBack = back.createGraphics();
ball.moveAndDraw(graphToBack);
paddle.draw(graphToBack);
for (int i = 0; i < walls.size(); i++) //draw walls
walls.get(i).draw(graphToBack);
for (int i = 0; i < squares.size(); i++) //draw squares
squares.get(i).draw(graphToBack);
//see if the paddle can move
if (keys[0] == true && paddle.getX() > WALL_SIZE + 15)
paddle.moveLeftAndDraw(window); //move paddle left and draw it
if (keys[1] == true && paddle.getX() + paddle.getWidth() < walls.get(walls.size() - 1).getX() - 15)
paddle.moveRightAndDraw(window); //move paddle right and draw it
//see if the ball hits the top walls
if (ball.getY() + ball.getYSpeed() <= WALL_SIZE)
ball.setYSpeed(-ball.getYSpeed());
//see if the ball hits the left walls
if (ball.getX() + ball.getXSpeed() <= WALL_SIZE)
ball.setXSpeed(-ball.getXSpeed());
//see if the ball hits the right walls
if (ball.getX() + ball.getWidth() + ball.getXSpeed() >= walls.get(walls.size() - 1).getX())
ball.setXSpeed(-ball.getXSpeed());
//see if the ball hits the paddle
if (ball.didCollideTop(paddle)) //top of paddle
ball.setYSpeed(-ball.getYSpeed());
else if (ball.didCollideLeft(paddle) || ball.didCollideRight(paddle)) //sides of paddle
ball.setXSpeed(-ball.getXSpeed());
//checks if the ball hits a square
for (int i = squares.size() - 1; i >= 0; i--)
{
if (ball.didCollideLeft(squares.get(i)) || ball.didCollideRight(squares.get(i)))
{
squares.remove(i);
ball.setXSpeed(-ball.getXSpeed());
}
else if (ball.didCollideTop(squares.get(i)) || ball.didCollideBottom(squares.get(i)))
{
squares.remove(i);
ball.setYSpeed(-ball.getYSpeed());
}
}
if (ball.getY() + ball.getHeight() > Frame.HEIGHT) //resets ball if it goes off screen
resetBall();
if (squares.size() == 0)
{
ball.setColor(Color.white);
graphToBack.setColor(randomColor());
graphToBack.setFont(window.getFont().deriveFont(200f));
graphToBack.drawString("You Win!", (int) (Frame.WIDTH / 6.5), (int) (Frame.HEIGHT / 2.5));
}
twoDGraph.drawImage(back, null, 0, 0);
}
/**
* Randomly generates true or false
* Used to randomize ball direction on reset
* #return true or false
*/
public boolean genRandom()
{
if ((int) (Math.random() * 2) == 0)
return true;
else
return false;
}
/**
* Generates a random color
* #return a random color
*/
public Color randomColor()
{
int r = (int) (1 + Math.random() * 255);
int g = (int) (1 + Math.random() * 255);
int b = (int) (1 + Math.random() * 255);
return new Color(r, g, b);
}
/**
* Resets the ball in middle
* Resets the ball's speed
* Chooses a random direction
*/
public void resetBall()
{
ball.setPos((int) (Frame.WIDTH / 2.1), (int) (Frame.HEIGHT / 1.15)); //reset position
ball.setXSpeed(BALL_SPEED); //reset speeds
ball.setYSpeed(-BALL_SPEED);
if (genRandom()) //random X direction
ball.setXSpeed(-BALL_SPEED);
}
public void keyPressed(KeyEvent e)
{
switch(toUpperCase(e.getKeyChar()))
{
case 'D' : keys[0] = true; break; //left
case 'J' : keys[1] = true; break; //right
}
}
public void keyReleased(KeyEvent e)
{
switch(toUpperCase(e.getKeyChar()))
{
case 'D' : keys[0] = false; break;
case 'J' : keys[1] = false; break;
}
}
public void run()
{
try
{
while(true)
{
Thread.currentThread().sleep(8);
repaint();
}
}
catch(Exception e){}
}
public void keyTyped(KeyEvent e) {}
}
Your blocks are arranged in a grid. If you store the blocks in a 2D array (indexed by grid row and column number), you can directly compute the grid coordinates of the ball (divide ball X / block width and ball Y / block height) then check to see if there is a block in that grid cell (or the neighboring cells, noting that the ball could be on a border of up to 4 cells [presuming its diameter is less than the smaller dimension of a cell -- but you can easily adapt if this is not the case]). This would be O(1); you would be directly determining the possible blocks that the ball could be overlapping.
Then you only need to do actual collision detection on up to 4 blocks (the ones in the grid cells that the ball is over) instead of every block -- and at no point would you have to iterate over all blocks.
Your wall cells could be worked into this grid as well.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I'm working on animation of moving balls, which must collide with walls and with each other.
Half the work is done. Balls already can collide with walls.
But I don't know how to make a collision of balls with each other. I tried using the loop throughout the ArrayList collection, but the balls are removed incorrectly.
Maybe you can help me with this task.
MainClass.java
import java.awt.EventQueue;
import javax.swing.Timer;
public class MainClass
{
public static Timer t;
public static void main(String[] args)
{
UI myUI = new UI();
EventQueue.invokeLater(myUI);
t = new Timer(10, myUI);
t.start();
}
}
UI.java.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
public class UI extends JFrame implements Runnable, ActionListener
{
private static final long serialVersionUID = 1L;
#Override
public void run()
{
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(600, 500);
setLocationRelativeTo(null);
setTitle("Bouncing Balls!");
add(new DrawingSurface());
setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e)
{
repaint();
}
}
DrawingSurface.java
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JPanel;
public class DrawingSurface extends JPanel
{
private static final long serialVersionUID = 1L;
static ArrayList<BouncingBall> bList = new ArrayList<BouncingBall>();
private final int numBalls = 2;
Random r = new Random();
int rangeMin = 1, rangeMax = 3;
double randomXSpeed;
double randomYSpeed;
// create a list of balls in the constructor. This way it happens only one time
// rather than EVERY time you re-draw...
public DrawingSurface()
{
for(int i = 0; i < numBalls ; i++)
{
randomXSpeed = rangeMin + (rangeMax - rangeMin) * r.nextDouble();
randomYSpeed = rangeMin + (rangeMax - rangeMin) * r.nextDouble();
// Notice I delegate ALL of the ball functionality to the BouncingBall class.
// I don't want give it anything to create a new ball.
// The only place the balls exist is in the ArrayList.
bList.add(new BouncingBall(r.nextInt(400), r.nextInt(300), randomXSpeed, randomYSpeed));
}
}
public void paintComponent(Graphics gContext)
{
// loop through the array list and tell each ball the size of the window
// and give it the graphics context so it can draw itself.
for(int i = 0; i < bList.size(); i++)
{
bList.get(i).updatePosition(getWidth(), getHeight(), (Graphics2D)gContext);
}
}
}
BouncingBall.java
import java.awt.Color;
import java.awt.Graphics2D;
import javax.swing.JPanel;
// a class that manages the position of randomly colored ball, and draws it.
public class BouncingBall extends JPanel
{
private static final long serialVersionUID = 1L;
public int x,y;
Double xMove;
Double yMove;
public final static int size = 20; //size of the ball
private Color c;
public BouncingBall(int width, int height, Double xSpeed, Double ySpeed)
{
x = width; //starting position
y = height; //starting position
xMove = xSpeed; //starting velocity
yMove = ySpeed; //starting velocity
//pick a random color
c = new Color((int)(Math.random()*255),(int)(Math.random()*255),(int)(Math.random()*255));
}
//pass in the height and width of the current window so we can tell where we should bounce
public void updatePosition(int width, int height, Graphics2D g)
{
//update the position
y += yMove;
x += xMove;
Double xSpeed = 0.0, ySpeed = 0.0;
//if the ball moves to the right edge of the window, turn around.
if(x > width - size)
{
x = width - size;
xMove *= -1;
if (xMove > 0) {
xSpeed = xMove + (Math.random() * (1));
}
if (xMove <= 0) {
xSpeed = xMove - (Math.random() * (1));
}
if (yMove > 0) {
ySpeed = yMove + (Math.random() * (1));
}
if (yMove <= 0) {
ySpeed = yMove - (Math.random() * (1));
}
DrawingSurface.bList.add(new BouncingBall(x, y, xSpeed, ySpeed));
c = new Color((int)(Math.random()*255),(int)(Math.random()*255),(int)(Math.random()*255));
}
//if the ball moves to the left edge of the window, turn around.
if(x < 1)
{
x = 1;
xMove *= -1;
if (xMove > 0) {
xSpeed = xMove + (Math.random() * (1));
}
if (xMove <= 0) {
xSpeed = xMove - (Math.random() * (1));
}
if (yMove > 0) {
ySpeed = yMove + (Math.random() * (1));
}
if (yMove <= 0) {
ySpeed = yMove - (Math.random() * (1));
}
DrawingSurface.bList.add(new BouncingBall(x, y, xSpeed, ySpeed));
c = new Color((int)(Math.random()*255),(int)(Math.random()*255),(int)(Math.random()*255));
}
//if the ball moves to the bottom of the screen, turn around.
if(y > height - size )
{
y = height - size;
yMove *= -1;
if (xMove > 0) {
xSpeed = xMove + (Math.random() * (1));
}
if (xMove <= 0) {
xSpeed = xMove - (Math.random() * (1));
}
if (yMove > 0) {
ySpeed = yMove + (Math.random() * (1));
}
if (yMove <= 0) {
ySpeed = yMove - (Math.random() * (1));
}
DrawingSurface.bList.add(new BouncingBall(x, y, xSpeed, ySpeed));
c = new Color((int)(Math.random()*255),(int)(Math.random()*255),(int)(Math.random()*255));
}
//if the ball moves to the top of the screen, turn around.
if(y < 1)
{
y = 1;
yMove *= -1;
if (xMove > 0) {
xSpeed = xMove + (Math.random() * (1));
}
if (xMove <= 0) {
xSpeed = xMove - (Math.random() * (1));
}
if (yMove > 0) {
ySpeed = yMove + (Math.random() * (1));
}
if (yMove <= 0) {
ySpeed = yMove - (Math.random() * (1));
}
DrawingSurface.bList.add(new BouncingBall(x, y, xSpeed, ySpeed));
c = new Color((int)(Math.random()*255),(int)(Math.random()*255),(int)(Math.random()*255));
}
g.setColor(c);
g.fillOval(x, y, size, size);
}
}
To detect the collision of each ball with one another, a simple way will be looping through the collection of balls with a pair of nested loops. So each ball will be checked against all other balls for collision:
//From your list of balls
ArrayList<BouncingBall> bList = new ArrayList<BouncingBall>();
for(BouncingBall b1 : bList)
for(BouncingBall b2 : bList)
if(b1.intersects(b2)){
//do whatever (such as bouncing off) when the balls collide
b1.flipDirection();
b2.flipDirection();
}
In order to use intersects() method, your BouncingBall class can extends to Rectangle class from Java. Alternatively, if you can't let BouncingBall class be extended to another class.
You can let them return the bounds:
class BouncingBall{
public Rectangle getBounds(){
return new Rectangle(x, y, width ,height);
}
}
Then you will still be able to make use of intersects() method:
//to check for collision using intersects() method
for(BouncingBall b1 : bList)
for(BouncingBall b2 : bList)
if(b1.getBounds().intersects(b2.getBounds())){
//do whatever (such as bouncing off) when the balls collide
b1.flipDirection();
b2.flipDirection();
}
Using nested loop to detect collision for a simple ball animation like this is sufficient. Unless you are making a game where every frame you need to check the collision for thousands or more entities, then you will need a different collision detection algorithm such as Quad-tree.
I have coded such program using the same algorithm before, and it works perfectly well:
I'm finishing up a coding assignment that requires me to set an array of blocks in motion, bouncing off of the window and each other, but unfortunately I'm totally lost as to where to go next. Any help would be appreciated (still getting the hang of coding, so I'm looking for all the help possible). Specifically, I need the rectangles to appear first, and then troubleshoot movement, and finally help with collision detection.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Driver implements ActionListener
{
private JFrame window;
private Timer timer;
private ChaseBlock[] blocks = new ChaseBlock[15];
// constants for graphics
private final int windowSize = 500;
private final int blockSize = 20;
/**
* Simple initiating main().
*
* #param args Not used.
*/
public static void main( String[] args )
{
Driver d = new Driver();
d.createWindow();
}
/**
* Set up the basic graphical objects.
*/
private void createWindow()
{
// create the window
window = new JFrame( "The Great Chase" );
window.setVisible( true );
window.setLayout( null );
window.getContentPane().setBackground( Color.white );
window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
window.setLocation( 50, 50 );
window.setSize(
windowSize + window.getInsets().left + window.getInsets().right,
windowSize + window.getInsets().top + window.getInsets().bottom );
window.setResizable( false );
window.repaint();
timer = new Timer(10, this);
timer.start();
blocks[1].setBackground(Color.blue);
window.repaint();
addBlocks();
}
private void addBlocks() {
for (int i = 0; i < blocks.length; i++) {
int x = i * blockSize + 10 * (i +1);
blocks[i] = new ChaseBlock(x, windowSize / x - blockSize / 2, blockSize, windowSize);
}
for (int i = 0; i < blocks.length; i++) {
window.add(blocks[i]);
}
}
private void animate() {
for (int i = 0; i < blocks.length; i++) {
blocks[i].move();
for (int b1 = 0; b1 < blocks.length; b1++)
for (int b2 = 0; b2 < blocks.length; b2++)
if (b1 != b2)
blocks[b1].checkCollision(blocks[b2]);
}
}
public void actionPerformed (ActionEvent e) {
animate();
}
}
This is the driver class that we use, with a Rectangle class also. Near to the bottom, the goal is to add the rectangles and make them move. My problem here is that the rectangles do not show up whatsoever or move.
import java.awt.Color;
public class ChaseBlock extends Rectangle {
private int dX, dY;
private int windowWidth = 500;
private int windowHeight = 500;
public ChaseBlock(int x, int y, int w, int h) {
super(x, y, w, h );
if (Math.random() < 0.5) {
dX = -1;
dY = -1;
setBackground(Color.green);
} else
dX = 1;
dY = 1;
setBackground(Color.blue);
}
public void move() {
setLocation(getX(), getY() + 5);
if(getX() < 0 || getY() + getWidth() >= windowWidth) {
dX = dX * -1;
}
if (getY() < 0 || getY() + getHeight() >= windowHeight) {
dY = dY * -1;
}
}
public void checkCollision(ChaseBlock blocks) {
boolean up = false;
boolean down = false;
boolean left = false;
boolean right = false;
boolean hit = false;
}
}
This is my class to define the movement and everything else. My problems here are that I need to use the checkCollision method to manage the collisions between the blocks themselves and the window, and in addition set colors for all the blocks.
First of all, the first condition for collision checks with window edges is incorrect. It should be
if(getX() < 0 || getX() + getWidth() >= windowWidth)
To detect collisions between two Axis Aligned Bounding Boxes (which is what you have as Rectangles), you just have to check whether the first's min and max points (x,y and x+h, y+h) are inside the second as follows:
if(a.x + a.h < b.x or a.x > b.x + b.h) return false;
if(a.y + a.h < b.y or a.y > b.y + b.h) return false;
return true;
If you want to find out which face (or which direction) the collision takes place on, you'll have to use the more long and slightly more complicated Separating Axis Theorem.
I have a program to move a square about using the keyboard. It moves around fine when I press one of the arrow keys, but if I move it to the edge of the frame, I want it to stop. It does not do this however. If I move the square to the edge of the frame it keeps going and moves right past the edge of the screen.
I have put controls into my code to try stopping the square, but they don't seem to be working. Not really sure what is going wrong here.
This is the code to set up and draw the square:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
#SuppressWarnings("serial")
public class MovingSquare extends JPanel implements ActionListener, KeyListener
{
// We need a timer to move the shape
Timer shapeTimer = new Timer(5, this);
// X and Y coordinates of square (top left corner), and the factors by which it will move or resize each time
double xPos = 0, yPos = 0, movementX = 0, movementY = 0;
// Size of the square
int squareSize = 40;
// Width and height of the parent frame
int windowWidth;
int windowHeight;
// Movement bounds of the square
// These will prevent it from being moved off the edge of the frame
int xBound;
int yBound;
// Constructor method for our class
public MovingSquare(int w, int h) // Constructor is passed the size of the parent frame
{
// Start the timer
shapeTimer.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
windowWidth = w;
windowHeight = h;
xBound = (windowWidth - squareSize);
yBound = (windowHeight - squareSize);
}
// This is where the fun starts! Painting the graphics object
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// Create a new rectangle (which is actually a square!)
Rectangle2D movableSquare = new Rectangle2D.Double(xPos, yPos, squareSize, squareSize);
// Draw the above square on the graphics object
g2.draw(movableSquare);
}
public void actionPerformed(ActionEvent e)
{
// Redraw the square when something happens
repaint();
// Set the new x and y coordinates, depending on which direction we have moved
xPos += movementX;
yPos += movementY;
}
public void moveUp()
{
// Check to see if the shape is already at the top edge of the screen
if (yPos == 0)
{
movementY = 0;
movementX = 0;
}
// Set the movement factor - negative Y because we are moving UP!
movementY = -0.5;
movementX = 0;
}
public void moveDown()
{
// Check to see if the shape is already at the bottom edge of the screen - specified by the X and Y bounds
if (yPos == yBound)
{
movementY = 0;
movementX = 0;
}
// Set the movement factor - positive Y because we are moving DOWN!
movementY = 0.5;
movementX = 0;
}
public void moveLeft()
{
// Check to see if the shape is already at the left hand edge of the screen
if (xPos == 0)
{
movementY = 0;
movementX = 0;
}
// Set the movement factor - negative X because we are moving LEFT!
movementX = -0.5;
movementY = 0;
}
public void moveRight()
{
// Check to see if the shape is already at the right hand edge of the screen - specified by the X and Y bounds
if (xPos == xBound)
{
movementY = 0;
movementX = 0;
}
// Set the movement factor - positive X because we are moving RIGHT!
movementX = 0.5;
movementY = 0;
}
public void enlargeSquare()
{
// Make the square larger
squareSize++;
}
public void shrinkSquare()
{
// Make the square smaller
squareSize--;
}
public void keyPressed(KeyEvent e)
{
// Get the Key Code of the key that has been pressed
int keyCode = e.getKeyCode();
// If the up key has been pressed
if (keyCode == KeyEvent.VK_UP)
{
// Move shape up
moveUp();
}
// If the down key has been pressed
if (keyCode == KeyEvent.VK_DOWN)
{
// Move shape down
moveDown();
}
// If the right key is pressed
if (keyCode == KeyEvent.VK_RIGHT)
{
// Move shape right
moveRight();
}
// If the left key is pressed
if (keyCode == KeyEvent.VK_LEFT)
{
// Move shape left
moveLeft();
}
// If the left brace key is pressed
if (keyCode == KeyEvent.VK_OPEN_BRACKET)
{
shrinkSquare();
}
// If the right brace key is pressed
if (keyCode == KeyEvent.VK_CLOSE_BRACKET)
{
enlargeSquare();
}
}
public void keyTyped(KeyEvent e)
{
}
public void keyReleased(KeyEvent e)
{
// Get the Key Code of the key that has been released
int keyCode = e.getKeyCode();
// If the down key was released
if (keyCode == KeyEvent.VK_UP)
{
movementX = 0;
movementY = 0;
}
// If the down key was released
if (keyCode == KeyEvent.VK_DOWN)
{
movementX = 0;
movementY = 0;
}
// If the right key was released
if (keyCode == KeyEvent.VK_RIGHT)
{
movementX = 0;
movementY = 0;
}
// If the left key was released
if (keyCode == KeyEvent.VK_UP)
{
movementX = 0;
movementY = 0;
}
}
}
And this is the main class:
import javax.swing.JFrame;
public class Square
{
public static void main(String args[])
{
// Set width and height of frame
int frameWidth = 1024;
int frameHeight = 768;
// Create new frame and set size
JFrame frmMain = new JFrame();
frmMain.setSize(frameWidth, frameHeight);
// Create a moving square and add to the frame
MovingSquare mySquare = new MovingSquare(frameWidth, frameHeight);
frmMain.add(mySquare);
// Final configuration settings for frame.
frmMain.setVisible(true);
frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frmMain.setTitle("Moving Square");
}
}
You actionPerformed method is updating the position of your square, well beyond what your moveXxx methods are, you should do your range checking there
#Override
public void actionPerformed(ActionEvent e)
{
// Set the new x and y coordinates, depending on which direction we have moved
xPos += movementX;
yPos += movementY;
if (xPos < 0) {
xPos = 0;
} else if (xPos + squareSize > xBound) {
xPos = xBound - squareSize;
}
if (yPos < 0) {
yPos = 0;
} else if (yPos + squareSize > yBound) {
yPos = yBound - squareSize;
}
// Redraw the square when something happens
repaint();
}
If you know the width and height of ths screen and the rect, you can easiely check if it's inside:
int fX0 = 0, // left x border
fX1 = frameWidth, // right x border
fY0 = 0, // top y border
fY1 = frameHeight; // bottom y border
int rX0, rX1, rY0, rY1; // keep these values updated width the rectangles position:
//rX0 = rectangle position x
//rX1 = rectangle position x + rectangle width
//rY0 = rectangle position y
//rY1 = rectangle position y + rectangle height
// Then, to check if the rect is inside the frame:
if (
rX0 >= fX0 &&
rX1 < fX1 &&
rY0 >= fY0 &&
rY1 < fY1
) { /* the rectangle is inside the frame bounds */ }
else { /* it's not inside the bounds, e.g. cancel movement */ }
The second method of doing this is by using the Rectangle class (from awt I think):
Rectangle f = new Rectangle(0, 0, frameWidth, frameHeight); // bounds of the frame
Rectangle r = new Rectangle(...); // Your square bounds
// To check if r is inside f
if (
f.contains(r)
) { /* the rectangle is inside the frame bounds */ }
else { /* it's not inside the bounds, e.g. cancel movement */ }
You can make an extra method:
public boolean Check(){
if(xPos >0 && xPos-squareSize<frameWidth && yPos>0 && yPos-squareSize<frameHeight){
//use a flag for example and make it false
}else
//flag==true
return flag;
}
Into KeyEvent method(if flag==true) then do the movement else do nothing or reset the xpos and ypos coordinates back to default or to a specific position of the JFrame.
Also it is easy to add an If(into KeyEvent methods)statement to check the coordinates and if they are accepted then do the movement.
For example:
public void KeyPressed(KeyEvent key){
if(Check()==true){
..........//the code you have into KeyPressed method
}
}//end of method KeyPressed
So I'm trying to get this square to bounce off of the wall. I'm fairly new to coding, but I can't understand why this is happening. It seems to be bouncing very badly, as in it completely reverses the direction in which it hits, so it does not bounce logically.
The most frustrating problem though, is that it only bounces once. It bounces once off of the side and then when it encounters a second wall, it just goes off into the abyss.
Here is a snippet of code used to write it:
void moveTheBox() {
while (inside == true) {
if ((bigBoxX <= 0) || (bigBoxY <= 0) ||
(bigBoxX >= 600 - bigBoxSize) ||
(bigBoxY >= 600 - bigBoxSize)) {
bigBoxDeltaX = bigBoxDeltaX * -1;
bigBoxDeltaY = bigBoxDeltaY * -1;
while ((bigBoxX >= 0) || (bigBoxY >= 0) ||
(bigBoxX <= 600 - bigBoxSize) ||
(bigBoxY <= 600 - bigBoxSize)) {
bigBoxX = bigBoxX + bigBoxDeltaX;
bigBoxY = bigBoxY + bigBoxDeltaY;
repaint();
pause();
}
} else {
bigBoxX = bigBoxX + bigBoxDeltaX;
bigBoxY = bigBoxY + bigBoxDeltaY;
repaint();
pause();
}
}
}
Edit: I figured out 4 minutes after you posted that. I fixed the awkward bouncing and the 1 bounce issue.
Here is the final product:
void moveTheBox() {
int i = 0;
while(i == 0){
if ((bigBoxX <= 0) || (bigBoxX >= 600-bigBoxSize)){
bigBoxDeltaX = bigBoxDeltaX * -1;
while((bigBoxX >= 0) || (bigBoxY >=0) || (bigBoxX <= 600-bigBoxSize) || (bigBoxY <= 600 - bigBoxSize)){
bigBoxX = bigBoxX + bigBoxDeltaX;
bigBoxY = bigBoxY + bigBoxDeltaY;
repaint();
pause();
break;
}
}else if ((bigBoxY <= 0) || (bigBoxY >= 600-bigBoxSize)){
bigBoxDeltaY = bigBoxDeltaY * -1;
while((bigBoxX >= 0) || (bigBoxY >=0) || (bigBoxX <= 600-bigBoxSize) || (bigBoxY <= 600 - bigBoxSize)){
bigBoxX = bigBoxX + bigBoxDeltaX;
bigBoxY = bigBoxY + bigBoxDeltaY;
repaint();
pause();
break;
}
}else{
bigBoxX = bigBoxX + bigBoxDeltaX;
bigBoxY = bigBoxY + bigBoxDeltaY;
repaint();
pause();
}
}
}
import java.awt.*;
import java.util.Formatter;
import javax.swing.*;
/**
* One ball bouncing inside a rectangular box.
* All codes in one file. Poor design!
*/
// Extends JPanel, so as to override the paintComponent() for custom rendering codes.
public class BouncingBallSimple extends JPanel {
// Container box's width and height
private static final int BOX_WIDTH = 640;
private static final int BOX_HEIGHT = 480;
// Ball's properties
private float ballRadius = 200; // Ball's radius
private float ballX = ballRadius + 50; // Ball's center (x, y)
private float ballY = ballRadius + 20;
private float ballSpeedX = 3; // Ball's speed for x and y
private float ballSpeedY = 2;
private static final int UPDATE_RATE = 30; // Number of refresh per second
/** Constructor to create the UI components and init game objects. */
public BouncingBallSimple() {
this.setPreferredSize(new Dimension(BOX_WIDTH, BOX_HEIGHT));
// Start the ball bouncing (in its own thread)
Thread gameThread = new Thread() {
public void run() {
while (true) { // Execute one update step
// Calculate the ball's new position
ballX += ballSpeedX;
ballY += ballSpeedY;
// Check if the ball moves over the bounds
// If so, adjust the position and speed.
if (ballX - ballRadius < 0) {
ballSpeedX = -ballSpeedX; // Reflect along normal
ballX = ballRadius; // Re-position the ball at the edge
} else if (ballX + ballRadius > BOX_WIDTH) {
ballSpeedX = -ballSpeedX;
ballX = BOX_WIDTH - ballRadius;
}
// May cross both x and y bounds
if (ballY - ballRadius < 0) {
ballSpeedY = -ballSpeedY;
ballY = ballRadius;
} else if (ballY + ballRadius > BOX_HEIGHT) {
ballSpeedY = -ballSpeedY;
ballY = BOX_HEIGHT - ballRadius;
}
// Refresh the display
repaint(); // Callback paintComponent()
// Delay for timing control and give other threads a chance
try {
Thread.sleep(1000 / UPDATE_RATE); // milliseconds
} catch (InterruptedException ex) { }
}
}
};
gameThread.start(); // Callback run()
}
/** Custom rendering codes for drawing the JPanel */
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g); // Paint background
// Draw the box
g.setColor(Color.BLACK);
g.fillRect(0, 0, BOX_WIDTH, BOX_HEIGHT);
// Draw the ball
g.setColor(Color.BLUE);
g.fillOval((int) (ballX - ballRadius), (int) (ballY - ballRadius),
(int)(2 * ballRadius), (int)(2 * ballRadius));
// Display the ball's information
g.setColor(Color.WHITE);
g.setFont(new Font("Courier New", Font.PLAIN, 12));
StringBuilder sb = new StringBuilder();
Formatter formatter = new Formatter(sb);
formatter.format("Ball #(%3.0f,%3.0f) Speed=(%2.0f,%2.0f)", ballX, ballY,
ballSpeedX, ballSpeedY);
g.drawString(sb.toString(), 20, 30);
}
/** main program (entry point) */
public static void main(String[] args) {
// Run GUI in the Event Dispatcher Thread (EDT) instead of main thread.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Set up main window (using Swing's Jframe)
JFrame frame = new JFrame("A Bouncing Ball");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new BouncingBallSimple());
frame.pack();
frame.setVisible(true);
}
});
}
}
Refer the tutorial
I think this is weird because when I ran it before on Eclipse, this program worked. However, when I try to run it using 'java PacMan', then nothing shows up. I think it is because I am missing a main method, but I'm not sure how to integrate that into my program without screwing everything up.
/*
PacMan.java
This is a lame replica of the game PacMan. The PacMan
starts out on a random location on the board with
6 ghosts & cheese. It must try to eat the cheese without
getting eaten by the ghosts. The ghosts only move around
when PacMan does. All characters on the board can only move
around when PacMan does and can only move up, down, left,
and right; diagonal movement isn't allowed. When PacMan eats
all the cheese, the words 'YOU WIN! :)' will be displayed across
the board; if you lose, 'YOU LOST! :(' will be shown.
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PacMan extends JApplet implements KeyListener
{
private int chez, ghosts;
private int currentx, currenty, direction, a;
private int [][] grid;
private int [] alienx, alieny;
private DrawingArea canvas;
private boolean lost;
public void init()
{
setSize(500, 560);
generateBoard();
canvas = new DrawingArea(); // drawing area
getContentPane().add(canvas, BorderLayout.CENTER);
JPanel buttonPanel = new ButtonPanel();
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
canvas.setBackground(Color.black);
canvas.addKeyListener(this);
canvas.requestFocus();
}
public void generateBoard()
{
lost = false;
chez = ghosts = currentx = currenty = 0;
grid = new int[10][10]; // sets 2D array size
alienx = new int[6];
alieny = new int[6];
do
{
currentx = (int)(Math.random() * 10);
currenty = (int)(Math.random() * 10);
if(grid[currentx][currenty] == 0 && chez < 6)
{
grid[currentx][currenty] = 1; // placement holder because 0 won't be read in
chez++; // '1' is the cheese
}
if(grid[currentx][currenty] == 0 && ghosts < 6)
{
grid[currentx][currenty] = 2; // placement holder because 0 won't be read in
alienx[ghosts] = currentx;
alieny[ghosts] = currenty;
ghosts++; // '2' is the ghosts
}
}
while(chez < 6 || ghosts < 6);
do // this makes sure PacMan never respawns on a ghost or cheese
{
currentx = (int)(Math.random() * 10);
currenty = (int)(Math.random() * 10);
}
while(grid[currentx][currenty] != 0);
grid[currentx][currenty] = 3; // this is PacMan
direction = 0;
}
public void moveGhosts(int a)
{
int oldx = alienx[a], oldy = alieny[a]; // coordinates of the aliens
grid[oldx][oldy] = 0; // erases original spacing for monster/alien
int stuff; // used for random statement
boolean done = true;
stuff = (int)(Math.random() * 8 + 1); // amount of random moves XD
do
{
switch(stuff)
{
case 1: alienx[a]++; break; // down
case 2: alienx[a]--; break; // up
case 3: alieny[a]++; break; // right
case 4: alieny[a]--; break; // left
case 5: alienx[a]++; alieny[a]--; break; // down, left
case 6: alienx[a]++; alieny[a]++; break; // down, right
case 7: alienx[a]--; alieny[a]--; break; // up, left
case 8: alienx[a]--; alieny[a]++; break; // up, right
}
if(alieny[a] == 10)
alieny[a] = 0;
if(alieny[a] == -1)
alieny[a] = 9;
if(alienx[a] == 10)
alienx[a] = 0;
if(alienx[a] == -1)
alienx[a] = 9;
if(grid[alienx[a]][alieny[a]] == 0 || grid[currentx][currenty] == 3)
done = true; // only runs when ghost is on empty space or eats PacMan
else
done = false; // makes sure ghost never eats cheese
for(int b = 0; b < 6; b++)
if(alienx[b] == alienx[a] && alieny[b] == alieny[a] && b != a)
done = false; // prevents ghosts from overlaping each other
if (!done)
{
oldx = alienx[a];
oldy = alieny[a];
}
} while (!done);
grid[alienx[a]][alieny[a]] = 2;
}
class DrawingArea extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
displayBoard(g);
this.requestFocus();
}
public void displayBoard(Graphics g)
{
g.setColor(Color.gray);
g.fillRect(116, 464, 270, 50); // gray background for board
g.fillRect(30, 10, 445, 445); // gray background for 'cheese remaining'
g.setColor(Color.yellow);
g.drawRect(116, 464, 270, 50); // outlines 'cheese remaining' box
if(grid[currentx][currenty] == 1) // makes cheese disappear when PacMan eats it
{
grid[currentx][currenty] = 0;
chez--;
}
if(grid[currentx][currenty] == 2) // makes PacMan disappear when he hits a ghost
lost = true;
Font progress = new Font("Sans Serif", Font.PLAIN, 25);
g.setFont(progress);
if(!lost && chez > 0)
g.drawString("Cheese Remaining:"+ chez, 130, 500); // shows # of cheese left
else if(lost == true)
g.drawString("YOU LOST! :(", 175, 500);
else if(chez == 0)
g.drawString("YOU WON! :)", 175, 500);
g.setColor(Color.white);
for(int row = 0; row < grid.length; row++)
for(int col = 0; col < grid[row].length; col++)
{
g.setColor(Color.white);
g.fillRect(30 + col * 45, 10 + row * 45, 40, 40); // prints grid
g.setColor(Color.blue); // primitive PacMan
if(!lost)
g.fillArc(30 + currentx * 45, 10 + currenty * 45, 40, 40, direction + 30, 300);
if(grid[col][row] == 1) // prints out cheese randomly if array encounters invisible '1'
{
// while scanning
g.setColor(Color.yellow);
g.fillRect(33 + col * 45, 13 + row * 45, 33, 33); // blocks of cheese
}
if(grid[col][row] == 2) // prints out ghosts randomly if array encounters invisible '2'
{
// while scanning
g.setColor(Color.red);
g.fillOval(30 + col * 45, 10 + row * 45, 40, 40); // ghosts' body
g.setColor(Color.black);
g.fillOval(38 + col * 45, 20 + row * 45, 8, 8); // left eye
g.fillOval(53 + col * 45, 20 + row * 45, 8, 8); // right eye
g.drawLine(38 + col * 45, 37 + row * 45, 61 + col * 45, 37 + row * 45); // mouth
}
}
}
}
public void keyPressed(KeyEvent evt)
{
int value = evt.getKeyCode();
if(!lost && chez > 0)
switch(value)
{
case KeyEvent.VK_LEFT:
{
currentx--; // PacMan moves left
if(currentx == -1) // makes sure the PacMan can 'loop' around through 'portals'
currentx = 9;
direction = 180; //faces left
for(int a = 0; a < 6; a++)
moveGhosts(a);
} break;
case KeyEvent.VK_RIGHT:
{
currentx++; // PacMan moves right
if(currentx == 10) // makes sure the PacMan can 'loop' around through 'portals'
currentx = 0;
direction = 0; //faces right
for(int a = 0; a < 6; a++)
moveGhosts(a);
} break;
case KeyEvent.VK_UP:
{
currenty--; // PacMan moves up
if(currenty == -1) // makes sure the PacMan can 'loop' around through 'portals'
currenty = 9;
direction = 90; //faces up
for(int a = 0; a < 6; a++)
moveGhosts(a);
} break;
case KeyEvent.VK_DOWN:
{
currenty++; // PacMan moves down
if(currenty == 10) // makes sure the PacMan can 'loop' around through 'portals'
currenty = 0;
direction = 270; //faces down
for(int a = 0; a < 6; a++)
moveGhosts(a);
} break;
}
if(value == KeyEvent.VK_SPACE)
generateBoard(); // call on method to regenerate board
canvas.repaint(); // repaints the board
}
class ButtonPanel extends JPanel implements ActionListener
{
private JButton left, right, up, down, reset;
public ButtonPanel() // displays available buttons
{
left = new JButton("LEFT");
left.addActionListener(this);
this.add(left);
right = new JButton("RIGHT");
right.addActionListener(this);
this.add(right);
up = new JButton("UP");
up.addActionListener(this);
this.add(up);
down = new JButton("DOWN");
down.addActionListener(this);
this.add(down);
reset = new JButton("RESET");
reset.addActionListener(this);
this.add(reset);
}
public void actionPerformed(ActionEvent evt)
{
String command = evt.getActionCommand();
if(!lost && chez > 0)
{
if(command.equals("LEFT"))
{
currentx--; // PacMan moves left
if(currentx == -1) // makes sure the PacMan can 'loop' around through 'portals'
currentx = 9;
direction = 180; //faces left
for(int a = 0; a < 6; a++)
moveGhosts(a);
}
else if(command.equals("RIGHT"))
{
currentx++; // PacMan moves right
if(currentx == 10) // makes sure the PacMan can 'loop' around through 'portals'
currentx = 0;
direction = 0; //faces right
for(int a = 0; a < 6; a++)
moveGhosts(a);
}
else if(command.equals("UP"))
{
currenty--; // PacMan moves up
if(currenty == -1) // makes sure the PacMan can 'loop' around through 'portals'
currenty = 9;
direction = 90; //faces up
for(int a = 0; a < 6; a++)
moveGhosts(a);
}
else if(command.equals("DOWN"))
{
currenty++; // PacMan moves down
if(currenty == 10) // makes sure the PacMan can 'loop' around through 'portals'
currenty = 0;
direction = 270; //faces down
for(int a = 0; a < 6; a++)
moveGhosts(a);
}
}
if(command.equals("RESET"))
generateBoard(); // call on method to regenerate board
canvas.repaint(); // repaints the board
}
}
public void keyTyped(KeyEvent evt) {}
public void keyReleased(KeyEvent evt) {}
public void actionPerformed(ActionEvent evt) {}
}
Like they said in the comments because it is an applet you can not treat it as a regular java application. However, you can embed it into a browser or you can use the java tool appletviewer to run it without a browser. In your command line type in the following:
appletviewer [ options ] myApplet.html
Just put a myApplet.html in the folder:
<Html>
<Head>
<Title>Pac Man Applet</Title>
</Head>
<Body>
<Applet Code="MyApplet.class" width=200 Height=100>
</Applet>
</Body>
</Html>
The additional option is to create a stub application that will allow you to run an applet within a java application: Here is link to example. The example code is at bottom.