Collision of balls with each other [closed] - java

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:

Related

How do I create a restart button that shows in the button left corner while the game is running?

Our restart button is only appearing by itself. Our two tanks disappear when we run the game. We haven't coded the function of the button, we're just trying to figure out how to make the button appear in the lower left corner
We moved the code into the game object class, but it does not change anything
This is our game object class. The button is at the bottom.
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameObject {
// Every moving object in our game is a separate GameObject. However, since there are different types of moving objects (eg. players and
// projectiles), each with their own variables and functions, we don't want to instantiate GameObjects (otherwise, we wouldn't be able
// to distinguish between a player and a projectile). Instead, we want to create derived (also known as child) classes to implement the
// separate functionality, and instantiate the derived/child classes instead. The purpose of this GameObject "parent" class is to
// provide the derived/child classes with a set of useful functions for handling physics interactions (eg. collisions with the terrain
// or the walls of the arena) between different GameObjects (eg. player/player, player/projectile, or even projectile/projectile). Note:
// depending on the features you implement, you may not end up using all of the functions provided in this class.
// Every GameObject contains its own velocity, position, size, and mass information (necessary for moving and handling physics
// interactions). We store this information in the GameObject class instead of the derived/child classes because every type of
// GameObject has the same set of velocity, position, size, and mass variables.
public double vX; // Velocity in the x direction. Positive if moving right. Negative if moving left.
public double vY; // Velocity in the y direction. Positive if moving down. Negative if moving up. Notice how it's flipped from the usual
// coordinate system in math!
public double posX; // Position along the x direction. Ranges from 0 (left edge) to maximumX (see below).
public double posY; // Position along the y direction. Ranges from 0 (top edge) to maximumY (see below).
public int width; // Width of the bounding box of the GameObject.
public int height; // Height of the bounding box of the GameObject.
public int mass; // Used in realistic physics collision calculations. Ignore it if you don't want to implement that feature.
public double radius; // Used for circular GameObjects only.
public int maximumX; // Maximum x position for a GameObject, equal to the arena width subtracted by the game object's width.
public int maximumY; // Maximum y position for a GameObject, equal to the arena height subtracted by the game object's height.
// Constructor. All derived (ie. child) classes call this constructor in their own constructors. The resulting derived/child class
// object can then call the other functions in this class.
public GameObject(int arenaWidth, int arenaHeight, double vX, double vY, double posX, double posY, int width, int height, int mass) {
this.vX = vX;
this.vY = vY;
this.posX = posX;
this.posY = posY;
this.width = width;
this.height = height;
this.mass = mass;
this.maximumX = arenaWidth - width;
this.maximumY = arenaHeight - height;
radius = Math.min(width, height) / 2.0;
}
// Note: No need to change this function since we're going to override it in the the child classes.
public boolean move(Map map, double translateX, double translateY) {
return false;
}
// Check if the calling GameObject currently intersects with the obj GameObject.
public boolean currentlyIntersects(GameObject obj) {
return (posX + width >= obj.posX && posY + height >= obj.posY && obj.posX + obj.width >= posX && obj.posY + obj.height >= posY);
}
// Check if the calling GameObject will intersect with the obj GameObject, after both have moved according to their velocities. A note
// of caution: what might go wrong if either player moves too fast?
public boolean willIntersect(GameObject obj) {
double nextX = posX + vX;
double nextY = posY + vY;
double nextObjX = obj.posX + obj.vX;
double nextObjY = obj.posY + obj.vY;
return (nextX + width >= nextObjX && nextY + height >= nextObjY && nextObjX + obj.width >= nextX && nextObjY + obj.height >= nextY);
}
// Clip the calling GameObject to within the arena's x bounds, if it has moved outside the arena along the x direction.
public boolean xClip() {
if (posX < 0) {
posX = 0;
return true;
} else if (posX > maximumX) {
posX = maximumX;
return true;
}
return false;
}
// Clip the calling GameObject to within the arena's y bounds, if it has moved outside the arena along the y direction.
public boolean yClip() {
if (posY < 0) {
posY = 0;
return true;
} else if (posY > maximumY) {
posY = maximumY;
return true;
}
return false;
}
// If the calling GameObject will move outside the arena along either direction (after moving according to its velocity), this function
// tells you which of the four edges of the arena it hit. If the calling GameObject will stay within the bounds of the arena, this
// function returns null.
public Direction hitEdgeDirection() {
if (posX + vX < 0) {
return Direction.LEFT;
} else if (posX + vX > maximumX) {
return Direction.RIGHT;
}
if (posY + vY < 0) {
return Direction.UP;
} else if (posY + vY > maximumY) {
return Direction.DOWN;
} else {
return null;
}
}
// If the calling GameObject will intersect with the "other" GameObject (after both move according to their velocities), this function
// tells you which of the four sides of the calling GameObject that the "other" GameObject hit. If the calling GameObject will not
// intersect with the "other" GameObject, this function returns null. Note: this function is great for figuring out when and where two
// rectangles intersect, but is it a good choice for handling circle/rectangle or circle/circle intersections?
public Direction hitObjectDirection(GameObject other) {
if (this.willIntersect(other)) {
double dx = other.posX + other.width / 2.0 + other.vX - (posX + width / 2.0 + vX);
double dy = other.posY + other.height / 2.0 + other.vY - (posY + height / 2.0 + vY);
double theta = Math.acos(dx / (Math.sqrt(dx * dx + dy * dy)));
double diagTheta = Math.atan2(height / 2.0, width / 2.0);
if (theta <= diagTheta) {
return Direction.RIGHT;
} else if (theta <= Math.PI - diagTheta) {
if (dy > 0) {
return Direction.DOWN;
} else {
return Direction.UP;
}
} else {
return Direction.LEFT;
}
} else {
return null;
}
}
// Change the calling GameObject's velocity (to simulate a "bouncing" effect) based on which direction it intersected another GameObject
// or the edge of the arena. If the passed in direction is null, this function does nothing (why is this a good idea?). This function is
// best used with the hitEdgeDirection and hitObjectDirection functions above.
public void bounce(Direction d) {
if (d == null) {
return;
}
// Note: We probably should use a "switch" statement here instead. But for pedagogical purposes it's left as a simple if/else
// conditional.
if (d == Direction.UP) {
vY = Math.abs(vY);
} else if (d == Direction.DOWN) {
vY = -Math.abs(vY);
} else if (d == Direction.LEFT) {
vX = Math.abs(vX);
} else if (d == Direction.RIGHT) {
vX = -Math.abs(vX);
}
}
// TODO: (Challenge!) If you want to implement realistic sphere-sphere collisions that take into account the laws of physics, do so in
// the function below.
public boolean bounceWith(GameObject otherObj, Map map, long frames, double[] actualVelocities) {
return false;
}
// Calculate the distance from (pointX, pointY)---perhaps representing the center of a circle---to the closest point on a rectangle
// bounded by minX (left), maxX (right), minY (top), and maxY (bottom). If the point is inside the rectangle, this function returns 0.
public double pointToRectSqrDist(double minX, double maxX, double minY, double maxY, double pointX, double pointY) {
double dx = Math.max(Math.max(minX - pointX, 0), pointX - maxX);
double dy = Math.max(Math.max(minY - pointY, 0), pointY - maxY);
return dx * dx + dy * dy;
}
// Rotate the point (x, y) "degrees" degrees around (centerX, centerY) in counterclockwise fashion, and return the resulting point in an
// array of length 2. If the returned array is "result", then (result[0], result[1]) is the final point.
public double[] rotatePoint(double centerX, double centerY, double degrees, double x, double y) {
double s = Math.sin(Math.toRadians(degrees));
double c = Math.cos(Math.toRadians(degrees));
x -= centerX;
y -= centerY;
double xNew = x * c - y * s;
double yNew = x * s + y * c;
double[] result = new double[2];
result[0] = xNew + centerX;
result[1] = yNew + centerY;
return result;
}
// Note: No need to change this function since we're going to override it in the the child classes.
public void draw(Graphics g) {
}
public static void main(String []args){
JButton b= new JButton("Reset");
JFrame f = new JFrame();
f.setSize(1200,800);
f.setVisible(true);
f.getDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel();
f.add(p);
p.add(b);
b.setSize(50,50);
b.setVisible(true);
b.setLocation(50, 50);
}
}
This is our arena class:
// TODO: Feel free to import any other libraries that you need.
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
#SuppressWarnings("serial")
public class Arena extends JPanel {
public int arenaWidth;
public int arenaHeight;
public Player player1;
public Player player2;
Timer timer;
public static int INTERVAL = 35;
public long lastTick;
// TODO: Add other variables to keep track of the game state or other game objects (eg. the map) that will be in your game. Don't forget
// to instantiate them in reset()!
// Constructor. Called inside Game.java for setting up the Arena on game start.
public Arena() {
// Create a timer that calls the tick() function every INTERVAL milliseconds. Every call of the tick() function is a "frame".
timer = new Timer(INTERVAL, new ActionListener() {
public void actionPerformed(ActionEvent e) {
tick();
}
});
lastTick = System.currentTimeMillis();
timer.start();
setFocusable(true);
// TODO: To recognize key presses, you need to fill in the following.
addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent butthole) {
if (butthole.getKeyCode() == KeyEvent.VK_W) {
player1.isWPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_S) {
player1.isSPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_A) {
player1.isAPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_D) {
player1.isDPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_SPACE) {
player1.isSpacePressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_UP) {
player2.isUpPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_DOWN) {
player2.isDownPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_LEFT) {
player2.isLeftPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_RIGHT) {
player2.isRightPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_ENTER) {
player2.isEnterPressed = true;
}
}
public void keyReleased(KeyEvent butthole) {
if (butthole.getKeyCode() == KeyEvent.VK_W) {
player1.isWPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_S) {
player1.isSPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_A) {
player1.isAPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_D) {
player1.isDPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_SPACE) {
player1.isSpacePressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_UP) {
player2.isUpPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_DOWN) {
player2.isDownPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_LEFT) {
player2.isLeftPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_RIGHT) {
player2.isRightPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_ENTER) {
player2.isEnterPressed = false;
}
}
});
}
// Resets the game to its initial state.
public void reset() {
this.removeAll();
this.setBackground(Color.WHITE);
this.setOpaque(true);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
arenaWidth = (int) screenSize.getWidth();
arenaHeight = (int) screenSize.getHeight();
player1 = new Player(arenaWidth, arenaHeight, 100, 100, 1);
player2 = new Player(arenaWidth, arenaHeight, arenaWidth - Player.INIT_SIZE - 100, arenaHeight - Player.INIT_SIZE - 100, 2);
requestFocusInWindow();
}
// Function called once per "frame".
void tick() {
// While tick() should be called once every INTERVAL amount of time, there's no guarantee of that, particularly if you have a lot
// of background apps running. Thus, we need to calculate the time difference (timeDelta) between every two calls of the tick()
// function. Note: 1 divided by this difference is commonly known as the "frames per second", or fps.
long currentTime = System.currentTimeMillis();
long timeDelta = (currentTime - lastTick)/35;
lastTick = currentTime;
if ((player1.isWPressed && player1.isSPressed) ||
(!player1.isWPressed && !player1.isSPressed)) {
player1.vX = 0;
player1.vY = 0;
} else if (player1.isWPressed) {
//System.out.println("Up");
player1.vX = Math.cos(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
player1.vY = Math.sin(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
//player1.posY -= player1.speed*timeDelta;
// MOVE FORWARD
} else if (player1.isSPressed) {
//System.out.println("Down");
player1.vX = -Math.cos(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
player1.vY = -Math.sin(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
//player1.posY += player1.speed*timeDelta;
// MOVE BACKWARD;
}
if (player1.isAPressed) {
//System.out.println("Left");
player1.RotationDegree -= player1.rotateSpeed* timeDelta;
// MOVE BACKWARD;
}
if (player1.isDPressed) {
//System.out.parintln("Right");
player1.RotationDegree += player1.rotateSpeed* timeDelta;
// MOVE BACKWARD;
}
if(player1.RotationDegree > 360) {
player1.RotationDegree -= 360;
}
else if(player1.RotationDegree < 0) {
player1.RotationDegree += 360;
}
player1.move(null, player1.vX, player1.vY);
if ((player2.isUpPressed && player2.isDownPressed) ||
(!player2.isUpPressed && !player2.isDownPressed)) {
player2.vX = 0;
player2.vY = 0; }
else if (player2.isUpPressed) {
//System.out.println("Up");
player2.vX = Math.cos(player2.RotationDegree*Math.PI/180)*player2.speed* timeDelta;
player2.vY = Math.sin(player2.RotationDegree*Math.PI/180)*player2.speed* timeDelta;
//player2.posY -= player2.speed*timeDelta;
// MOVE FORWARD
}
else if (player2.isDownPressed) {
//System.out.println("Down");
player2.vX = -Math.cos(player2.RotationDegree*Math.PI/180)*player2.speed * timeDelta;
player2.vY = -Math.sin(player2.RotationDegree*Math.PI/180)*player2.speed * timeDelta;
//player2.posY += player2.speed*timeDelta;
// MOVE BACKWARD;
}
if (player2.isLeftPressed) {
//System.out.println("Left");
player2.RotationDegree -= player2.rotateSpeed*timeDelta;
// MOVE BACKWARD;
}
if (player2.isRightPressed) {
//System.out.println("Right");
player2.RotationDegree += player2.rotateSpeed*timeDelta;
// MOVE BACKWARD;
}
if(player2.RotationDegree > 360) {
player2.RotationDegree -= 360;
}
else if(player2.RotationDegree < 0) {
player2.RotationDegree += 360;
}
player2.move(null, player2.vX, player2.vY);
player1.currentReload -= timeDelta;
if (player1.currentReload <= 0)
;
{
if (player1.isSpacePressed) {
// create bullet and fire
BasicWeapon newBullet = new BasicWeapon(arenaWidth, arenaHeight, player1.posX + player1.radius, player1.posY + player1.radius, player1);
player1.bullets.add(newBullet);
player1.currentReload = player1.MaxReload;
}
}
ArrayList<PlayerProjectile> bulletsToDelete1 = new ArrayList<PlayerProjectile>();
for (int i = 0; i < player1.bullets.size(); i++) {
PlayerProjectile bulletToChange = player1.bullets.get(i);
bulletsToDelete1.add(bulletToChange);
}
player1.bullets.removeAll(bulletsToDelete1);
// TODO: Update the game state each frame. This can be broken into the following steps:
// Step 1: Handle the keys pressed during the last frame by both players and calculate their resulting velocities/orientations.
// Step 2: Move the players and detect/handle player/player collisions and player/terrain collisions.
// Step 3: Decide whether a bullet should be fired for each player and create new bullet(s) if so. Also, handle reload mechanics.
// Step 4: Move all bullets via their calculated velocities (up to bullet range). Handle bullet/player & bullet/terrain collisions.
// Step 5: Decide whether the game has ended. If so, stop the timer and print a message to the screen indicating who's the winner.
// Note: If you implement other features (eg. weapon swapping, damage counters...etc.), you might also need to add more steps above.
// Update the display: this function calls paintComponent as part of its execution.
repaint(); }
// TODO: Draw all of the objects in your game.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//AffineTransform at = AffineTransoform.getTranslateInstance();
player1.draw(g);
player2.draw(g);
}
// Returns the dimensions of the Arena (for properly resizing the JPanel on the screen).
#Override
public Dimension getPreferredSize() {
return new Dimension(arenaWidth, arenaHeight);
}
}
THIS IS OUR PLAYER CLASS
// TODO: Feel free to import any other libraries that you need.
//import java.*;
import java.awt.*;
import java.util.ArrayList;
//import javax.swing.*;
//import java.awt.event.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Player extends GameObject {
public static void main(String []args){
}
// TODO: Set these values as you see fit. However, which variables should you not set as negative numbers? Which variables should you
// not set as zero? Which v'ariables should you not set as a very large positive number? Why?
public static final int INIT_SIZE = 30;
public static final int INIT_MASS = 0;
public static final int INIT_DAMAGE = 0;
public static final double INIT_SPEED = 10;
public static final double INIT_ROTATE_SPEED = 10;
public static final int INIT_HEALTH = 100;
// Member variables of the player that you can change over the course of the game.
public int damage = INIT_DAMAGE;
public double speed = INIT_SPEED;
public double rotateSpeed = INIT_ROTATE_SPEED;
public int health = INIT_HEALTH;
public double orientation = 0;
public int id;
// TODO: You may need to set up extra variables to store the projectiles fired by this player, the reload status/time of this player,
// the key press/release status of this player, and any other player-related features you decide to implement. Make sure to update the
// constructor appropriately as well!
long currentReload = 0;
long MaxReload = BasicWeapon.INIT_RELOAD;
ArrayList<PlayerProjectile> bullets = new ArrayList<PlayerProjectile>();
double RotationDegree = 0;
boolean isWPressed = false;
boolean isSPressed = false;
boolean isAPressed = false;
boolean isDPressed = false;
boolean isSpacePressed = false;
boolean isEnterPressed = false;
boolean isUpPressed = false;
boolean isDownPressed = false;
boolean isLeftPressed = false;
boolean isRightPressed = false;
boolean isLeftCLickPressed = false;
// Constructor that calls the super (ie. parent) class's constructor and instantiates any other player-specific variables.
public Player(int arenaWidth, int arenaHeight, double startPosX, double startPosY, int id) {
super(arenaWidth, arenaHeight, 0, 0, startPosX, startPosY, INIT_SIZE, INIT_SIZE, INIT_MASS);
this.id = id;
}
// TODO: This function should move the player and handle any player-terrain interactions.
#Override
public boolean move(Map map, double translateX, double translateY) {
posX += translateX;
posY += translateY;
xClip();
yClip();
return false;
}
//UPDATE PLAYER POSTION HERE
//}
public void draw(Graphics g) {
// TODO: Draw the barrel(s) for the player here
double xChords[] = new double[4];
double YChords[] = new double[4];
xChords[0] = posX + 0.6 * width;
xChords[1] = posX + 0.6 * width;
xChords[2] = posX + 1.5 * width;
xChords[3] = posX + 1.5 * width;
YChords[0] = posY + 0.4 * height;
YChords[1] = posY + 0.5 * height;
YChords[2] = posY + 0.5 * height;
YChords[3] = posY + 0.4 * height;
double[] point0 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[0], YChords[0]);
double[] point1 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[1], YChords[1]);
double[] point2 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[2], YChords[2]);
double[] point3 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[3], YChords[3]);
int rotatedPointsX[] = new int[4];
int rotatedPointsY[] = new int[4];
rotatedPointsX[0] = (int)Math.round(point0[0]);
rotatedPointsX[1] = (int)Math.round(point1[0]);
rotatedPointsX[2] = (int)Math.round(point2[0]);
rotatedPointsX[3] = (int)Math.round(point3[0]);
rotatedPointsY[0] = (int)Math.round(point0[1]);
rotatedPointsY[1] = (int)Math.round(point1[1]);
rotatedPointsY[2] = (int)Math.round(point2[1]);
rotatedPointsY[3] = (int)Math.round(point3[1]);
g.drawPolygon(rotatedPointsX, rotatedPointsY, 4);
g.setColor(Color.BLACK);
g.fillPolygon(rotatedPointsX, rotatedPointsY, 4);
if (id == 1) {
g.setColor(new Color(255, 215, 0));
} else if (id == 2) {
g.setColor(Color.RED);
}
// Body
g.fillOval((int) posX, (int) posY, width, height);
g.setColor(Color.BLACK);
g.drawOval((int) posX, (int) posY, width, height);
// TODO: Draw the health bar for the player here.
}
}
Only the button appears, and player1 and player2 don't.
I think what you are trying to achieve is something like :
public static void main(String []args){
JButton button= new JButton("Reset");
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.add(button);
frame.add(panel, BorderLayout.NORTH);
frame.add(new Arena(), BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
For future question please do not post so much code. See: don't just copy in your entire program!

Asteroids Ball Angle Calculation [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Right now I am working on making the classic game Asteroids. I have gotten the ship to move 360 degrees using the arrow keys. However, when I try to shoot the balls out of the ship at that angle, they only leave at 45 degree increments. I think that means there is something wrong with my math in the ball class:
public Ball(double ballAngle){
angle = ballAngle;
xRatio = Math.cos((angle + 90) * 3.14 / 180);
yRatio = Math.sin((angle + 90) * 3.14 / 180);
xChange = xRatio * speed;
yChange = yRatio * speed;
}
public void update(){
x = (int) Math.round(x + xChange);
y = (int) Math.round(y + yChange);
}
However, I can't seem to find what is going on. I have tried debugging it by printing the values an following them step by step, but still can't solve it. Here is the rest of my code incase you need to see it.
Main Game Class:
package Asteroids;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.util.ArrayList;
#SuppressWarnings("serial")
public class Asteroids extends JPanel implements KeyListener{
public final static int BOX_WIDTH = 600;
public final static int BOX_HEIGHT = 600;
public final static int UPDATE_RATE = 300;
public final static double shipSpeed = 1;
ArrayList<Ball> balls = new ArrayList<Ball>();
int i = 0;
int ballCount = 0;
boolean spawn = false;
Ship ship = new Ship(BOX_WIDTH / 2,BOX_HEIGHT / 2);
public Asteroids(){
//Set window size
setPreferredSize(new Dimension(BOX_WIDTH,BOX_HEIGHT));
//Start game thread
Thread gameThread = new Thread() {
public void run(){
while(true){
ship.update();
for (int j=0; j<ballCount; j++){
balls.get(j).update();
}
if(spawn){
i++;
}
if(i % 100 == 0){
balls.add(new Ball(ship.getAngle()));
ballCount ++;
}
repaint();
try {Thread.sleep(1000 / UPDATE_RATE);}
catch (InterruptedException ex) {}
}
}
};
gameThread.start();
}
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.black);
g.fillRect(0, 0, BOX_WIDTH, BOX_HEIGHT);
for (int j=0; j<ballCount; j++){
balls.get(j).draw(g);
}
ship.draw(g);
}
public void keyPressed(KeyEvent e){
if(e.getKeyCode() == KeyEvent.VK_LEFT){
ship.setSpeed(-shipSpeed);
}
if(e.getKeyCode() == KeyEvent.VK_RIGHT){
ship.setSpeed(shipSpeed);
}
if(e.getKeyCode() == KeyEvent.VK_SPACE){
spawn = true;
}
}
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_LEFT){
ship.setSpeed(0);
}
if(e.getKeyCode() == KeyEvent.VK_RIGHT){
ship.setSpeed(0);
}
if(e.getKeyCode() == KeyEvent.VK_SPACE){
spawn = false;
}
}
public void keyTyped(KeyEvent e) {}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
//Create Frame
JFrame frame = new JFrame("ASTEROIDS");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Asteroids asteroids = new Asteroids();
frame.setContentPane(asteroids);
frame.setSize(BOX_WIDTH,BOX_HEIGHT);
frame.pack();
frame.addKeyListener(asteroids);
frame.setVisible(true);
}
});
}
public int getBoxHeight(){
return BOX_HEIGHT;
}
public int getBoxWidth(){
return BOX_WIDTH;
}
}
Ship Class:
package Asteroids;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
public class Ship {
Random rng = new Random();
int r = rng.nextInt(256);
int g = rng.nextInt(256);
int b = rng.nextInt(256);
Color color = new Color(r,g,b);
double speed = 0;
double angle = 0;
final static int shipLength = 20;
final static int shipAngle = 40;
static int x;
static int y;
int point1x;
int point2x;
int point3x;
int point1y;
int point2y;
int point3y;
public Ship(int xPos, int yPos){
x = xPos;
y = yPos;
point1x = x;
point2x = (int) Math.round( x - (Math.tan(shipAngle * 6.28 / 360) * shipLength));
point3x = (int) Math.round( x + (Math.tan(shipAngle * 6.28 / 360) * shipLength));
point1y = y - shipLength;
point2y = y + shipLength;
point3y = y + shipLength;
}
public void update(){
angle = angle + speed;
if(angle < 0){
angle = angle + 360;
}
else if(angle >= 360){
angle = angle - 360;
}
}
public void draw(Graphics g) {
g.setColor(color);
point1x = x - (int) Math.round( Math.sin(angle * 6.28 / 360) * shipLength);
point2x = x + (int) Math.round( Math.sin((angle + shipAngle) * 6.28 / 360) * shipLength);
point3x = x + (int) Math.round( Math.sin((angle - shipAngle) * 6.28 / 360) * shipLength);
point1y = y + (int) Math.round( Math.cos(angle * 6.28 / 360) * shipLength);
point2y = y + (int) Math.round( Math.cos((angle + 180 + shipAngle) * 6.28 / 360) * shipLength);
point3y = y + (int) Math.round( Math.cos((angle - 180 - shipAngle) * 6.28 / 360) * shipLength);
int xpoints[] = {point1x, point2x, point3x};
int ypoints[] = {point1y, point2y, point3y};
int npoints = 3;
g.fillPolygon(xpoints, ypoints, npoints);
}
public void setSpeed(double s){
speed = s;
}
public static int getX(){
return x;
}
public static int getY(){
return y;
}
public double getAngle(){
return angle;
}
}
Ball Class:
package Asteroids;
import java.awt.Color;
import java.awt.Graphics;
public class Ball {
int x = Ship.getX();
int y = Ship.getY();
double angle;
double xRatio;
double yRatio;
double xChange;
double yChange;
final static int speed = 1;
final static int diameter = 5;
public Ball(double ballAngle){
angle = ballAngle;
xRatio = Math.cos((angle + 90) * 3.14 / 180);
yRatio = Math.sin((angle + 90) * 3.14 / 180);
xChange = xRatio * speed;
yChange = yRatio * speed;
}
public void update(){
x = (int) Math.round(x + xChange);
y = (int) Math.round(y + yChange);
}
public void draw(Graphics g) {
g.setColor(Color.white);
g.fillOval(x - diameter/2, y - diameter/2, diameter, diameter);
}
}
I would suspect its a rounding error. You're storing x and y as int primitives, but then adding doubles to it, storing it back in the int. If you changed x and y to be doubles, and then round them only before you display the location, you shouldn't have this problem

