Game running slower with many objects

Hello, Im making a simple asteroids 2d game, and Im having a problem when I have many objects in the game, for example when I destroy and asteroid, I create an explosion object(when the animation is over I remove the object from the linkedlist), the problem is whenever I destroy many asteroids in a short period of time, the game simply lags and slows down, at first I thought its because of the gameloop I used, which was a “free wheeling loop” with no fps control/cap, and whenever the FPS changed, the animation speed changes, so I changed the game loop to a fixed fps game loop, and the fps is now constant on 144 fps(I have 144 hz screen, so I’ve set the fps to 144), but even on 60 fps or higher than 144 fps, the game still lags even tho the FPS stays constant, I thought maybe adding more threads to the game would help, but Im not quite sure about it and what I should do, and I would like to get some assistance with that.

A little explanation about the code, I have the main game class, which controls the game loop, update method and rendering method, I have an abstract class which all the objects in the game are inherited from, and I have a class that handles all the objects in the game and are stored in a linkedlist, the class updates and renders all the objects(the actual method is running through the main game class ofc).

I’ll add some code so it will be easier to understand it.

This is the main game class with the game loop:

package com.asteroids.controller;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;

import com.asteroids.model.*;
import com.asteroids.view.*;

public class Game extends Canvas implements Runnable {

	private static final long serialVersionUID = -8921419424614180143L;
	public static final int WIDTH = 1152, HEIGHT = WIDTH / 8 * 5;

	private Thread thread;
	private boolean isRunning;
	LoadImages loadImages = new LoadImages();
	private Player player = new Player();
	private AllObjects objects;
	private KeyInput keyInput;
	private long delay = 80;
	private long currentTime = System.currentTimeMillis();
	private long expectedTime = currentTime + delay;
	public static BufferedImage test;
	private int fps = 0;

	public Game() {
		new Window(WIDTH, HEIGHT, "Asteroids!", this);
		objects = new AllObjects();
		objects.addObject(player);
		for (int i = 0; i < 100; i++) {
			objects.addObject(new Rock((int) (Math.random() * (Game.WIDTH - 64) + 1),
					(int) (Math.random() * (Game.HEIGHT - 64) + 1)));
		}
		keyInput = new KeyInput(player);
		this.addKeyListener(keyInput);
	}

	public void run() {

		int ups = 144;
		int fps = 144;

		long initialTime = System.nanoTime();
		final double timeU = 1000000000 / ups;
		final double timeF = 1000000000 / fps;
		double deltaU = 0, deltaF = 0;
		// int frames = 0, ticks = 0;
		long timer = System.currentTimeMillis();

		while (isRunning) {
			destroyBullets();
			destroyAsteroids();
			// used to set delay between every bullet(milliseconds)
			currentTime = System.currentTimeMillis();
			if (KeyInput.shoot && currentTime >= expectedTime) {

				// calculates the accurate position of the x,y on the "circumference" of the
				// player
				float matchedX = player.getX() + 1 + (float) ((player.getRadius() + 32) * Math.cos(player.getRadian()));
				float matchedY = player.getY() - 7 + (float) ((player.getRadius() + 32) * Math.sin(player.getRadian()));
				objects.addObject(new Bullet(matchedX, matchedY, player));
				expectedTime = currentTime + delay;
			}
			long currentTime = System.nanoTime();
			deltaU += (currentTime - initialTime) / timeU;
			deltaF += (currentTime - initialTime) / timeF;
			initialTime = currentTime;

			if (deltaU >= 1) {
				tick();
				deltaU--;
			}

			if (deltaF >= 1) {
				render();
				deltaF--;
			}

			if (System.currentTimeMillis() - timer > 1000) {
				this.fps = fps;
				System.out.println("FPS: " + fps);
				timer += 1000;
			}
		}
		
		stop();
		System.exit(1);

	}

