-VaTTeRGeR
-I was actually just asking about whether or not I understood texturepacker and textureatlas right because of the huge draw call change just from repacking the textures. It didn’t make any sense if the textures are all considered one for texture binding. I thought maybe I was wrong and that splitting up my spritesheets and then repacking them made it worse than when I left them together and split them up using split in my constructor. But if you want to help analyze the other problems i’d be glad to post some info I just was hoping to narrow it down a bit first since i’m a bit embarrassed by my code and its a bit massive. I’ll try to post some snippets at the bottom of this post though.
-65K
on desktop no, but on my android device (moto g3) it runs at 20 fps and then drops to like 10-14 as more enemies spawn. Maybe my phone is just older than I think it is but I use it as the standard for a lower end device I think my apps should be able to run on. It seemed to work ok (though I didn’t check the actual fps) on my brothers moto m (no idea how strong of a phone it is, but its a few years newer than mine at least). Actually while talking about performance I also have a issue where my app takes 130ish MB of memory (checked using android profiler), this is obviously because I keep everything in memory all the time. But 90% of my pngs are used constantly anyway. For the ones I can remove would they have to be in a seperate texture atlas? I imagine you can’t dispose parts of a texture atlas, its either all or nothing? Last I checked anything over idk like 40MB is too much for android apps so thats another issue i’ll probably need to figure out later (unless I am reading outdated info).
#of enemies and other draws on the screen = (it’s one of the shorter videos I have, sometimes theres almost twice as many enemies if you aren’t killing them fast enough) https://www.youtube.com/watch?v=1ELx2tR_N54
-I should mention I have a normal spritebatch and a interface spritebatch, I render all the normal batch first then the interface are all grouped at the end (as much as possibly anyway)
-bitmap text is not in the textureatlas, it’s stored as a individual png (I might try to change this soon though since text is rendered pretty frequently)
-some objects such as the fire pits use particle emitters, these aren’t stored in the texture atlas either (i’m pretty sure they can’t be)
-the lightning is code generated, idk if the constantly changing alpha value trigger’s more renders? it’s from a tutorial on code generated lightning, I was going to leave it out for visibility but I ran some tests and it seems to have a huge impact on the render calls so i’ll include it at the bottom (its still really bad without it)
-max sprites in batch is only ever like 700, so still far below the 1000 default cap
-the level itself is a combination of framebuffered tiles (the ones that don’t change) and the rest is rendered similiar to the skeleton i’ll post below
-retrieval
atlas = new TextureAtlas("texturepacked.atlas");
skeletonFallFrame = atlas.findRegion("skeletonfall");
skeletonWalkAnimation = new Animation<TextureRegion>(0.1f, atlas.findRegions("skeletonwalk"));
skeletonAttackAnimation = new Animation<TextureRegion>(0.2f, atlas.findRegions("skeletonattack"));
skeletonFallFrameFrozen = atlas.findRegion("skeletonfallfrozen");
skeletonWalkAnimationFrozen = new Animation<TextureRegion>(0.1f, atlas.findRegions("skeletonwalkfrozen"));
skeletonAttackAnimationFrozen = new Animation<TextureRegion>(0.2f, atlas.findRegions("skeletonattackfrozen"));
-rendering (it only renders whats in view, I read that libgdx doesn’t really do this automatically but even if it does, this saves other code from running anyway)
public void render(SpriteBatch batch, float minX, float maxX, float minY, float maxY)
{
if(minX <= (x + width) && x <= maxX && minY <= (y + height) && y <= maxY)
{
if (frozen == true)
{
if(animationType == ActionResolver.AnimationType.moving)
{
currentFrame = myWorld.gameArt.skeletonWalkAnimationFrozen.getKeyFrame(frameTimer, true);
}
else if(animationType == ActionResolver.AnimationType.falling)
{
currentFrame = myWorld.gameArt.skeletonFallFrameFrozen;
}
else if(animationType == ActionResolver.AnimationType.attacking)
{
currentFrame = myWorld.gameArt.skeletonAttackAnimationFrozen.getKeyFrame(frameTimer, true);
}
}
else
{
if(animationType == ActionResolver.AnimationType.moving)
{
currentFrame = myWorld.gameArt.skeletonWalkAnimation.getKeyFrame(frameTimer, true);
}
else if(animationType == ActionResolver.AnimationType.falling)
{
currentFrame = myWorld.gameArt.skeletonFallFrame;
}
else if(animationType == ActionResolver.AnimationType.attacking)
{
currentFrame = myWorld.gameArt.skeletonAttackAnimation.getKeyFrame(frameTimer, true);
}
}
if(destroyed == false)
{
batch.draw(currentFrame, x, y, width, height);
lifeBar.render(batch);
}
}
}
-render loop (this is fairly huge so i’ll try to take as little as I can without screwing up the snippet)
public void render()
{
float minX= myWorld.landscapeCamera.frustum.planePoints[0].x;
float minY= myWorld.landscapeCamera.frustum.planePoints[0].y;
float maxX= myWorld.landscapeCamera.frustum.planePoints[2].x;
float maxY= myWorld.landscapeCamera.frustum.planePoints[2].y;
batch.begin();
Gdx.gl.glClearColor(0f, 0f, 0f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(myWorld.landscapeCamera.combined);
myWorld.level.render(batch, minX, maxX, minY, maxY, 5);
//draw units at different depths
for(int d = 5; d >= 0; d--)
{
if(d == 0)
{
myWorld.lightning.render(batch, minX, maxX, minY, maxY);
}
for (int i = 0; i < myWorld.enemies.size(); i++)
{
if (myWorld.enemies.get(i).getDrawDepth() == d)
{
myWorld.enemies.get(i).render(batch, minX, maxX, minY, maxY);
}
}
//draw allies, moneybags, etc cut for visiblity
}
myWorld.level.render(batch, minX, maxX, minY, maxY, 0);
for(int i = 0; i < myWorld.overheadTexts.size(); i++)
{
myWorld.overheadTexts.get(i).render(batch, minX, maxX, minY, maxY);
}
batch.end();
interfaceBatch.setProjectionMatrix(myWorld.interfaceCamera.combined);
interfaceBatch.begin();
//render interface stuff cut for visiblity (my interface batch has about 10-20 render calls,
//my batch loop is the once that has 100s but both are worse than they should be)
-code generated lightning (parts removed for visiblity), I was thinking it might help if I move the lightning render to the end, but then that might screw up its drawdepth, but it might also help reduce draw calls because I think its blend flushes the spritebatch?
public static void initialize(GameWorld myWorld)
{
LightningSegment = myWorld.gameArt.atlas.findRegion("lightningsegment");
HalfCircle = myWorld.gameArt.atlas.findRegion("halfcircle");
Pixel = myWorld.gameArt.atlas.findRegion("pixel");
HalfCircle2 = myWorld.gameArt.atlas.findRegion("halfcircle");
HalfCircle2.flip(true, false);
}
for(int i=0; i<bolts.size(); i++)
{
bolts.get(i).draw(batch);
}
//inside the bolts draw method
if (alpha <= 0)
return;
for(int i=0; i<Segments.size(); i++)
{
Line segment = Segments.get(i);
segment.Draw(spriteBatch, new Color(tint).mul(alpha * alphaMultiplier));
}
//inside the segment draw function, I noticed it is making a new vector2 every call which is very bad
//for rendering so I am going to see if I can figure out a way to do that part in the update
//function or some other way
Vector2 tangent = new Vector2(B).sub(new Vector2(A));
float theta = (float)Math.toDegrees(Math.atan2(tangent.y, tangent.x));
float scale = Thickness / Art.HalfCircle.getRegionHeight();
Color prevColor = spriteBatch.getColor();
spriteBatch.setColor(tint);
int blfn = spriteBatch.getBlendDstFunc();
spriteBatch.setBlendFunction(spriteBatch.getBlendSrcFunc(), Blending.SourceOver.ordinal());
spriteBatch.draw(Art.LightningSegment, A.x, A.y, 0,Thickness/2, getLength(), Thickness, 1,1, theta);
spriteBatch.draw(Art.HalfCircle, A.x, A.y, 0,Thickness/2, scale * Art.HalfCircle.getRegionWidth(), Thickness, 1,1, theta);
spriteBatch.draw(Art.HalfCircle2, B.x, B.y, 0,Thickness/2, scale * Art.HalfCircle2.getRegionWidth(), Thickness, 1,1, theta);
spriteBatch.setColor(prevColor);
spriteBatch.setBlendFunction(spriteBatch.getBlendSrcFunc(), blfn);
sorry if thats too much code, I tried to cut away as much as possible while still keeping things that seemed like they might be relevant. An Interesting thing came up though, before my lightning generator was using individual pngs. Noticing this I move them to the texture packer. But instead of helping this made the draw calls way worse, going from 100s on avg to now 200-300s. Does it make any sense that moving a png to the texture packer would be worse than using a individual png? (maybe because its changing the alpha value of the texture at runtime?).