Block Collision Code Troubleshooting

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.

What is an efficient way to stop a thread indefinitely?

Right now, I am currently making a marbles game where two players try and shoot their marbles into a circle. The player with more marbles in the circle wins.
The player whose turn it is will click and drag in the opposite direction of which they want the ball to move (sort of like a sling shot). In my Marble class, there is a thread which starts when the mouse click is released. The marble will move and bounce off the walls, slowly reaching a X and Y velocity of zero.
When this happens though, my thread continues looping and eating through the computers CPU.
You can tell that the thread is still running because it continues printing out the velocity even when the ball has stopped moving.
I need help trying to figure out a efficient way of stopping the thread without using the Thread class's stop() method.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Ball
{
float x, y, lastX, lastY;
int width, height;
float xVelocity, yVelocity;
float speed;
public Ball()
{
width = (int) (Math.random() * 50 + 10);
height = (int) (Math.random() * 50 + 10);
x = (float) (Math.random() * (Marble.gamePanel.getWidth() - width) + width/2);
y = (float) (Math.random() * (Marble.gamePanel.getHeight() - height) + height/2);
lastX = x;
lastY = y;
xVelocity = (float) Math.random() * speed*2 - speed;
yVelocity = (float) Math.random() * speed*2 - speed;
}
public void update()
{
lastX = x;
lastY = y;
x += xVelocity;
y += yVelocity;
if(x + width/2 >= Marble.gamePanel.getWidth())
{
xVelocity *= -0.75;
x = Marble.gamePanel.getWidth() - width/2;
}
else if (x - width/2 <= 0)
{
xVelocity *= -0.75;
x = width/2;
}
if (y + height/2 >= Marble.gamePanel.getHeight())
{
yVelocity *= -0.75;
y = Marble.gamePanel.getHeight() - height/2;
}
else if (y - height/2 <= 0)
{
yVelocity *= -0.75;
y = height/2;
}
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import java.util.ArrayList;
/**
* Write a description of class Marble here.
*
* #author (Ryan Lawes)
* #version (5-23-14)
*/
public class Marble extends JFrame implements MouseListener, MouseMotionListener
{
public static GamePanel gamePanel = new GamePanel();
private boolean running = false;
private int fps = 60;
private int frameCount = 0;
private int mouseXC;//X coordinate of where the mouse was clicked
private int mouseYC;//Y coordinate of where the mouse was clicked
private int mouseXR;//X coordinatte of where the mouse was released
private int mouseYR;//Y coordinatte of where the mouse was released
public Marble()
{
super("Fixed Timestep Game Loop Test");
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
JPanel p = new JPanel();
p.setLayout(new GridLayout(1,2));
cp.add(gamePanel, BorderLayout.CENTER);
cp.add(p, BorderLayout.SOUTH);
setSize(500, 500);
addMouseListener(this);
addMouseMotionListener(this);
}
public static void main(String[]args)
{
Marble mrb = new Marble();
mrb.setVisible(true);
}
//Starts a new thread and runs the game loop in it.
public void runGameLoop()
{
Thread loop = new Thread()
{
public void run()
{
gameLoop();
}
};
loop.start();
}
//Only run this in another Thread!
private void gameLoop()
{
//This value would probably be stored elsewhere.
final double GAME_HERTZ = 30.0;
//Calculate how many ns each frame should take for our target game hertz.
final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
//At the very most we will update the game this many times before a new render.
//If you're worried about visual hitches more than perfect timing, set this to 1.
final int MAX_UPDATES_BEFORE_RENDER = 5;
//We will need the last update time.
double lastUpdateTime = System.nanoTime();
//Store the last time we rendered.
double lastRenderTime = System.nanoTime();
//If we are able to get as high as this FPS, don't render again.
final double TARGET_FPS = 60;
final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;
//Simple way of finding FPS.
int lastSecondTime = (int) (lastUpdateTime / 1000000000);
while(running)
{
double now = System.nanoTime();
int updateCount = 0;
//Do as many game updates as we need to, potentially playing catchup.
while(now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER)
{
gamePanel.update();
lastUpdateTime += TIME_BETWEEN_UPDATES;
updateCount++;
}
//If for some reason an update takes forever, we don't want to do an insane number of catchups.
//If you were doing some sort of game that needed to keep EXACT time, you would get rid of this.
if(now - lastUpdateTime > TIME_BETWEEN_UPDATES)
{
lastUpdateTime = now - TIME_BETWEEN_UPDATES;
}
//Render. To do so, we need to calculate interpolation for a smooth render.
float interpolation = Math.min(1.0f, (float) ((now - lastUpdateTime) / TIME_BETWEEN_UPDATES));
drawGame(interpolation);
lastRenderTime = now;
//Update the frames we got.
int thisSecond = (int) (lastUpdateTime / 1000000000);
}
//////if(gamePanel.ballXVel == 0 && gamePanel.ballYVel == 0)
//////{
////// running = !running;
//////}
}
private void drawGame(float interpolation)
{
gamePanel.setInterpolation(interpolation);
gamePanel.repaint();
}
public void mouseClicked(MouseEvent e)
{
}
public void mousePressed(MouseEvent e)
{
/**
* I want to make it so you have to wait until the marble is done rolling before you can click again
*/
mouseXC = (int) e.getPoint().getX();
mouseYC = (int) e.getPoint().getY();
}
public void mouseReleased(MouseEvent e)
{
mouseXR = (int) e.getPoint().getX();
mouseYR = (int) e.getPoint().getY();
gamePanel.ballXVel = (mouseXC - mouseXR)/2;
gamePanel.ballYVel = (mouseYC - mouseYR)/2;
//////////while(gamePanel.ballXVel != 0 && gamePanel.ballYVel != 0)
//////////{
running = !running;
runGameLoop();
////////}
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
public void mouseDragged(MouseEvent e)
{
}
public void mouseMoved(MouseEvent e)
{
}
}
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
public class GamePanel extends JPanel
{
float interpolation;
float ballX, ballY, lastBallX, lastBallY;
int ballWidth, ballHeight;
float ballXVel, ballYVel;
int lastDrawX, lastDrawY;
int ballSpeed = 10;
public GamePanel()
{
ballX = lastBallX = 100;
ballY = lastBallY = 100;
ballWidth = 25;
ballHeight = 25;
}
public void setInterpolation(float interp)
{
interpolation = interp;
}
public void update()
{
lastBallX = ballX;
lastBallY = ballY;
ballX += ballXVel;
ballY += ballYVel;
//Slowly decreases the ball's velocity
ballXVel *= 0.975;
ballYVel *= 0.975;
//Prints out the velocity
//////////if(ballXVel > 0.3 && ballYVel > 0.3)
//////////{
System.out.println(ballXVel);
System.out.println(ballYVel);
//////////}
//If the ball is at a slow enough velocity, it will stop moving alltogether
if(ballXVel > 0 && ballYVel > 0)//if x and y velocity are positive
{
if(ballXVel < 0.3 && ballYVel < 0.3)
{
ballXVel = 0;
ballYVel = 0;
}
}
if(ballXVel < 0 && ballYVel < 0)//if x and y velocity are negative
{
ballXVel *=-1;//Changes to a positive number so it is easier to check
ballYVel *=-1;//Changes to a positive number so it is easier to check
if(ballXVel < 0.3 && ballYVel < 0.3)
{
ballXVel = 0;
ballYVel = 0;
}
ballXVel *=-1;//Changes the velocity back if it doesn't get set to 0. Even if it does, 0*-1 is still 0
ballYVel *=-1;//Changes the velocity back if it doesn't get set to 0. Even if it does, 0*-1 is still 0
}
if(ballXVel < 0 && ballYVel > 0)//if x velocity is negative and y velocity is positive
{
ballXVel *=-1;//Changes to a positive number so it is easier to check
if(ballXVel < 0.3 && ballYVel < 0.3)
{
ballXVel = 0;
ballYVel = 0;
}
ballXVel *=-1;//Changes the velocity back if it doesn't get set to 0. Even if it does, 0*-1 is still 0
}
if(ballXVel > 0 && ballYVel < 0)//if x velocity is positive and y velocity is negative
{
ballYVel *=-1;//Changes to a positive number so it is easier to check
if(ballXVel < 0.3 && ballYVel < 0.3)
{
ballXVel = 0;
ballYVel = 0;
}
ballYVel *=-1;//Changes the velocity back if it doesn't get set to 0. Even if it does, 0*-1 is still 0
}
if(ballX + ballWidth/2 >= getWidth())
{
ballXVel *= -0.8;
ballX = getWidth() - ballWidth/2;
}
else if(ballX - ballWidth/2 <= 0)
{
ballXVel *= -0.8;
ballX = ballWidth/2;
}
if(ballY + ballHeight/2 >= getHeight())
{
ballYVel *= -0.8;
ballY = getHeight() - ballHeight/2;
}
else if(ballY - ballHeight/2 <= 0)
{
ballYVel *= -0.8;
ballY = ballHeight/2;
}
}
public void paintComponent(Graphics g)
{
//BS way of clearing out the old rectangle to save CPU.
g.setColor(getBackground());
g.fillRect(lastDrawX-1, lastDrawY-1, ballWidth+2, ballHeight+2);
g.setColor(Color.RED);
int drawX = (int) ((ballX - lastBallX) * interpolation + lastBallX - ballWidth/2);
int drawY = (int) ((ballY - lastBallY) * interpolation + lastBallY - ballHeight/2);
g.fillOval(drawX, drawY, ballWidth, ballHeight);
lastDrawX = drawX;
lastDrawY = drawY;
g.setColor(Color.BLACK);
}
}
The code you have commented:
//////if(gamePanel.ballXVel == 0 && gamePanel.ballYVel == 0)
//////{
////// running = !running;
//////}
Put it inside the while(running) loop like:
float EPSILON = 0.00000001f;
if(Math.abs(gamePanel.ballXVel - 0.0f) < EPSILON && Math.abs(gamePanel.ballYVel - 0.0f) < EPSILON)
{
running = false;
}
You have put it outside the while loop. The interpreter will never reach the code because running=true;
EDIT :
I tested the solution myself, it worked. See:
I suggest you change your gamePanel.update() method to return a boolean value, indicating if the marvel is still moving.
You can then use the result of that method to break out of the loop once the marvel has stopped.
In GamePanel#update() something like that.
final float epsilon = 0.00001f;
return (Math.abs(ballXVel) < epsilon) && (Math.abs(ballXVel) < epsilon);
And in Marble#gameLoop something like that:
running = !gamePanel.update();

Optimize Breakout (game) block hit-checking

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.

Categories

Resources