	private void stop() {
		try {
			thread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.exit(1);

	}

	private void render() {
		BufferStrategy bs = this.getBufferStrategy();
		if (bs == null) {
			this.createBufferStrategy(3);
			return;
		}

		Graphics g = bs.getDrawGraphics();
		g.drawImage(LoadImages.getbackground(), 0, 0, getWidth(), getHeight(), this);
		objects.render(g);
		player.render(g);
		g.setColor(Color.red);
		g.drawString("FPS: " + fps, 5, 10);
		g.dispose();
		bs.show();

	}

	private void tick() {
		player.tick();
		objects.tick();
	}

	// starting thread and game loop.
	public void start() {
		thread = new Thread(this);
		thread.start();
		isRunning = true;
	}

	// minimum and maximum possible position for object.
	public static float Bounds(float value, float min, float max) {
		if (value >= max) {
			return value = max;
		}
		if (value <= min) {
			return value = min;
		} else {
			return value;
		}

	}

	// detects collision between two objects
	public boolean collision(GameObject a, GameObject b) {
		return (b.getX() - a.getX() + 10) * (b.getX() - a.getX() + 10)
				+ (b.getY() - a.getY() + 10) * (b.getY() - a.getY() + 10) < (a.getRadius() + b.getRadius())
						* (a.getRadius() + b.getRadius());
	}

	// destroys bullets once they go out of the screen
	public void destroyBullets() {
		for (int i = 0; i < objects.getSize(); i++) {
			if (objects.get(i).getId() == ID.BULLET) {
				GameObject bullet = objects.get(i);
				if (bullet.getX() > Game.WIDTH || bullet.getX() < 0 || bullet.getY() > Game.HEIGHT
						|| bullet.getY() < 0) {
					objects.removeObject(bullet);
				}
			}
		}
	}

	// whenever a collision between an asteroid and a bullet occurs, the asteroid is
	// destroyed
	public void destroyAsteroids() {
		GameObject bullet = null;
		GameObject bigRock = null;
		for (int i = 0; i < objects.getSize(); i++) {
			if (objects.get(i).getId() == ID.BULLET) {
				bullet = (Bullet) objects.get(i);
				for (int q = 0; q < objects.getSize(); q++) {
					if (objects.get(q).getId() == ID.BIGROCK) {
						bigRock = objects.get(q);
						if (bullet != null && bigRock != null) {
							if (collision(bigRock, bullet)) {
								objects.addObject(new Explosion(bigRock.getX(), bigRock.getY(), objects));
								objects.removeObject(bigRock);
								objects.removeObject(bullet);
							}
						}
					}
				}
			}
		}
	}
}

This is the explosion class for example:

package com.asteroids.model;

import java.awt.Graphics;
import java.awt.Image;

import com.asteroids.controller.*;
import com.asteroids.view.LoadImages;


public class Explosion extends GameObject {

	private AllObjects objects;
	private Image explosion;
	private float frame = 0;
	private float animSpeed = 0.4f;
	private int frameCount = 48;

	public Explosion(float x, float y, AllObjects objects) {
		super(x, y, ID.EXPLOSION, 1);
		this.objects = objects;
	}

	public void render(Graphics g) {
		explosion(g);
	}

	public void explosion(Graphics g) {
		frame += animSpeed;
		if (frame > frameCount) {
			frame -= frameCount;
		}
		explosion = LoadImages.getExplosion().getSubimage((int) frame * 256, 0, 256, 256);
		g.drawImage(explosion, (int) x, (int) y, 110, 110, null);
		if (frame >= 47f) {
			objects.removeObject(this);
		}
	}

	public void tick() {
		
	}

	public void setAnimSpeed(float animSpeed) {
		this.animSpeed = animSpeed;
	}
}

I don’t want to add too much code, because I think it just makes it more “clumsy” and inapprehensible, but if more code is needed let me know and I’ll update the post. Thanks for the help!

First at all- I suggest using Libgdx. AWT is old and not dedicated for games.
Second thing- about this FPS- where do you update FPS counter? I don’t see it. It looks like it’s never updated (that’s why it’s constant). Try to fix and check how much do you get :slight_smile:

Thanks for the reply, and you are totally correct, I tested something and forgot to bring it back, and its totally the fps dropping…the next question is, how to fix it? multithreading?

Long time ago I was using canvas to render graphics. But it’s performance is low as hell. Specially when drawing a lot of sprites. This solution is not scaling because it’s not designed to work on heavy load.

If you want to fix it well- move to library that is supporting opengl. libgdx for me is the best in Java. If you want low level programming for some reason- go jogl/lwjgl. As far as I remember- awt is running graphic calculations on CPU by default which is… bad idea for games.

If possible, can you briefly explain how does the low level programming is reflected here? What are the advantages for that, and what are the disadvantages for that? Why libgdx is considered by many as the best library, and which library in your opinion would best suite what Im trying to do? (I assume that in this level of programming and game it doesn’t make a real difference, but would still like to know). Thanks again.

It looks like this:

Below described tools (ordered by abstraction level):


Strict Java ui tools (AWT family): AWT- java ui library. Designed to create ui on top of native os. Pretty old. Swing- low weight java ui lib based on AWT (These days no one is using AWT but Swing for Java ui. Or JavaFX) JavaFX- Java RIA framework
What is opengl? OpenGL- crossplatform graphic API. That's why it's popular in Java in comparison to DirectX

Java Graphics (OpenGL family):
JOGL- OpenGL in java
LWJGL- (Lightweight Java Game Library) built on top of JOGL
Libgdx- java game framework. Built on top of LWJGL.


Low level programming (low abstraction level) gives you more control. But you have to take care of various things that you won’t in higher abstraction. It’s good if you want to learn how it works under the hood.

Higher abstraction level (frameworks/engines here) gives you many usable tools that makes you code faster and not think about things like this render loop and fps. If you want to get more control, you have to dig in this framework (Sometimes it’s painful). You focus only on the things that you want.

Libgdx is considered as the best because it’s mature, easy, crossplatform and rich.


Tip: Don't use AWT family for gamedev. It's like using crayon to paint graffiti on the wall. Whatever game you will be making in Java- use Libgdx. Easy and powerful enough.

To the original question of why your current code is running slowly, it’s likely one of the following:

