2D Game, Performance Improvements? - android

The game I'm developing is progressing, more and more elements are being added, but I'm of course facing new problems, one of which is the performance.
Currently, I'm having 3 threads, two of which perform calculations, the other one updates the canvas. These three threads are synchronized with a CyclicBarrier, to have all calculations finished when beginning to draw the canvas.
I'm using several bitmaps in different sizes. In the drawing method, bitmaps are being rotated (by using drawBitmap-matrix combination with scaling/translating/rotating added into the matrix for "native" (i guess) management of it) and of course drawn. The problem I am facing is that whenever I have too many "moving and rotating" elements on the screen, it gets choppy.
Matrix matrix = new Matrix();
matrix.setTranslate(view.getX(), view.getY());
matrix.preScale((1.0f * view.getWidth() / view.getCurrentBitmap().getWidth()), (1.0f * view.getHeight() / view.getCurrentBitmap().getHeight()));
matrix.postRotate(view.getRotation(), view.getX() + view.getWidth()/2f, view.getY() + view.getHeight()/2f);
mCanvas.drawBitmap(view.getCurrentBitmap(), matrix, mBasicPaint);
For example, this is how the player-object is being drawn according to the rotation and position:
private void drawPlayer(final Canvas mCanvas) {
final Bitmap playerBitmap = mPlayer.getCurrentBitmap();
mPlayer.nextFrame();
if(playerBitmap != null) {
if(mPlayer.getRotation() != 0) {
Matrix matrix = new Matrix();
matrix.setTranslate(-mPlayer.getCurrentBitmap().getWidth()/2f, -mPlayer.getCurrentBitmap().getHeight()/2f);
matrix.postRotate(mPlayer.getRotation());
matrix.postTranslate(mPlayer.getX() + + mPlayer.getCurrentBitmap().getWidth()/2f, mPlayer.getY() + mPlayer.getCurrentBitmap().getHeight()/2f);
matrix.postScale((1.0f * mPlayer.getWidth() / mPlayer.getCurrentBitmap().getWidth()), (1.0f * mPlayer.getHeight() / mPlayer.getCurrentBitmap().getHeight()), mPlayer.getX(), mPlayer.getY());
mCanvas.drawBitmap(mPlayer.getCurrentBitmap(), matrix, mBasicPaint);
} else {
Matrix matrix = new Matrix();
matrix.setScale((1.0f * mPlayer.getWidth() / mPlayer.getCurrentBitmap().getWidth()), (1.0f * mPlayer.getHeight() / mPlayer.getCurrentBitmap().getHeight()));
matrix.postTranslate(mPlayer.getX(), mPlayer.getY());
mCanvas.drawBitmap(mPlayer.getCurrentBitmap(), matrix, mBasicPaint);
}
} else log("bitmap = null!");
}
(This is a kind of deprecated version, the .getCurrentBitmap() calls are reduced to one in the current version.)
How could I improve the performance? Should I create some sort of a "Loading..." screen, in which I pre-load EVERY bitmap (in its biggest size) and a pre-rotated version of each bitmap? (This would result in, if I go with 2-4 degree steps, 90-180 versions of each bitmap, which seems kind of.. a lot?) Or would this, with the rotated bitmaps stored as well, be too much on the memory? I don't know anything about OpenGL etc, this is why I'm using a SurfaceView and no other game engine, and I'm sure it has to work like this as well - somehow.

You are creating a Matrix (matrix) and Bitmap (playerBitmap) object each time you call drawPlayer method. As far as I understand, you will call this method each frame. So, each frame, you are creating 2 large objects that need to be Garbage collected when you exit the method and this will slow down your frame rate. You can create only 2 objects matrix and playerBitmap as class level variables and refresh them on each call of drawPlayer, reducing the number of GC calls.

Related

Bitmap - Matrix operations (scale, rotate and translate)

