My program is throwing a NullPointerException error upon trying to call the paint method of a ship.
import java.awt.*;
import java.awt.event.*;
public class Ship extends Polygon implements ActionListener, KeyListener {
final static Point[] shape = new Point[4];
final static Point START_POSITION = new Point(400, 300);
private int windowX;
private int windowY;
Point pull;
public Ship(int maxX, int maxY) {
super(shape, START_POSITION, 0);
windowX = maxX;
windowY = maxY;
shipInit();
init(shape);
Point[] pointArray = getPoints();
pull = new Point(0, 0);
}
public void shipInit() {
shape[0] = new Point(0, 0);
shape[1] = new Point(30, 10);
shape[2] = new Point(0, 20);
shape[3] = new Point(10, 10);
}
public void paint(Graphics brush) {
brush.setColor(Color.white);
Point[] points = getPoints();
if (position.x > windowX) {
position.x -= windowX;
} else if (position.x < 0) {
position.x += windowX;
} else if (position.y > windowY) {
position.y -= windowY;
} else if (position.y < 0) {
position.x += windowY;
}
for (int i = 0; i < points.length; i++) {
int x1;
int y1;
int x2;
int y2;
if (i == points.length - 1) {
x1 = (int) points[i].x;
y1 = (int) points[i].y;
x2 = (int) points[0].x;
y2 = (int) points[0].y;
} else {
x1 = (int) points[i].x;
y1 = (int) points[i].y;
x2 = (int) points[i + 1].x;
y2 = (int) points[i + 1].y;
}
brush.drawLine(x1, y1, x2, y2);
}
}
public void move() {
position.x += 2;
position.y += 5;
// position.y += 1;
}
public void accelerate(double acceleration) {
pull.x += (acceleration * Math.cos(Math.toRadians(rotation)));
pull.y += (acceleration * Math.sin(Math.toRadians(rotation)));
}
public void keyPressed(KeyEvent e) {
int keyPressed = e.getKeyCode();
if (keyPressed == KeyEvent.VK_W)
System.out.println("Hi");
if (keyPressed == KeyEvent.VK_UP) {
accelerate(5);
position.x += pull.x;
position.y += pull.y;
}
if (keyPressed == KeyEvent.VK_RIGHT) {
rotation += 5;
}
if (keyPressed == KeyEvent.VK_LEFT) {
rotation -= 5;
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
}
And the class the calls the paint
import java.awt.*;
import java.awt.event.*;
class Asteroids extends Game {
private static int frameWidth = 800;
private static int frameHeight = 600;
Ship player;
public Asteroids() {
super("Asteroids!", frameWidth, frameHeight);
init();
}
public void init() {
player = new Ship(800, 600);
this.addKeyListener(player);
repaint();
}
public void paint(Graphics brush) {
brush.setColor(Color.black);
brush.fillRect(0, 0, width,height);
brush.setColor(Color.white);
String hi = "hi";
brush.drawString(hi, 299, 399);
player.paint(brush);
}
public static void main (String[] args) {
new Asteroids();
}
}
The error says that the error is thrown when calling player.paint(brush). Can anyone figure out why?
I have never coded in this before, but a quick trip to the universe of google explained to me that paint() and init() runs on 2 different threads.
That said, your paint() method gets called BEFORE the init() method.
A quick way to fix this would be to have a boolean that gets set inside init()
Like this:
import java.awt.*;
import java.awt.event.*;
class Asteroids extends Game {
private static int frameWidth = 800;
private static int frameHeight = 600;
Ship player;
private boolean hasInitialized; // Initialization check
public Asteroids() {
super("Asteroids!", frameWidth, frameHeight);
init();
}
public void init() {
player = new Ship(800, 600);
this.addKeyListener(player);
hasInitialized = true; // We set the boolean to true to indicate that we have initialized.
repaint();
}
public void paint(Graphics brush) {
if (!hasInitialized) return; // Nope, not ready yet, we'll stop here.
brush.setColor(Color.black);
brush.fillRect(0, 0, width,height);
brush.setColor(Color.white);
String hi = "hi";
brush.drawString(hi, 299, 399);
player.paint(brush);
}
public static void main (String[] args) {
new Asteroids();
}
}
I'm not 100% certain this will fix it, considering you already have init() inside the constructor.
But it's worth a try!
Source: Java, applet: How to block the activation of paint() before init() finishes it's work
Related
I am making a game and tutorials for how to make it on youtube. Here is the link to the channel. I explain the first part of what I have and why I have it because I know that is helpful for filling you in.
Link to part 1(Then watch the rest of parts. #Chris, this is helpful for solving the problem so don't flag the post).
https://www.youtube.com/watch?v=IRn_ZGhJZ94
I noticed as I was testing out my code for part 4. before recording, the game lagged HORRIBLY. I have alot of code, and any help is appreciated.
Game class:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;
#SuppressWarnings("serial")
public class Game extends JPanel implements ActionListener{
Timer mainTimer;
Paddle paddle;
Ball ball;
int blockCount = 16;
static ArrayList<Block> blocks = new ArrayList<Block>();
public Game() {
setFocusable(true);
paddle = new Paddle(250, 300);
addKeyListener(new KeyAdapt(paddle));
ball = new Ball(275, 280);
mainTimer = new Timer(10, this);
mainTimer.start();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/background.png");
g2d.drawImage(ic.getImage(), 0, 0, null);
paddle.draw(g2d);
ball.draw(g2d);
for(int i = 0; i < blockCount; i++) {
Block b = blocks.get(i);
b.draw(g2d);
}
}
#Override
public void actionPerformed(ActionEvent arg0) {
paddle.update();
ball.update();
for(int i = 0; i < blocks.size(); i++) {
Block b = blocks.get(i);
b.update();
}
repaint();
startGame();
}
public void addBlock(Block b) {
blocks.add(b);
}
public static void removeBlock(Block b) {
blocks.remove(b);
}
public static ArrayList<Block> getBlockList() {
return blocks;
}
public void startGame() {
for(int i = 0; i < blockCount; i++) {
addBlock(new Block(i*60 + 7, 20));
addBlock(new Block(i*60 + 7, 0));
}
}
}
Main class(The frame part):
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame("Game");
frame.setSize(500, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Game());
frame.setResizable(false);
frame.setVisible(true);
}
}
Key Adapt class:
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class KeyAdapt extends KeyAdapter{
Paddle p;
public KeyAdapt(Paddle paddle) {
p = paddle;
}
public void keyPressed(KeyEvent e) {
p.keyPressed(e);
}
public void keyReleased(KeyEvent e) {
p.keyReleased(e);
}
}
Paddle class:
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
public class Paddle {
int velX;
int speed = 3;
static int x1, y1;
public Paddle(int x1, int y1) {
this.x1 = x1;
this.y1 = y1;
}
public void update() {
x1+=velX;
checkCollisions();
}
public void draw(Graphics2D g2d) {
g2d.drawImage(getPaddleImg(), x1, y1, null);
}
public static Image getPaddleImg() {
ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/paddle.png");
return ic.getImage();
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if(key==KeyEvent.VK_D) {
velX = speed;
} else if(key==KeyEvent.VK_A){
velX = -speed;
}
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if(key==KeyEvent.VK_D) {
velX = 0;
} else if(key==KeyEvent.VK_A){
velX = 0;
}
}
public void checkCollisions() {
if(getBounds().getX() + getBounds().getWidth() >= 500) {
x1 = 440;
} else if(getBounds().getX() <= 0) {
x1 = 0;
}
}
public static Rectangle getBounds() {
return new Rectangle(x1, y1 - 1, getPaddleImg().getWidth(null), getPaddleImg().getHeight(null));
}
}
Ball class:
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
public class Ball {
int velX;
int velY;
int speed = 3;
int x, y;
public Ball(int x, int y) {
this.x = x;
this.y = y;
}
public void update() {
x+=velX;
y+=velY;
checkCollisions();
}
public void draw(Graphics2D g2d) {
g2d.drawImage(getBallImg(), x, y, null);
}
public Image getBallImg() {
ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/ball.png");
return ic.getImage();
}
public void checkCollisions() {
for(int i = 0; i < Game.getBlockList().size(); i++) {
Block b = Game.getBlockList().get(i);
if(getBounds().intersects(b.getBounds()) && velX!=-speed) {
velY=speed;
velX =- speed;
Game.removeBlock(b);
}
else if(getBounds().intersects(b.getBounds())) {
velY=speed;
velX = speed;
Game.removeBlock(b);
}
}
if(getBounds().intersects(Paddle.getBounds())) {
velY = -speed;
} else if (getBounds().getY() <= 0 && velX!=speed) {
velY = speed;
velX =- speed;
}else if (getBounds().getY() <= 0 && velX!=-speed) {
velY = speed;
velX = speed;
} else if(getBounds().getY() >= 400) {
JOptionPane.showMessageDialog(null, "You Lost! :( ");
System.exit(0);
}
if(getBounds().getX() <= 0) {
velX = speed;
} else if(getBounds().getX() >= 500 - getBounds().getWidth()) {
velX = -speed;
}
}
public Rectangle getBounds() {
return new Rectangle(x, y, getBallImg().getWidth(null), getBallImg().getHeight(null));
}
}
Block class:
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import javax.swing.ImageIcon;
public class Block {
int x2, y2;
public Block(int x2, int y2) {
this.x2 = x2;
this.y2 = y2;
}
public void update() {
}
public void draw(Graphics2D g2d){
g2d.drawImage(getBlockImg(), x2, y2, null);
}
public static Image getBlockImg() {
ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/block.png");
return ic.getImage();
}
public Rectangle getBounds() {
return new Rectangle(x2, y2, getBlockImg().getWidth(null), getBlockImg().getHeight(null));
}
}
I also have a folder called Eclipse Game on my desktop and I refer to it in my code.
Again, I understand this is alot but any idea with making it lag less is helpful. Also, watching the tutorial (look at the beginning for the link) on making what I have finished so far will help make it less confusing for you to understand how the code works. The game seriously lags so much I cannot play.
There are multiple issues.
The first, as I already mentioned in my comment, is that you're calling startGame() inside your timer action listener:
#Override
public void actionPerformed(ActionEvent arg0) {
paddle.update();
ball.update();
for(int i = 0; i < blocks.size(); i++) {
Block b = blocks.get(i);
b.update();
}
repaint();
startGame();
}
This is adding 3,200 blocks every second to the game, so you don't want that. I think the simplest place to put startGame() is at the end of the game constructor:
public Game() {
setFocusable(true);
paddle = new Paddle(250, 300);
addKeyListener(new KeyAdapt(paddle));
ball = new Ball(275, 280);
mainTimer = new Timer(10, this);
mainTimer.start();
startGame();
}
The other really big problem is that you're constantly reloading the images all the time. For example, look at this snippet:
if(getBounds().intersects(b.getBounds()) && velX!=-speed) {
velY=speed;
velX =- speed;
Game.removeBlock(b);
}
else if(getBounds().intersects(b.getBounds())) {
velY=speed;
velX = speed;
Game.removeBlock(b);
}
That is 4 calls to getBounds(), and if we take a look at that:
return new Rectangle(x2, y2, getBlockImg().getWidth(null), getBlockImg().getHeight(null));
You are loading 2 images which in total is 4*2*blockCount images every 10ms, just for this one method. Instead of loading images all the time, do something like this:
class GameResources {
static Image ballImage;
static Image paddleImage;
static Image blockImage;
// call GameResources.loadResources() at the
// beginning of main() or something
static void loadResources() {
// load all 3 images once here and be done
ballImage = ...;
paddleImage = ...;
blockImage = ...;
}
Then finally, you have an issue with removing items from the list while iterating over it, Ball.checkCollisions:
for(int i = 0; i < Game.getBlockList().size(); i++) {
Block b = Game.getBlockList().get(i);
if(getBounds().intersects(b.getBounds()) && velX!=-speed) {
velY=speed;
velX =- speed;
// removeBlock changes blocks.size()
Game.removeBlock(b);
}
else if(getBounds().intersects(b.getBounds())) {
velY=speed;
velX = speed;
// removeBlock changes blocks.size()
Game.removeBlock(b);
}
}
Instead you need to do something like this:
Iterator<Block> iter = Game.getBlockList().iterator();
while (it.hasNext()) {
Block b = it.next();
if(getBounds().intersects(b.getBounds()) && velX!=-speed) {
velY=speed;
velX =- speed;
// safely removing
it.remove();
}
else if(getBounds().intersects(b.getBounds())) {
velY=speed;
velX = speed;
// safely removing
it.remove();
}
}
And another possible boundary issue in Game.paint:
// using blockCount after possibly
// removing items from the list
// vvvvvvvvvv
for(int i = 0; i < blockCount; i++) {
Block b = blocks.get(i);
b.draw(g2d);
}
For simple iterations like this, you should use for-each:
for(Block b : blocks) {
b.draw(g2d);
}
After all of that the game runs pretty smoothly, except for some type of issue with the key listener which I didn't have time to figure out. I might look at it again after dinner.
edit:
I noticed a lot of other small things, so here is the program fixed up a bit more with my comments.
Some of the classes aren't public anymore just because I had them all in one source file.
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JOptionPane;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyAdapter;
import java.util.ArrayList;
import java.util.Iterator;
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Dimension;
import java.net.URL;
import javax.imageio.ImageIO;
import java.io.IOException;
import java.io.File;
public class BlockGame {
public static void main(String[] args) {
// Swing program should always begin on the Swing
// thread with a call to invokeLater.
// See https://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
// change this to
// .loadImages();
GameResources.loadInternetImages();
} catch (IOException x) {
x.printStackTrace();
return;
}
JFrame frame = new JFrame("Game");
// frame.setSize(500, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame.add(new Game());
// Instead of calling setSize on the JFrame
// directly, set a preferred size on the game
// panel, then call pack() on the JFrame
Game game = new Game();
game.setPreferredSize(new Dimension(500, 400));
frame.add(game);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
// I started the game here instead
// of in the game loop, so the panel
// is visible and stuff beforehand.
game.startGame();
}
});
}
}
class Game extends JPanel implements ActionListener {
Timer mainTimer;
Paddle paddle;
Ball ball;
// I removed this because it's only ever
// used by startGame.
// int blockCount = 16;
// I changed this to an instance variable
// (not static) and passed the game in to
// update so the game objects can access
// it.
ArrayList<Block> blocks = new ArrayList<Block>();
public Game() {
setFocusable(true);
paddle = new Paddle(250, 300);
addKeyListener(new KeyAdapt(paddle));
ball = new Ball(275, 280);
mainTimer = new Timer(10, this);
// I moved this to the startGame() method
// mainTimer.start();
}
// Swing programs should override paintComponent
// instead of paint.
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// You should create a copy instead of
// directly using the graphics object which
// the component uses.
// This is so any changes you make to it
// don't affect the Swing paint routines.
Graphics2D g2d = (Graphics2D) g.create();
// ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/background.png");
// g2d.drawImage(ic.getImage(), 0, 0, null);
// Painting static resource.
g2d.drawImage(GameResources.backgroundImage, 0, 0, null);
paddle.draw(g2d);
ball.draw(g2d);
// This loop will throw an out of bounds
// exception once the first block is removed.
// vvvvvvvvvv
// for(int i = 0; i < blockCount; i++) {
// Block b = blocks.get(i);
// b.draw(g2d);
// }
// using for each
for (Block b : blocks) {
b.draw(g2d);
}
// Dispose the copied graphics when you're done.
g2d.dispose();
}
#Override
public void actionPerformed(ActionEvent arg0) {
paddle.update(this);
ball.update(this);
// for(int i = 0; i < blocks.size(); i++) {
// Block b = blocks.get(i);
// b.update();
// }
for (Block b : blocks) {
b.update(this);
}
repaint();
// I moved this to main
// startGame();
}
public void addBlock(Block b) {
blocks.add(b);
}
public void removeBlock(Block b) {
blocks.remove(b);
}
public ArrayList<Block> getBlockList() {
return blocks;
}
// I added this method so that the
// ball can access the paddle without
// static variables.
public Paddle getPaddle() {
return paddle;
}
public void startGame() {
// So the method won't be called twice
// and put the game in some unexpected
// state.
if (mainTimer.isRunning()) {
throw new IllegalStateException("game already started");
}
int initialBlockCount = 16;
for(int i = 0; i < initialBlockCount; i++) {
addBlock(new Block(i*60 + 7, 20));
addBlock(new Block(i*60 + 7, 0));
}
mainTimer.start();
}
}
// Generally speaking you should use
// Swing key bindings now, instead of
// key listeners.
//
// Key listeners have problems with
// the focus system: Swing components
// only send out key events when they
// have the focus.
//
// Key bindings don't have this issue.
//
// You can set up key bindings so they
// trigger any time the key is pressed
// in the focused window.
//
// https://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html
//
class KeyAdapt extends KeyAdapter {
Paddle p;
public KeyAdapt(Paddle paddle) {
p = paddle;
}
public void keyPressed(KeyEvent e) {
p.keyPressed(e);
}
public void keyReleased(KeyEvent e) {
p.keyReleased(e);
}
}
class Paddle {
int velX;
int speed = 3;
// I changed these from static
// to instance variables.
int x1, y1;
// I added these variables to
// help with the key listener
// logic.
boolean leftPressed, rightPressed;
public Paddle(int x1, int y1) {
this.x1 = x1;
this.y1 = y1;
}
public void update(Game game) {
x1 += velX;
checkCollisions();
}
public void draw(Graphics2D g2d) {
g2d.drawImage(GameResources.paddleImage, x1, y1, null);
}
// public static Image getPaddleImg() {
// ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/paddle.png");
// return ic.getImage();
// }
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
// This logic is a little more robust
// because it handles cases where both
// keys are being held at the same time.
// Also see computeVelX().
if (key == KeyEvent.VK_D) {
leftPressed = true;
// velX = speed;
} else if (key == KeyEvent.VK_A) {
rightPressed = true;
// velX = -speed;
}
computeVelX();
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
// This logic is a little more robust
// because it handles cases where both
// keys are being held at the same time.
// Also see computeVelX().
if (key == KeyEvent.VK_D) {
leftPressed = false;
// velX = 0;
} else if (key == KeyEvent.VK_A) {
rightPressed = false;
// velX = 0;
}
computeVelX();
}
public void computeVelX() {
// This way the keys will never
// "stick". If both keys are
// held at the same time, velX
// is just 0 until one of the
// keys is released.
velX = 0;
if (leftPressed) {
velX += speed;
}
if (rightPressed) {
velX -= speed;
}
}
public void checkCollisions() {
// I used a variable instead of calling
// getBounds() repeatedly.
Rectangle bounds = getBounds();
if (bounds.getX() + bounds.getWidth() >= 500) {
x1 = 440;
} else if (bounds.getX() <= 0) {
x1 = 0;
}
}
// I change this from static to an instance method.
public Rectangle getBounds() {
// return new Rectangle(x1, y1 - 1, getPaddleImg().getWidth(null), getPaddleImg().getHeight(null));
int width = GameResources.paddleImage.getWidth(null);
int height = GameResources.paddleImage.getHeight(null);
return new Rectangle(x1, y1 - 1, width, height);
}
}
class Ball {
int velX;
int velY;
int speed = 3;
int x, y;
public Ball(int x, int y) {
this.x = x;
this.y = y;
}
public void update(Game game) {
x += velX;
y += velY;
checkCollisions(game);
}
public void draw(Graphics2D g2d) {
// g2d.drawImage(getBallImg(), x, y, null);
g2d.drawImage(GameResources.ballImage, x, y, null);
}
// public Image getBallImg() {
// ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/ball.png");
// return ic.getImage();
// }
public void checkCollisions(Game game) {
// Using an iterator instead of looping with size()
// directly, because we want to remove items from
// the list while iterating.
// The problem with removing while iterating with
// size() is that once you remove an element, the
// list shifts all the other elements back by 1,
// so on the next iteration of the loop you end
// up skipping an item.
// (Say you remove the element at index 5. Then
// all the elements shift back, so that e.g. the
// element at index 6 is now at index 5. The variable
// i is incremented, so you end up skipping the element
// that was at index 6 before the removal.
Iterator<Block> iter = game.getBlockList().iterator();
Rectangle bounds = getBounds();
while (iter.hasNext()) {
Block b = iter.next();
Rectangle bBounds = b.getBounds();
if (bounds.intersects(bBounds) && velX != -speed) {
velY = speed;
velX =- speed;
// Game.removeBlock(b);
iter.remove();
} else if (bounds.intersects(bBounds)) {
velY = speed;
velX = speed;
// Game.removeBlock(b);
iter.remove();
}
}
//
Rectangle pBounds = game.getPaddle().getBounds();
if (bounds.intersects(pBounds)) {
velY = -speed;
} else if (bounds.getY() <= 0 && velX != speed) {
velY = speed;
velX =- speed;
} else if (bounds.getY() <= 0 && velX != -speed) {
velY = speed;
velX = speed;
} else if (bounds.getY() >= 400) {
JOptionPane.showMessageDialog(null, "You Lost! :( ");
System.exit(0);
}
if (bounds.getX() <= 0) {
velX = speed;
} else if(bounds.getX() >= 500 - bounds.getWidth()) {
velX = -speed;
}
}
public Rectangle getBounds() {
// return new Rectangle(x, y, getBallImg().getWidth(null), getBallImg().getHeight(null));
int width = GameResources.ballImage.getWidth(null);
int height = GameResources.ballImage.getHeight(null);
return new Rectangle(x, y, width, height);
}
}
class Block {
int x2, y2;
public Block(int x2, int y2) {
this.x2 = x2;
this.y2 = y2;
}
public void update(Game game) {
}
public void draw(Graphics2D g2d){
// g2d.drawImage(getBlockImg(), x2, y2, null);
g2d.drawImage(GameResources.blockImage, x2, y2, null);
}
// public static Image getBlockImg() {
// ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/block.png");
// return ic.getImage();
// }
public Rectangle getBounds() {
// return new Rectangle(x2, y2, getBlockImg().getWidth(null), getBlockImg().getHeight(null));
int width = GameResources.blockImage.getWidth(null);
int height = GameResources.blockImage.getHeight(null);
return new Rectangle(x2, y2, width, height);
}
}
class GameResources {
public static Image backgroundImage;
public static Image blockImage;
public static Image ballImage;
public static Image paddleImage;
public static void loadImages() throws IOException {
// Load images once here.
// I didn't test this method since I don't have the images, but it
// should work. ImageIO.read will give better error messages than
// using ImageIcon. ImageIcon.getImage() will just return null if
// there was a problem, which doesn't tell you what the problem
// actually was.
paddleImage =
ImageIO.read(new File("C:/Users/Elliot/Desktop/Eclipse Game/paddle.png"));
ballImage =
ImageIO.read(new File("C:/Users/Elliot/Desktop/Eclipse Game/ball.png"));
blockImage =
ImageIO.read(new File("C:/Users/Elliot/Desktop/Eclipse Game/block.png"));
backgroundImage =
ImageIO.read(new File("C:/Users/Elliot/Desktop/Eclipse Game/background.png"));
}
public static void loadInternetImages() throws IOException {
// These images are from
// http://stackoverflow.com/questions/19209650/example-images-for-code-and-mark-up-qas
paddleImage =
ImageIO.read(new URL("http://i.stack.imgur.com/gYxHm.png"));
ballImage =
ImageIO.read(new URL("http://i.stack.imgur.com/gJmeJ.png"));
blockImage =
ImageIO.read(new URL("http://i.stack.imgur.com/F0JHK.png"));
backgroundImage =
ImageIO.read(new URL("http://i.stack.imgur.com/P59NF.png"));
}
}
EDIT
: it seems that in my move() method, java has to skip one of the 2 if statements
Keeping it simple. I'm trying to make a GUI in java that models the following class Truck behavior: the blue squares are supposed to run up their diagonales, which they do. But when they are supposed to bounce back once they reach the edge point of the square they just fly away. I've placed a condition to prevent this, but it never passes. I'll upload GUI and Drawable, but I don't think they are needed.
Class Truck:
package construction_site;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.plaf.ButtonUI;
public class Truck extends Thread implements Drawable {
private boolean isFull = false;
private int x, y;
private int capacity;
private Panel panel;
static int r = 20;
private int dx, dy;
private Site site;
private Building building;
public Truck(int x, int y, Panel panel, Building building, Site site) {
this.x = x;
this.y = y;
this.building = building;
this.panel = panel;
this.site = site;
this.start();
}
public void setCapacity(int capacity) {
this.capacity = capacity;
}
#Override
public void run() {
super.run();
while (true) {
move();
panel.repaint();
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setDirection(int i) {
if (i == 0) {
dx = -1;
dy = -1;
}
if (i == 1) {
dx = 1;
dy = -1;
}
if (i == 2) {
dx = -1;
dy = 1;
}
if (i == 3) {
dx = 1;
dy = 1;
}
}
private void move() {
if (site.truckOnSite(x, y)) {
site.loadTruck(this);
dx *= -1;
dy *= -1;
}
if (building.containsTruck(this)) {
building.unloadTruck(this);
dx *= -1;
dy *= -1;
}
x += dx;
y += dy;
}
public int getDx() {
return dx;
}
public void setFull(boolean isFull) {
this.isFull = isFull;
}
#Override
public void draw(Graphics g) {
g.setColor(Color.BLUE);
if (isFull)
g.fillRect(x - 10, y - 10, r, r);
else
g.drawRect(x - 10, y - 10, r, r);
}
}
Class Panel:
package construction_site;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.ArrayList;
import javax.swing.JPanel;
public class Panel extends JPanel {
private ArrayList<Drawable> drawables = new ArrayList<>();
private Site site;
private Building[] buildings = new Building[4];
private Truck[] trucks = new Truck[4];
private Van[] vans = new Van[4];
public Panel(int w, int h) {
setPreferredSize(new Dimension(w, h));
site = new Site(100, 75, 500, 500, this);
drawables.add(site);
buildings[0] = new Building(100, 75, this);
buildings[1] = new Building(100 + 500, 75, this);
buildings[2] = new Building(100, 75 + 500, this);
buildings[3] = new Building(100 + 500, 75 + 500, this);
for (Building b : buildings)
drawables.add(b);
trucks[0] = new Truck(100 + 250, 75 + 250, this, buildings[0], site);
trucks[1] = new Truck(100 + 250, 75 + 250, this, buildings[1], site);
trucks[2] = new Truck(100 + 250, 75 + 250, this, buildings[2], site);
trucks[3] = new Truck(100 + 250, 75 + 250, this, buildings[3], site);
for (int i = 0; i < 4; i++)
trucks[i].setDirection(i);
for (Truck t : trucks)
drawables.add(t);
}
#Override
public void paint(Graphics g) {
super.paint(g);
for (Drawable d : drawables)
d.draw(g);
g.setColor(Color.GREEN);
for (int i = 1; i <= 3; i++)
g.drawLine(buildings[0].getX(), buildings[0].getY(), buildings[i].getX(), buildings[i].getY());
g.drawLine(buildings[1].getX(), buildings[1].getY(), buildings[2].getX(), buildings[2].getY());
}
}
Class Building:
package construction_site;
import java.awt.Color;
import java.awt.Graphics;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Building extends Thread implements Drawable {
private Panel panel;
private boolean done = false;
private int x, y;
static int w = 100;
static int h = 100;
private ReentrantLock lock = new ReentrantLock();
private Condition insufficientMaterial = lock.newCondition();
private Condition insufficientMisc = lock.newCondition();
private Condition sufficient = lock.newCondition();
private int material = 0;
private int misc = 0;
private int spent = 0;
public Building(int x, int y, Panel panel) {
this.x = x;
this.y = y;
this.panel = panel;
this.start();
}
#Override
public void run() {
super.run();
lock.lock();
while(material < 100 )
try {
insufficientMaterial.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
spent += 150;
misc -= 50;
material -= 100;
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void unloadVan(Van v) {
lock.lock();
System.out.println("Truck got in");
if (material >= 100 && misc >=50) {
}
misc += 50;
v.setCapacity(0);
insufficientMisc.signalAll();
lock.unlock();
}
public void unloadTruck(Truck t) {
lock.lock();
material += 10000;
t.setCapacity(0);
t.setFull(false);
insufficientMaterial.signalAll();
lock.unlock();
}
public void setMaterial(int material) {
this.material = material;
}
public void setMisc(int misc) {
this.misc = misc;
}
public boolean containsVan(int x2, int y2) {
return ( Math.sqrt((x - x2)*(x - x2) + (y - y2)*(y - y2)) <= h/2 );
}
public boolean containsTruck(Truck t) {
return (x == t.getX() && y == t.getY());
}
public int getX() {
return x;
}
public int getY() {
return y;
}
#Override
public void draw(Graphics g) {
g.setColor(Color.BLACK);
g.drawRect(x - 50, y - 50, 100, 100);
}
}
Class Site:
package construction_site;
import java.awt.Color;
import java.awt.Graphics;
public class Site implements Drawable {
private int x, y, w, h;
private Panel panel;
public Site(int x, int y, int w, int h, Panel panel) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.panel = panel;
}
public boolean contains(int x2, int y2) {
return (x == x2 && y == 2);
}
public boolean truckOnSite(int x2, int y2) {
return ( x2 == x + w / 2 && y2 == y + h / 2);
}
public boolean vanOnSite(int x2, int y2) {
return ( (x2 == x && y2 == y + h / 2) || (x2 == x + w && y2 == y + h / 2) );
}
public void loadVan(Van v) {
v.setCapacity(5000);
}
public void loadTruck(Truck t) {
t.setCapacity(10000);
t.setFull(true);
}
#Override
public void draw(Graphics g) {
g.setColor(Color.GRAY);
g.fillRect(x, y, w, h);
}
}
As it turned out much later, it doesn't seem truncation errors were the problem at hand. It was a mistake at setting the trucks' starting point and the condition that decides when they're supposed to turn the other way. What looked like the truck bouncing off, was actually the oppsoite truck from the same diagonal passing through. A simple change in direction of my Truck's move() method did the trick.
public void setDirection(int i) {
if (i == 0) {
dx = -1;
dy = -1;
}
if (i == 1) {
dx = 1;
dy = -1;
}
if (i == 2) {
dx = -1;
dy = 1;
}
if (i == 3) {
dx = 1;
dy = 1;
}
}
I am creating a simple game where shapes fall and the player shoots them, but I am having problems creating bullet at every click of the mouse. I have tried various logic with no help, so am just going to put the code up here so you guys can take a look at it and help me out.
The bullet I created is not been created on every click just one is created and it moves on every click which is wrong........I want one bullet to be created per click.
// My main class: mousework2
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.awt.geom.*;
public class mousework2 extends JFrame
{
public static int Width = 300;
public static int Height = 400;
private JPanel p1;
private Image pixMage,gunMage;
public mousework2()
{
super("shoot-em-up");
this.setSize(Width, Height);
this.setResizable(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Dimension pos = Toolkit.getDefaultToolkit().getScreenSize();
int x = (pos.width - Width) / 2;
int y = (pos.height - Height) / 2;
this.setLocation(x, y);
p1 = new CreateImage();
this.add(p1);
this.getContentPane();
Thread t = new recMove(this);
t.start();
}
class recMove extends Thread
{
JFrame b;
public recMove(JFrame b)
{
this.b = b;
}
public void run()
{
while (true) {
b.repaint();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
}
}
class CreateImage extends JPanel implements MouseListener
{
ArrayList<fallShape> rect = new ArrayList<fallShape>();
int x_pos = mousework.Width / 2;
int y_pos = mousework.Height - 50;
int bx_pos = mousework.Width / 2;
int by_pos = mousework.Height;
int y_speed = -10;
boolean clicked;
public CreateImage()
{
for (int i = 0; i < 10; i++) {
rect.add(new fallShape(15, 15, rect));
}
Toolkit picx = Toolkit.getDefaultToolkit();
gunMage = picx.getImage("gunner.jpg");
gunMage = gunMage.getScaledInstance(200, -1, Image.SCALE_SMOOTH);
Toolkit pic = Toolkit.getDefaultToolkit();
pixMage = pic.getImage("ballfall3.jpg");
pixMage = pixMage.getScaledInstance(200, -1, Image.SCALE_SMOOTH);
addMouseListener(this);
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseMoved(MouseEvent e)
{
x_pos = e.getX() - 5;
}
});
}
public void mousePressed(MouseEvent e)
{
if (e.getButton() == 1) {
clicked = true;
}
}
public void mouseReleased(MouseEvent e)
{
if (e.getButton() == 1) {
clicked = false;
}
}
public void mouseExited(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseClicked(MouseEvent e)
{
}
public void paint(Graphics g)
{
super.paint(g);
g.drawImage(pixMage, 0, 0, Width, Height, null);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(gunMage,x_pos,y_pos,10,20,null);
if (clicked) {
by_pos += y_speed;
Shape bullet = new Rectangle2D.Float(bx_pos, by_pos, 3, 10);
g2.setColor(Color.BLACK);
g2.fill(bullet);
g2.draw(bullet);
}
g2.setColor(Color.RED);
for (fallShape b : rect) {
b.move();
g2.fill(b);
}
}
}
public static void main(String[] args)
{
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run()
{
new mousework2().setVisible(true);
}
});
}
}
And:
// My falling shapes class: fallShape
import java.awt.geom.*;
import java.util.*;
public class fallShape extends Rectangle2D.Float
{
public int x_speed, y_speed;
public int l, b;
public int height = mousework.Height;
public int width = mousework.Width;
public ArrayList<fallShape> fall;
public fallShape(int breadth, int length, ArrayList<fallShape> fall)
{
super((int) (Math.random() * (mousework.Width - 20) + 1), 0, breadth, length);
this.b = breadth;
this.l = length;
this.x_speed = (int) Math.random() * (10) + 1;
this.y_speed = (int) Math.random() * (10) + 1;
this.fall = fall;
}
public void move()
{
Rectangle2D rec = new Rectangle2D.Float(super.x, super.y, b, l);
for (fallShape f : fall) {
if (f != this && f.intersects(rec)) {
int rxspeed = x_speed;
int ryspeed = y_speed;
x_speed = f.x_speed;
y_speed = f.y_speed;
f.x_speed = rxspeed;
f.y_speed = ryspeed;
}
}
if (super.x < 0) {
super.x =+ super.x;
//super.y =+ super.y;
x_speed = Math.abs(x_speed);
}
if (super.x> mousework.Width - 30) {
super.x =+ super.x;
super.y =+ super.y;
x_speed =- Math.abs(x_speed);
}
if (super.y < 0) {
super.y = 0;
y_speed = Math.abs(y_speed);
}
super.x += x_speed;
super.y += y_speed;
}
}
if(clicked){
by_pos+=y_speed;
This code only draws the bullet when the mouse is down. This is because you are setting clicked to false in your mouseReleased method:
public void mouseReleased(MouseEvent e){
if(e.getButton()==1)
clicked=false;
}
If you were to remove the body of the mouseReleased method, your bullet would move properly.
However, say you wanted to have more than just one bullet. Currently, your paint method only draws one bullet at a time. To draw multiple bullets, you would need to create a list of the coordinates of the bullets, and add a new coordinate pair to the list whenever you click. Then, in the paint method, just update each position in a for loop.
ArrayList<Integer> by_poss = new ArrayList<>();
by_poss is the list of all the y-positions of your bullets.
public void mousePressed(MouseEvent e){
if(e.getButton() == 1)
by_poss.add(mousework.Height);
}
The mousePressed method adds a new "bullet", in the form of a y-position, to the coordinates.
public void mouseReleased(MouseEvent e){
//do nothing
}
Nothing needs to happen in the mouseReleased method.
//update the bullets
public void paint(Graphics g){
...
g2.setColor(Color.BLACK);
Shape bullet;
for(int i = 0; i < by_poss.size(); i++){
by_poss.set(i, by_poss.get(i) + y_speed); //move the bullet
bullet = new Rectangle2D.Float(bx_pos, by_poss.get(i), 3, 10);
g2.fill(bullet);
g2.draw(bullet);
}
...
}
The for loop in your paint method draws all the bullets, one by one, usin g the y-positions from the by_poss list.
I am trying to draw 7 random circles across a JPanel using an array. I managed to get the array to work but now I am having trouble spacing out the circles. When i run the program i see multiple circles being drawn but they are all on the same spot. All the circles are of different size and color. The other problem i have is making the circles move towards the bottom of the screen.
public class keyExample extends JPanel implements ActionListener, KeyListener{
private Circle[] circles = new Circle[7];
Timer t = new Timer(5,this);
//current x and y
double x = 150, y = 200;
double changeX = 0, changeY = 0;
private int circlex = 0,circley = 0; // makes initial starting point of circles 0
private javax.swing.Timer timer2;
public keyExample(){
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
NewCircle();
timer2 = new javax.swing.Timer(33,new MoveListener());
timer2.start();
}
public void NewCircle(){
Random colors = new Random();
Color color = new Color(colors.nextInt(256),colors.nextInt(256),colors.nextInt(256));
Random num= new Random();
int radius = num.nextInt(45);
for (int i = 0; i < circles.length; i++)
circles[i] = new Circle(circlex,circley,radius,color);
}
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLUE);
g2.fill(new Rectangle2D.Double(x,y,40,40));
for (int i = 0; i < circles.length; i++)
circles[i].fill(g);
}
public void actionPerformed(ActionEvent e){
repaint();
x += changeX;
y += changeY;
changeX = 0;
changeY = 0;
}
public void up() {
if (y != 0){
changeY = -3.5;
changeX = 0;
}
}
public void down() {
if (y <= 350){
changeY = 3.5;
changeX = 0;
}
}
public void left() {
if (x >= 0) {
changeX = -3.5;
changeY = 0;
}
}
public void right() {
if (x <= 550) {
changeX = 3.5;
changeY = 0;
}
}
private class MoveListener implements ActionListener{
public void actionPerformed(ActionEvent e){
repaint();
Random speed = new Random();
int s = speed.nextInt(8);
}
}
public void keyPressed(KeyEvent e){
int code = e.getKeyCode();
if (code == KeyEvent.VK_UP){
up();
}
if (code == KeyEvent.VK_DOWN){
down();
}
if (code == KeyEvent.VK_RIGHT){
right();
}
if (code == KeyEvent.VK_LEFT){
left();
}
}
public void keyTyped(KeyEvent e) { }
public void keyReleased(KeyEvent e) { }
}
Circle class
import java.awt.*;
public class Circle{
private int centerX, centerY, radius, coord;
private Color color;
public Circle(int x, int y, int r, Color c){
centerX = x;
centerY = y;
radius = r;
color = c;
}
public void draw(Graphics g){
Color oldColor = g.getColor();
g.setColor(color);
g.drawOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
g.setColor(oldColor);
}
public void fill(Graphics g){
Color oldColor = g.getColor();
g.setColor(color);
g.fillOval(centerX - radius, centerY - radius, radius *2, radius * 2);
g.setColor(oldColor);
}
public boolean containsPoint(int x, int y){
int xSquared = (x - centerX) * (x - centerX);
int ySquared = (y - centerY) * (y - centerY);
int RadiusSquared = radius * radius;
return xSquared + ySquared - RadiusSquared <=0;
}
public void move(int xAmount, int yAmount){
centerX = centerX + xAmount;
centerY = centerY + yAmount;
}
}
This is one of the problems with relying on borrowed code that you don't understand...
Basically, all you need to do is change the creation of the circles, for example...
for (int i = 0; i < circles.length; i++) {
circles[i] = new Circle(circlex, circley, radius, color);
circlex += radius;
}
You may wish to re-consider the use of KeyListener, in favour of Key Bindings before you discover that KeyListener doesn't work the way you expect it to...
For some strange reason, you're calling NewCirlces from within the MoveListener's actionPerfomed method, meaning that the circles are simply being re-created on each trigger of the Timer...instead, call it first in the constructor
You're also calling within your paintComponent method...this should mean that the circles never move and instead, random change size...
Updated with runnable example...
I modified your paint code NewCircle and the MoveListener a little...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class CircleExample extends JPanel implements ActionListener, KeyListener {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new CircleExample());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
private Circle[] circles = new Circle[7];
Timer t = new Timer(5, this);
//current x and y
double x = 150, y = 200;
double changeX = 0, changeY = 0;
private int circlex = 0, circley = 0; // makes initial starting point of circles 0
private javax.swing.Timer timer2;
public CircleExample() {
NewCircle();
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
timer2 = new javax.swing.Timer(33, new MoveListener());
timer2.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public void NewCircle() {
for (int i = 0; i < circles.length; i++) {
Random colors = new Random();
Color color = new Color(colors.nextInt(256), colors.nextInt(256), colors.nextInt(256));
Random num = new Random();
int radius = num.nextInt(90);
circles[i] = new Circle(circlex, circley, radius, color);
circlex += radius;
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLUE);
g2.fill(new Rectangle2D.Double(x, y, 40, 40));
for (int i = 0; i < circles.length; i++) {
circles[i].fill(g);
}
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
x += changeX;
y += changeY;
changeX = 0;
changeY = 0;
}
public void up() {
if (y != 0) {
changeY = -3.5;
changeX = 0;
}
}
public void down() {
if (y <= 350) {
changeY = 3.5;
changeX = 0;
}
}
public void left() {
if (x >= 0) {
changeX = -3.5;
changeY = 0;
}
}
public void right() {
if (x <= 550) {
changeX = 3.5;
changeY = 0;
}
}
private class MoveListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
Random speed = new Random();
for (Circle circle : circles) {
int s = speed.nextInt(8);
circle.move(0, s);
}
repaint();
}
}
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if (code == KeyEvent.VK_UP) {
up();
}
if (code == KeyEvent.VK_DOWN) {
down();
}
if (code == KeyEvent.VK_RIGHT) {
right();
}
if (code == KeyEvent.VK_LEFT) {
left();
}
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public class Circle {
private int centerX, centerY, radius, coord;
private Color color;
public Circle(int x, int y, int r, Color c) {
centerX = x;
centerY = y;
radius = r;
color = c;
}
public void draw(Graphics g) {
Color oldColor = g.getColor();
g.setColor(color);
g.drawOval(centerX, centerY, radius, radius);
g.setColor(oldColor);
}
public void fill(Graphics g) {
Color oldColor = g.getColor();
g.setColor(color);
g.fillOval(centerX, centerY, radius, radius);
g.setColor(oldColor);
}
public boolean containsPoint(int x, int y) {
int xSquared = (x - centerX) * (x - centerX);
int ySquared = (y - centerY) * (y - centerY);
int RadiusSquared = radius * radius;
return xSquared + ySquared - RadiusSquared <= 0;
}
public void move(int xAmount, int yAmount) {
centerX = centerX + xAmount;
centerY = centerY + yAmount;
}
}
}
I have the following program, it's a SubKiller game and I have one question. How do I make the boat launch the bomb anytime I press the down key? So far I have made to launch it multiple times. I can launch the first bomb but when I'm trying to launch the second one, the first one disappears and it doesn't continue it's way. I am stuck on this matter for almost two days, help me please.
I am going to provide you the SSCCE code.
This is the class called SubKillerPanel, basically everything is here, the boat is here, the bomb is here, the submarine is here.
import java.awt.event.*;
import javax.swing.*;
import java.awt.Graphics;
import java.awt.Color;
public class SubKillerPanel extends JPanel {
private static final long serialVersionUID = 1L;
private Timer timer;
private int width, height;
private Boat boat;
private Bomb bomb;
private Submarine sub;
public SubKillerPanel() {
setBackground(Color.WHITE);
ActionListener action = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (boat != null) {
boat.updateForNewFrame();
bomb.updateForNewFrame();
sub.updateForNewFrame();
}
repaint();
}
};
timer = new Timer( 20, action );
addMouseListener( new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
requestFocus();
}
}
);
addFocusListener( new FocusListener() {
public void focusGained(FocusEvent evt) {
timer.start();
repaint();
}
public void focusLost(FocusEvent evt) {
timer.stop();
repaint();
}
}
);
addKeyListener( new KeyAdapter() {
public void keyPressed(KeyEvent evt) {
int code = evt.getKeyCode(); // ce tasta a fost apasata
if (code == KeyEvent.VK_LEFT) {
boat.centerX -= 15;
}
else
if (code == KeyEvent.VK_RIGHT) {
boat.centerX += 15;
}
else
if (code == KeyEvent.VK_DOWN) {
if (bomb.isFalling == false)
bomb.isFalling = true;
bomb.centerX = boat.centerX;
bomb.centerY = boat.centerY;
}
}
}
);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (boat == null) {
width = getWidth();
height = getHeight();
boat = new Boat();
sub = new Submarine();
bomb = new Bomb();
}
if (hasFocus())
g.setColor(Color.CYAN);
else {
g.setColor(Color.RED);
g.drawString("CLICK TO ACTIVATE", 20, 30);
g.setColor(Color.GRAY);
}
g.drawRect(0,0,width-1,height-1);
g.drawRect(1,1,width-3,height-3);
g.drawRect(2,2,width-5,height-5);
boat.draw(g);
sub.draw(g);
bomb.draw(g);
}
private class Boat {
int centerX, centerY;
Boat() {
centerX = width/2;
centerY = 80;
}
void updateForNewFrame() {
if (centerX < 0)
centerX = 0;
else
if (centerX > width)
centerX = width;
}
void draw(Graphics g) {
g.setColor(Color.blue);
g.fillRoundRect(centerX - 40, centerY - 20, 80, 40, 20, 20);
}
}
private class Bomb {
int centerX, centerY;
boolean isFalling;
Bomb() {
isFalling = false;
}
void updateForNewFrame() {
if (isFalling) {
if (centerY > height) {
isFalling = false;
}
else
if (Math.abs(centerX - sub.centerX) <= 36 && Math.abs(centerY - sub.centerY) <= 21) {
sub.isExploding = true;
sub.explosionFrameNumber = 1;
isFalling = false; // Bomba reapare in barca
}
else {
centerY += 10;
}
}
}
void draw(Graphics g) {
if ( !isFalling ) {
centerX = boat.centerX;
centerY = boat.centerY + 23;
}
g.setColor(Color.RED);
g.fillOval(centerX - 8, centerY - 8, 16, 16);
}
}
private class Submarine {
int centerX, centerY;
boolean isMovingLeft;
boolean isExploding;
int explosionFrameNumber;
Submarine() {
centerX = (int)(width*Math.random());
centerY = height - 40;
isExploding = false;
isMovingLeft = (Math.random() < 0.5);
}
void updateForNewFrame() {
if (isExploding) {
explosionFrameNumber++;
if (explosionFrameNumber == 30) {
centerX = (int)(width*Math.random());
centerY = height - 40;
isExploding = false;
isMovingLeft = (Math.random() < 0.5);
}
}
else {
if (Math.random() < 0.04) {
isMovingLeft = ! isMovingLeft;
}
if (isMovingLeft) {
centerX -= 5;
if (centerX <= 0) {
centerX = 0;
isMovingLeft = false;
}
}
else {
centerX += 5;
if (centerX > width) {
centerX = width;
isMovingLeft = true;
}
}
}
}
void draw(Graphics g) {
g.setColor(Color.BLACK);
g.fillOval(centerX - 30, centerY - 15, 60, 30);
if (isExploding) {
g.setColor(Color.YELLOW);
g.fillOval(centerX - 4*explosionFrameNumber, centerY - 2*explosionFrameNumber,
8*explosionFrameNumber, 4*explosionFrameNumber);
g.setColor(Color.RED);
g.fillOval(centerX - 2*explosionFrameNumber, centerY - explosionFrameNumber/2,
4*explosionFrameNumber, explosionFrameNumber);
}
}
}
}
The SubKiller class, where the main method is located.
import javax.swing.JFrame;
public class SubKiller {
public static void main(String[] args) {
JFrame window = new JFrame("Sub Killer Game");
SubKillerPanel content = new SubKillerPanel();
window.setContentPane(content);
window.setSize(700, 700);
window.setLocation(0,0);
window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
window.setResizable(false);
window.setVisible(true);
}
}
You only have a single Bomb being tracked by the graphics at any time. You should build a collection of Bombs and instantiate a new one whenever the down key is pressed, then iterate through all the collection of Bombs and draw them as needed.
So, instead of private Bomb bomb;
You would have private List<Bomb> bombs;
Afterwards, anywhere you update the single bomb you can use a for loop to go through the list of bombs and have them all update, and then if they are no longer being drawn, remove them from the list.