Java Multithreading Mouse Click - java

After making changes based on a previous post I have taken the following code a few steps further by introducing single/double click recognition. Why are the balls being created in the top left corner and not where the mouse is clicked?
BouncingBalls.java
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class BouncingBalls extends JPanel implements MouseListener {
protected List<RandomBall> randomBalls = new ArrayList<RandomBall>(20);
protected List<VerticalBall> verticalBalls = new ArrayList<VerticalBall>(20);
private Container container;
private DrawCanvas canvas;
private Boolean doubleClick = false;
private final Integer waitTime = (Integer) Toolkit.getDefaultToolkit()
.getDesktopProperty("awt.multiClickInterval");
private static int canvasWidth = 500;
private static int canvasHeight = 500;
public static final int UPDATE_RATE = 30;
int count = 0;
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
public BouncingBalls(int width, int height) {
canvasWidth = width;
canvasHeight = height;
container = new Container();
canvas = new DrawCanvas();
this.setLayout(new BorderLayout());
this.add(canvas, BorderLayout.CENTER);
this.addMouseListener(this);
start();
}
public void start() {
Thread t = new Thread() {
public void run() {
while (true) {
update();
repaint();
try {
Thread.sleep(1000 / UPDATE_RATE);
} catch (InterruptedException e) {
}
}
}
};
t.start();
}
public void update() {
for (RandomBall ball : randomBalls) {
ball.ballBounce(container);
}
for (VerticalBall ball : verticalBalls) {
ball.verticalBounce(container);
}
}
class DrawCanvas extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
container.draw(g);
for (RandomBall ball : randomBalls) {
ball.draw(g);
}
for (VerticalBall ball : verticalBalls) {
ball.draw(g);
}
}
public Dimension getPreferredSize() {
return (new Dimension(canvasWidth, canvasHeight));
}
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame("Stack Answer 2");
f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
f.setContentPane(new BouncingBalls(canvasHeight, canvasWidth));
f.pack();
f.setVisible(true);
}
});
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
if (e.getClickCount() >= 2) {
doubleClick = true;
verticalBalls.add(new VerticalBall(getX(), getY(), canvasWidth, canvasHeight));
System.out.println("double click");
} else {
Timer timer = new Timer(waitTime, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (doubleClick) {
/* we are the first click of a double click */
doubleClick = false;
} else {
count++;
randomBalls.add(new RandomBall(getX(), getY(), canvasWidth, canvasHeight));
/* the second click never happened */
System.out.println("single click");
}
}
});
timer.setRepeats(false);
timer.start();
}
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
}
RandomBall.java
import java.awt.Color;
import java.awt.Graphics;
public class RandomBall {
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
private int x;
private int y;
private int canvasWidth = 500;
private int canvasHeight = 500;
private boolean leftRight;
private boolean upDown;
private int deltaX;
private int deltaY;
private int radius = 20;
private int red = random(255);
private int green = random(255);
private int blue = random(255);
RandomBall(int x, int y, int width, int height) {
this(x, y, width, height, false, false);
}
RandomBall(int x, int y, int width, int height, boolean leftRight, boolean upDown) {
this.x = x;
this.y = y;
this.canvasWidth = width;
this.canvasHeight = height;
this.leftRight = leftRight;
this.upDown = upDown;
updateDelta();
}
public void draw(Graphics g) {
g.setColor(new Color(red, green, blue));
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius),
(int) (2 * radius));
}
private void updateDelta() {
final int minimumMovement = 5;
final int maxExtra = 10;
deltaY = minimumMovement + (int) (Math.random() * maxExtra);
deltaX = minimumMovement + (int) (Math.random() * maxExtra);
}
public void ballBounce(Container container) {
// controls horizontal ball motion
if (leftRight) {
x += deltaX;
if (x >= getWidth()) {
leftRight = false;
updateDelta();
}
} else {
x += -deltaX;
if (x <= 0) {
leftRight = true;
updateDelta();
}
}
// controls vertical ball motion
if (upDown) {
y += deltaY;
if (y >= getHeight()) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
public void verticalBounce(Container container) {
// controls vertical ball motion
if (upDown) {
y += deltaY;
if (y >= getHeight()) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return canvasWidth;
}
public int getHeight() {
return canvasHeight;
}
}
VerticalBall.java
import java.awt.Color;
import java.awt.Graphics;
public class VerticalBall {
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
private int x;
private int y;
private int canvasWidth = 500;
private int canvasHeight = 500;
private boolean upDown;
private int deltaY;
private int radius = 20;
private int red = random(255);
private int green = random(255);
private int blue = random(255);
VerticalBall(int x, int y, int width, int height) {
this(x, y, width, height, false);
}
VerticalBall(int x, int y, int width, int height, boolean upDown) {
this.x = x;
this.y = y;
this.canvasWidth = width;
this.canvasHeight = height;
this.upDown = upDown;
updateDelta();
}
public void draw(Graphics g) {
g.setColor(new Color(red, green, blue));
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius),
(int) (2 * radius));
}
private void updateDelta() {
final int minimumMovement = 5;
final int maxExtra = 10;
deltaY = minimumMovement + (int) (Math.random() * maxExtra);
}
public void verticalBounce(Container container) {
// controls vertical ball motion
if (upDown) {
y += deltaY;
if (y >= getHeight()) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return canvasWidth;
}
public int getHeight() {
return canvasHeight;
}
}
Container.java
import java.awt.Color;
import java.awt.Graphics;
public class Container {
private static final int HEIGHT = 500;
private static final int WIDTH = 500;
private static final Color COLOR = Color.WHITE;
public void draw(Graphics g) {
g.setColor(COLOR);
g.fillRect(0, 0, WIDTH, HEIGHT);
}
}

Basically, because that's where you're telling them to be created...
verticalBalls.add(new VerticalBall(getX(), getY(), canvasWidth, canvasHeight));
getX() and getY() are getting the component's location (which happens to be close enough to 0x0 for arguments sake)...
Instead, you should be using the MouseEvent information...
public void mousePressed(MouseEvent e) {
if (e.getClickCount() >= 2) {
doubleClick = true;
verticalBalls.add(new VerticalBall(e.getX(), e.getY(), canvasWidth, canvasHeight));
System.out.println("double click");
}
}
This will create an issue for your Timer...
Instead you may need to introduce a couple of variables to hold the position information...
final int x = e.getX();
final int y = e.getX();
if (e.getClickCount() >= 2) {
...
} else {
Timer timer = new Timer(waitTime, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (doubleClick) {
/* we are the first click of a double click */
doubleClick = false;
} else {
count++;
randomBalls.add(new RandomBall(x, y, canvasWidth, canvasHeight));
/* the second click never happened */
System.out.println("single click");
}
}
});
timer.setRepeats(false);
timer.start();
}

