This is my frog class which is being called by a GUI class.
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
public class Frog implements Runnable {
private int x, y, dx, dy;
private int coordinates[]=new int[2];
private boolean hungry;
private JLabel jLabel;
private ImageIcon image;
private String name;
public Frog() {
}
public Frog(String name, boolean hungry) {
image = new ImageIcon("images/frog.jpeg");
jLabel = new JLabel(image);
setName(name);
setHungry(hungry);
setCoordinates();
}
private void setName(String name) {
this.name=name;
}
public int[] getCoordinates() {
return coordinates;
}
public boolean isHungry() {
return hungry;
}
public void setHungry(boolean hungry) {
this.hungry = hungry;
}
public void display(Graphics paper) {
paper.setColor(Color.black);
paper.drawOval(x, y, dx, dx);
}
public void setCoordinates() {
for(int i = 0; i < 2; i++) {
Random rand = new Random();
int p = rand.nextInt(100);
coordinates[i] = p;
}
setX(coordinates[0]);
setY(coordinates[1]);
}
public void move() {
x = (int)Math.random() * 350;
y = (int)Math.random() * 350;
dx=20;
dx=20;
x += dx;
y += dy;
if (x > 800 || x < 0)
dx=-dx;
if (y > 600 || y < 0)
dy=-dy;
}
public void setDx(int dx) {
this.dx = dx;
}
public void setDy(int dy) {
this.dy = dy;
}
public void setX(int x){
this.x = x;
}
public void setY(int y){
this.y = y;
}
public void run() {
while(!hungry) {
move();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
I have created this class to move a frog object randomly but when I call the move method nothing is happening. I want the frog on a JPanel to randomly move around the screen. I have another class which uses the frog object.
It is used from a main class containing the following:
public void actionPerformed(ActionEvent event) {
if (event.getSource() == MakePet) {
String petName = namePlace.getText();
frog = new Frog(petName,false);
panel.add(pet);
panel.add(prey);
frog.move();
Thread fro = new Thread(frog);
fro.start();
}
}
public static void main(String[] args) {
GUI demo = new GUI();
demo.setSize(520,720);
demo.createGUI();
demo.show();
}
For an example of timer based animation, have a look at http://www.java2s.com/Code/Java/2D-Graphics-GUI/Timerbasedanimation.htm
Another example of timer based animation, where Swing components are used to draw images on a JPanel was given Jérôme's comment: Randomly moving images on a JPanel
If you require having multiple frogs with their own threads of control, then you will need to handle synchronization. Otherwise I would simply call move from the timer before repainting the panel. And then change move:
public void move() {
if (!hungry) {
return;
}
...
}
Set the timer's interval appropriately based on the framerate you are looking to achieve. The below is based on the answer from the question Jérôme linked to:
Timer t = new Timer(1000 / DESIRED_FRAMERATE, (event) -> {
frogs.forEach(Frog::move);
panel.repaint();
});
Note that this is Java 8 using lambdas.
The above in pre-Java 8:
Timer t = new Timer(1000 / DESIRED_FRAMERATE, new ActionListener() {
public void actionPerformed(ActionEvent event) {
for (Frog frog : frogs) {
frog.move();
}
panel.repaint();
}
});
You should create the Timer in the initialization code of your JFrame or JPanel. If you have a class extending JPanel, you don't need to reference panel when calling repaint if the Timer is inside that class. Also, here I am guessing you have a collection of Frog objects, typically a list, called frogs. If there's only ever one frog, you don't need the loop. Frog's display method needs to be called from the JPanel's paint method, so I am guessing you have a class extending JPanel.
Related
I decided I wanted to experiment with making a game and I like Java, so I started following a tutorial here. I did deviate from the video a few times when I felt it was cleaner while being synonymous with the tutorial's code, but not in any way I thought would affect how the code worked. For example, it made sense to me that there should only ever be one instance of the Renderer, and Object Registry so I made them Singletons.
The code I have so far is supposed to create a window with a black background, and a blue square in the middle of the window representing the player, that much is working. However, it should also be sliding around in response to the wasd keys, and even more slowly drifting in one direction regardless. Instead it's doing nothing.
I spent no less than an hour trying to figure out why it wasn't working. It seems to be ticking just fine and the data looks like it's updating properly, but even though my render methods are also being called, the screen just isn't changing.
This is all the code I have so far, since I'm out of ideas as to the problem. I apologize for listing a whole project.
public class Game implements Runnable {
private final Thread thread;
private boolean running = false;
Game() {
thread = new Thread(this);
}
public static void main(String[] args) {
Game game = new Game();
game.start();
new Player(600,450,0));
}
private void start() {
thread.start();
running = true;
}
#Override
public void run() {
double tps = 10.0;
double nsPerTick = 1000000000 / tps;
double delta = 0;
int frames = 0;
long timer = System.currentTimeMillis();
long lTime = System.nanoTime();
long now;
while (running) {
now = System.nanoTime();
delta += (now - lTime) / nsPerTick;
lTime = now;
while (delta >= 1) {
Registry.getInstance().tick();
delta--;
}
if (running) Renderer.getInstance().run();
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames);
frames = 0;
}
}
stop();
}
private void stop() {
try {
thread.join();
} catch (Exception e) {
e.printStackTrace();
}
running = false;
}
}
public class Renderer extends Canvas {
private static final Renderer renderer = new Renderer();
private final Window window;
private final BufferStrategy bs;
private final Graphics g;
boolean black = true;
private Renderer() {
window = new Window(1200, 900, "First Game", this);
this.createBufferStrategy(2);
bs = this.getBufferStrategy();
g = bs.getDrawGraphics();
addKeyListener(Controller.getInstance());
}
public static Renderer getInstance() {return renderer;}
public void run() {
g.setColor(Color.BLACK);
//this was to see if even the background would update, it wouldn't
//g.setColor(black ? Color.BLACK : Color.WHITE);
//black = !black;
g.fillRect(0,0,1200, 900);
Registry.getInstance().render();
g.dispose();
bs.show();
}
public Graphics getGraphics() {return g;}
private static class Window extends Canvas {
private Window(int width, int height, String title, Renderer renderer) {
JFrame frame = new JFrame(title);
frame.setPreferredSize(new Dimension(width, height));
frame.setMinimumSize(new Dimension(width, height));
frame.setMaximumSize(new Dimension(width, height));
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.add(renderer);
frame.setVisible(true);
}
}
}
public class Registry {
private static final Registry reg = new Registry();
private final LinkedList<GameObject> objects = new LinkedList<>();
private Registry() {
}
public static Registry getInstance() { return reg; }
public void tick() { //System.out.println("tick");
objects.forEach(GameObject::tick); }
public void render() { objects.forEach(GameObject::render); }
public void add(GameObject gameObject) { objects.add(gameObject); }
public void remove(GameObject gameObject) { objects.remove(gameObject);}
}
public class Controller extends KeyAdapter {
private static final Controller controller = new Controller();
private final HashMap<Character,Boolean> keyStates = new HashMap<>();
private Controller() {
}
public static Controller getInstance() {
return controller;
}
public void keyPressed(KeyEvent e) {
if (!keyStates.getOrDefault(e.getKeyChar(), true)) System.out.println(e.getKeyChar() + " down");
keyStates.put(e.getKeyChar(),true);
}
public void keyReleased(KeyEvent e) {
keyStates.put(e.getKeyChar(),false);
System.out.println(e.getKeyChar() + " up " + keyStates.size());
}
public boolean isKeyDown(char c) {return keyStates.getOrDefault(c,false);}
}
public abstract class GameObject {
protected Graphics graphics = Renderer.getInstance().getGraphics();
protected final ObjectType type;
protected float x,y,r;
protected GameObject(ObjectType objectType, float x, float y, float r) {
this.type = objectType;
this.x = x;
this.y = y;
this.r = r;
Registry.getInstance().add(this);
}
public abstract void tick();
public abstract void render();
public void destroy() { Registry.getInstance().remove(this); }
public float getX() { return x; }
public void setX(float x) { this.x = x; }
public float getY() { return y; }
public void setY(float y) { this.y = y; }
public float getR() { return r; }
public void setR(float r) { this.r = r; }
}
public class Player extends GameObject {
private final Controller controller;
public Player(float x, float y, float r) {
super(ObjectType.PLAYER, x, y, r);
controller = Controller.getInstance();
}
#Override
public void tick() {
this.x += 1;
if (controller.isKeyDown('w')) x += 2;
if (controller.isKeyDown('a')) y -= 2;
if (controller.isKeyDown('s')) x -= 2;
if (controller.isKeyDown('d')) y += 2;
}
#Override
public void render() {
graphics.setColor(Color.BLUE);
graphics.fillRect((int) (this.x-12),(int) (this.y-12), 24,24);
}
}
The problem lies in your handling of Graphics. There's only ONE active Graphics object you can effectively address.
Refactor your code, so that you pass the current Graphics object via parameter through the target methods (like here: Player.render() should become Player.render(Gpahics g). And get rid of the Gameobject.graphics member variable. That is the culprit.
Adding to that, the best way to do simple rendering is to override the paint(Graphics g) method or the paintComponents(Graphics g), and from outside call repaint() on the JPanel/Canvas. This way the UI instigates drawing itself, takes care of the actual frequency, and draws default components/design too if there is some.
I am making Conways Game of Life. In the mouse listener I want the cell to appear/disappear on the screen when I click once. I use a 40x40 boolean array (gameState) of 20x20 pixel cells. I want to paint the squares in my paint method using the co-ordinates of my mouse which i get in its clicked method. However, I am getting a null-pointer exception at line 71 and do not know what to do to solve it.
Main
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferStrategy;
public class mainApplication extends JFrame implements Runnable, MouseListener {
private static final Dimension windowsize = new Dimension(80, 600);
private BufferStrategy strategy;
private Graphics offscreenGraphics;
private static boolean isGraphicsInitialised = false;
private static int rows = 40;
private static int columns = 40;
private static int height = windowsize.height;
private static int width = windowsize.width;
private static Cells cells;
private int xArrayElement,yArrayElement, xPosition, yPosition;
private static boolean gameState[][] = new boolean[rows][columns];
public mainApplication() {
System.out.println(System.getProperty("user.dir"));
setDefaultCloseOperation(EXIT_ON_CLOSE);
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width / 2 - windowsize.width / 2;
int y = screensize.height / 2 - windowsize.height / 2;
setBounds(x, y, screensize.width, screensize.height);
setVisible(true);
createBufferStrategy(2);
strategy = getBufferStrategy();
offscreenGraphics = strategy.getDrawGraphics();
isGraphicsInitialised = true;
// MouseEvent mouseEvent = new MouseEvent();
addMouseListener(this);
// addMouseMotionListener(MouseEvent);
Thread t = new Thread(this);
t.start();
}
public void mousePressed(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mouseClicked(MouseEvent e) {
if(e.getClickCount() == 1){
xPosition = e.getX();
yPosition = e.getY();
cells.setPosition(xPosition,yPosition);
xArrayElement = (xPosition/20);
yArrayElement = (yPosition/20);
if(gameState[xArrayElement][yArrayElement]){
gameState[xArrayElement][yArrayElement] = false;
}
else if (!gameState[xArrayElement][yArrayElement]) {
gameState[xArrayElement][yArrayElement] = true;
}
}
}
#Override
public void run() {
while (true) {
try { //threads entry point
Thread.sleep(20); //forces us to catch exception
}
catch (InterruptedException e) {
}
}
}
public void paint(Graphics g) {
if (isGraphicsInitialised) {
g = strategy.getDrawGraphics();
g.setColor(Color.BLACK);
g.fillRect(0, 0, 800, 800);
if (gameState[xArrayElement][yArrayElement]) {
g.setColor(Color.WHITE);
cells.paint(g);
}
else if (!gameState[xArrayElement][yArrayElement]) {
g.setColor(Color.BLACK);
g.fillRect(xPosition, yPosition, 20, 20);
}
strategy.show();
}
}
public static void main(String[]args){
mainApplication test = new mainApplication();
}
}
Cells class
import java.awt.*;
public class Cells {
int x;
int y;
public Cells(){
}
public void setPosition(int xi, int xj){
x = xi;
y = xi;
System.out.println(xi);
System.out.println("sjdkgffdjv" + y);
}
public boolean cellState(boolean visible){
return visible;
}
public void paint(Graphics g){
g.drawRect(x, y, 20,20);
}
}
It's because you haven't initialized your cells variable in Main class..
So try this
private static Cells cells = new Cells();
As #nullPointer has pointed out (sorry, dad joke) you're getting a NPE because you haven't initialized the class member Cells. There are also a few other points to make that might unrelated to the question.
Don't create that thread
Swing already uses a thread to handle UI events and drawing so creating another thread is dangerous.
Make Cells immutable
Cells should be immutable. At no point should you need to set the position of a cell. If you need to change where a Cell is at, just dispose of the object and create a new one at that position.
I made a fairly simple code and i got into an error which confused me.
So I have a class that creates two totally different variables and creating them using the new keyword
Player playerLeft = new Player(5,150);
Player playerRight = new Player( 150,150);
Player class:
import javax.swing.*;
import java.awt.*;
public class Player extends JComponent {
private int posY;
private int posX;
public Player(int x, int y) {
posX = x;
posY = y;
//repaint();
}
public float getMovementY() {
return movementY;
}
public void setMovementY(int movementY) {
this.movementY = movementY;
}
int movementY = 0;
public void paintComponent(Graphics g) {
Graphics2D _g2 = (Graphics2D) g;
Rectangle rect = new Rectangle(posX, posY, 20, 150);
_g2.fill(rect);
}
public void setLocation(int x, int y) {
posY = y;
posX = x;
repaint();
}
public void move() {
setLocation(posX, posY + movementY);
}
}
It's probably me not knowing something about Java but for me when I try to instantiate playerRight it just overwrites player left and drawsOut playerRight only.
Here is the complete code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Timer;
import java.util.TimerTask;
public class mainJFrame extends JFrame implements KeyListener {
int relativeTimeMillsec = 0;
Player playerLeft = new Player(5, 150);
Player playerRight = new Player(150, 150);
Timer timer = new Timer();
TimerTask task = new TimerTask() {
#Override
public void run() {
relativeTimeMillsec++;
refreshTimeText(relativeTimeMillsec);
calcMovements();
}
};
//components
JLabel timeCounterLabel = new JLabel("Time: " + 0, SwingConstants.CENTER);
public mainJFrame() {
createComponents();
addKeyListener(this);
}
public void createComponents() {
this.setTitle("The title");
this.setSize(800, 600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
timer.scheduleAtFixedRate(task, 0, 10);
JButton testButton = new JButton("Label");
testButton.setSize(100, 25);
testButton.setLocation(this.getWidth() / 2 - testButton.getWidth() / 2, this.getHeight() / 2 - testButton.getHeight() / 2);
timeCounterLabel.setSize(200, 25);
timeCounterLabel.setLocation(this.getWidth() / 2 - timeCounterLabel.getWidth() / 2, 10);
//playerRight = new Player(this.getWidth()-45,this.getHeight()/2);
// this.add(testButton);
this.add(timeCounterLabel);
this.add(playerLeft);
this.add(playerRight);
}
public void paintComponent(Graphics g) {
{
super.repaint();
}
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_S) {
playerLeft.movementY = +2;
} else if (e.getKeyCode() == KeyEvent.VK_W) {
playerLeft.movementY = -2;
}
if (e.getKeyCode() == KeyEvent.VK_UP) {
playerRight.movementY = +2;
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
playerRight.movementY = -2;
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
private double calcRealRelativeTime(int _relTime) {
return relativeTimeMillsec / (double) 100;
}
private void refreshTimeText(int _relTime) {
timeCounterLabel.setText("Time: " + Math.round(calcRealRelativeTime(_relTime)));
}
private void calcMovements() {
playerLeft.move();
playerRight.move();
}
}
Understand that a JFrame's contentPane (the container that holds its components) uses BorderLayout by default, and this code:
this.add(timeCounterLabel);
this.add(playerLeft);
this.add(playerRight);
is adding all components to the same default BorderLayout.CENTER position, meaning any components added will replace components added previously.
But more importantly, yours is a common problem and stems from your having your Player class extend from a GUI component. Don't do this, as then you will have a great deal of difficulty drawing multiple Player objects and having them interact easily (as you're finding out). Instead have Player be a logical (non-component) class, and have only one class extend JPanel and do all the drawing. This class can hold Player objects, perhaps held in a collection such as an ArrayList<Player>, and then iterate through the collection within its paintComponent method.
Other issues:
Do not use java.util.Timer and java.util.TimerTask for Swing animations since these classes do not follow Swing threading rules. Use instead a javax.swing.Timer.
Learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others
If/when you do override a painting method such as paintComponent, be sure to call the super's method within your override, usually on the first line, so as not to break the painting chain. Also, use the #Override annotation before this method and any other methods that you think that you may be overriding so that the compiler catches possible errors with this.
For example (but not a complete example)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
#SuppressWarnings("serial")
public class SimpleAnimation extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 600;
private static final int TIMER_DELAY = 20;
private Player2 playerLeft = new Player2(5, 150, Color.RED);
private Player2 playerRight = new Player2(150, 150, Color.BLUE);
public SimpleAnimation() {
playerLeft.setySpeed(1);
playerRight.setySpeed(-1);
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
playerLeft.draw(g);
playerRight.draw(g);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
playerRight.move();
playerLeft.move();
repaint();
}
}
private static void createAndShowGui() {
SimpleAnimation mainPanel = new SimpleAnimation();
JFrame frame = new JFrame("SimpleAnimation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class Player2 {
private static final int RECT_WIDTH = 20;
private static final int RECT_HEIGHT = 50;
private int x;
private int y;
private int ySpeed;
private Color color;
public Player2(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setySpeed(int ySpeed) {
this.ySpeed = ySpeed;
}
public int getySpeed() {
return ySpeed;
}
public void setLocation(int x, int y) {
setX(x);
setY(y);
}
public void move() {
setLocation(x, y + ySpeed);
}
public void draw(Graphics g) {
g.setColor(color);
g.fillRect(x, y, RECT_WIDTH, RECT_HEIGHT);
}
}
I just start learning Java. I write a program like this:
Bacteria.class
import java.awt.*;
public class Bacteria extends Creature{
public static final int COLOR_STEP = 256/MAX_AGE;
Bacteria(Board board, int _x, int _y){
super.board = board;
//initialize age, x, y
super.age = 1;
super.x = _x;
super.y = _y;
}
//Draw a circle representing the creature at its position
//This function is done.
public void draw(Graphics g) {
g.setColor(new Color(0, COLOR_STEP * age, 0));
g.fillOval(x * board.CELL_SIZE, y * board.CELL_SIZE, board.CELL_SIZE, board.CELL_SIZE);
}
public void tick() {
//your code
// this is how the creature lives in one tick
// make the creature get older if it is not dead.
// if it's got older than MAX_AGE then it should die
// ...call board.addDead() to register the dead creature
// if it can have baby then try reproduce()
age++;
if (this.canHaveBaby()) {
this.reproduce();
}
//this.reproduce();
if (age > MAX_AGE) {
board.addDead(this);
}
}
}
Creature.class
import java.awt.*;
public class Creature {
public static final int MAX_AGE = 5;
public Board board;
public int age; // age of the creature measures in ticks
public int x; // (x,y) is the position of the creature
public int y;
Creature(Board board, int _x, int _y) {
this.board = board;
//initialize age, x, y
age = 1;
x = _x;
y = _y;
}
public Creature() {
}
//Draw a circle representing the creature at its position
//This function is done.
public void draw(Graphics g){}
public void tick(){}
public void reproduce() {
//your code
// if it can have a baby then produce a baby
// ...in an empty cell next to its cell.
// board.emptyCell() should be used
// ....to check whether a cell in the board is empty
// and call board.addBaby() to place the baby to the board
//int x_current = x, y_current = y;
if(board.emptyCell(x, y+1)){
Creature baby = new Creature(board,x, y+1);
//System.out.println("1");
board.addBaby( baby);
}
else if(board.emptyCell(x+1,y)){
Creature baby = new Creature(board,x+1, y);
board.addBaby(baby);
}
else if(board.emptyCell(x+1,y+1)){
Creature baby = new Creature(board,x+1, y+1);
board.addBaby(baby);
}
else if(board.emptyCell(x-1,y)){
Creature baby = new Creature(board,x-1, y);
board.addBaby(baby);
}
else if(board.emptyCell(x,y-1)){
Creature baby = new Creature(board,x, y-1);
board.addBaby(baby);
}
else if(board.emptyCell(x-1,y-1)){
Creature baby = new Creature(board,x-1, y-1);
board.addBaby(baby);
}
else if(board.emptyCell(x-1,y+1)){
Creature baby = new Creature(board,x-1, y+1);
board.addBaby(baby);
}
else if(board.emptyCell(x+1,y-1)){
Creature baby = new Creature(board,x+1, y-1);
board.addBaby(baby);
}
}
public boolean canHaveBaby() {
//your code
// if the creature is dead or it is too young
// then it cannot have a baby
if(age == 0 || age > MAX_AGE){
return false;
}
return true;
}
public boolean atPosition(int _x, int _y) {
//your code
// return true if the creature is at the position (_x,_y)
// you need this function for board.empty to function correctly
if(_x == x && _y == y){
return true;
}
return false;
}
}
Board.class
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
public class Board extends JPanel {
public static final int CELL_SIZE = 10;
private final int rows = 10;
private final int cols = 20;
ArrayList<Creature> creatureList;
ArrayList<Creature> babies, dead;
// ArrayList<Bacteria> bacteriaList;
// ArrayList<Bacteria> babies_bac, dead_bac;
Board() {
super();
setBackground(Color.white);
creatureList = new ArrayList<Creature>();
//creatureList.size();
babies = new ArrayList<Creature>();
dead = new ArrayList<Creature>();
setPreferredSize(new Dimension(cols * CELL_SIZE, rows * CELL_SIZE));
creatureList.add(new Bacteria(this, 1, 1));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Creature creature: creatureList) {
creature.draw(g);
}
}
public void tick() {
dead = new ArrayList<Creature>();
babies = new ArrayList<Creature>();
for (Creature creature: creatureList) {
creature.tick();
}
creatureList.addAll(babies);
creatureList.removeAll(dead);
}
public void addBaby(Creature baby) {
if (baby != null) babies.add(baby);
}
public void addDead(Creature deadCreature) {
if (deadCreature != null) dead.add(deadCreature);
}
public boolean emptyCell(int x, int y) {
if (x < 0 || y < 0 || x >= cols || y >= rows) return false;
for (Creature creature : creatureList) {
if (creature.atPosition(x, y)) {
return false;
}
}
for (Creature creature : creatureList) {
if (creature.atPosition(x, y)) {
return false;
}
}
return true;
}
public String getPopulation() {
return String.format("%d", creatureList.size());
}
}
and BacteriaSimulator.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class BacteriaSimulator extends JFrame {
private Board board;
private JLabel label;
public BacteriaSimulator() {
initUI();
setTitle("Bacteria Simulator");
setSize(350, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private void initUI() {
JPanel panel = new JPanel();
board = new Board();
panel.add(board);
label = new JLabel(board.getPopulation());
panel.add(label);
JButton button = new JButton("Tick");
button.addActionListener(new ButtonNextListener());
panel.add(button);
add(panel);
pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
BacteriaSimulator ex = new BacteriaSimulator();
ex.setVisible(true);
}
});
}
private class ButtonNextListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
board.tick();
label.setText(board.getPopulation());
repaint();
}
}
}
I want Board.class work with creatureList. However, Board is still initialized with a bacteria (creatureList.add(new Bacteria(this, 1, 1));). I got a problem that is Board.class can not run draw() and tick() method which I want. How to draw objects bacteria and make the right method of tick ()? Can you help me! Thank you!
Your draw() and tick() functions are part of Bacteria.java. They cannot be used from Board.java unless you create an instance of Bacteria and then invoke the methods using that.
Eventually got the balls to move, but the app doesn't execute perfectly and I'm still unsure about the run() method setup... I don't think I need it but it's part of the assignment. Please let me know if there's a better way.
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
public class BouncingBallApp extends JFrame
{
//start of main method
public static void main(String[] args)
{
//crate container
Container container = new Container();
//crate BouncingBallApp instance
BouncingBallApp bBalls = new BouncingBallApp();
//set the window closing feature(close with X click)
bBalls.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//crate boucing ball panel(BBP) instance and add
BouncingBallPanel BBP = new BouncingBallPanel();
container.add(BBP);
//make the BBP the MouseListner
bBalls.addMouseListener(BBP);
//set window background and size
bBalls.setBackground(Color.WHITE);
bBalls.setSize(400, 300);
BBP.setSize(400, 300);
BBP.setLayout(null);
bBalls.setContentPane(BBP);
//set window visible
bBalls.setVisible(true);
}//end of main method
}//end of Class BouncingBall App
class BouncingBallPanel extends JPanel implements MouseListener
{
//create an empty array for 20 Ball objects
public Ball[] array;
private int count = 0;
Random generator = new Random();
public BouncingBallPanel()
{
array = new Ball[20];
}
public void mouseClicked(MouseEvent event)
{
array[count] = new Ball(this);
count++;
if( count == 1)
{
final Runnable update = new Runnable()
{
public void run()
{
for (int j = 0; j < array.length; j++)
{
if(array[j] != null)
{
array[j].move();
}//end of if
}//end of for
}//end of run method
};//end of runnalbe update
(new Thread(new Ball(this))).start();
Runnable graphic = new Runnable()
{
public void run()
{
while(true)
{
try
{
EventQueue.invokeLater(update);
Thread.sleep(generator.nextInt(10 +100));
}catch (InterruptedException exp){}
}//end of while
}//end of run
};//end of runnable
new Thread(graphic).start();
}//end of if
}//end of mouseClicked method
//empty interfaces for mouse events
public void mouseExited(MouseEvent event){}
public void mouseReleased(MouseEvent event){}
public void mouseEntered(MouseEvent event){}
public void mousePressed(MouseEvent event){}
//paint component method
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
//loop for each ball and draw all balls in array
for(int i = 0; i < array.length; i++)
{
if(array[i] != null)
{
g2d.setColor(array[i].getColor());
g2d.fillOval((int)array[i].getX(), (int)array[i].getY(), (int)array[i].getDiameter(), (int)array[i].getDiameter());
}
}//end of for loop
}//end of paintComponent loop
}//end of Class BouncingBallPanel
class Ball implements Runnable
{
//set up variables
private double x;
private double y;
private int deltaX;
private int deltaY;
private double diameter;
private Color color;
BouncingBallPanel BBP2;
Random random = new Random();
public Ball(BouncingBallPanel a)
{
x = random.nextInt(400);
y = random.nextInt(300);
deltaX = 1 + random.nextInt(10);
deltaY = 1 + random.nextInt(10);
diameter = 5 + random.nextInt(20);
color = new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256));
BBP2 = a;
}// end of constructor
public double getX()
{
return x;
}
public double getY() {
return y;
}
public double getDiameter() {
return diameter;
}
public Color getColor() {
return color;
}
public void move() {
x += deltaX;
y += deltaY;
if (x > 400 - getDiameter()|| x <0)
{
deltaX = -deltaX;
}
if (y > 300 - getDiameter() || y < 0)
{
deltaY = -deltaY;
}
}// end of method move
#Override
public void run()
{
while(true)
{
move();
BBP2.repaint();
try{
Thread.currentThread().sleep(10 + random.nextInt(100));
}catch(InterruptedException exp){}
}//end of while
}//end of run method
}//end of Ball
Two problems:
You never create threads for the balls. Just making an object Runnable does not implicitly create a thread. You need something like this in the constructor:
new Thread(this).start();
In the Swing event model, using Timer is better than background threads. Using threads, you will see graphical artifacts if an object moves while the window is being painted.