So I am basically creating a 2D physics engine I want to use for other projects in the future.
The only problem here is the performance of my Surface View.
I get a constant 60 logic-updates per second, but only 6 frames per second no matter what I do.
If I call the draw() method in my calculating thread, I get 6 fps.
If I create a whole new thread which is only drawing, without any delay in the loop
#Override
public void run() {
while(running) {
if(holder.getSurface().isValid()) {
canvas = holder.lockCanvas();
canvas.drawColor(Color.DKGRAY);
for (GameObject object : objects) {object.draw(canvas);}
holder.unlockCanvasAndPost(canvas);
}
gs.totalFrames++;
}
}
(gs = Game Surface) I get 6 fps. The draw() function of my game object is pretty simple:
public void draw(Canvas canvas) {
canvas.drawCircle((float) x, (float) y, 10, paint);
}
Why is the performance so bad? (I only have 5 game objects, so that's 5 circles, which should not be a performance problem at all!)
I also tried a runnable posting itself delayed, but that was bad, too.
Calling the draw() function in the GameSurface from the thread instead of doing the drawing directly in it, didn't change anything. A tiny delay didn't improve it either.
Do I somehow use the SurfaceView in a way it's not supposed to be used in?
What is the problem?
Related
I am currently trying to move some rectangle objects (displayed as bitmaps on my surfaceview).
They should all move with the same speed, therefor my code looks like this:
new Thread (new Runnable()
{
#Override
public void run()
{
while(true)
{
newTime = System.currentTimeMillis();
frameTime = newTime - currentTime;
currentTime = newTime;
physics(frameTime);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
i move my rectangle objects in the physics method based on the frametime parameter.
So my problem is the following: with the code i just posted all my rectangles move at the same speed, but the graphics are lagging. The lag disappears as soon as i remove the Thread.sleep(1), but then my rectangle objects wont move with the same speed anymore (some rectangles move faster than others).
any ideas?
Edit:
the physics and movedown methods are just normal methods in my classes, both are not in any threads or something. they are only getting called from my Thread i posted above
public void physics(double delta)
{
for(int i=0; i<=5; i++)
{
rectangles[i].moveDown(delta);
}
}
public void moveDown(double delta)
{
setY((double) (getY() + ((sH)*(delta/1000))));
//sH is the screen height
}
Edit2:
Graphics code
while(true)
{
if(!ourHolder.getSurface().isValid())
{
continue;
}
Canvas canvas = ourHolder.lockCanvas();
synchronized(ourHolder) {
graphics(canvas);
// in this method all the drawings happen
// e.g. canvas.drawBitmap
}
ourHolder.unlockCanvasAndPost(canvas);
}
It is difficult to analyse given the code you posted, since it's unclear what physics(frameTime); is actually doing. It seems like each moving rectangle is updated in its own Thread. Then the result you get has to be expected because one thread or the other will be called more often than the others depending on thread scheduling. Instead, use one single thread to control your simulation (e.g. update your frameTime and provide it to the other threads). However, IMHO you have to rethink your architecture.
It's hard to tell from your posted code what the problem could be, but one thing I notice is that you could experience some clock drift. See: Does time jump in android devices?.
Although, it looks like you are moving the rectangles using a fixed offset, so even if there were clock drift, I would expect them to jump around but by the same amount. So I agree with Axel that there appears to be some thread interaction going on.
How are you drawing to the screen? If you have another thread processing the graphics, the two threads could get out of sync and you'll need to ensure you are locking things properly. If you are using the deltas to update the location on the screen in your paint method, maybe instead use an absolute position that you update in your physics() method and draw based on that. It seems you probably need to rethink some of the architecture.
I'm trying to test out some different methods of drawing to a Canvas, without triggering garbage collection. Even the most basic examples cause frequent gcs. Example:
class Panel extends View {
private int mX = 0;
private Paint mPaint = new Paint();
public Panel(Context context) {
super(context);
mPaint.setColor(0xFFFF0000);
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
canvas.drawRect(mX, 0, mX+40, 40, mPaint);
mX++;
postInvalidate();
}
}
I get the same result with SurfaceView (lunar lander example). GCs about every 10 seconds or so, pretty jarring in a realtime game. I'm not making any allocations in the draw loop above, so something must be allocated in the canvas etc classes (unfortunately).
I had success with an opengl test, no gcs, but I was hoping to avoid getting into opengl. I'm pretty familiar with it, but it's going to be tough drawing some effects I wanted to achieve using opengl.
Thanks
postInvalidate() may have to create an object. Use invalidate() instead, there is no reason to use postInvalidate() here.
Instead of guessing you should take a look at what is allocated.
I'm a completely newbie to android programming, having done some java for my computing levels but nothing too complex!
I'm working on a game where an object falls down the screen and has to be sorted into the relevant 'box' when it reaches the bottom.
I've got a surface view running with a thread etc, using canvas draw methods, however, i can't for the life of me see how i will be able to make the falling object reach a speed where it'll present a challenge to the user.
Running the thread with a change of 1 in the y direction causes the object to crawl down the screen. Greater changes in Y lead to jumpy graphics.
Would OpenGL make any difference or are there other canvas methods i can implement?
Hope that makes sense!
Thanks in advance
----Thread------
public void run()
{
Canvas canvas;
while(running)
{
canvas = null;
try{
canvas = this.surfaceholder.lockCanvas();
synchronized(surfaceholder)
{
gamepanel.Check();
this.gamepanel.onDraw(canvas);
}
}finally
{
if(canvas != null)
{
surfaceholder.unlockCanvasAndPost(canvas);
}
}
}
}
----SurfaceView-------
protected void onDraw(Canvas canvas){
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.gamebackground), 0, 0, null);
SortItOut.sortitout.Letter.draw(canvas);
}
-----Letter----- (Each object is a different letter)
public static void draw(Canvas canvas)
{
y += 1;
canvas.drawBitmap(LetterObject, x, y, null);
}
Those are the methods i would believe are relevant (The Check method is simply to check whether the object has reached the bottom of the screen).
You must load all your bitmaps in the constructor for the SurfaceView, never in onDraw()
Aside from the bitmap loading problem, you can make it fall faster my increasing the rate of the y change. If you do it too much, the box will appear to jump, but I bet you could get away with up to 10 pixel changes before that would happen (experiment).
You would only need to do OpenGL in this case if performance was slowing you down. I don't think that's the case. Although, I would stop loading the bitmap in the onDraw method and put it in the onCreate or some constructor. onDraw gets called hundreds of times and that's killing your app.
I've read quite a few tutorials on game programming on android,
and all of them provide basically the same solution as to drawing the game, that is having a dedicated thread spinning like this:
public void run() {
while(true) {
if(!surfaceHolder.getSurface().isValid()) continue;
Canvas canvas = surfaceHolder.lockCanvas();
drawGame(canvas); /* do actual drawing here */
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
now I'm wondering, isn't this wasteful? Suppose I've a game with very simple graphics, so that the actual time in drawGame is little;
then I'm going to draw the same things on and on, stealing cpu from the other threads;
a possibility could be skipping the drawing and sleeping a bit if the game state hasn't changed,
which I could check by having the state update thread mantaining a suitable status flag.
But maybe there are other options. For example, couldn'it be possible to synchronize with rendering,
so that I don't post updates too often? Or am I missing something and that is precisely what lockCanvas does,
that is it blocks and burns no cpu until proper time?
Thanks in advance
L.
I would say the tutorials you have seen are wrong, you really want to wait in the main loop. 16 milliseconds would be the target frame time in the example below
public void run() {
while(true) {
long start = System.currentTimeMillis();
if(!surfaceHolder.getSurface().isValid()) continue;
Canvas canvas = surfaceHolder.lockCanvas();
drawGame(canvas); /* do actual drawing here */
surfaceHolder.unlockCanvasAndPost(canvas);
long frameTime = System.currentTimeMillis() - start;
try {
Thread.sleep(Math.max(0, 16 - ( frameTime )));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
You don't need to draw canvas from a loop in a thread, you can do this on request, like when moving the finger over the screen.
If the animation is not intensive, one can use just a custom view and then invalidate() the view from some user input event.
It is also possible to stop the thread and then create and start it again, as many time as needed witin the same SurfaceView class.
I am trying to perform basic task of rotating a canvas 20 times a
second using timer but it doesn't seem to be working properly and its
lagging. for example, if I rotate rectangle 0.3 degrees per 50 ms it
should rotate 6 degree in on second, but that is not the case. It
really slow in rotation. Here is my sample code:
//Code for update task
class UpdateTimeTask extends TimerTask {
public void run() {
hndView.post(new Runnable() {
public void run() {
hndView.invalidate(); //this code invalidates custom view that calls onDraw to draw rotated hand
}
});
}
}
//Code for onDraw method of custom view that needs to be update
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
//ang is angle to rotate and inc is float value of 0.3 degree to be incremented
ang = ang + inc;
if (ang >= 360) ang = ang - 360;
canvas.rotate(ang, canvas.getWidth()/2, canvas.getHeight()/2);
canvas.drawRect((canvas.getWidth()/2 - 2), (canvas.getHeight()/2 - 125), (canvas.getWidth()/2 + 2), (canvas.getHeight()/2 + 10), mTextPaint);
canvas.restore();
}
//code to schedule task
Timer timer = new Timer();
UpdateTimeTask tt = new UpdateTimeTask();
timer.schedule(tt, 0, 50);
Can anyone please tell me what am I doing wrong here? Should I used
different approach to perform this task? Because its hard to believe
that you cannot have simple smooth rotation of rectangle 20 times in
one second.
Timer/TimerTask are not supposed to be precise and are not supposed to be used for this purpose. Follow the recipes for 2D game development, such as the LunarLander sample that is included in your SDK. Or, you could just search StackOverflow and find all sorts of useful posts on the subject.
I believe you are not using a SurfaceView.
Drawing like that to a canvas was meant for controls and not fast rendering(read >10fps)
If you want performance you need either to use a SurfaceView where you'll average a 25-30 fps or opengl
Please read : http://developer.android.com/guide/topics/graphics/index.html
The number of calls to invalidate need not equal the number of calls to onDraw. If your timer ends up running twice successively before the UI thread gets a chance to run, then the double invalidate will end up only causing a single rotation. Consider putting in debug code to validate the number of calls to those two methods.