You need to use the getX and getY from the MouseEvent, not from the JPanel.
As a sidenote, your use of a separate Thread to periodically update the gui is bad because you can't use separate threads to call swing methods. use a swing Timer instead.

Related

Key is unregistered when other key is pressed

I'm recreating Asteroids, the spaceship can move forward and backwards and change it's rotation. When I for example move forward, the velocity keeps increasing until I release the forward key. But it also stops increasing the velocity when I for example hit the rotate left key. I don't understand why it does this, and I want to be able to increase the rotation degrees while increasing or decreasing my velocity (i.e. pressing UP or DOWN arrow). It also stops moving forward or rotating when I shoot (i.e. space bar).
Also, is my code readable and the aproach OO?
Asteroids:
import javax.swing.*;
public class Asteroids {
public static void createAndShowGui() {
GamePanel gamePanel = new GamePanel();
JFrame frame = new JFrame("Asteroids");
frame.getContentPane().add(gamePanel);
frame.pack();
frame.setVisible(true);
frame.setResizable(false);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLocation(2000, 50);
gamePanel.requestFocusInWindow();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
GamePanel.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
public class GamePanel extends JPanel implements ActionListener {
private final int WIDTH = 1600;
private final int HEIGHT = 900;
private ArrayList<Rock> rocks;
private ArrayList<Bullet> bullets;
private SpaceShip spaceShip;
private boolean keyHeld;
private int keyCode;
public GamePanel() {
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setUp();
}
public void setUp() {
Timer animationTimer = new Timer(15, this);
spaceShip = new SpaceShip(WIDTH, HEIGHT);
bullets = new ArrayList<>();
rocks = new ArrayList<>();
addKeyListener(new KeyListener());
for (int i = 0; i < 12; i++) {
rocks.add(new Rock(WIDTH, HEIGHT));
}
animationTimer.start();
}
class KeyListener extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
keyCode = e.getKeyCode();
keyHeld = true;
}
#Override
public void keyReleased(KeyEvent e) {
keyHeld = false;
}
}
#Override
public void actionPerformed(ActionEvent e) {
checkRockCollisions();
for (int i = 0; i < bullets.size(); i++)
checkBulletOffScreen(bullets.get(i));
spaceShip.checkForCollisionWithFrame(WIDTH, HEIGHT);
moveObjects();
checkForPressedKeys();
repaint();
}
public void moveObjects() {
for (Rock rock : rocks)
rock.moveForward();
for (Bullet bullet : bullets)
bullet.moveForward();
spaceShip.moveSpaceShip();
}
public void checkRockCollisions() {
for (int i = 0; i < rocks.size(); i++) {
Rock rock = rocks.get(i);
rocks.stream().filter(rockToCheck -> !rock.equals(rockToCheck)).forEach(rock::checkRockCollision);
for (int j = 0; j < bullets.size(); j++) {
Bullet bullet = bullets.get(j);
if (rock.checkBulletCollision(bullet)) {
rocks.remove(rock);
bullets.remove(bullet);
}
}
if (rock.checkSpaceShipCollision(spaceShip))
resetSpaceShip();
rock.checkFrameCollision(WIDTH, HEIGHT);
}
}
public void checkBulletOffScreen(Bullet bullet) {
if (bullet.getxPos() + bullet.getBulletWidth() < 0 || bullet.getxPos() > WIDTH || bullet.getyPos() + bullet.getBulletHeight() < 0 || bullet.getyPos() > HEIGHT)
bullets.remove(bullet);
}
public void resetSpaceShip() {
spaceShip = new SpaceShip(WIDTH, HEIGHT);
}
public void checkForPressedKeys() {
if (keyHeld) {
switch (keyCode) {
case KeyEvent.VK_RIGHT:
spaceShip.increaseRotationDegree();
break;
case KeyEvent.VK_LEFT:
spaceShip.decreaseRotationDegree();
break;
case KeyEvent.VK_UP:
spaceShip.increaseForwardVelocity();
break;
case KeyEvent.VK_DOWN:
spaceShip.decreaseForwardVelocity();
break;
case KeyEvent.VK_SPACE:
bullets.add(new Bullet(spaceShip.getxPos() + spaceShip.getSpaceShipRadius(), spaceShip.getyPos() + spaceShip.getSpaceShipRadius(), spaceShip.getRotationDegree()));
keyHeld = false;
break;
}
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
RenderingHints mainRenderingHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHints(mainRenderingHints);
for (Rock rock : rocks)
rock.display(g2d);
for (Bullet bullet : bullets)
bullet.display(g2d);
spaceShip.display(g2d);
g2d.dispose();
}
}
SpaceShip.java
import java.awt.*;
public class SpaceShip {
private double xPos;
private double yPos;
private double xVelocity;
private double yVelocity;
private int spaceShipDiameter;
private int rotationDegree;
private int spaceShipRadius;
public SpaceShip(final int WIDTH, final int HEIGHT) {
spaceShipDiameter = 30;
spaceShipRadius = spaceShipDiameter / 2;
xPos = WIDTH / 2 - spaceShipRadius;
yPos = HEIGHT / 2 - spaceShipRadius;
rotationDegree = 0;
}
public int getSpaceShipRadius() {
return spaceShipRadius;
}
public double getxPos() {
return xPos;
}
public double getyPos() {
return yPos;
}
public int getRotationDegree() {
return rotationDegree;
}
public void increaseForwardVelocity() {
xVelocity += 0.04 * Math.sin(Math.toRadians(this.rotationDegree));
yVelocity += 0.04 * -Math.cos(Math.toRadians(this.rotationDegree));
}
public void decreaseForwardVelocity() {
xVelocity -= 0.04 * Math.sin(Math.toRadians(this.rotationDegree));
yVelocity -= 0.04 * -Math.cos(Math.toRadians(this.rotationDegree));
}
public void moveSpaceShip() {
this.xPos += xVelocity;
this.yPos += yVelocity;
}
public void increaseRotationDegree() {
if (rotationDegree >= 350)
rotationDegree = 0;
else
rotationDegree+=10;
}
public void decreaseRotationDegree() {
if (rotationDegree <= 0)
rotationDegree = 350;
else
rotationDegree-=10;
}
public void checkForCollisionWithFrame(final int WIDTH, final int HEIGHT) {
if (xPos + spaceShipDiameter < 0)
xPos = WIDTH;
else if (xPos > WIDTH)
xPos = 0 - spaceShipDiameter;
else if (yPos + spaceShipDiameter < 0)
yPos = HEIGHT;
else if (yPos > HEIGHT)
yPos = 0 - spaceShipDiameter;
}
public void display(Graphics2D g2d) {
g2d.setColor(Color.BLACK);
g2d.rotate(Math.toRadians(rotationDegree), xPos + spaceShipRadius, yPos + spaceShipRadius);
g2d.fillOval((int) xPos, (int) yPos, spaceShipDiameter, spaceShipDiameter);
g2d.setColor(Color.YELLOW);
g2d.drawLine((int) xPos + spaceShipRadius, (int) yPos , (int) xPos + spaceShipRadius, (int) yPos + spaceShipRadius);
}
}
Rock.java
import java.awt.*;
import java.util.Random;
public class Rock {
private int xPos;
private int yPos;
private int rockDiameter;
private int xVelocity;
private int yVelocity;
private int rockRadius;
private Color rockColor;
public Rock(int WIDTH, int HEIGHT) {
Random r = new Random();
rockDiameter = r.nextInt(40) + 30;
rockRadius = rockDiameter / 2;
xPos = r.nextInt(WIDTH - rockDiameter);
yPos = r.nextInt(HEIGHT - rockDiameter);
xVelocity = r.nextInt(6) - 3;
yVelocity = r.nextInt(6) - 3;
rockColor = new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255));
}
public void moveForward() {
xPos += xVelocity;
yPos += yVelocity;
}
public void checkRockCollision(Rock rock) {
if (calculateDistances(rock.xPos, rock.yPos, rock.rockRadius))
switchVelocities(rock);
}
public void switchVelocities(Rock rock) {
int tempXVelocity = this.xVelocity;
int tempYVelocity = this.yVelocity;
this.xVelocity = rock.xVelocity;
this.yVelocity = rock.yVelocity;
rock.xVelocity = tempXVelocity;
rock.yVelocity = tempYVelocity;
moveForward();
rock.moveForward();
}
public boolean checkBulletCollision(Bullet bullet) {
return calculateDistances(bullet.getxPos(), bullet.getyPos(), bullet.getBulletRadius());
}
public boolean checkSpaceShipCollision(SpaceShip spaceShip) {
return calculateDistances(spaceShip.getxPos(), spaceShip.getyPos(), spaceShip.getSpaceShipRadius());
}
public boolean calculateDistances(double objectxPos, double objectyPos, int objectRadius) {
int radiusOfBoth = objectRadius + rockRadius;
double horDistance = Math.abs((objectxPos + objectRadius) - (xPos + rockRadius));
double verDistance = Math.abs((objectyPos + objectRadius) - (yPos + rockRadius));
double diagDistance = Math.sqrt(Math.pow(horDistance, 2) + Math.pow(verDistance, 2));
return diagDistance <= radiusOfBoth;
}
public void checkFrameCollision(final int WIDTH, final int HEIGHT) {
if (xPos < 0) {
xVelocity *= -1;
xPos = 0;
} else if (xPos + rockDiameter > WIDTH) {
xVelocity *= -1;
xPos = WIDTH - rockDiameter;
} else if (yPos < 0) {
yVelocity *= -1;
yPos = 0;
} else if (yPos + rockDiameter > HEIGHT) {
yVelocity *= -1;
yPos = HEIGHT - rockDiameter;
}
}
public void display(Graphics2D g2d) {
g2d.setColor(rockColor);
g2d.fillOval(xPos, yPos, rockDiameter, rockDiameter);
}
}
Bullet.java
import java.awt.*;
public class Bullet {
private double xPos;
private double yPos;
private int bulletWidth;
private int bulletHeight;
private double xVelocity;
private double yVelocity;
private int bulletRadius;
public Bullet(double startingXPos, double startingYPos, int rotationDegree) {
bulletHeight = 10;
bulletWidth = 10;
bulletRadius = bulletWidth / 2;
xPos = startingXPos - bulletRadius;
yPos = startingYPos - bulletRadius;
xVelocity = 5 * Math.sin(Math.toRadians(rotationDegree));
yVelocity = 5 * -Math.cos(Math.toRadians(rotationDegree));
}
public void moveForward() {
this.xPos += xVelocity;
this.yPos += yVelocity;
}
public double getxPos() {
return xPos;
}
public double getyPos() {
return yPos;
}
public int getBulletWidth() {
return bulletWidth;
}
public int getBulletHeight() {
return bulletHeight;
}
public int getBulletRadius() {
return bulletRadius;
}
public void display(Graphics2D g2d) {
g2d.setColor(Color.GREEN);
g2d.fillOval((int) xPos, (int) yPos, bulletWidth, bulletHeight);
}
}
You're only keeping track of the most recent key pressed. Instead, you need to keep track of every key being pressed. One way to do that is by using a boolean variable for each key. In your keyPressed() function, set the appropriate boolean to true, and in the keyReleased() function, set it to false. Then in your game loop, just check which booleans are true and take the appropriate action.