I need some help with matrix operations. What I'm trying to achieve is:
Scale down
Move to a specific position
Rotate by some degree (in the center of the bitmap)
My code currently looks like this:
Matrix matrix = new Matrix();
matrix.preRotate(mShip.getRotation(), mShip.getX() + mShip.getCurrentBitmap().getWidth()/2f, mShip.getY() + mShip.getCurrentBitmap().getHeight()/2f);
matrix.setScale((1.0f * mShip.getWidth() / mShip.getCurrentBitmap().getWidth()), (1.0f * mShip.getHeight() / mShip.getCurrentBitmap().getHeight()));
matrix.postTranslate(mShip.getX(), mShip.getY());
mCanvas.drawBitmap(mShip.getCurrentBitmap(), matrix, mBasicPaint);
But the rotation has the wrong center, and I can't figure out how to solve this - I've already looked around on SO but did only find similar problems, no solutions to this.
I think that I might have to apply one of the operations to another one's values as they are executed in a sequence but I cant figure out how to.
Try this code:
Matrix matrix = new Matrix();
matrix.setTranslate(-mShip.getCurrentBitmap().getWidth()/2f, -mShip.getCurrentBitmap().getHeight()/2f);
matrix.postRotate(mShip.getRotation());
matrix.postTranslate(mShip.getX(), mShip.getY());
matrix.postScale((1.0f * mShip.getWidth() / mShip.getCurrentBitmap().getWidth()), (1.0f * mShip.getHeight() / mShip.getCurrentBitmap().getHeight()), mShip.getX(), mShip.getY());

Using Hardware Layer in Custom View onDraw

So I'm trying to understand how I can properly use hardware acceleration (when available) in a custom View that is persistently animating. This is the basic premise of my onDraw():
canvas.drawColor(mBackgroundColor);
for (Layer layer : mLayers) {
canvas.save();
canvas.translate(layer.x, layer.y);
//Draw that number of images in a grid, offset by -1
for (int i = -1; i < layer.xCount - 1; i++) {
for (int j = -1; j < layer.yCount - 1; j++) {
canvas.drawBitmap(layer.bitmap, layer.w * i, layer.h * j, null);
}
}
//If the layer's x has moved past its width, reset back to a seamless position
layer.x += ((difference * layer.xSpeed) / 1000f);
float xOverlap = layer.x % layer.w;
if (xOverlap > 0) {
layer.x = xOverlap;
}
//If the layer's y has moved past its height, reset back to a seamless position
layer.y += ((difference * layer.ySpeed) / 1000f);
float yOverlap = layer.y % layer.h;
if (yOverlap > 0) {
layer.y = yOverlap;
}
canvas.restore();
}
//Redraw the view
ViewCompat.postInvalidateOnAnimation(this);
I'm enabling hardware layers in onAttachedToWindow() and disabling them in onDetachedFromWindow(), but I'm trying to understand whether or not I'm actually using it. Essentially, the i/j loop that calls drawBitmap() never changes; the only thing that changes is the Canvas translation. Is the Bitmap automatically saved to the GPU as a texture behind the scenes, or is there something I need to do manually to do so?
On what view(s) are you setting View.LAYER_TYPE_HARDWARE exactly? If you are setting a hardware layer on the view that contains the drawing code shown above, you are causing the system to do a lot more work than necessary. Since you are only drawing bitmaps you don't need to do anything here. If you call Canvas.drawBitmap() the framework will cache the resulting OpenGL texture on your behalf.
You could however optimize your code a little more. Instead of calling drawBitmap(), you could use child views. If you move these children using the offset*() methods (or setX()/setY()) the framework will apply further optimizations to avoid calling the draw() methods again.
In general, hardware layers should be set on views that are expensive to draw and whose content won't change often (so pretty much the opposite of what you're doing :)
You can use Android's Tracer for OpenGL ES to see if your view issue OpenGL commands.
From developer.android.com
Tracer is a tool for analyzing OpenGL for Embedded Systems (ES) code in your Android application. The tool allows you to capture OpenGL ES commands and frame by frame images to help you understand how your graphics commands are being executed.
There is also a tutorial about Android Performance Study by Romain Guy which describes its use almost step by step.

Background image taking too long to draw (Canvas) Jerky Sprites......?

