Hello.
I am currently working on a “little” game which will look something like Civ. First I drew all hexagons by myself but I found some nice sprites so I wanted to use the sprite library. But somehow it is unplayable with 5 fps. Are there some parts where I could have made a mistake that is crushing the performance? I did not wrote code because it is quite long and most of it may be useless for the problem. If there are parts I will send the code.
The Sprite library has been arround a long time and no one else has reported a similar problem. It is impossible to say what the problem is wihout seeing some code.
One common issue is loading graphics in the draw() method, all graphics should be loaded in setup()
I am loading the Sprites here. The method is called in setup:
void setBiome(Dot d) {
String terrain = "";
boolean toElevate = true;
if ( d.altitude >= 90 ) {
terrain = "mountain";
toElevate = false;
} else if ( d.altitude <= 20 ) {
terrain = "ocean";
toElevate = false;
} else if ( d.altitude <= 25 ) {
terrain = "coast";
toElevate = false;
} else if ( d.altitude <= 30 ) {
terrain = "desertdunes";
toElevate = false;
} else {
if ( d.heat > 80 ) {
if ( d.moisture < 20 ) {
if ( d.altitude > 85 ) {
terrain = "desertmountain";
toElevate = false;
}
terrain = "desert";
} else if ( d.moisture < 40 ) {
terrain = "savannah";
toElevate = false;
} else if ( d.moisture < 70 ) {
terrain = "grassland";
toElevate = false;
} else {
terrain = "rainforest";
toElevate = false;
}
} else if ( d.heat > 50 ) {
if ( d.moisture < 20 ) {
terrain = "grassland";
toElevate = false;
} else if ( d.moisture < 50 ) {
terrain = "plain";
} else if ( d.moisture < 70 ) {
terrain = "plaintrees";
} else {
switch ((int) random(3)) {
case 0: terrain = "swamp"; break;
case 1: terrain = "swamp2"; break;
case 2: terrain = "swampwater"; break;
}
toElevate = false;
}
} else if ( d.heat > 20 ) {
if ( d.moisture < 30 ) {
terrain = "grassland";
toElevate = false;
} else if ( d.moisture < 50 ) {
terrain = "plain";
} else if ( d.moisture < 80 ) {
terrain = "plaintrees";
} else {
switch ((int) random(3)) {
case 0: terrain = "swamp"; break;
case 1: terrain = "swamp2"; break;
case 2: terrain = "swampwater"; break;
}
toElevate = false;
}
} else {
if ( d.moisture < 30 ) {
terrain = "snow";
} else if ( d.moisture < 50 ) {
terrain = "snowtrees";
toElevate = false;
} else if ( d.moisture < 80 ) {
terrain = "snowforest";
} else {
terrain = "snow";
toElevate = false;
}
}
if ( toElevate) {
if ( d.altitude > 70 ) {
terrain += "hills";
}
}
}
terrain += ".png";
hexagon = new Sprite(app, terrain, 10);
hexagon.setXY(start + x * 24, start + ((x + 2) % 2) * 14 + y * 28);
hexagon.setZorder(z);
}
And this is my draw:
void setup() {
fullScreen(P2D);
setupMap();
double deltaTime = timer.getElapsedTime();
S4P.updateSprites(deltaTime);
}
void draw() {
drawMap();
}
void drawMap() {
background(255);
translate(-xOff, -yOff);
S4P.drawSprites();
}
I have a 200 * 200 map of hexagons if it helps. But not every hexagon is in the screen.
I tried switching S4P.drawSprites(); to looping trough an ArrayList and drawing each Sprite if it is on the screen. The fps go up but the screen does not update or scale.
40,000 sprites might be doing it. Does the background have to be done as sprites?
Hey There!
You shouldn’t be using S4P.drawSprites !
You should only draw to he sprites that are in the boundary of the screen ! Because it is alot to render ! Also use P2D.
can you help us out with a little argumentation WHY NOT?
I suspect the problem is drawing the terrain of 40000 hexagons. If you think about it the hexagons have zero velocity and never move relative to each other you only need to recreate the background when you pan or zoom.
So here is my suggestions change the statement
hexagon = new Sprite(app, terrain, 10);
to
hexagon = new Sprite(app, terrain, 10, false);
This will prevent the terrain hexagon sprite from being updated or drawn with
S4P.updateSprites(deltaTime);
and
S4P.drawSprites();
so you can manage these sprites yourself and S4P manages the moving sprites that appear on top.
As stated above from @quark. It is so you canange their drawing yourself. I believe S4P
Draw sprites draws all registered sprites with his amount that can be faxing but if he only draws the ones in the boundary of the screen with controlling the sprites with draw(). He wouldn’t face such low perfomance.
Thank you very much. Then I didnt understand the method right because I thought that it only renders the sprites that are on screen. But thats great, because the library is very helpful. I will try
I changed my code so that i deregister every sprite except those which are on the screen. Now I have ~400 but I keep going down to 5 fps. I also thought about processing the background to one image after generation but it has to be updated every turn so it would be quite taxing. Will it help to draw the sprites with the draw() method inside them? Or are 400 sprites still too much? Thank you in advance
I suggested that you did not register the hexagonal terrain Sprites when they were created, see my last post. Only register the game sprites e.g. tanks, bullets, players etc. because they move and there appearance changes.
You need to come up with something different with the background. You might try a PGrapics buffer, to store the background and copy this to the screen every frame. You then only update the buffer image if the background appearance changes.
400 hundred sprites is not a lot unless they are very large.
400 isn’t that much maybe your rendering method is messy ?
Have a look at my repo here for a little project I was working on.
If you look at the reworkGrid class. I check and update what sprites are currently being featured on screen by:
public void update(int x, int y) {
cellX = x/TILESIZE;
cellXLong = (x/TILESIZE)+(width/TILESIZE)+TILEPUSH;
cellY = y/TILESIZE;
cellYLong=(y/TILESIZE)+(height/TILESIZE)+TILEPUSH;
}
public void show() {
//Render only the most important tiles
for (int j = cellY; j<cellYLong; j++ ) {
for (int i = cellX; i<cellXLong; i++) {
if (j<buildWorld.length&&i<buildWorld.length&&j>=0&&i>=0)buildWorld[j][i].draw();
}
}
This is very quick because we don’t scroll through all tiles to calculate the range but just by simple mapping which saves from scrolling through the whole array to check which are on screen and which aren’t.
As an update: I now handle the drawing of sprites by myself and draw them only if they are on screen. With a scale of 1 it works fine with 400 - 2000 sprites (only tested with 2000 but I think I could go higher) but when I set the scale something is messing with the fps. Im currently debugging to see where the problem is. If someone is interested in some code I will sent it. Maybe the game will be finished soon Thank you for your help
If you are downscaling them aka more are on screen it gonna happen that the FPS drops. But if you just are upscaling them the FPS should increase.
Yes I thought that too but even if I upscale the fps go down to 5. Im trying to put the drawing of the sprites inside a Thread. Maybe it helps.
Showing further code is going to allow us to fully understand what going on !
You cannot put drawing commands in a separate thread since all drawing must be done in the main Thread.
Hexagon is the sprite.
Those are the methods responsible for drawing
void draw() {
switch (currentScreen) {
case "MapTest":
// scale(scale);
translate(-xOff, -yOff);
checkForCameraMovement(); break;
}
}
// Drawing Tiles in order of their z value
void drawTiles() {
for(int y = 0; y < size; y++) {
ArrayList<Tile> temp = new ArrayList<Tile>();
for(int x = 0; x < size; x++) {
int z = (x+2) % 2;
Tile current = mapGrid[x][y];
if (z == 1) {
temp.add(current);
} else {
current.drawIfOnScreen();
}
}
for (Tile t : temp) {
t.drawIfOnScreen();
}
}
}
void drawIfOnScreen() {
if (isOnScreen()) {
hexagon.draw();
}
}
boolean isOnScreen() {
boolean b = (x * w > xOff - 50) && (x * w < xOff + width/scale + 50) && (y * h > yOff - 50) && (y * h < yOff + height/scale + 50);
return b;
}