Java 2D Game adding bombs

I'm very new to game design (this is my first attempt) and this project will be used to create an android game.
I'm trying to make a simple game (as simple as possible).
What I need:
A background
a ship (that can move left an right at the bottom of the screen)
Enemies (Bombs dropping down from the sky)
projectiles (to shoot bombs with, shoot straight up)
Score (in the upper corner)
I have studied this tutorial:
http://www.kilobolt.com/game-development-tutorial.html
and changed code to get this:
http://i297.photobucket.com/albums/mm231/mabee84/Battleship.png
the black rectangles are projectiles.
Now I need to create the bombs but I can't figure out how to implement them.
they need to spawn at fixed y-value and a random x-value (within the screen)
Upon shooting on the bombs they should die but if bombs hit the ship game is over.
Please help i'm a bit stuck.
package kiloboltgame;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.URL;
import java.util.ArrayList;
public class StartingClass extends Applet implements Runnable, KeyListener {
private Ship ship;
public static Bomb b1, b2;
public static int score = 0;
private Font font = new Font(null, Font.BOLD, 30);
private Image image, Battleship, Background, Bomb;
private static Background bg1, bg2;
private URL base;
private Graphics second;
#Override
public void init() {
setSize(800, 480);
setBackground(Color.BLACK);
setFocusable(true);
addKeyListener(this);
Frame frame = (Frame) this.getParent().getParent();
frame.setTitle("BattleShip");
try{
base = getDocumentBase();
}catch (Exception e){
//TODO: handle exception
}
//Image Setups
Battleship = getImage(base, "data/Battleship.png");
Background = getImage(base, "data/Background.png");
Bomb = getImage(base, "data/Bomb1.png");
}
#Override
public void start() {
bg1 = new Background(0, 0);
bg2 = new Background(800, 0);
ship = new Ship();
b1 = new Bomb(340, 100);
b2 = new Bomb(700, 100);
Thread thread = new Thread(this);
thread.start();
}
#Override
public void stop() {
// TODO Auto-generated method stub
}
#Override
public void destroy() {
// TODO Auto-generated method stub
}
#Override
public void run() {
while (true) {
ship.update();
ArrayList projectiles = ship.getProjectiles();
for(int i = 0; i < projectiles.size(); i++){
Projectile p = (Projectile) projectiles.get(i);
if(p.isVisible() == true){
p.update();
}else{
projectiles.remove(i);
}
}
b1.update();
b2.update();
bg1.update();
bg2.update();
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void update(Graphics g) {
if(image == null){
image = createImage(this.getWidth(), this.getHeight());
second = image.getGraphics();
}
second.setColor(getBackground());
second.fillRect(0, 0, getWidth(), getHeight());
second.setColor(getForeground());
paint(second);
g.drawImage(image, 0, 0, this);
}
#Override
public void paint(Graphics g) {
g.drawImage(Background, bg1.getBgX(), bg1.getBgY(), this);
ArrayList projectiles = ship.getProjectiles();
for(int i = 0; i < projectiles.size(); i++){
Projectile p = (Projectile) projectiles.get(i);
g.setColor(Color.BLACK);
g.fillRect(p.getX(), p.getY(), 5, 10);
}
g.drawImage(Battleship, ship.getCenterX() + 230, ship.getCenterY() -23, this);
g.drawImage(Bomb, b1.getCenterX() - 20, b1.getCenterY() - 20, this);
g.drawImage(Bomb, b2.getCenterX() - 20, b2.getCenterY() - 20, this);
g.setFont(font);
g.setColor(Color.BLACK);
g.drawString(Integer.toString(score), 710, 30);
}
#Override
public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:
ship.moveLeft();
break;
case KeyEvent.VK_RIGHT:
ship.moveRight();
break;
case KeyEvent.VK_CONTROL:
ship.shoot();
score = score +100;
break;
}
}
#Override
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
ship.stop();
break;
case KeyEvent.VK_RIGHT:
ship.stop();
break;
}
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
public static Background getBg1() {
return bg1;
}
}
package kiloboltgame;
import java.util.ArrayList;
public class Ship {
//In Java, Class Variables should be private so that only its methods can change them.
private int centerX = 100;
private int centerY = 382;
private int speedX = 0;
private int speedY = 1;
private ArrayList<Projectile> projectiles = new ArrayList<Projectile>();
public void update() {
// Moves Character or Scrolls Background accordingly.
if (speedX < 0) {
centerX += speedX;
} else if (speedX == 0) {
System.out.println("Do not scroll the background.");
} else {
if (centerX <= 440) {
centerX += speedX;
} else {
System.out.println("Scroll Background Here");
}
}
// Updates Y Position
if (centerY + speedY >= 382) {
centerY = 382;
}else{
centerY += speedY;
}
// Prevents going beyond X coordinate of 0
if (centerX + speedX <= -230) {
centerX = -229;
}
}
public void moveRight() {
speedX = 6;
}
public void moveLeft() {
speedX = -6;
}
public void shoot(){
Projectile p = new Projectile(centerX + 285, centerY -10);
projectiles.add(p);
}
public ArrayList getProjectiles(){
return projectiles;
}
public void stop() {
speedX = 0;
}
public int getCenterX() {
return centerX;
}
public int getCenterY() {
return centerY;
}
public int getSpeedX() {
return speedX;
}
public int getSpeedY() {
return speedY;
}
public void setCenterX(int centerX) {
this.centerX = centerX;
}
public void setCenterY(int centerY) {
this.centerY = centerY;
}
public void setSpeedX(int speedX) {
this.speedX = speedX;
}
public void setSpeedY(int speedY) {
this.speedY = speedY;
}
}
package kiloboltgame;
public class Background {
private int bgX, bgY, speedX;
public Background(int x, int y){
bgX = x;
bgY = y;
speedX = 0;
}
public void update() {
bgX += speedX;
if (bgX <= -800){
bgX += 1600;
}
}
public int getBgX() {
return bgX;
}
public int getBgY() {
return bgY;
}
public int getSpeedX() {
return speedX;
}
public void setBgX(int bgX) {
this.bgX = bgX;
}
public void setBgY(int bgY) {
this.bgY = bgY;
}
public void setSpeedX(int speedX) {
this.speedX = speedX;
}
}
public class Projectile {
private int x, y, speedY;
private boolean visible;
public Projectile(int startX, int startY) {
x = startX;
y = startY;
speedY = -7;
visible = true;
}
public void update() {
y += speedY;
if(y > 480){
visible = false;
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getSpeedY() {
return speedY;
}
public boolean isVisible() {
return visible;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setSpeedY(int speedY) {
this.speedY = speedY;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
}
package kiloboltgame;
public class Enemy {
private int maxHealth, currentHealth, power, speedX, centerX, centerY;
private Background bg = StartingClass.getBg1();
//Behavioral Methods
public void update(){
centerX += speedX;
speedX = bg.getSpeedX();
}
public void die(){
}
public void attack(){
}
public int getMaxHealth() {
return maxHealth;
}
public int getCurrentHealth() {
return currentHealth;
}
public int getPower() {
return power;
}
public int getSpeedX() {
return speedX;
}
public int getCenterX() {
return centerX;
}
public int getCenterY() {
return centerY;
}
public Background getBg() {
return bg;
}
public void setMaxHealth(int maxHealth) {
this.maxHealth = maxHealth;
}
public void setCurrentHealth(int currentHealth) {
this.currentHealth = currentHealth;
}
public void setPower(int power) {
this.power = power;
}
public void setSpeedX(int speedX) {
this.speedX = speedX;
}
public void setCenterX(int centerX) {
this.centerX = centerX;
}
public void setCenterY(int centerY) {
this.centerY = centerY;
}
public void setBg(Background bg) {
this.bg = bg;
}
}
package kiloboltgame;
public class Bomb extends Enemy {
public Bomb(int centerX, int centerY) {
setCenterX(centerX);
setCenterY(centerY);
}
}
This is all code that i have so far (I know the background is f*ed since the game this is based on is scrolling right and i haven't fixed it yet.
I recommend putting all object creation in a seperate part of the program. I'd make a BombFactory with a makeBomb mathod that returns a new Bomb instance. Inside the factory, figure out the x-coordinate, for instance using a randomiser. As parameters, you could specify a y-coordinate and possibly an upper and lower bound for the x. This way you can make new Bombs on the fly.
public class BombFactory {
private final Random rand;
public BombFactory() {
this.rand = new Random();
}
public Bomb makeBomb(int lowerboundX, int rangeX, int yPos) {
final int xPos = lowerboundX + rand.nextInt(rangeX);
return new Bomb(xPos, yPos);
}
}
As for the behaviour, I'd look into inheritance and interfaces some more. I see a lot of methods occurring more than once. You generally want to avoid that kind of duplication. You can start by taking all the methods having something to do with coords or movement and putting them in an abstract base class.
You can make a method inside Enemy that checks for a collision and responds to that in different ways, depending on how the subclass overrides it. In case of a Bomb, it would probably always kill itself and whatever it came in contact with.

Java Multithread

After making changes based on user suggestions I have taken the following code a few steps further by introducing single/double click recognition. Why are the balls being created in the top left corner and not where the mouse is clicked?
BouncingBalls.java
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class BouncingBalls extends JPanel implements MouseListener {
protected List<RandomBall> randomBalls = new ArrayList<RandomBall>(20);
protected List<VerticalBall> verticalBalls = new ArrayList<VerticalBall>(20);
private Container container;
private DrawCanvas canvas;
private Boolean doubleClick = false;
private final Integer waitTime = (Integer) Toolkit.getDefaultToolkit()
.getDesktopProperty("awt.multiClickInterval");
private static int canvasWidth = 500;
private static int canvasHeight = 500;
public static final int UPDATE_RATE = 30;
int count = 0;
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
public BouncingBalls(int width, int height) {
canvasWidth = width;
canvasHeight = height;
container = new Container();
canvas = new DrawCanvas();
this.setLayout(new BorderLayout());
this.add(canvas, BorderLayout.CENTER);
this.addMouseListener(this);
start();
}
public void start() {
Thread t = new Thread() {
public void run() {
while (true) {
update();
repaint();
try {
Thread.sleep(1000 / UPDATE_RATE);
} catch (InterruptedException e) {
}
}
}
};
t.start();
}
public void update() {
for (RandomBall ball : randomBalls) {
ball.ballBounce(container);
}
for (VerticalBall ball : verticalBalls) {
ball.verticalBounce(container);
}
}
class DrawCanvas extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
container.draw(g);
for (RandomBall ball : randomBalls) {
ball.draw(g);
}
for (VerticalBall ball : verticalBalls) {
ball.draw(g);
}
}
public Dimension getPreferredSize() {
return (new Dimension(canvasWidth, canvasHeight));
}
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame("Stack Answer 2");
f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
f.setContentPane(new BouncingBalls(canvasHeight, canvasWidth));
f.pack();
f.setVisible(true);
}
});
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
if (e.getClickCount() >= 2) {
doubleClick = true;
verticalBalls.add(new VerticalBall(getX(), getY(), canvasWidth, canvasHeight));
System.out.println("double click");
} else {
Timer timer = new Timer(waitTime, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (doubleClick) {
/* we are the first click of a double click */
doubleClick = false;
} else {
count++;
randomBalls.add(new RandomBall(getX(), getY(), canvasWidth, canvasHeight));
/* the second click never happened */
System.out.println("single click");
}
}
});
timer.setRepeats(false);
timer.start();
}
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
}
RandomBall.java
import java.awt.Color;
import java.awt.Graphics;
public class RandomBall {
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
private int x;
private int y;
private int canvasWidth = 500;
private int canvasHeight = 500;
private boolean leftRight;
private boolean upDown;
private int deltaX;
private int deltaY;
private int radius = 20;
private int red = random(255);
private int green = random(255);
private int blue = random(255);
RandomBall(int x, int y, int width, int height) {
this(x, y, width, height, false, false);
}
RandomBall(int x, int y, int width, int height, boolean leftRight, boolean upDown) {
this.x = x;
this.y = y;
this.canvasWidth = width;
this.canvasHeight = height;
this.leftRight = leftRight;
this.upDown = upDown;
updateDelta();
}
public void draw(Graphics g) {
g.setColor(new Color(red, green, blue));
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius),
(int) (2 * radius));
}
private void updateDelta() {
final int minimumMovement = 5;
final int maxExtra = 10;
deltaY = minimumMovement + (int) (Math.random() * maxExtra);
deltaX = minimumMovement + (int) (Math.random() * maxExtra);
}
public void ballBounce(Container container) {
// controls horizontal ball motion
if (leftRight) {
x += deltaX;
if (x >= getWidth()) {
leftRight = false;
updateDelta();
}
} else {
x += -deltaX;
if (x <= 0) {
leftRight = true;
updateDelta();
}
}
// controls vertical ball motion
if (upDown) {
y += deltaY;
if (y >= getHeight()) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
public void verticalBounce(Container container) {
// controls vertical ball motion
if (upDown) {
y += deltaY;
if (y >= getHeight()) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return canvasWidth;
}
public int getHeight() {
return canvasHeight;
}
}
VerticalBall.java
import java.awt.Color;
import java.awt.Graphics;
public class VerticalBall {
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
private int x;
private int y;
private int canvasWidth = 500;
private int canvasHeight = 500;
private boolean upDown;
private int deltaY;
private int radius = 20;
private int red = random(255);
private int green = random(255);
private int blue = random(255);
VerticalBall(int x, int y, int width, int height) {
this(x, y, width, height, false);
}
VerticalBall(int x, int y, int width, int height, boolean upDown) {
this.x = x;
this.y = y;
this.canvasWidth = width;
this.canvasHeight = height;
this.upDown = upDown;
updateDelta();
}
public void draw(Graphics g) {
g.setColor(new Color(red, green, blue));
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius),
(int) (2 * radius));
}
private void updateDelta() {
final int minimumMovement = 5;
final int maxExtra = 10;
deltaY = minimumMovement + (int) (Math.random() * maxExtra);
}
public void verticalBounce(Container container) {
// controls vertical ball motion
if (upDown) {
y += deltaY;
if (y >= getHeight()) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return canvasWidth;
}
public int getHeight() {
return canvasHeight;
}
}
Container.java
import java.awt.Color;
import java.awt.Graphics;
public class Container {
private static final int HEIGHT = 500;
private static final int WIDTH = 500;
private static final Color COLOR = Color.WHITE;
public void draw(Graphics g) {
g.setColor(COLOR);
g.fillRect(0, 0, WIDTH, HEIGHT);
}
}
Change your move method in the Ball class with this one (your conditions are not corrects) :
public void move(Container container) {
if (leftRight) {
x += deltaX;
if (x >= canvasWidth) {
leftRight = false;
updateDelta();
}
} else {
x += -deltaX;
if (x <= 0) {
leftRight = true;
updateDelta();
}
}
if (upDown) {
y += deltaY;
upDown = !(y >= (canvasHeight));
if (y >= (canvasHeight)) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
I run it and it works
Your movement logic is overly complicated for simply moving a ball, you should think about conservation of momentum if you want it to bounce off the walls.
The problem is
x >= (Ball.this.getWidth() - canvasWidth / 2)` and `y >= (Ball.this.getHeight() - canvasHeight / 2)
You are creating balls using the first constructor
balls.add(new Ball(x, y, canvasWidth, canvasHeight));
So you are just checking if x>=0 and y>=0, it will keep bouncing around with +- deltax/deltay positions.
A simpler way of making it bounce off walls would be
public void move(Container container) {
if(x>=canvasWidth || x<=0){
deltaX = -1*deltaX;
}
if(y>=canvasHeight || y<=0){
deltaY = -1*deltaY;
}
x+= deltaX;
y+= deltaY;
}
change
public void move(Container container) {
if (leftRight) {
x += deltaX;
if (x >= (Ball.this.getWidth() - canvasWidth / 2)) {
leftRight = false;
updateDelta();
}
} else {
x += -deltaX;
if (x <= 0) {
leftRight = true;
updateDelta();
}
}
if (upDown) {
y += deltaY;
upDown = !(y >= (Ball.this.getHeight() - canvasHeight / 2));
if (y >= (Ball.this.getHeight() - canvasHeight / 2)) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
to
public void move(Container container) {
if (leftRight) {
x += deltaX;
if (x >= getWidth()) {
leftRight = false;
updateDelta();
}
} else {
x += -deltaX;
if (x <= 0) {
leftRight = true;
updateDelta();
}
}
if (upDown) {
y += deltaY;
if (y >= getHeight()) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
It's because you're using Ball.this.getWidth() and getHeight()...which are set to the canvas height in the constructor. If you use the radius instead, it works. For example:
if ( x >= ( canvasWidth - radius ) )
{
leftRight = false;
updateDelta();
}
And:
if ( y >= ( canvasHeight - radius ) )
{
upDown = false;
updateDelta();
}

Multiple bouncing balls thread issue

I created a program that makes multiple bouncing balls with random color, speed and radius. When user clicks on the screen a new random ball should appear and move around screen. But i have a multi-thread issue. When i click on the screen a ball appears and doesn't moving at all. When another click comes nothing happens.
BouncingBalls Class
public class BouncingBalls extends JPanel implements MouseListener{
private Ball ball;
protected List<Ball> balls = new ArrayList<Ball>(20);
private Container container;
private DrawCanvas canvas;
private int canvasWidth;
private int canvasHeight;
public static final int UPDATE_RATE = 30;
int x = random(480);
int y = random(480);
int speedX = random(30);
int speedY = random(30);
int radius = random(20);
int red = random(255);
int green = random(255);
int blue = random(255);
int count = 0;
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
public BouncingBalls(int width, int height){
canvasWidth = width;
canvasHeight = height;
ball = new Ball(x, y, speedX, speedY, radius, red, green, blue);
container = new Container();
canvas = new DrawCanvas();
this.setLayout(new BorderLayout());
this.add(canvas, BorderLayout.CENTER);
this.addMouseListener(this);
}
public void start(){
Thread t = new Thread(){
public void run(){
while(true){
update();
repaint();
try {
Thread.sleep(1000 / UPDATE_RATE);
} catch (InterruptedException e) {}
}
}
};
t.start();
}
public void update(){
ball.move(container);
}
class DrawCanvas extends JPanel{
public void paintComponent(Graphics g){
super.paintComponent(g);
container.draw(g);
ball.draw(g);
}
public Dimension getPreferredSize(){
return(new Dimension(canvasWidth, canvasHeight));
}
}
public static void main(String[] args){
javax.swing.SwingUtilities.invokeLater(new Runnable(){
public void run(){
JFrame f = new JFrame("Bouncing Balls");
f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
f.setContentPane(new BouncingBalls(500, 500));
f.pack();
f.setVisible(true);
}
});
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
count++;
balls.add(new Ball(x, y, speedX, speedY, radius, red, green, blue));
balls.get(count-1).start();
start();
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
}
Ball Class
import java.awt.Color;
import java.awt.Graphics;
public class Ball{
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
private BouncingBalls balls;
int x = random(480);
int y = random(480);
int speedX = random(30);
int speedY = random(30);
int radius = random(20);
int red = random(255);
int green = random(255);
int blue = random(255);
int i = 0;
public Ball(int x, int y, int speedX, int speedY, int radius, int red, int green, int blue){
this.x = x;
this.y = y;
this.speedX = speedX;
this.speedY = speedY;
this.radius = radius;
this.red = red;
this.green = green;
this.blue = blue;
}
public void draw(Graphics g){
for(Ball ball : balls){
g.setColor(new Color(red, green, blue));
g.fillOval((int)(x - radius), (int)(y - radius), (int)(2 * radius), (int)(2 * radius));
}
}
public void move(Container container){
x += speedX;
y += speedY;
if(x - radius < 0){
speedX = -speedX;
x = radius;
}
else if(x + radius > 500){
speedX = -speedX;
x = 500 - radius;
}
if(y - radius < 0){
speedY = -speedY;
y = radius;
}
else if(y + radius > 500){
speedY = -speedY;
y = 500 - radius;
}
}
}
Container Class
import java.awt.Color;
import java.awt.Graphics;
public class Container {
private static final int HEIGHT = 500;
private static final int WIDTH = 500;
private static final Color COLOR = Color.WHITE;
public void draw(Graphics g){
g.setColor(COLOR);
g.fillRect(0, 0, WIDTH, HEIGHT);
}
}
You're maintaing two different references to your ball.
You have a reference to a single Ball called ball and a List of balls. Your update and paint methods only reference the single ball
Ball doesn't seem to have a start method (that I can see) so this balls.get(count-1).start(); doesn't make sense...
Updated
You don't need the reference to ball
While not a bad idea, while testing, you should probably call start in the constructor
Your update method in BouncingBalls should looping through the balls list, calling move on each ball in the list...
The paintComponent method of DrawCanvas needs access to and should make use of the balls list. This might be better achievable through a model interface
Do not construct a new Ball with parameters, as it's giving each ball the same properties, especially when you assign random values to it when you construct it any way...
Ball doesn't have (or need) a start method
public class BouncingBalls extends JPanel implements MouseListener {
// private Ball ball;
protected List<Ball> balls = new ArrayList<Ball>(20);
private Container container;
private DrawCanvas canvas;
private int canvasWidth;
private int canvasHeight;
public static final int UPDATE_RATE = 30;
int x = random(480);
int y = random(480);
int speedX = random(30);
int speedY = random(30);
int radius = random(20);
int red = random(255);
int green = random(255);
int blue = random(255);
int count = 0;
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
public BouncingBalls(int width, int height) {
canvasWidth = width;
canvasHeight = height;
// ball = new Ball(x, y, speedX, speedY, radius, red, green, blue);
container = new Container();
canvas = new DrawCanvas();
this.setLayout(new BorderLayout());
this.add(canvas, BorderLayout.CENTER);
this.addMouseListener(this);
start();
}
public void start() {
Thread t = new Thread() {
public void run() {
while (true) {
update();
repaint();
try {
Thread.sleep(1000 / UPDATE_RATE);
} catch (InterruptedException e) {
}
}
}
};
t.start();
}
public void update() {
for (Ball ball : balls) {
ball.move(container);
}
}
class DrawCanvas extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
container.draw(g);
for (Ball ball : balls) {
ball.draw(g);
}
// ball.draw(g);
}
public Dimension getPreferredSize() {
return (new Dimension(canvasWidth, canvasHeight));
}
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame("Bouncing Balls");
f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
f.setContentPane(new BouncingBalls(500, 500));
f.pack();
f.setVisible(true);
}
});
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
count++;
balls.add(new Ball());
// balls.add(new Ball(x, y, speedX, speedY, radius, red, green, blue));
// balls.get(count - 1).start();
// start();
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
public static class Ball {
public int random(int maxRange) {
return (int) Math.round(Math.random() * maxRange);
}
int x = random(480);
int y = random(480);
int speedX = random(30);
int speedY = random(30);
int radius = random(20);
int red = random(255);
int green = random(255);
int blue = random(255);
int i = 0;
public Ball() { //int x, int y, int speedX, int speedY, int radius, int red, int green, int blue) {
// this.x = x;
// this.y = y;
// this.speedX = speedX;
// this.speedY = speedY;
// this.radius = radius;
// this.red = red;
// this.green = green;
// this.blue = blue;
}
public void draw(Graphics g) {
g.setColor(new Color(red, green, blue));
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius), (int) (2 * radius));
}
public void move(Container container) {
x += speedX;
y += speedY;
if (x - radius < 0) {
speedX = -speedX;
x = radius;
} else if (x + radius > 500) {
speedX = -speedX;
x = 500 - radius;
}
if (y - radius < 0) {
speedY = -speedY;
y = radius;
} else if (y + radius > 500) {
speedY = -speedY;
y = 500 - radius;
}
}
}
public static class Container {
private static final int HEIGHT = 500;
private static final int WIDTH = 500;
private static final Color COLOR = Color.WHITE;
public void draw(Graphics g) {
g.setColor(COLOR);
g.fillRect(0, 0, WIDTH, HEIGHT);
}
}
}
Updated
As pointed out by the commentators, ArrayList is not thread safe, it's not a good idea to have multiple threads trying to access it simultaneously. While adding is slightly safer then removing, it is still bad practice.
You can either replace ArrayList with Vector, which would be the simpler solution, or synchronize the access to the list around a common monitor lock. Given your example, I'd use a java.util.Vector
You Can try this alternate Java Programm for bouncing 10 multi-colored balls on a single "START" button.....
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javaimage.io.*;
class Thr extends Thread
{
boolean up=false;
Ballbounce parent;
int top,left;
Color c;
Thr(int t,int l,Color cr,ex5 p)
{
top=l;
if(top > 170)
top=170-t/8;
left=t;
c=cr;
parent=p;
}
public void run()
{
try
{
while(true)
{
Thread.sleep(37);
if(top >= 188)
up=true;
if(top <= 0)
up=false;
if(!up)
top=top+2;
else
top=top-2;
parent.p.repaint();
}
}catch(Exception e){}
}
}
class Ballbounce extends JFrame implements ActionListener
{
int top=0,left=0,n=0,radius=50;
Color C[]={Color.black,Color.cyan,Color.orange,Color.red,Color.yellow,Color.pink,Color.gray,Color.blue,Color.green,Color.magenta};
Thr t[]=new Thr[10];
GPanel p;
JButton b;
Panel p1;
Ballbounce()
{
setSize(700,300);
setVisible(true);
setLayout( new BorderLayout());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(p=new GPanel(this),BorderLayout.CENTER);
b= new JButton("Start");
b.addActionListener(this);
add(p1=new Panel(),BorderLayout.SOUTH);
p1.setBackground(Color.lightGray);
p1.add(b);
}
public static void main(String args[])
{
new Ballbounce();
}
public void actionPerformed(ActionEvent e)
{
t[n]=new Thr(left+(radius+13)*n+29,top+n*25,C[n],this);
t[n].start();
n++;
p.repaint();
if(n >9)
b.setEnabled(false);
}
}
class GPanel extends JPanel
{
Ballbounce parent;
GPanel(Ballbounce p)
{
parent=p;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
setBackground(Color.white);
for(int i=0;i< parent.n;i++)
{
g.setColor(parent.t[i].c);
g.fillOval(parent.t[i].left,parent.t[i].top,parent.radius,parent.radius);
}
}
}
I hope you will like it....
If u are unable to understand the code... You can question it anytime...... :)
Enjoy the code... :)

