I am writing an game in java with Libgdx and have a question about how to have multiple instances of the same object in an ArrayList without abusing the garbage collector.
this is the code of my main game state. I am still really new, so I assume my coding habits are downright awful:
package com.frog.game;
import java.util.ArrayList;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.math.MathUtils;
import com.frog.entities.Frog;
import com.frog.entities.Grass;
import com.frog.entities.LilyPad;
import com.frog.gamestates.GameState;
import com.frog.managers.GameStateManager;
import com.frog.managers.Jukebox;
import com.frog.managers.Save;
import com.frog.managers.SimpleDirectionGestureDetector;
import com.frog.managers.SpeedManager;
import com.frog.game.Game;
public class PlayState extends GameState {
private SpriteBatch sb;
private Frog frog;
private BitmapFont font;
private ArrayList<LilyPad> lilypads;
private Grass grass;
private float hopY;
public boolean tmp;
private SpeedManager speedManager;
private float speed;
private float[] placement;
public PlayState(GameStateManager gsm) {
super(gsm);
}
#SuppressWarnings("deprecation")
public void init() {
speedManager = new SpeedManager();
lilypads = new ArrayList<LilyPad>();
sb = new SpriteBatch();
frog = new Frog();
frog.init();
grass = new Grass();
grass.init();
hopY = Game.SIZE * 0.8f;
placement = new float[] {
0,
Game.SIZE,
Game.SIZE * 2
};
addPad();
FreeTypeFontGenerator gen = new FreeTypeFontGenerator(
Gdx.files.internal("PressStart2P-Regular.ttf")
);
font = gen.generateFont((Game.WIDTH / 10));
gen.dispose();
Gdx.input.setInputProcessor(new SimpleDirectionGestureDetector(new SimpleDirectionGestureDetector.DirectionListener() {
#Override
public void onUp() {
// TODO Auto-generated method stub
}
#Override
public void onRight() {
frog.moveRight();
}
#Override
public void onLeft() {
frog.moveLeft();
}
#Override
public void onDown() {
}
}));
}
This is the method I use to add another lilypad at the top of the screen. I make it appear in one of three places randomly. Since the main function of these lilypads is to scroll down the screen, instances of the lilypad are being added and removed from the array non stop. I know this kills because each time I add a new lilypad, I have to run init() for it, otherwise I will get a nullpointerexception. init() instantiates a bunch of objects in order to make that lilypad render, such as textures, the default Y value etc. Is there any way I could run the init() method once for the entire arraylist, even though I am constantly adding & removing them? I have considered wrapping the same lilypads around the screen when they hit the bottom, so I would only need like 7, and then I wouldn't have to add or remove anymore, but would have to rework a big chunk of code for that, and consider it a last resort. The game already runs smooth, it just has a few barely noticeable stutters here and there that I would like to avoid.
private void addPad() {
lilypads.add(new LilyPad(placement[MathUtils.random(0, 2)], 300, false));
lilypads.get(lilypads.size() - 1).init();
}
public void update(float dt) {
speed = speedManager.speed(dt);
speedManager.update(dt);
// update grass
if(!grass.shouldStop()) {
grass.update(dt, speed);
frog.introPlay(speed);
}
// update lily pads
for(int i = 0; i < lilypads.size(); i++) {
lilypads.get(i).update(dt, speed);
This is where I call the addPad() method. It basically says if the last added pad is fully visible on screen, add the next.
if(lilypads.get(i).getY() < (Game.HEIGHT - Game.SIZE) && lilypads.get(i).past()) {
addPad();
}
// hop frog
if(lilypads.get(i).getY() < hopY && lilypads.get(i).past2()) {
// play hop
if(lilypads.get(i).getX() > frog.getX() - Game.SIZE / 2 && lilypads.get(i).getX() < frog.getX() + Game.SIZE / 2){
frog.incrementScore();
Jukebox.play("hop");
lilypads.get(i).splash();
frog.play(speed);
} else {
Jukebox.stopAll();
Save.gd.setTempScore(frog.getScore());
gsm.setState(GameStateManager.GAMEOVER);
return;
}
return;
}
if(lilypads.get(i).getY() < (-Game.SIZE)){
lilypads.remove(i);
}
}
}
public void draw() {
// draw grass
if(!grass.shouldStop()) {
grass.draw(sb);
}
// draw pads
for(int i = 0; i < lilypads.size(); i++){
lilypads.get(i).draw(sb, true);
}
// draw frog
frog.draw(sb, speed, true);
sb.setColor(1, 1, 1, 1);
sb.begin();
font.draw(sb, Long.toString(frog.getScore()),
Game.WIDTH * 0.92f -font.getBounds(Long.toString(frog.getScore())).width, Game.HEIGHT * 0.958f);
sb.end();
}
public void handleInput() {
}
public void dispose() {
sb.dispose();
Jukebox.stopAll();
}
Thanks for the support.
Use libGDX's custom collections for that. You can find all of them here.
In your case you want to use an Array. They are optimized to not cause the garbage collector to kick in, for example by re-using the iterators.
With a standard ArrayList the following piece of code in your render method would create a new Iterator in every frame.
for(LilyPad lilyPad : lilipads){
lilypad.draw(sb, true);
}
In general try to avoid using new whenever you can. This wiki article might help you with it and it explains how pooling works.
Another common trick to avoid GC especially when working with Vector2 or Vector3 is keeping a private final Vector2 tmp = new Vector2() and always only using tmp.set(x, y) to change it. This principle could also be applied to any other custom class that you are creating yourself to hold some data.
Related
I'm trying to make a simple 2D game in Java.
As far as I know, my game should consist of two threads: "event dispatch thread" (for GUI operations) and "game thread" (for game loop).
I created an outline but could not find where to place the game loop.
in short, I'm trying to create a game loop without freezing my UI thread.
I would be grateful if you could give any information about the things I did wrong.
That's my game loop (You can also give tips to create a better game loop):
while(true) {
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("Forge and Attack");
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setUndecorated(true);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setFocusable(true);
frame.add(new MyPanel());
}
}
class MyPanel extends JPanel implements KeyListener, MouseListener {
public MyPanel() {
setBackground(Color.BLACK);
setOpaque(true);
addKeyListener(this);
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
}
});
}
#Override
public void paint(Graphics g) {
}
}
I thought this an interesting topic to expand on... I have covered the questions you asked as well as showed the maybe a better or correct way of doing certain things like painting, and listening for keys pressed, as well as some others like separation of concerns and making the entire game more reusable/expandable.
1. Where to place the game loop?
So this isn't straight forward and can depend on each individuals coding style, but really all we seek to achieve here is to create the game loop and start it at an appropriate time. I believe code speaks a 1000 words (sometimes it might just be 1000 words :)), but below is some code which in the most minimally possible way (still producing a valid working example) shows where a game loop can be created/placed and used in the code, the code is heavily commented for clarity and understanding:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class MyGame {
private Scene scene;
private Sprite player;
private Thread gameLoop;
private boolean isRunning;
public MyGame() {
createAndShowUI();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(MyGame::new);
}
/**
* Here we will create our swing UI as well as initialise and setup our
* sprites, scene, and game loop and other buttons etc
*/
private void createAndShowUI() {
JFrame frame = new JFrame("MyGame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
player = new Sprite(/*ImageIO.read(getClass().getResourceAsStream("...."))*/);
this.scene = new Scene();
this.scene.add(player);
this.addKeyBindings();
this.setupGameLoop();
frame.add(scene);
frame.pack();
frame.setVisible(true);
// after setting the frame visible we start the game loop, this could be done in a button or wherever you want
this.isRunning = true;
this.gameLoop.start();
}
/**
* This method would actually create and setup the game loop The game loop
* will always be encapsulated in a Thread, Timer or some sort of construct
* that generates a separate thread in order to not block the UI
*/
private void setupGameLoop() {
// initialise the thread
gameLoop = new Thread(() -> {
// while the game "is running" and the isRunning boolean is set to true, loop forever
while (isRunning) {
// here we do 2 very important things which might later be expanded to 3:
// 1. We call Scene#update: this essentially will iterate all of our Sprites in our game and update their movments/position in the game via Sprite#update()
this.scene.update();
// TODO later on one might add a method like this.scene.checkCollisions in which you check if 2 sprites are interesecting and do something about it
// 2. We then call JPanel#repaint() which will cause JPanel#paintComponent to be called and thus we will iterate all of our sprites
// and invoke the Sprite#render method which will draw them to the screen
this.scene.repaint();
// here we throttle our game loop, because we are using a while loop this will execute as fast as it possible can, which might not be needed
// so here we call Thread#slepp so we can give the CPU some time to breathe :)
try {
Thread.sleep(15);
} catch (InterruptedException ex) {
}
}
});
}
private void addKeyBindings() {
// here we would use KeyBindings (https://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html) and add them to our Scene/JPanel
// these would allow us to manipulate our Sprite objects using the keyboard below is 2 examples for using the A key to make our player/Sprite go left
// or the D key to make the player/Sprite go to the right
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "A pressed");
this.scene.getActionMap().put("A pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.LEFT = true;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "A released");
this.scene.getActionMap().put("A released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.LEFT = false;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "D pressed");
this.scene.getActionMap().put("D pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.RIGHT = true;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "D released");
this.scene.getActionMap().put("D released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.RIGHT = false;
}
});
}
public class Scene extends JPanel {
private final ArrayList<Sprite> sprites;
public Scene() {
// we are using a game loop to repaint, so probably dont want swing randomly doing it for us
this.setIgnoreRepaint(true);
this.sprites = new ArrayList<>();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
// this method gets called on Scene#repaint in our game loop and we then render each in our game
sprites.forEach((sprite) -> {
sprite.render(g2d);
});
}
#Override
public Dimension getPreferredSize() {
// because no components are added to the JPanel, we will have a default sizxe of 0,0 so we instead force the JPanel to a size we want
return new Dimension(500, 500);
}
public void add(Sprite go) {
this.sprites.add(go);
}
private void update() {
// this method gets called on Scene#update in our game loop and we then update the sprites movement and position our game
sprites.forEach((go) -> {
go.update();
});
}
}
public class Sprite {
private int x = 50, y = 50, speed = 5;
//private final BufferedImage image;
public boolean LEFT, RIGHT, UP, DOWN;
public Sprite(/*BufferedImage image*/) {
//this.image = image;
}
public void render(Graphics2D g2d) {
//g2d.drawImage(this.image, this.x, this.y, null);
g2d.fillRect(this.x, this.y, 100, 100);
}
public void update() {
if (LEFT) {
this.x -= this.speed;
}
if (RIGHT) {
this.x += this.speed;
}
if (UP) {
this.y -= this.speed;
}
if (DOWN) {
this.y += this.speed;
}
}
}
}
2. Tips to create a better game loop
This very much like the first point in my answer is very subjective to what you are trying to achieve and at what granularity will your problem be satisfied with. So instead of prescribing 1 type of game loop. Let us look at the various kinds we can have:
First what is a game loop?*
The game loop is the overall flow control for the entire game program. It’s a loop because the game keeps doing a series of actions over and over again until the user quits. Each iteration of the game loop is known as a frame. Most real-time games update several times per second: 30 and 60 are the two most common intervals. If a game runs at 60 FPS (frames per second), this means that the game loop completes 60 iterations every second.
a. While loop
This we have seen in the above example and is simply a while loop encapsulated inside a Thread with possibly a Thread#sleep call to help throttle CPU usage. This and the Swing Timer are probably the most basic you can use.
gameLoop = new Thread(() -> {
while (isRunning) {
this.scene.update();
this.scene.repaint();
try {
Thread.sleep(15);
} catch (InterruptedException ex) {
}
}
});
Pros:
Easy to implement
All updating and rendering, repainting is done in a separate thread from the EDT
Cons:
Cannot guarantee the same frame rate on various PCs, so movement of the game might look better/worse or slower/faster on various computers depending on the hardware
b. Swing Timer
Similar to the while loop, a Swing Timer can be used in which an action event is fired periodically, because it is fired periodically we can simply use an if statement to check if the game is running and then call our necessary methods
gameLoop = new Timer(15, (ActionEvent e) -> {
if (isRunning) {
MyGame.this.scene.update();
MyGame.this.scene.repaint();
}
});
Pros:
Easy to implement
Cons:
Runs on the EDT which is not necessary or wanted as we are not updating any Swing components but rather simply painting to it
Cannot guarantee the same frame rate on various PCs, so movement of the game might look better/worse or slower/faster on various computers depending on the hardware
c. Fixed time step*
This is a more complex game loop (but simpler than a variable time step game loop). This works on the premise that we want to achieve a specific FPS i.e. 30 or 60 frames per second, and thus we simply make sure we call our update and rendering methods that exact number of times per seconds.
Update methods do not accept a "time elapsed", as they assume each update is for a fixed time period. Calculations may be done as position += distancePerUpdate. The example includes an interpolation during render.
gameLoop = new Thread(() -> {
//This value would probably be stored elsewhere.
final double GAME_HERTZ = 60.0;
//Calculate how many ns each frame should take for our target game hertz.
final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
//If we are able to get as high as this FPS, don't render again.
final double TARGET_FPS = 60;
final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;
//At the very most we will update the game this many times before a new render.
//If you're worried about visual hitches more than perfect timing, set this to 1.
final int MAX_UPDATES_BEFORE_RENDER = 5;
//We will need the last update time.
double lastUpdateTime = System.nanoTime();
//Store the last time we rendered.
double lastRenderTime = System.nanoTime();
while (isRunning) {
double now = System.nanoTime();
int updateCount = 0;
//Do as many game updates as we need to, potentially playing catchup.
while (now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER) {
MyGame.this.scene.update();
lastUpdateTime += TIME_BETWEEN_UPDATES;
updateCount++;
}
//If for some reason an update takes forever, we don't want to do an insane number of catchups.
//If you were doing some sort of game that needed to keep EXACT time, you would get rid of this.
if (now - lastUpdateTime > TIME_BETWEEN_UPDATES) {
lastUpdateTime = now - TIME_BETWEEN_UPDATES;
}
//Render. To do so, we need to calculate interpolation for a smooth render.
float interpolation = Math.min(1.0f, (float) ((now - lastUpdateTime) / TIME_BETWEEN_UPDATES));
MyGame.this.scene.render(interpolation);
lastRenderTime = now;
//Yield until it has been at least the target time between renders. This saves the CPU from hogging.
while (now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES) {
//allow the threading system to play threads that are waiting to run.
Thread.yield();
//This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it.
//You can remove this line and it will still work (better), your CPU just climbs on certain OSes.
//FYI on some OS's this can cause pretty bad stuttering. Scroll down and have a look at different peoples' solutions to this.
//On my OS it does not unpuase the game if i take this away
try {
Thread.sleep(1);
} catch (Exception e) {
}
now = System.nanoTime();
}
}
});
This loop will need other changes such to take place to allow for interpolation:
Scene:
public class Scene extends JPanel {
private float interpolation;
#Override
protected void paintComponent(Graphics g) {
...
sprites.forEach((sprite) -> {
sprite.render(g2d, this.interpolation);
});
}
public void render(float interpolation) {
this.interpolation = interpolation;
this.repaint();
}
}
Sprite:
public class Sprite {
public void render(Graphics2D g2d, float interpolation) {
g2d.fillRect((int) (this.x + interpolation), (int) (this.y + interpolation), 100, 100);
}
}
Pros:
predictable, deterministic FPS on various computers/hardware
clearer calculation code
Cons:
not synced to monitor v-sync (causes jittery graphics unless you interpolate) - this example interpolates
limited max frame rate (unless you interpolate) - this example interpolates
d. Variable time step*
Usually used when a physics system is being implemented, or whenever a record of elapsed time is needed, I.e. animations. Physics/animation updates are passed a "time elapsed since last update" argument and are hence framerate-dependent. This may mean doing calculations as position += distancePerSecond * timeElapsed.
gameLoop = new Thread(() -> {
// how many frames should be drawn in a second
final int FRAMES_PER_SECOND = 60;
// calculate how many nano seconds each frame should take for our target frames per second.
final long TIME_BETWEEN_UPDATES = 1000000000 / FRAMES_PER_SECOND;
// track number of frames
int frameCount;
// if you're worried about visual hitches more than perfect timing, set this to 1. else 5 should be okay
final int MAX_UPDATES_BETWEEN_RENDER = 1;
// we will need the last update time.
long lastUpdateTime = System.nanoTime();
// store the time we started this will be used for updating map and charcter animations
long currTime = System.currentTimeMillis();
while (isRunning) {
long now = System.nanoTime();
long elapsedTime = System.currentTimeMillis() - currTime;
currTime += elapsedTime;
int updateCount = 0;
// do as many game updates as we need to, potentially playing catchup.
while (now - lastUpdateTime >= TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BETWEEN_RENDER) {
MyGame.this.scene.update(elapsedTime);//Update the entity movements and collision checks etc (all has to do with updating the games status i.e call move() on Enitites)
lastUpdateTime += TIME_BETWEEN_UPDATES;
updateCount++;
}
// if for some reason an update takes forever, we don't want to do an insane number of catchups.
// if you were doing some sort of game that needed to keep EXACT time, you would get rid of this.
if (now - lastUpdateTime >= TIME_BETWEEN_UPDATES) {
lastUpdateTime = now - TIME_BETWEEN_UPDATES;
}
MyGame.this.scene.repaint(); // draw call for rendering sprites etc
long lastRenderTime = now;
//Yield until it has been at least the target time between renders. This saves the CPU from hogging.
while (now - lastRenderTime < TIME_BETWEEN_UPDATES && now - lastUpdateTime < TIME_BETWEEN_UPDATES) {
Thread.yield();
now = System.nanoTime();
}
}
});
Scene:
public class Scene extends JPanel {
private void update(long elapsedTime) {
// this method gets called on Scene#update in our game loop and we then update the sprites movement and position our game
sprites.forEach((go) -> {
go.update(elapsedTime);
});
}
}
Sprite:
public class Sprite {
private float speed = 0.5f;
public void update(long elapsedTime) {
if (LEFT) {
this.x -= this.speed * elapsedTime;
}
if (RIGHT) {
this.x += this.speed * elapsedTime;
}
if (UP) {
this.y -= this.speed * elapsedTime;
}
if (DOWN) {
this.y += this.speed * elapsedTime;
}
}
}
Pros:
smooth
easier to to code
Cons:
non-deterministic, unpredictable at very small or large steps
Very new here. Trying to learn Java.
I figured I'd write a very simple "Game Of Life" auto-algorithm to see if I could grasp some of the basics - (The syntax and concepts are pretty straightforward from other languages etc.)
I've managed to muddle through and seem to be "getting the hang of it" but I've reached a pretty steep hill now.
The code described here will compile and is executable, but as you will see, despite the #override to DrawComponent and the repaint() call, the actual image displayed on-screen does not appear to change...
DIRECT QUESTION IS:
How can I ensure the display is updated every "loop"?
FLUFF & SAMPLE SOURCE:
I appreciate the procedural and non-distributed // rigid single encapsulated class here is not the ideal approach for Java, nor is this "game of life" a particularly appropriate use fothe language - BUT Iwanted to focus on getting core elements and understanding first before I 'advance' onto other things. Secondly, I appreciate this is not a very efficient solution either, but again, I wanted to learn by trying something straightforward and readable so I could easily SEE what was happening and ensure it was working properly as expected.
Third, my naming or formatting conventions may be unusual or non-standard, for this I apologise and again appreciate there are preferred ways. This was not necessarily a priority for me in learning how the language works at this time.
I welcome any advice and tips but please consider I am a complete newcomer to using Java so may not be familiar with concepts and terminologies etc.
/*
Project.java
By: PJ Chowdhury
Entire program encapsulated in single class
Created 29-Oct-2018
Updated: 07-Nov-2018
Added graphics library
*/
//Import the basic required graphics classes.
import java.awt.image.BufferedImage;
import java.awt.*;
//Import the basic applet classes.
import javax.swing.*;
//Core class
public class project
{
//Control value - determines when overpopualted or extinct
private static int supercount;
//Control value - how many surrounding cells must be alive for the central cell to survive
private static byte thrive=4;
//Define & declare effective constant size values
private static byte size=64;
private static byte cellsize=4;
//Declare effective singleton arrays of cells
private static boolean[][] cells;
private static boolean[][] prolif;
//Declare Window Frame
public static JFrame frame;
//Declare Graphics
public static JPanel panel;
//main entry-point. Execution must include parameter argument.
public static void main(String[] args)
{
initialise();
do
{
runtime();
defaultcells();
}
while (1>0); //Bad form of course. I wanted an infinite loop. The window can be closed at user request.
}
//Initialises window & graphics frame
public static void initialiseframe()
{
//Create Window
frame = new JFrame("Life Cells");
//Define window parameters
frame.setSize((int)cellsize*size,(int)cellsize*size);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
//Create a window panel to accept graphics
panel = new JPanel()
{
//Overload PaintComponent method to redraw image when the frame panel is redrawn
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
}
};
//attach this panel as a gadget to the frame window
frame.add(panel);
//frame.pack();// Deprecated as this resizes frame window to a minimal size
frame.validate(); // required since panel was added after setVisible was called
frame.repaint(); // required since panel was added after setVisible was called
}
//Initialises & defaults cells
public static void initialisecells()
{
//Define array sizes
cells = new boolean[size][size];
prolif = new boolean[size][size];
// Populate with defaults
defaultcells();
}
//Sets randomised state for each cell
public static void defaultcells()
{
byte x;
byte y;
for (y=0;y<size;y++)
{
for (x=0;x<size;x++)
{
if (Math.random()>=0.5)
{
}
else
{
}
}
}
}
//Wraps initialisation routines
public static void initialise()
{
initialiseframe();
initialisecells();
}
//iterates cells (twice) to determine if they survive or decline and draw to image
public static void process()
{
//Prepare image for cell drawing
Graphics g=panel.getGraphics();
byte x;
byte y;
supercount=0;
//First pass - check if cell will thrive
for (y=0;y<size;y++)
{
for (x=0;x<size;x++)
{
checkcell(x,y);
}
}
//Second pass - apply thrive or wither
for (y=0;y<size;y++)
{
for (x=0;x<size;x++)
{
if (updatecell(x,y))
{
}
if (cells[x][y])
{
}
}
}
}
//sets prolif equivalent depending on status of surrounding cells. This is used in update to set these cells to thrive
public static void checkcell(byte x, byte y)
{
byte count=getsurrounding((int)x,(int)y);
if (count>thrive)
{
prolif[x][y]=true;
}
else
{
if (count<thrive)
{
prolif[x][y]=false;
}
else
{
prolif[x][y]=cells[x][y];
}
}
}
//updates cell with prolif equivalent and returns true if cahnged
public static boolean updatecell(byte x, byte y)
{
if (cells[x][y]!=prolif[x][y])
{
cells[x][y]=prolif[x][y];
return true;
}
return false;
}
//returns number of thriving "cells" surrounding cell at given coords
public static byte getsurrounding(int x, int y)
{
int u=(x-1);
int v=(y-1);
int ux;
int vy;
byte count=0;
for (v=(y-1);v<(y+2);v++)
{
//Use wraparound for edge cells
vy=(v+size) % size;
for (u=(x-1);u<(x+2);u++)
{
//Use wraparound for edge cells
ux=(u+size) % size;
//Only for surrounding cells, not this cell
if ((ux!=x) & (vy!=y))
{
}
}
}
return count;
}
//Draw cell at x,y : not the most efficient nor elegant method...
public static void drawcell(Graphics g, int x, int y, boolean live)
{
if (live)
{
// Draw this cell alive
//g.setColor(Color.GREEN);
}
else
{
// Draw this cell dead
g.setColor(Color.BLACK);
}
g.fillRect(x*cellsize, y*cellsize,cellsize,cellsize);
panel.repaint(x*cellsize, y*cellsize,cellsize,cellsize);
}
//Returns true if population is healthy. False if extinct or overcrowded
public static boolean populationanalysis()
{
return ((supercount<thrive)||(supercount>(int)(size*size)<<1));
}
//Main Loop method
public static void runtime()
{
int sanity=5000;
int loopcount=0;
do
{
process();
loopcount++;
if (populationanalysis())
{
break;
}
}
while (loopcount<sanity);
}
}
I have no idea how to fix this problem. What I am doing is trying to break up a monster class I have into different things, like one class for player, one for fireball, etc. I had everything working before I tried to break up the class but now I am getting an error. I was wondering if anyone could help me solve it and explain to me how not to repeat this error again. Thank you in advance.
EDIT: The error is on: animationTimer = new Timer(animationDelay, this);
EDIT:1 error found:
File: C:\Users\jozef\Java\Dragon Ball Z\Player.java [line: 45]
Error: incompatible types: Player cannot be converted to java.awt.event.ActionListener
Also, I do format properly but when i try to copy and paste my code into the box to post here it doesn't count it as code so i have to indent every line to get it to appear as code and no normal text.
import java.awt.Graphics;
import java.awt.MediaTracker;
import javax.swing.ImageIcon;
import java.awt.Image;
import java.awt.event.ActionEvent;
import javax.swing.Timer;
public class Player {
int x;
int y;
ImageIcon pictures[];
int total;
int current;
boolean sideMove;
int move;
Timer animationTimer;
int animationDelay = 80;
public Player(int startX, int startY, ImageIcon image[], boolean sideMove, int move) {
x = startX;
y = startY;
pictures = image;
total = pictures.length;
this.sideMove = sideMove;
this.move = move;
startAnimation();
}
public void draw(Graphics g) {
if (pictures[current].getImageLoadStatus() == MediaTracker.COMPLETE) {
Image img = pictures[current].getImage();
g.drawImage(img, x, y, null);
current = (current + 1) % total;
}
update();
}
public void update() {
if (sideMove == true) {
x += move;
} else {
y += move;
}
}
public void startAnimation() {
if (animationTimer == null) {
current = 0;
animationTimer = new Timer(animationDelay, this); // *** error ***
animationTimer.start();
} else if (!animationTimer.isRunning())
animationTimer.restart();
}
public void stopAnimation() {
animationTimer.stop();
}
}
Here:
animationTimer = new Timer(animationDelay, this);
Since the Player class does not implement ActionListener this can not be passed in to the Timer constructor as a valid parameter. A possible solution is to have your Player class implement ActionListener giving it an appropriate actionPerformed method:
public class Player implements ActionListener {
#Override
protected void actionPerformed(ActionEvent e) {
// your coded here
}
// .... rest of your code
or better still, use a different ActionListener such as an anonymous inner class.
e.g.,
public void startAnimation() {
if (animationTimer == null) {
current = 0;
animationTimer = new Timer(animationDelay, e -> timerActionPerformed(e));
animationTimer.start();
} else if (!animationTimer.isRunning()) {
animationTimer.restart();
}
}
private void timerActionPerformed(ActionEvent e) {
// TODO repeated code goes here
}
Side recommendations:
You've got code within your painting method that changes your Player object's state, something that you'll want to avoid. Understand that you can only partially control when or even if an object will be painted, and so it would be best to keep these separate.
Myself, I'd get the Timer out of the Player class and instead use a Timer as your game loop or animation controller in a more general overall control class, perhaps the Game class (or whatever your "universe" class is called), the class that holds and controls all of your logical entities such as your Player objects.
This is the signature of javax.swing.Timer constructor:
public Timer(int delay, ActionListener listener)
You are providing an int and a Player..
You should create an ActionListener as well and provide it to the constructor or you could pass this but Player class should implements ActionListener inteface (you should write actionPerformed method in the Player class).
Read more info about Timer Here (official java doc).
I am creating an app where bubbles bounce around the screen, you get points for popping them, and then they re-spawn.
Each bubble is exactly identical except for the speed and direction integers used to control their motion around the screen.
As of now I have a single ImageView called "bubble". Then I run the method randoms() which I created.
Code for randoms():
public void randoms(){
ySpeedRand = new Random();
xSpeedRand = new Random();
yPolarityRand = new Random();
xPolarityRand = new Random();
ySpeed = ySpeedRand.nextInt(5) + 4;
xSpeed = xSpeedRand.nextInt(5) + 4;
yPolarity = yPolarityRand.nextInt(3) + 1;
xPolarity = xPolarityRand.nextInt(3) + 1;
if (xPolarity == 1){
xSpeed*=-1;
}
if (yPolarity == 2){
ySpeed*=-1;
}
}
Then I have a listener to check when the bubble is tapped and then I make it invisible, rerun the randoms() block, and then make it visible.
This is the handler that controls the position of the bubbles:
final Handler handler = new Handler();
Runnable run = new Runnable() {
#Override
public void run() {
bubble.setX(bubble.getX()+xSpeed);
bubble.setY(bubble.getY()+ySpeed);
handler.postDelayed(this, 5);
}
};handler.post(run);
And I use this code to check if the bubble is still on the screen:
final Handler checkHandler = new Handler();
Runnable check = new Runnable() {
#Override
public void run() {
if (bubble.getX()>SCREEN_WIDTH-bubble.getWidth()/2){xSpeed*=-1;}
if (bubble.getX()<0){xSpeed*=-1;}
if (bubble.getY()>SCREEN_HEIGHT-bubble.getHeight()){ySpeed*=-1;}
if (bubble.getY()<bubble.getHeight()/2+barHeight+actionBorder.getHeight()){ySpeed*=-1;}
handler.postDelayed(this, 50);
}
};checkHandler.post(check);
Is there an easy way to simply expand this system so I can call a method createbubble() and then have the new bubbles assume the exact properties as the old ones, but with newly generated randoms?
I am 100% stuck and I cant find anything. Any help is extremely appreciated!
I was a little bit confused reading your question. Not sure why you're trying to use ImageView. Going with this as your goal:
Create a game which spawns bubbles which can be popped
When popped, bubbles should be moved to a random space on the screen
When bubbles move off the screen, reverse their direction.
I would make the following classes at least:
Launcher, where your main() method is stored and your game loop run
Window, which extends Canvas and wherein you'll setup a JFrame
Control, where physics are calculated every time Launcher completes a loop
Entity, an abstract java object
Bubble
Here's one way to make a game loop: http://www.java-gaming.org/index.php?topic=24220.0
Here's an example of how to make a JFrame:
import java.awt.Canvas;
import javax.swing.JFrame;
public class Window extends Canvas{
public Window(Launcher launcher){
JFrame frame = new JFrame("Title");
frame.setSize(700,500);
frame.setResizable(false);
frame.setLocationRelativeTo(null);;
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(launcher);
frame.setVisible(true);
launcher.setFrame(frame);
}
}
To keep track of the bubbles, I would declare and initialize a LinkedList<> in the Control class. To create bubbles run something like this in Control:
public class Control{
public LinkedList<Entity> entity = new LinkedList<Entity>();
Random rand = new Random();
public Control(){
//You will have to define these constants in the Launcher class
entity.add(
rand.nextInt(Launcher.WINDOW_WIDTH),
rand.nextInt(Launcher.WINDOW_HEIGHT)
);
}
//Called every time Launcher completes a game loop
tick(){
for(int i=0; i<entity.size(); i++){
entity.get(i).tick();
}
}
}
In the Entity class:
public abstract class Entity{
protected double x;
protected double y;
public Entity(int x, int y){
this.x = x;
this.y = y;
}
public abstract void tick();
public abstract void render(Graphics g);
//Getters and setters here ...
}
In the Bubble class:
public class Bubble extends Entity{
public double x = 0;
public double y = 0;
public Bubble(int x, int y){
super(x,y);
}
public void tick(){
//Physics go here
}
public void render(){
//Graphics functions go here
}
}
If any of this doesn't make sense, let me know and I'll explain in more detail and even go as far as to make the entire program for you considering the simplicity of the task.
I'm very new to java (and to object oriented programming for that matter). As a first java project I've been instructed to get a tag cloud engine applet up and running. So I found WordCram and a small example bit of code for using PApplets and thought I'd have an easy time...
But while my code doesn't generate any errors per-se, it throws the following upon running:
Exception in thread "Animation Thread" java.lang.NullPointerException
at processing.core.PApplet.handleDraw(PApplet.java:2336)
at processing.core.PGraphicsJava2D.requestDraw(PGraphicsJava2D.java:243)
at processing.core.PApplet.run(PApplet.java:2176)
at java.lang.Thread.run(Thread.java:722)
I have a TON of code, and I'm hesitant to post it all here... But over the course of trying to get to the bottom of my code I'm always frustrated at how little of the code the inquirer gives: so I'll tentatively paste it here and I'll look out for people telling me to reformat or edit out some or most of it.
First of two classes:
package tagengine;
import java.awt.*;
public class TagFrame extends Frame {
public TagFrame()
{
super("Embedded PApplet");
Engine embed = new Engine();
setLayout(new BorderLayout());
add(embed, BorderLayout.CENTER);
embed.init();
}
public static void main(String[] args)
{
TagFrame tagFrame = new TagFrame();
}
}
And the second:
package tagengine;
import processing.core.PApplet;
import processing.core.PFont;
import processing.core.PVector;
import wordcram.*;
public class Engine extends PApplet {
#Override public void setup() {
this.size(900, 500);
this.colorMode(HSB);
this.noLoop();
this.setVisible(true);
}
#Override public void draw() {
this.background(68);
new WordCram(this)
.fromWebPage("http://en.wikipedia.org/wiki/Barcamp")
.withColors(color(0, 0, 0),
color(0, 0, 255),
color(30, 255, 255)) // mustard, red)
.withFonts(PFont.list())
.withAngler(moreRandomByWeight())
.withPlacer(crazyPlacer())
.sizedByWeight(8, 100)
.maxNumberOfWordsToDraw(300)
.drawAll();
}
WordAngler moreRandomByWeight() {
return new WordAngler() {
#Override public float angleFor(Word w) {
float range = (1 - w.weight) * PI;
return random(-range, range);
}
};
}
WordPlacer crazyPlacer() {
return new WordPlacer() {
#Override public PVector place(Word w, int rank, int words,
int ww, int wh, int fw, int fh) {
float x = (fw - ww) * (1 - w.weight);
float y = random(fh - wh);
return new PVector(x, y);
}
};
}
#Override public void mousePressed() {
redraw();
}
}
You're adding your Engine to the BorderLayout before calling init(), and it looks like an immediate paint operation is trying to execute before the Engine is ready to be painted.