  1. You’re doing something silly inside your LoadImages class (you’ve not provided code).
  2. Your gameloop timing logic is flawed; hard to say without studying & running your code.
  3. Invoking getSubimage every frame might be expensive, either copying pixels, or preventing proper caching of the image data on an accelerated surface. (just call getSubimage once, and store the result)
  4. Your graphics card is some integrated solution that the JRE has identified as not compatible with Java2D’s hardware rendering pipeline. (thus causing it to use software pipelines that are many magnitudes slower)

Profile your code to identify where all the time is being spent.

While I wouldn’t recommend starting a large project with Java2D*, on even moderately capable hardware, when used correctly, it’s more than capable of rendering many thousands of fully alpha blended images @ 60+ fps

*take the advice given above, and learn one of the many Java bindings/wrappers for OpenGL.

First thanks a lot for the very informative answer, I really appreciate it and I will try working on Libgdx/LWJGL whichever I’ll find more comfortable to me I suppose.

I will, but just in case you are interested, this is a link to the complete project: https://github.com/pardovot/MyProjects/tree/master/Asteroids!
Feel free to take a look and tell me what you think might cause the problem if you want, just a few more things:

I created the LoadImages class because before that whenever I created an explosion object, I used “explosion = ImageIO.read(new File(“res/explosions/type_C.png”));” in the constructor, thats a spritesheet image, and is quite heavy, and it made my game very lag each time an explosion was created, therefore I decided to make that class and just load all the images in the main game constructor so it won’t happen, whether its a good or bad idea, I don’t really know, Im new to that. I also cannot use getSubImage only once, because that is a sprite sheet(you’ll figure if you’ll see the actual image in github), so each time I need to get a different “image” from that sprite sheet.

One more thing, as I said, Im new to that stuff, and the project is probably not very organized etc, so sorry about that. Thanks.

Centralising all your resource loading into some kind of repository is fine
However calling getSubimage every time you’re drawing the image is going to be creating a new Image object. It’ll share the underlying source pixel data (I think), so won’t involve a huge amount of work, but will possibly interfere with the hardware rendering pipeline (which involves caching images in vram) thus dramatically slow rendering

So like, what other options do I have? I can obviously create 48 images and load them in the main game constructor, but it seems really dumb thing to do, what else can I try? thanks.

Image[]explosionFrames = …

Though better yet would be to generalize what’s currently contained in Explosion, into some kind of AnimatedSprite.

As I said in my 1st reply, you should profile your code to see what’s the slow bit; getSubimage might not be the source of the problem, without inspecting how the Java2D’s hardware acceleration cache is implemented it’s impossible to know. (However it’s a logical place to start as I’ve never relied upon that particular method to be performant)

I see, I will maybe try that, or just start with libgdx or lwjgl, thanks alot for the help anyway :slight_smile:

Sorry to contradict you but LibGDX has several backends including one (unofficial) based on JOGL 2, another one using GWT and another one using Android OpenGL framework as far as I remember:

Yeah. Simplification. I didn’t wanted to add too many new terms for newbie.

so, it means if we are using 2D and we use JOGL instead that will make us to have a better performance in our game?

OpenGL is the best in case of performance for Java because of it’s nature. So yes. Just like you said.

You access a linked list by index, which is… could be improved.
Then you do this in a nested loop for collision checking, checking each bullet with each rock.
And in two methods.
So, refactor the object storage and collision handling.
I wouldn’t call getSubImage() while rendering.

No need for LibGdx for this, but a better choice in the long run for sure.

Btw.: I see empty catch blocks. Dont.

You might start by migrating to JavaFX first (canvas works fine there), saves a lot of headaches.