Java-Gaming.org Hi !
Featured games (91)
games approved by the League of Dukes
Games in Showcase (763)
Games in Android Showcase (229)
games submitted by our members
Games in WIP (852)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Game running slower with many objects  (Read 1453 times)
0 Members and 1 Guest are viewing this topic.
Offline msacco

Senior Newbie





« Posted 2018-06-11 13:10:26 »

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:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
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:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
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!
Offline ByerN
« Reply #1 - Posted 2018-06-11 13:51:46 »

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 Smiley
Offline msacco

Senior Newbie





« Reply #2 - Posted 2018-06-11 13:59:06 »

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 Smiley

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?
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline ByerN
« Reply #3 - Posted 2018-06-11 14:06:00 »

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.
Offline msacco

Senior Newbie





« Reply #4 - Posted 2018-06-11 14:14:10 »

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.
Offline ByerN
« Reply #5 - Posted 2018-06-11 14:39:31 »

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.
Offline Abuse

JGO Ninja


Medals: 69


falling into the abyss of reality


« Reply #6 - Posted 2018-06-11 14:48:28 »

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.
Offline msacco

Senior Newbie





« Reply #7 - Posted 2018-06-11 15:26:07 »

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.

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.

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.

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.
Offline Abuse

JGO Ninja


Medals: 69


falling into the abyss of reality


« Reply #8 - Posted 2018-06-11 16:35:03 »

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
Offline msacco

Senior Newbie





« Reply #9 - Posted 2018-06-11 16:44:11 »

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.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Abuse

JGO Ninja


Medals: 69


falling into the abyss of reality


« Reply #10 - Posted 2018-06-11 17:08:30 »

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)
Offline msacco

Senior Newbie





« Reply #11 - Posted 2018-06-11 17:47:57 »

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 to be performant)

I see, I will maybe try that, or just start with libgdx or lwjgl, thanks alot for the help anyway Smiley
Offline gouessej
« Reply #12 - Posted 2018-06-11 18:26:10 »

Libgdx- java game framework. Built on top of LWJGL.
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:
https://github.com/libgdx/libgdx/tree/master/backends

Julien Gouesse | Personal blog | Website | Jogamp
Offline ByerN
« Reply #13 - Posted 2018-06-11 18:29:14 »

Libgdx- java game framework. Built on top of LWJGL.
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:
https://github.com/libgdx/libgdx/tree/master/backends

Yeah. Simplification. I didn't wanted to add too many new terms for newbie.
Offline pavul

Junior Devvie


Medals: 4
Exp: 4 years



« Reply #14 - Posted 2018-06-12 20:15:36 »

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?

its better not know why it works, that know why is not working
Offline ByerN
« Reply #15 - Posted 2018-06-12 20:20:04 »

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.
Offline 65K
« Reply #16 - Posted 2018-06-12 20:30:22 »

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.

Lethal Running - a RPG about a deadly game show held in a futuristic dystopian society.
Offline beeaware
« Reply #17 - Posted 2018-06-13 16:12:35 »

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

You know you're getting old when you played Pong while it was still hot...
Pages: [1]
  ignore  |  Print  
 
 

 
EgonOlsen (543 views)
2018-06-10 19:43:48

EgonOlsen (647 views)
2018-06-10 19:43:44

EgonOlsen (444 views)
2018-06-10 19:43:20

DesertCoockie (789 views)
2018-05-13 18:23:11

nelsongames (1022 views)
2018-04-24 18:15:36

nelsongames (1055 views)
2018-04-24 18:14:32

ivj94 (1636 views)
2018-03-24 14:47:39

ivj94 (561 views)
2018-03-24 14:46:31

ivj94 (1442 views)
2018-03-24 14:43:53

Solater (566 views)
2018-03-17 05:04:08
Java Gaming Resources
by philfrei
2017-12-05 19:38:37

Java Gaming Resources
by philfrei
2017-12-05 19:37:39

Java Gaming Resources
by philfrei
2017-12-05 19:36:10

Java Gaming Resources
by philfrei
2017-12-05 19:33:10

List of Learning Resources
by elect
2017-03-13 14:05:44

List of Learning Resources
by elect
2017-03-13 14:04:45

SF/X Libraries
by philfrei
2017-03-02 08:45:19

SF/X Libraries
by philfrei
2017-03-02 08:44:05
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!