how to notify multithread event on swing game

I am supposed to make a little game simulation. in this game there are three button . when user click start tank and car will close each other in 90 degrees when user click shut button tank will throw bullet to car.
i made and a simulation for this. tank throw bullet to car but when bullet crash car i couldn't this. i just need increase score of how many times tank hit car.
here is the source code
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Vehicle extends Thread {
private JPanel box;
private int XSIZE;
private int YSIZE;
private int time;
private int x;
private int y;
private int dx = 5;
private int dy = 5;
private int dim;
public Vehicle(JPanel b, int i) {
box = b;
this.dim = i;
if (i == 0) {
x = 0;
y = 100;
time = 1000;
XSIZE = 9;
YSIZE = 20;
} else {
time = 200;
y = box.getSize().height;
x = box.getSize().width / 2;
XSIZE = 6;
YSIZE = 10;
}
}
public void draw() {
Graphics g = box.getGraphics();
g.fillOval(x, y, XSIZE, YSIZE);
g.dispose();
}
public void moveHorizontal() {
if (!box.isVisible())
return;
Graphics g = box.getGraphics();
g.setColor(Color.BLUE);
g.setXORMode(box.getBackground());
g.fillOval(x, y, XSIZE, YSIZE);
x += dx;
Dimension d = box.getSize();
if (x < 0) {
x = 0;
dx = -dx;
}
if (x + XSIZE >= d.width) {
x = d.width - XSIZE;
dx = -dx;
}
g.fillOval(x, y, XSIZE, YSIZE);
g.dispose();
}
public JPanel getBox() {
return box;
}
public void setBox(JPanel box) {
this.box = box;
}
public void moveVertical() {
if (!box.isVisible())
return;
Graphics g = box.getGraphics();
g.setXORMode(box.getBackground());
g.fillOval(x, y, XSIZE, YSIZE);
y += dy;
Dimension d = box.getSize();
if (y < 0) {
y = 0;
dy = -dy;
}
if (y + YSIZE >= d.height) {
y = d.height - YSIZE;
dy = -dy;
}
g.fillOval(x, y, XSIZE, YSIZE);
g.dispose();
}
public void move(int i) {
if (i == 0) {
moveHorizontal();
} else {
moveVertical();
}
}
public int getYSIZE() {
return YSIZE;
}
public void setYSIZE(int ySIZE) {
YSIZE = ySIZE;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public int getXSIZE() {
return XSIZE;
}
public void setXSIZE(int xSIZE) {
XSIZE = xSIZE;
}
public void setY(int y) {
this.y = y;
}
public void run() {
try {
draw();
for (;;) {
move(dim);
sleep(time);
}
} catch (InterruptedException e) {
}
}
}
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Bullet extends Thread {
private JPanel box;
private int XSIZE = 3;
public int getXSIZE() {
return XSIZE;
}
public void setXSIZE(int xSIZE) {
XSIZE = xSIZE;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
private int YSIZE = 1;
private int x;
private int y;
private int dx = 3;
public Bullet(JPanel b, Vehicle tank, Vehicle car) {
box = b;
x = tank.getX() + tank.getXSIZE();
if (x >= tank.getBox().getSize().width / 2)
dx = -dx;
y = tank.getY() + tank.getYSIZE() / 2;
;
}
public void draw() {
Graphics g = box.getGraphics();
g.fillOval(x, y, XSIZE, YSIZE);
g.dispose();
}
public void move() {
if (!box.isVisible())
return;
Graphics g = box.getGraphics();
g.setColor(Color.RED);
g.setXORMode(box.getBackground());
g.fillOval(x, y, XSIZE, YSIZE);
x += dx;
Dimension d = box.getSize();
if (x < 0) {
x = 0;
}
if (x + XSIZE >= d.width) {
x = d.width - XSIZE;
}
g.fillOval(x, y, XSIZE, YSIZE);
g.dispose();
}
public void run() {
try {
draw();
for (;;) {
move();
sleep(20);
}
} catch (InterruptedException e) {
}
}
}
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
#SuppressWarnings("serial")
public class Tank_Shut_Car_ThreadFrame extends JFrame {
private JPanel canvas;
private boolean isOn = false;
private Vehicle tank;
private Vehicle car;
private JLabel score;
public static int sc = 0;
public Tank_Shut_Car_ThreadFrame() {
setResizable(false);
setSize(600, 400);
setTitle("Tank Shut Car");
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
Container contentPane = getContentPane();
canvas = new JPanel();
contentPane.add(canvas, "Center");
canvas.setLayout(null);
score = new JLabel("0");
score.setBounds(527, 11, 36, 14);
canvas.add(score);
JLabel lblScore = new JLabel("score");
lblScore.setBounds(481, 11, 36, 14);
canvas.add(lblScore);
JPanel p = new JPanel();
addButton(p, "Start", new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (!isOn) {
tank = new Vehicle(canvas, 0);
tank.start();
car = new Vehicle(canvas, 1);
car.start();
isOn = true;
}
}
});
addButton(p, "Shut", new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (isOn) {
Bullet bullet = new Bullet(canvas, tank, car);
bullet.start();
score.setText("" + sc);
}
}
});
addButton(p, "Close", new ActionListener() {
public void actionPerformed(ActionEvent evt) {
canvas.setVisible(false);
System.exit(0);
}
});
contentPane.add(p, "South");
}
public void addButton(Container c, String title, ActionListener a) {
JButton button = new JButton(title);
c.add(button);
button.addActionListener(a);
}
}
import javax.swing.JFrame;
public class Test {
public static void main(String[] args) {
JFrame frame = new Tank_Shut_Car_ThreadFrame();
frame.setVisible(true);
}
}
Okay, so I had a play around with this (just for fun)
Now, this is far from a "proper" or "complete" game engine, but it provides a basic idea of how it might be possible to mix a core thread engine with UI components.
Rather then pasting the entire code, I've uploaded the source it to Tank.zip
But the basic engine looks like this...
public class GameEngine extends Thread {
public static final Object ASSET_LOCK = new Object();
private List<GameAsset> lstAssets;
private GameScreen screen;
public GameEngine(GameScreen screen) {
// Let the thread die when the JVM closes
setDaemon(true);
// Want to be below the UI thread (personal preference)
setPriority(NORM_PRIORITY - 1);
// A list of game assests
lstAssets = new ArrayList<GameAsset>(25);
// A reference to the screen
this.screen = screen;
// Add global key listener, this is simpler to the trying to attach a key listener
// to the screen.
Toolkit.getDefaultToolkit().addAWTEventListener(new EventHandler(), AWTEvent.KEY_EVENT_MASK);
}
public GameAsset[] getAssets() {
synchronized (ASSET_LOCK) {
return lstAssets.toArray(new GameAsset[lstAssets.size()]);
}
}
/*
* Allows for assets to be added
*/
public void addAsset(GameAsset asset) {
synchronized (ASSET_LOCK) {
lstAssets.add(asset);
}
}
#Override
public void run() {
while (true) {
try {
sleep(40);
} catch (InterruptedException ex) {
}
synchronized (ASSET_LOCK) {
GameAsset[] assets = lstAssets.toArray(new GameAsset[lstAssets.size()]);
for (GameAsset asset : assets) {
if (lstAssets.contains(asset)) {
asset.update(this, screen);
}
}
screen.repaint(new ArrayList<GameAsset>(lstAssets));
}
}
}
/**
* Allows the removal of an asset...
*/
public void removeAsset(GameAsset asset) {
synchronized (ASSET_LOCK) {
lstAssets.remove(asset);
}
}
/**
* Key event handling...
*/
protected class EventHandler implements AWTEventListener {
#Override
public void eventDispatched(AWTEvent event) {
KeyEvent keyEvent = (KeyEvent) event;
if (keyEvent.getID() == KeyEvent.KEY_PRESSED) {
synchronized (ASSET_LOCK) {
GameAsset[] assets = lstAssets.toArray(new GameAsset[lstAssets.size()]);
for (GameAsset asset : assets) {
if (lstAssets.contains(asset)) {
asset.processKeyPressed(GameEngine.this, screen, keyEvent);
}
}
}
} else if (keyEvent.getID() == KeyEvent.KEY_RELEASED) {
synchronized (ASSET_LOCK) {
GameAsset[] assets = lstAssets.toArray(new GameAsset[lstAssets.size()]);
for (GameAsset asset : lstAssets) {
if (lstAssets.contains(asset)) {
asset.processKeyReleased(GameEngine.this, screen, keyEvent);
}
}
}
}
}
}
}
Swing is not thread safe. Events should be fired on the event-dispatching thread.
http://www.javamex.com/tutorials/threads/invokelater.shtml

Categories