Hey all I'm at a crossroads with my app that I've been working on.
It's a game and an 'arcade / action' one at that, but I've coded it using Surfaceview rather than Open GL (it just turned out that way as the game changed drastically from it's original design).
I find myself plagued with performance issues and not even in the game, but just in the first activity which is an animated menu (full screen background with about 8 sprites floating across the screen).
Even with this small amount of sprites, I can't get perfectly smooth movement. They move smoothly for a while and then it goes 'choppy' or 'jerky' for a split second.
I noticed that (from what I can tell) the background (a pre-scaled image) is taking about 7 to 8 ms to draw. Is this reasonable? I've experimented with different ways of drawing such as:
canvas.drawBitmap(scaledBackground, 0, 0, null);
the above code produces roughly the same results as:
canvas.drawBitmap(scaledBackground, null, screen, null);
However, if I change my holder to:
getHolder().setFormat(PixelFormat.RGBA_8888);
The the drawing of the bitmap shoots up to about 13 MS (I am assuming because it then has to convert to RGB_8888 format.
The strange thing is that the rendering and logic move at a very steady 30fps, it doesn't drop any frames and there is no Garbage Collection happening during run-time.
I've tried pretty much everything I can think of to get my sprites moving smoothly
I recently incorporated interpolation into my gameloop:
float interpolation = (float)(System.nanoTime() + skipTicks - nextGameTick)
/ (float)(skipTicks);
I then pass this into my draw() method:
onDraw(interpolate)
I have had some success with this and it has really helped smooth things out, but I'm still not happy with the results.
Can any one give me any final tips on maybe reducing the time taken to draw my bitmaps or any other tips on what may be causing this or do you think it's simply a case of Surfaceview not being up to the task and therefore, should I scrap the app as it were and start again with Open GL?
This is my main game loop:
int TICKS_PER_SECOND = 30;
int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
int MAX_FRAMESKIP = 10;
long next_game_tick = GetTickCount();
int loops;
bool game_is_running = true;
while( game_is_running ) {
loops = 0;
while( GetTickCount() > next_game_tick && loops < MAX_FRAMESKIP) {
update_game();
next_game_tick += SKIP_TICKS;
loops++;
}
interpolation = float( GetTickCount() + SKIP_TICKS - next_game_tick )
/ float( SKIP_TICKS );
display_game( interpolation );
}
Thanks
You shouldn't use Canvas to draw fast sprites, especially if you're drawing a fullscreen image. Takes way too long, I tell you from experience. I believe Canvas is not hardware accelerated, which is the main reason you'll never get good performance out of it. Even simple sprites start to move slow when there are ~15 on screen. Switch to OpenGL, make an orthographic projection and for every Sprite make a textured quad. Believe me, I did it, and it's worth the effort.
EDIT: Actually, instead of a SurfaceView, the OpenGL way is to use a GLSurfaceView. You create your own class, derive from it, implement surfaceCreated, surfaceDestroyed and surfaceChanged, then you derive from Renderer too and connect both. Renderer handles an onDraw() function, which is what will render, GLSurfaceView manages how you will render (bit depth, render modes, etc.)

Any good ideas for drawing 2d tile based map? (i have my own format,not Tiled"TMX")

for(int i =0;i<element[id].Element.length;i++)//SCAN EACH MAMBER OF THIS ELEMENT
{
dRectangle =
new Rect((int)(element[id].Element[i].x + WorldPos.x),
(int)(element[id].Element[i].y + WorldPos.y),
(int)(element[id].Element[i].x + WorldPos.x + elementSize.x),
(int)(element[id].Element[i].y + WorldPos.y + elementSize.y));
if(element[id].Element[i].x + WorldPos.x <= SCREEN_WIDTH + lementSize.x&&
element[id].Element[i].x + WorldPos.x<=-elementSize.x)
if(element[id].Element[i].y + WorldPos.y <= SCREEN_HEIGHT + elementSize.y&&element[id].Element[i].y + WorldPos.y<=-elementSize.y)//----- now the element is inbounds the screen.Draw it;
canvas.drawBitmap(ElemtMap.getBmap(), sRectangle,dRectangle, null);*/
}
//TODO: DECIDE WHICH SPRITE TO DRAW
// HOW MANY TIMES WE ARE GOING TO DRAW SAME SPRITE (AND POSITION);
/*
*
* for(int i =0;i<How_Many_Time_This_Element_Used;i++)<----scan all
* {
* if(the sprite is inbounds screen)<------sprite[id].position;
* draw(ELEMENT ID,ElementPosition[i]);
*
* }
*
*/
canvas.restore();
}
this is how i draw the sprit.
and it is veeeeery slow, like hell.
First, I've read the map and get [How many elements do we have in the elementMap].
And then i have created an Single_element class to store each element,and how many times
this element used,and each position.
Then,now i've got the number of total element, i created element[total_element]
use. I have to scan each element to decide which element to draw, how many times
to draw it,and where to draw.
then,my game beceme veeeeeeeeeerryyy sllloooooowww:(
i know this is a BAD way to draw.
so, is there any idea ,example or whatever to deal with [Draw tiled map]?
I've downloaded and checked AndEngine. nothing helped.:S(or maybe im too stupid to understand XD)
thanks!
Edit: it is
dRectangle =
new Rect((int)(element[id].Element[i].x + WorldPos.x),
(int)(element[id].Element[i].y + WorldPos.y),
(int)(element[id].Element[i].x + WorldPos.x + elementSize.x),
(int)(element[id].Element[i].y + WorldPos.y + elementSize.y));
slow my game down:S `
You are likely feeling the effect of instantiating and garbage collecting a big number of Rect objects.
Each iteration causes a number of Rect objects to be instantiated and discarded and the Garbage collection pauses affect the speed and smoothness of your application. In games programming on android it is often necessary to reuse objects rather than instantiate new for this very reason, although it is better programming practice in general to avoid mutable objects.
In you case, if my theory is right you can solve this by instantiating a new Rect assign to dRectangle before the loop;
Rect dRectangle = new Rect();
then use the set method to change its values on each iteration.
http://developer.android.com/reference/android/graphics/Rect.html#set(int, int, int, int)
dRectangle.set((int)(element[id].Element[i].x + WorldPos.x),
(int)(element[id].Element[i].y + WorldPos.y),
(int)(element[id].Element[i].x + WorldPos.x + elementSize.x),
(int)(element[id].Element[i].y + WorldPos.y + elementSize.y));
I´d also ask you to reconsider AndEngine, though that was not your question :-)
1) Some art of mapping with SpriteName->Sprite
2) Load description with Map
3) Find what rect of world should be drawn
4) Draw background. If you store Bg in 2d char array, it's pretty fast. So e.g. W is water, S is Sand Then if you have array like this:
SSWSWSSS
SSWSWSSS
SSWWWSSS
SSSSWSSS
SSSSWSSS
itereate over array once. and for every Char you find in mapping S->sand.png, W->water.png
Then draw game objects. They are typical stored in a ArrayList. Just make AABB test, wheather go should be drawn and draw if needed.
Or use sweep and prune, quadtree for that.
P.S. I do not understand your code.

Spritesheet programmatically cutting: best practices

I have a big spritesheet (3808x1632) composed by 42 frames.
I would present an animation with these frames and I use a thread to load a bitmap array with all the frames, with a splash screen waiting for its end.
I'm not using a SurfaceView (and a draw function of a canvas), I just load frame by frame in an ImageView in my main layout.
My approach is similar to Loading a large number of images from a spritesheet
The completion actually takes almost 15 seconds, not acceptable.
I use this kind of function:
for (int i=0; i<TotalFramesTeapotBG; i++) {
xStartTeapotBG = (i % framesInRowsTeapotBG) * frameWidthTeapotBG;
yStartTeapotBG = (i / framesInRowsTeapotBG) * frameHeightTeapotBG;
mVectorTeapotBG.add(Bitmap.createBitmap(framesBitmapTeapotBG, xStartTeapotBG, yStartTeapotBG, frameWidthTeapotBG, frameHeightTeapotBG));
}
framesBitmapTeapotBG is the big spritesheet.
Looking more deeply, I've read in the logcat that the createBitmap function takes a lot of time, maybe because the spritesheet is too big.
I found somewhere that I could make a window on the big spritesheet, using the rect function and canvas, creating small bitmaps to be loaded in the array, but it was not really clear. I'm talking about that post: cut the portion of bitmap
My question is: how can I speed the spritesheet cut?
Edit:
I'm trying to use this approach but I cannot see the final animation:
for (int i=0; i<TotalFramesTeapotBG; i++) {
xStartTeapotBG = (i % framesInRowsTeapotBG) * frameWidthTeapotBG;
yStartTeapotBG = (i / framesInRowsTeapotBG) * frameHeightTeapotBG;
Bitmap bmFrame = Bitmap.createBitmap(frameWidthTeapotBG, frameHeightTeapotBG, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bmFrame);
Rect src = new Rect(xStartTeapotBG, yStartTeapotBG, frameWidthTeapotBG, frameHeightTeapotBG);
Rect dst = new Rect(0, 0, frameWidthTeapotBG, frameHeightTeapotBG);
c.drawBitmap(framesBitmapTeapotBG, src, dst, null);
mVectorTeapotBG.add(bmFrame);
}
Probably, the Bitmap bmFrame is not correctly managed.
The short answer is better memory management.
The sprite sheet you're loading is huge, and then you're making a copy of it into a bunch of little bitmaps. Supposing the sprite sheet can't be any smaller, I'd suggest taking one of two approaches:
Use individual bitmaps. This will reduce the memory copies as well as the number of times Dalvik will have to grow the heap. However, these benefits may be limited by the need to load many images off the filesystem instead of just one. This would be the case in a normal computer, but Android systems may get different results since they're run off flash memory.
Blit directly from your sprite sheet. When drawing, just draw straight from sprite sheet using something like Canvas.drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint). This will reduce your file loads to one large allocation that probably only needs to happen once in the lifetime of your activity.
I think the second option is probably the better of the two since it will be easier on the memory system and be less work for the GC.
Thanks to stevehb for the suggestion, I finally got it:
for (int i = 0; i < TotalFramesTeapotBG; i++) {
xStartTeapotBG = (i % framesInRowsTeapotBG) * frameWidthTeapotBG;
yStartTeapotBG = (i / framesInRowsTeapotBG) * frameHeightTeapotBG;
Bitmap bmFrame = Bitmap.createBitmap(frameWidthTeapotBG, frameHeightTeapotBG, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bmFrame);
Rect src = new Rect(xStartTeapotBG, yStartTeapotBG, xStartTeapotBG+frameWidthTeapotBG, yStartTeapotBG+frameHeightTeapotBG);
Rect dst = new Rect(0, 0, frameWidthTeapotBG, frameHeightTeapotBG);
c.drawBitmap(framesBitmapTeapotBG, src, dst, null);
mVectorTeapotBG.add(bmFrame);
}
The computation time falls incredibly! :)
Use a LevelListDrawable. Cut the sprites into individual frames and drop them in your drawable resource directory. Either programmatically or through an xml based level-list drawable create your drawable. Then use ImageView.setImageLevel() to pick your frame.
I use a method of slicing based on rows and columns. However your sprite sheet is rather huge. You have to think you are putting that whole sheet into memory. 3808x1632x4 is the size of the image in memory.
Anyway, what I do is I take an image (lets say a 128x128) and then tell it there are 4 columns and 2 rows in the Sprite(bitmap, 4, 2) constructor. Then you can slice and dice based on that. bitmap.getWidth() / 4 etc... pretty simple stuff. However if you want to do some real stuff use OpenGL and use textures.
Oh I also forgot to mention there are some onDraw stuff that needs to happen. Basically you keep an index counter and slice a rectangle from the bitmap and draw that from a source rectangle to a destination rectangle on the canvas.

Categories

Resources