I'm trying to create a pong game in Java, and I'm having a little trouble with the ball physics. It all works pretty much perfectly, until the ball gets too slow. I made it so the ball decreases in speed by 0.02 pixels per frame per frame, but I don't want the it to get too slow, so I have a check that prevents it from decelerating once the x and y velocities get below 2. The problem is once this happens, it doesn't keep the current trajectory if the x or y velocity is significantly smaller than the other. It seems to smooth out so that it is almost as straight line, except for moving up or down 1 pixel every few seconds. Here is my ball class:
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.Rectangle;
public class Ball {
private static int DIAMETER = 30;
double x = 0;
double y = 0;
double xVel = 5;
double yVel = 5;
double prevX = 0;
double prevY = 0;
double minSpeed = 2;
double maxSpeed = 10;
int drawX = 0;
int drawY = 0;
boolean tooSlow = false;
public game game;
public Ball(game game) {
this.game = game;
}
public void move()
{
prevX = xVel;
prevY = yVel;
if(xVel > maxSpeed)
xVel = maxSpeed;
if(yVel > maxSpeed)
yVel = maxSpeed;
if(Math.abs(xVel) < minSpeed && Math.abs(yVel) < minSpeed)
tooSlow = true;
else
tooSlow = false;
if(x + xVel < 0){
xVel *= -1;
}
if(x + xVel > 484 - DIAMETER){
xVel *= -1;
}
if(y + yVel < 0){
yVel *= -1;
}
if(y + yVel > 261 - DIAMETER){
yVel *= -1;
}
if(collision()){
if(game.paddle.paddleTop().intersects(getBounds())){
y += game.paddle.y - game.paddle.prevY;
yVel = -1 * Math.abs(yVel) + (game.paddle.y - game.paddle.prevY);
}
if(game.paddle.paddleBottom().intersects(getBounds())){
y += game.paddle.y - game.paddle.prevY;
yVel = Math.abs(yVel) + (game.paddle.y - game.paddle.prevY);
}
if(game.paddle.paddleRight().intersects(getBounds())){
x += game.paddle.x - game.paddle.prevX;
xVel = Math.abs(xVel) + (game.paddle.x - game.paddle.prevX);
yVel += (game.paddle.y - game.paddle.prevY) / 2;
}
if(game.paddle.paddleLeft().intersects(getBounds())){
x += game.paddle.x - game.paddle.prevX;
xVel = -1 * Math.abs(xVel) + (game.paddle.x - game.paddle.prevX);
yVel += (game.paddle.y - game.paddle.prevY) / 2;
}
}
x += xVel;
y += yVel;
if(!tooSlow)
slow();
drawX = (int)x;
drawY = (int)y;
}
public void slow()
{
xVel = (Math.abs(xVel) - 0.02) * Math.signum(xVel);
yVel = (Math.abs(yVel) - 0.02) * Math.signum(yVel);
}
public void paint(Graphics2D g) {
g.fillOval(drawX, drawY, 30, 30);
}
public Rectangle getBounds()
{
return new Rectangle(drawX, drawY, DIAMETER, DIAMETER);
}
public boolean collision()
{
return game.paddle.getBounds().intersects(getBounds());
}
}
These are the rest that I use to run the game (in case it helps):
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.awt.event.ActionEvent;
public class Paddle {
int x = 50;
int y = 10;
int xVel = 0;
int yVel = 0;
int prevX = 50;
int prevY = 10;
int speed = 4;
private static final int WIDTH = 10;
private static final int HEIGHT = 80;
private game game;
public Paddle(game game)
{
this.game = game;
}
public void move()
{
prevX = x;
prevY = y;
if(y + yVel > 0 && y + yVel < 261 - HEIGHT){
y += yVel;
}
if(x + xVel > 0 && x + xVel < 484 - WIDTH){
x += xVel;
}
}
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_UP){
yVel = -speed;
}
if(e.getKeyCode() == KeyEvent.VK_DOWN){
yVel = speed;
}
if(e.getKeyCode() == KeyEvent.VK_LEFT){
xVel = -speed;
}
if(e.getKeyCode() == KeyEvent.VK_RIGHT){
xVel = speed;
}
}
public void keyReleased(KeyEvent e)
{
yVel = 0;
xVel = 0;
}
public void paint(Graphics2D g)
{
g.fillRect(x, y, WIDTH, HEIGHT);
}
public Rectangle getBounds()
{
return new Rectangle(x, y, WIDTH, HEIGHT);
}
public Rectangle paddleTop()
{
return new Rectangle(x + WIDTH / 2, y, 1, 1);
}
public Rectangle paddleLeft()
{
return new Rectangle(x, y, 1, HEIGHT);
}
public Rectangle paddleRight()
{
return new Rectangle(x + WIDTH, y, 1, HEIGHT);
}
public Rectangle paddleBottom()
{
return new Rectangle(x + WIDTH / 2, y + HEIGHT, 1, 1);
}
public Rectangle test()
{
return new Rectangle(x, y, WIDTH, HEIGHT);
}
}
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.event.ActionListener;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.awt.event.ActionEvent;
import javax.swing.JPanel;
import javax.swing.Timer;
public class game extends JPanel implements ActionListener, KeyListener{
private Timer t;
Ball ball = new Ball(this);
Paddle paddle = new Paddle(this);
public game()
{
t = new Timer(15,this);
t.start();
addKeyListener(this);
setFocusable(true);
}
public void actionPerformed(ActionEvent e)
{
ball.move();
paddle.move();
repaint();
}
public void keyPressed(KeyEvent e)
{
paddle.keyPressed(e);
}
public void keyReleased(KeyEvent e)
{
paddle.keyReleased(e);
}
public void keyTyped(KeyEvent e) {}
public void paint(Graphics g)
{
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
ball.paint(g2d);
paddle.paint(g2d);
}
}
import javax.swing.JFrame;
public class main {
public static void main(String[]args)
{
JFrame frame = new JFrame("MLG Pong");
game display = new game();
frame.add(display);
frame.setSize(500,300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
System.out.println(""+display.getHeight()+" "+display.getWidth());
}
}
Sorry for my messy coding, I only learned Java a few months ago.
The problem is in the slow() method:
public void slow()
{
xVel = (Math.abs(xVel) - 0.02) * Math.signum(xVel);
yVel = (Math.abs(yVel) - 0.02) * Math.signum(yVel);
}
You are in fact substracting a vector with another direction from the velocity vector. At the beginning, the change in direction is small, but later it becomes bigger. You always substract (+-0.02, +-0.02) from the velocity vector.
An easy fix would be:
public void slow()
{
xVel = xVel*0.99;
yVel = yVel*0.99;
}
but this slows down big velocities faster than small ones.
If you don't want this behaviour, you have to calculate a small vector of constant length that has the same direction as the velocity vector, and substract this.
For example:
public void slow()
{
double len = Math.sqrt(xVel*xVel + yVel*yVel);
double sx = xVel/len*0.02;
double sy = yVel/len*0.02;
xVel = xVel - sx;
yVel = yVel - sy;
}
You don't have to check the velocity in x and y direction separately. You have to calc the velocity of the ball (in flight direction) and check the result against the minimum Speed.
Change the line
if(Math.abs(xVel) < minSpeed && Math.abs(yVel) < minSpeed)
to
if(xVel*xVel + yVel*yVel < minSpeed * minSpeed)
There is an error in the slow down calculation. When the speed is very slow abs(v)-0.02 is a non-sense.
By the way, the way you calculate is not good. You should modify the vector not directly their projections:
Take a velocity vector: vv=(length,angle). Then modify the length as you want, either by substracting some constant or use a factor on each frame. Then vx=length*cos(angle) and vy=length*sin(angle).
Related
I am making Pong in Java right now using Eclipse. I am trying to get the balls to bounce off the padels proportionally to where they hit the padel. If they hit at the top or bottom, I would like them to bounce off at 75 degrees up at the top or 75 degrees down at the bottom. If they hit near the center, then I would like them to bounce off closer to horizontal.
I am calculating the angle in the ball class with this math:
double speed = getSpeed() + ballIncrease;
yPos = Math.abs(yPos);
double angle = multiplier * yPos;
double ratio = Math.tan(angle);
xSpeed = ratio * (speed/(ratio + 1));
ySpeed = Math.sqrt((speed * speed) - (xSpeed * xSpeed));
I think that it should be correct, but the balls don't behave like I expect them to. I was wondering if anyone can help me solve this problem.
package Pong;
/*Place cursor here to start
*
*/
//Import Libraries
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
#SuppressWarnings("serial")
public class Pong extends JPanel implements KeyListener{
//Create random instances
Random r2 =new Random();
public final static int padelStart = 150;
public final static int padelSpace = 10;
final static int BOX_WIDTH = 1200;
public final static int BOX_HEIGHT = 800;
double ballX = BOX_WIDTH/2;
double ballY = BOX_HEIGHT/2;
public static Padel padel1 = new Padel(padelSpace,padelStart,true);
public static Padel padel2 = new Padel(BOX_WIDTH-padelSpace-padel1.getWidth(),padelStart,false);
Ball ball1 = new Ball(ballX,ballY);
Ball ball2 = new Ball(300,300);
public static int padel1y1;
public static int padel1y2;
public static int padel2y1;
public static int padel2y2;
//Constants
public final static int UPDATE_RATE = 200;
//Main game class
public Pong () {
//Set window size
setPreferredSize(new Dimension(BOX_WIDTH,BOX_HEIGHT));
//Start game thread
Thread gameThread = new Thread() {
public void run(){
while(true){
//Draw objects
padel1y1 = padel1.getY();
padel1y2 = padel1.getY() + Padel.padelLength;
padel2y1 = padel2.getY();
padel2y2 = padel2.getY() + Padel.padelLength;
padel1.update();
padel2.update();
ball1.update();
ball2.update();
repaint();
//Wait
try {Thread.sleep(1000 / UPDATE_RATE);}
catch (InterruptedException ex) {}
}
}
};
gameThread.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.white);
g.fillRect(0,0,BOX_WIDTH,BOX_HEIGHT);
padel1.draw(g);
padel2.draw(g);
ball1.draw(g);
ball2.draw(g);
}
public void keyPressed(KeyEvent e){
if(e.getKeyCode() == KeyEvent.VK_UP){padel2.setSpeed(-Padel.padelSpeed);}
if(e.getKeyCode() == KeyEvent.VK_DOWN){padel2.setSpeed(Padel.padelSpeed);}
if(e.getKeyCode() == KeyEvent.VK_W){padel1.setSpeed(-Padel.padelSpeed);}
if(e.getKeyCode() == KeyEvent.VK_S){padel1.setSpeed(Padel.padelSpeed);}
}
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_UP){padel2.setSpeed(0);}
if(e.getKeyCode() == KeyEvent.VK_DOWN){padel2.setSpeed(0);}
if(e.getKeyCode() == KeyEvent.VK_W){padel1.setSpeed(0);}
if(e.getKeyCode() == KeyEvent.VK_S){padel1.setSpeed(0);}
}
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("PONGPONGPONGPONGPONGPONGPONGPONGPONGPONGPONGPONGPONGPONGPONGPONGPONGPONG");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Pong pong = new Pong();
frame.setContentPane(pong);
frame.setSize(BOX_WIDTH,BOX_HEIGHT);
frame.pack();
frame.addKeyListener(pong);
frame.setVisible(true);
}
});
}
}
Padel Class:
package Pong;
import java.awt.*;
import java.util.Random;
public class Padel {
public int y;
public int x;
public boolean ws;
public int speed = 0;
public final static int padelSpeed = 3;
public final static int padelWidth = 30;
public final static int padelLength = 200;
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);
public Padel(int xPos, int yPos, boolean wands){
y = yPos;
x = xPos;
ws = wands;
}
public void update(){
if(speed > 0 && y + padelLength < Pong.BOX_HEIGHT){
y = y + speed;
}
if(speed < 0 && y > 0){
y = y + speed;
}
}
public void draw(Graphics g) {
g.setColor(color);
g.fillRoundRect(x, y, padelWidth, padelLength, 25, 25);
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public int getWidth(){
return padelWidth;
}
public int getLength(){
return padelLength;
}
public int getSpeed(){
return speed;
}
public void setSpeed(int speed1){
speed = speed1;
}
public int getPadelSpeed(){
return padelSpeed;
}
}
Ball Class:
package Pong;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
public class Ball {
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);
public final static double ballSpeedMin = -2.0;
public final static double ballSpeedMax = 2.0;
public final static double middleSpeedCutoff = 1.0;
public final static double ballIncrease = .2;
public final int ballRadiusMin = 10;
public final int ballRadiusMax = 40;
double x;
double y;
double xSpeed;
double ySpeed;
int ballRadius;
public Ball(double xPos,double yPos){
x = xPos;
y = yPos;
xSpeed = getRandomSpeed();
ySpeed = getRandomSpeed();
ballRadius = getRandomRadius();
}
public double getRandomSpeed(){
Random r =new Random();
return ballSpeedMin + (ballSpeedMax - ballSpeedMin) * r.nextDouble();
}
public int getRandomRadius(){
Random r = new Random();
return r.nextInt((ballRadiusMax - ballRadiusMin) + 1) + ballRadiusMin;
}
public void update(){
x = x + xSpeed;
y = y + ySpeed;
ySpeed = verticalBounce();
double multiplier = .75;
if(getLowX() < Pong.padelSpace + Padel.padelWidth){
if(y>=Pong.padel1y1 && y<=Pong.padel1y2){
double yPos = y - Pong.padel1y1 - (Padel.padelLength/2);
if(yPos<0){
double speed = getSpeed() + ballIncrease;
yPos = Math.abs(yPos);
double angle = multiplier * yPos;
double ratio = Math.tan(angle);
xSpeed = ratio * (speed/(ratio + 1));
ySpeed = -Math.sqrt((speed * speed) - (xSpeed * xSpeed));
}
else if(yPos>0){
double speed = getSpeed() + ballIncrease;
yPos = Math.abs(yPos);
double angle = multiplier * yPos;
double ratio = Math.tan(angle);
xSpeed = ratio * (speed/(ratio + 1));
ySpeed = Math.sqrt((speed * speed) - (xSpeed * xSpeed));
}
else{
double speed = getSpeed();
xSpeed = speed;
ySpeed = 0;
}
}
else{
System.exit(0);
}
}
if(getHighX() > Pong.BOX_WIDTH - Pong.padelSpace - Padel.padelWidth){
if(y>Pong.padel2y1 && y<Pong.padel2y2){
double yPos = y - Pong.padel2y1 - (Padel.padelLength/2);
if(yPos<0){
double speed = getSpeed() + ballIncrease;
yPos = Math.abs(yPos);
double angle = multiplier * yPos;
double ratio = Math.tan(angle);
xSpeed = - ratio * (speed/(ratio + 1));
ySpeed = -Math.sqrt((speed * speed) - (xSpeed * xSpeed));
}
else if(yPos>0){
double speed = getSpeed() + ballIncrease;
yPos = Math.abs(yPos);
double angle = multiplier * yPos;
double ratio = Math.tan(angle);
xSpeed = - ratio * (speed/(ratio + 1));
ySpeed = Math.sqrt((speed * speed) - (xSpeed * xSpeed));
}
else{
double speed = getSpeed();
xSpeed = -speed;
ySpeed = 0;
}
}
else{
System.exit(0);
}
}
}
public void draw(Graphics g) {
g.setColor(color);
int xPos = (int) Math.round(x) - ballRadius;
int yPos = (int) Math.round(y) - ballRadius;
g.fillOval(xPos,yPos,ballRadius*2,ballRadius*2);
}
public double verticalBounce(){
if(y - ballRadius<0 || y + ballRadius > Pong.BOX_HEIGHT){
return -ySpeed;
}
else{
return ySpeed;
}
}
public double getY(){
return y;
}
public double getX(){
return x;
}
public double getYSpeed(){
return ySpeed;
}
public double getXSpeed(){
return xSpeed;
}
public void setYSpeed(double y){
ySpeed = y;
}
public void setXSpeed(double x){
xSpeed = x;
}
public double getLowX(){
return x - ballRadius;
}
public double getLowY(){
return y - ballRadius;
}
public double getHighX(){
return x + ballRadius;
}
public double getHighY(){
return y + ballRadius;
}
public double getSpeed(){
return Math.sqrt((xSpeed*xSpeed)+(ySpeed*ySpeed));
}
}
double multiplier = .75;
//...
double yPos = y - Pong.padel1y1 - (Padel.padelLength/2);
//...
double angle = multiplier * yPos;
Are you aware that the angles you pass in trigonometric functions are expressed in radians?Assuming your paddle length is 20, with yPos varying between [-10, 10] your angle will vary between [-7.5, 7.5]. Translate this in radians and you'll see your angle rotates around the circle a bit more than 2 times. Is really this what you want?
I am drawing a grid on a JPanel by overriding paint(g) and with it marking some points whichever clicked on the grid, but the main problem is, if someone minimize the grid or drag it around the screen, the part of the screen goes blank. I can't figure out how to change my code to have it refreshed every time the screen is minimized or dragged around, please help me with the code.
Here is my code:-
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
public class draw extends JPanel {
private int x,y,X,Y,xmin,xmax,ymin,ymax;
private int clickParam = 0;
private double d, theta;
private ArrayList<Integer> ab = new ArrayList<Integer>();
private ArrayList<Integer> or = new ArrayList<Integer>();
private ArrayList<Double> distance = new ArrayList<Double>();
private ArrayList<Double> angle = new ArrayList<Double>();
Graphics2D g2d = null;
public void drawing(){
repaint();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2d = g2;
int stroke[]={1,2,3,4,5};
g2.setStroke(new BasicStroke(stroke[1]));
xmin = 150;
ymin = 150;
xmax = 650;
ymax = 650;
int xsize = xmax - xmin;
int ysize = ymax - ymin;
g2.drawRect(xmin, ymin, xsize, ysize);
for(int i=50;i<=xsize;i+=50){
g2.drawLine(xmin+i, ymin, xmin+i, ymax);
g2.drawLine(xmin, ymin+i, xmax, ymin+i);
}
g2.setStroke(new BasicStroke(stroke[0]));
for(int i=10;i<=xsize;i+=10){
g2.drawLine(xmin+i, ymin, xmin+i, ymax);
g2.drawLine(xmin, ymin+i, xmax, ymin+i);
}
}
public void drawPoints(Graphics2D g2){
if(x >= xmin && x <= xmax && y >= ymin && y <= ymax)
g2.fillOval(x-3, y-3, 7, 7);
}
public void onClick(){
addMouseListener(new MouseAdapter(){
#Override
public void mousePressed(MouseEvent e){
setxy(e);
setXY();
clickParam = 2;
drawPoints(g2d);
if(x >= xmin && x <= xmax && y >= ymin && y <= ymax){
setDistance();
setAngle();
display();
}
}
});
}
public void setxy(MouseEvent e){
x = e.getX();
y = e.getY();
if(x >= xmin && x <= xmax && y >= ymin && y <= ymax){
ab.add(x);
or.add(y);
}
}
public void setXY(){
X = x - xmin;
Y = ymax - y;
}
public void setDistance(){
d = Math.sqrt(Math.pow(X/10, 2) + Math.pow(Y/10, 2));
distance.add(d);
}
public void setAngle(){
theta = Math.atan2(Y,X)*180/Math.PI;
angle.add(theta);
}
public void display(){
System.out.println("(X,Y) = ("+(X/10)+","+(Y/10)+")"+" & (d,theta) = ("+d+","+theta+")");
}
}
and the main class:-
import java.awt.Dimension;
import javax.swing.*;
public class mainClass {
public static void main(String args[]){
JFrame jframe = new JFrame("TEST");
draw d = new draw();
jframe.setPreferredSize(new Dimension(1000,1000));
jframe.pack();
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.add(d);
jframe.setVisible(true);
jframe.setResizable(false);
d.drawing();
d.onClick();
}
}
onClick is never called, therefore the MouseListener is never added.
Create a list of Points and loop through it in your paintComponent method.
drawPoints should only be called inside the paintComponent method, since it takes the current Graphics2D object.
Restructured code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
public class Draw extends JPanel { // Class names should start with an uppercase letter
private int xmin, xmax, ymin, ymax;
private ArrayList<Point> points = new ArrayList<Point>(); // Create a list where all clicked points will be stored
public Draw() {
xmin = 150;
ymin = 150;
xmax = 650;
ymax = 650;
addMouseListener(new MouseAdapter() { // Add MouseListener in the constructor
#Override
public void mousePressed(MouseEvent e) {
Point point = new Point(e.getX(), e.getY());
if (point.x >= xmin && point.x <= xmax && point.y >= ymin
&& point.y <= ymax) {
points.add(point); // Add the point to the list
repaint();
}
}
});
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
int stroke[] = { 1, 2, 3, 4, 5 };
g2.setStroke(new BasicStroke(stroke[1]));
int xsize = xmax - xmin;
int ysize = ymax - ymin;
g2.drawRect(xmin, ymin, xsize, ysize);
for (int i = 50; i <= xsize; i += 50) {
g2.drawLine(xmin + i, ymin, xmin + i, ymax);
g2.drawLine(xmin, ymin + i, xmax, ymin + i);
}
g2.setStroke(new BasicStroke(stroke[0]));
for (int i = 10; i <= xsize; i += 10) {
g2.drawLine(xmin + i, ymin, xmin + i, ymax);
g2.drawLine(xmin, ymin + i, xmax, ymin + i);
}
drawPoints(g2);
}
private void drawPoints(Graphics2D g2) {
for (Point point : points) { // Loop through the list of points
g2.fillOval(point.x - 3, point.y - 3, 7, 7);
}
}
public static void main(String args[]) {
JFrame jframe = new JFrame("TEST");
Draw d = new Draw();
jframe.setSize(800,800);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setContentPane(d);
jframe.setVisible(true);
}
}
I have a simple class that creates a frame with some balls in it that bounce off the sides. For some reason the balls bounce fine off of the north, west, and east sides of the frame but go slightly past the south side before bouncing off of it. I take the size of the ball into account when setting the boundaries and this works fine on the x axis but not the y.
import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
public class BallBounceFrame
{
public static void main(String[] args)
{
new BallBounceFrame();
}
JFrame frame;
static int WIDTH = 500;
static int HEIGHT = 500;
public BallBounceFrame()
{
frame = new JFrame("Ball Bounce Frame");
frame.setSize(WIDTH, HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
BallCanvas c = new BallCanvas(5);
frame.add(c, BorderLayout.CENTER);
frame.setVisible(true);
c.animate();
}
class BallCanvas extends JPanel
{
private static final long serialVersionUID = 1L;
ArrayList<Ball> balls = new ArrayList<Ball>();
public BallCanvas(int ballNum)
{
for(int i = 0; i < ballNum; i++)
{
balls.add(new Ball(20));
}
}
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.RED);
for(Ball b : balls)
{
b.move();
g2.fill(b);
}
}
public void animate()
{
while(true)
{
try
{
frame.repaint();
Thread.sleep(10);
}
catch(Exception e)
{
System.out.println(e);
}
}
}
}
class Ball extends Ellipse2D.Float
{
private int xVel, yVel;
private int size;
private int WIDTH = BallBounceFrame.WIDTH;
private int HEIGHT = BallBounceFrame.HEIGHT;
public Ball(int size)
{
super((int) (Math.random() * (BallBounceFrame.WIDTH - 20) + 1), (int) (Math.random() * (BallBounceFrame.HEIGHT - 20) + 1), size, size);
this.size = size;
this.xVel = (int) (Math.random() * 5 + 1);
this.yVel = (int) (Math.random() * 5 + 1);
}
public void move()
{
if(super.x < 0 || super.x > WIDTH - size) xVel *= -1;
if(super.y < 0 || super.y > HEIGHT - size ) yVel *= -1;
super.x += xVel;
super.y += yVel;
}
}
}
The problem is that WIDTH and HEIGHT are from the JFrame. Especially the window title caption decreases the panel's height. One could pass the panel boundary/size to the ball's move.
#Override
public void paint(Graphics g)
{
...
b.move(getSize());
...
}
public void move(Dimension panelSize)
{
if (x < 0 || x > panelSize.getWidth() - size) xVel *= -1;
if (y < 0 || y > panelSize.getHeight - size) yVel *= -1;
x += xVel;
y += yVel;
}
To keep the ball inside the bounds you might consider:
public void move(Dimension panelSize)
{
x += xVel;
y += yVel;
if (x < 0) {
x *= -1;
xVel *= -1;
} else if (x > panelSize.getWidth() - size) {
x -= 2 * (x - panelSize.getWidth() - size);
xVel *= -1;
}
if (y < 0) {
y *= -1;
yVel *= -1;
} else if (y > panelSize.getHeight() - size) {
y -= 2 * (y - panelSize.getHeight() - size);
yVel *= -1;
}
}
According to
\ | (xVel == 5)
\|
/|\
/ | \
/ | \
(One normally also calla pack() at the end of the BallBounceFrame to do the layout calculation.)
I think, the reason is the xVel/yVel adding in move.
When you get near the border, you check if you already passed over the border. If not, you add the velocity.
If the distance to the border is 10 and your velocity is 15, you will move 5 over the boundary frame. On the next move you will reverse velocity and bounce off. The move should possibly be split in this case. move 10, reverse velocity, move 5.
I am basically coding this basic arcade game and i need the circle to shoot out small rectangles that looks like bullets or missiles to hit the bad guys whenever the space bar is hit but i cant figure out how.
Heres my code so far:
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class main extends Applet implements Runnable, KeyListener {
private Image i;
private Graphics doubleG;
// x and y are used to set (x,y) positions
// dx and dy are the changes in position
private int x = 850;
private int y = 850;
private int x2 = 100;
private int y2 = 100;
private int dx = 50;
private int radius = 30;
private int dx2 = 4;
private int dy2 = 4;
private int x3 = 100;
private int y3 = 200;
private int dx3 = 5;
private int dy3 = 5;
private int x4 = 100;
private int y4 = 300;
private int dx4 = 3;
private int dy4 = 3;
public void init(){
setSize(1920,1080);
setBackground(Color.black);
addKeyListener(this);
}
public void start(){
Thread thread = new Thread(this);
thread.start();
}
public void run() {
while(true){
//Enemy
if(x2 + dx2 > this.getWidth() - radius - 1){
x2 = this.getWidth() - radius - 1;
dx2 = -dx2;
}
if(x2 + dx2 < 0 + radius){
x2 = 0 + radius;
dx2 = -dx2;
}
x2 += dx2;
// Enemy
if(x3 + dx3 > this.getWidth() - radius - 1){
x3 = this.getWidth() - radius -1;
dx3 = -dx3;
}
if(x3 + dx3 < 0 + radius){
x = 0 + radius;
dx3 = -dx3;
}
x3 += dx3;
// Enemy
if(x4 + dx4 > this.getWidth() - radius - 1){
x4= this.getWidth() - radius -1;
dx4 = -dx4;
}
if(x4 + dx4 < 0 + radius){
x4 = 0 + radius;
dx4 = -dx4;
}
x4 += dx4;
// EVERYTHING ABOVE KEEPS ASTEROIDS IN THE SCREEN ALLOWING IT TO BOUNCE OFF WALLS
repaint();
try{
Thread.sleep(17);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
public void stop(){
}
public void update(Graphics g){
// this function stops the flickering problem every time the ball moves by copying the image instead of repainting it
if(i == null){
i = createImage(this.getSize().width, this.getSize().height);
doubleG = i.getGraphics();
}
doubleG.setColor(getBackground());
doubleG.fillRect(0,0,this.getSize().width, this.getSize().height);
doubleG.setColor(getForeground());
paint(doubleG);
g.drawImage(i,0,0,this);
}
public void paint(Graphics g){
g.setColor(Color.BLUE);
g.fillOval(x, y, radius*2, radius*2);
g.setColor(Color.RED);
g.fillOval(x2, y2, radius + 10, radius + 10);
g.setColor(Color.RED);
g.fillOval(x3,y3, radius + 10, radius + 10);
g.setColor(Color.RED);
g.fillOval(x4, y4, radius + 10, radius + 10);
}
public void moveRight(){
if (dx-1 > -20){
dx += 1;
}
if(x + dx > this.getWidth() - radius - 1){
x = this.getWidth() - radius - 1;
dx = -dx;
}
x += dx;
}
public void moveLeft(){
if(dx - 1 > -20){
dx -= 1;
}
if(x + dx < 0 + radius){
x = 0 + radius;
dx = -dx;
}
x -= dx;
}
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:
moveLeft();
break;
case KeyEvent.VK_RIGHT:
moveRight();
break;
}
}
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
}
KeyListener will only raise KeyEvents if the component it is registered to is focusable AND has foucs.
You never call super.paint, expect some serious paint artifacts
Avoid overriding paint of top level containers (like Applet)
Consider using Swing based components over AWT, apart from been more update to date and more widely used, Swing components are also double buffered by default. Use a combination of JApplet and JPanel as the main drawing surface, overriding it's paintComponent method. In this case, also consider using a javax.swing.Timer over Thread, unless you want to try and maintain a variable delay between updates. This would also allow you to use the key bindings API overcoming the focus issues related to KeyListener
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I have been working on a Breakout game and have just about everything done except for the brick collision. The ball bounces of the wall and paddle fine, but when it comes to the brick it goes straight through them. I'm pretty sure the problem is in the checkBrick() part of the main class, but have no idea what to do about it.
Main Class:
import java.awt.*;
import java.awt.event.KeyEvent;
import java.applet.*;
import java.util.Random;
import javax.swing.JOptionPane;
public class Breakout extends Applet implements Runnable {
Ball ball = new Ball();
Paddle paddle = new Paddle(135, 375);
Brick[] brick = new Brick[50];
private int bX[] = new int[50];
private int bY[] = new int[50];
private int bW[] = new int[50];
private int bH[] = new int[50];
Thread t;
Random trajectory = new Random();
boolean lose;
Image buffer = null;
// The life cycle of the Applet
// Sets up window
public void init() {
setSize(377, 500);
buffer = createImage(377, 500);
// setBackground(Color.black);
System.out.println("init()");
}
public void start() {
if (t == null) {
t = new Thread(this);
t.start();
}
System.out.println("start()");
}
public void run() {
System.out.println("run()");
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
while (!lose) {
ball.move();
paddle.move();
checkWall();
checkPaddle();
checkBrick();
ball.move();
repaint();
try {
Thread.sleep(30);
} catch (InterruptedException ex) {
}
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
}
JOptionPane.showMessageDialog(null, "Game Over");
System.out.println("Termintated");
System.exit(0);
}
public void stop() {
System.out.println("stop()");
}
public void destroy() {
System.out.println("destroy()");
}
public void paint(Graphics g) {
Graphics screen = null;
screen = g;
g = buffer.getGraphics();
g.setColor(Color.black);
g.fillRect(0, 0, 377, 500);
createBricks(g);
createPaddle(g);
createBall(g);
screen.drawImage(buffer, 0, 0, this);
}
public void update(Graphics g) {
paint(g);
}
private void createBricks(Graphics g) {
int brickIndex = 0;
int brickX = 15, brickY = 160;
int brickW = 30, brickH = 10;
for (int i = 0; i <= 4; i++) {
brickX = 15;
brickY -= 20;
for (int n = 0; n < 10; n++) {
brick[brickIndex] = new Brick();
brick[brickIndex].setBounds(brickX, brickY, brickW, brickH);
bX[brickIndex] = brick[brickIndex].x();
bY[brickIndex] = brick[brickIndex].y();
bW[brickIndex] = brick[brickIndex].w();
bH[brickIndex] = brick[brickIndex].h();
brick[brickIndex].setColor(i);
brick[brickIndex].paint(g);
brickIndex++;
brickX += 35;
}
}
}
private void createPaddle(Graphics g) {
paddle.paint(g);
}
private void createBall(Graphics g) {
ball.paint(g);
}
private void checkWall() {
// If ball hits right wall it will bounce
if ((ball.getX() + ball.getR()) >= 380) {
ball.setVX(trajectory.nextInt(2) + -3);
}
// If ball hits left wall it will bounce
if ((ball.getX() - ball.getR()) < -10) {
ball.setVX(trajectory.nextInt(4) + 1);
}
// If ball hits ceiling it will bounce
if ((ball.getY() + ball.getR()) < 12)
ball.setVY(trajectory.nextInt(5) + 1);
// If ball goes through floor it will subtract a life
if ((ball.getY() + ball.getR()) > 515)
lose = true;
}
private void checkBrick() {
for (int i = 0; i < 50; i++) {
int tempX, tempY, tempW, tempH;
tempX = bX[i];
tempY = bY[i];
tempW = bW[i];
tempH = bH[i];
if ((ball.getX() + ball.getR()) < (tempX + tempW)
&& (ball.getX() + ball.getR()) >= tempX) {
if ((ball.getY() + ball.getR()) > (tempY + tempH)
&& (ball.getY() + ball.getR()) <= tempY) {
System.out.println("Brick " + i + " has been hit.");
}
}
}
}
private void checkPaddle() {
// Check for paddle
if ((ball.getX() + ball.getR()) < (paddle.getX() + 100)
&& (ball.getX() + ball.getR()) >= paddle.getX() + 5) {
if ((ball.getY() + ball.getR()) > (paddle.getY() - 5)
&& (ball.getY() + ball.getR()) <= (paddle.getY() + 5)) {
ball.setVX((trajectory.nextInt(7) + -2) + 1);
ball.setVY(trajectory.nextInt(1) + -3);
}
}
}
// Key Detectors
public boolean keyDown(Event e, int key) {
if (key == Event.RIGHT) {
paddle.setVX(0);
if ((paddle.getX() + 100) < 377)
paddle.setVX(10);
}
if (key == Event.LEFT) {
paddle.setVX(0);
if (paddle.getX() > 0)
paddle.setVX(-10);
}
return true;
}
// To make sure it doesn't just keep moving one way
public boolean keyUp(Event e, int key) {
paddle.setVX(0);
return true;
}
}
Ball Class:
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
public class Ball
{
private int x, y; //Position
private int vx, vy; //Velocity
private int r; //radius
//constructor
public Ball()
{
x = 177;
y = 315;
vx = 0;
vy = 5;
r = 15;
}
public void paint(Graphics g)
{
g.setColor(Color.white);
g.fillOval(x, y, r, r);
}
//returns the x of origin
public int getX()
{
return x;
}
//returns the y of origin
public int getY()
{
return y;
}
public int getVX()
{
return vx;
}
//returns the y of origin
public int getVY()
{
return vy;
}
//returns the radius r of the ball
public int getR()
{
return r;
}
//sets the velocity of x to a different value
public void setVX(int vx)
{
this.vx = vx;
}
//sets the velocity of y to a different value
public void setVY(int vy)
{
this.vy = vy;
}
//sets the x value
public void setX(int x)
{
this.x = x;
}
//sets the y value
public void setY(int y)
{
this.y = y;
}
//starts making the ball move by changing its coords
public void move()
{
x+= vx;
y+= vy;
}
}
Paddle Class:
import java.awt.Color;
import java.awt.Graphics;
public class Paddle {
// declares variables for x and y coordinates
int x, y;
//The velocity of to move paddle
int vx;
// constructor that takes in x and y coordinates for paddle
public Paddle(int x, int y)
{
this.x = x;
this.y = y;
}
public void paint(Graphics g)
{
// paints paddle
g.setColor(Color.WHITE);
g.fillRect(x, y, 100, 15);
g.setColor(Color.GREEN);
g.drawRect(x, y, 100, 15);
}
// gets x coordinate of paddle
public int getX() {
return x;
}
// sets x coordinate of paddle
public void setX(int x) {
this.x = x;
}
// gets y coordinate of paddle
public int getY() {
return y;
}
// sets y coordinate of paddle
public void setY(int y) {
this.y = y;
}
public void setVX(int vx)
{
this.vx = vx;
}
//Moves the paddle
public void move()
{
x+=vx;
}
}
Brick Class:
import java.awt.Color;
import java.awt.Graphics;
public class Brick
{
private Color color =(Color.cyan);
private int x, y, w, h;
public Brick()
{
//Garbage values that are there just for declaration
x = 0;
y = 0;
w = 10;
h = 10;
}
//Sets color for the brick
public void setColor(int paintC)
{
switch(paintC)
{
case 0:
color = (Color.magenta);
break;
case 1:
color = (Color.blue);
break;
case 2:
color = (Color.yellow);
break;
case 3:
color = (Color.orange);
break;
default:
color = (Color.red);
break;
}
}
//Sets the location then size of the brick
public void setBounds(int x, int y, int w, int h)
{
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
//returns x value
public int x()
{
return this.x;
}
//returns y value
public int y()
{
return this.y;
}
//returns width value
public int w()
{
return this.w;
}
//returns height value
public int h()
{
return this.h;
}
//Sets x for the brick
public void setX(int x)
{
this.x = x;
}
//Sets y for the brick
public void setY(int y)
{
this.y = y;
}
public void setW(int w)
{
this.w = w;
}
public void setH(int h)
{
this.h = h;
}
public void paint(Graphics g)
{
g.setColor(color);
g.fillRect(x, y, w, h);
g.setColor(Color.green);
g.drawRect(x, y, w, h);
}
}
I've begin running over your code, quite frankly can't be bothered trying to figure out your logic, but what I believe you're trying to deduce is if the brick "contains" the ball, rather then if the ball intersects with the brick.
You don't care how much of the ball or brick are intersecting, only if the they do...for example...
private void checkBrick() {
int tx = ball.getX();
int ty = ball.getY();
int tw = ball.getR();
int th = ball.getR();
tw += tx;
th += ty;
for (int i = 0; i < 50; i++) {
int tempX, tempY, tempW, tempH;
tempX = bX[i];
tempY = bY[i];
tempW = bW[i];
tempH = bH[i];
int rw = tempW + tempX;
int rh = tempH + tempY;
// overflow || intersect
if ((rw < tempX || rw > tx) &&
(rh < tempY || rh > ty) &&
(tw < tx || tw > tempX) &&
(th < ty || th > tempY)) {
System.out.println("Hit");
}
}
}
Now, I stole this from Rectangle#intersects
Basically, if you used the geometry class from the 2D Graphics API, you could reduce this down to...
private void checkBrick() {
Rectangle b = new Rectangle(ball.getX(), ball.getY(), ball.getR(), ball.getR());
for (int i = 0; i < 50; i++) {
int tempX, tempY, tempW, tempH;
tempX = bX[i];
tempY = bY[i];
tempW = bW[i];
tempH = bH[i];
Rectangle brick = new Rectangle(tempX, tempY, tempW, tempH);
System.out.println("brick = " + brick);
if (b.intersects(brick)) {
System.out.println("Break");
}
}
}
And, yes, I did actually run your code
The problem is that the method checkBrick() is not changing anything, it is just printing if the ball has a collision with the brick.
You may want to change the Ball velocity, as you did within checkWall() and checkPaddle().
private void checkBrick() {
for (int i = 0; i < 50; i++) {
...
if (...) {
ball.setVX(...); // Add these lines setting the correct values
ball.setVY(...);
}
}
}
You may also want to check if your if-conditions are correct, and do what you expected.
Assuming tempH is positive,
((ball.getY() + ball.getR()) > (tempY + tempH)
&& (ball.getY() + ball.getR()) <= tempY)
can't ever be true. The > needs to be < and the <= needs to be >=.
Additionally, you'll need to take some kind of action if the brick is hit, rather than just printing out the fact. Sorry, I'm not sure what's supposed to happen - does the brick disappear? Or the ball bounce? Or both?
Second answer (in addition to other answer which I believe is ALSO a problem), your logic is asking if the ball is contained within a brick, but when you create the ball its radius is greater than the height of a brick, so even correcting that logic won't fix the problem.
You should refactor your code to make it read out like natural language, this would help a lot with debugging (or writing less bugs in the first place!) i.e.
in brick class:
public int bottom()
{
return y;
}
public int top()
{
return y + h;
}
in ball class:
public int bottom()
{
return y - r;
}
public int top() {
return y + r;
}
then in main class:
private boolean withinY(brick) {
return (ball.bottom => brick.bottom() && ball.top =< brick.top());
}
then the logic reads nicer (psuedo):
foreach brick in wall {
if (ball.withinY(brick) and ball.withinX(brick))
BAM!!
}
You're checking if the ball is between the left and right side of the brick, but then checking if the ball is both above AND below the brick, because you've got your greater than and less than's mixed up. Also the center of the ball needs to be subtracted from it's Y position.
if ((ball.getY() + ball.getR()) **>** (tempY + tempH) &&
(ball.getY() **+** ball.getR()) **<=** tempY)
could be
if ((ball.getY() + ball.getR()) < (tempY + tempH) &&
(ball.getY() - ball.getR()) >= tempY)
but I'd suggest finding if the top of the ball is between the top and bottom of the brick, OR if the bottom of the ball is between the top and bottom of the brick:
if (((ball.getY() + ball.getR()) < (tempY + tempH) && (ball.getY() - ball.getR()) >= tempY)) ||
((ball.getY() - ball.getR()) < (tempY + tempH) && (ball.getY() - ball.getR()) >= tempY))) {
CODE
}
And use similar logic for finding between left and right sides of the brick