неделя, март 13, 2011

Android simple game loop 2 - View Canvas and Handler

This is another way to make the gameloop, however it's not done with explicitly created thread by the View, but with a Handler. Instead of calling postInvalidate() from another thread, here the Handler is calling invalidate().
In terms of performance, my tests showed this way to be something like 10-15% slower (frames per second) than the previous loop, however the movement of the objects is more smooth and nice (it's more noticable when there are many objects). I guess this is because the real drawing of the screen is synchronous with objects movement update.
In the case of Handler, first all objects' coords are updated and then all objects are drawn, because invalidate is called immediately after update.
In the case of thread in the View and postInvalidate(), all objects are updated as fast as the thread can run, and postInvalidate() is called on every cycle, but they are not synchronous operations. It seems that postInvalidate() has tiny random delays in regard to when the real drawing is done by the runtime, and causes the movement of the objects to appear a little bit sluggish (not so smooth).

Activity:
package com.test.drawhandler;

import android.app.Activity;
import android.os.Bundle;

public class DrawHandler extends Activity 
{
    private DrawView mDrawView = null;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mDrawView = new DrawView(this);
        setContentView(mDrawView);
    }
    
    public void onResume()
    {
        super.onResume();
        mDrawView.start();
    }
    
    public void onPause()
    {
        super.onPause();
        mDrawView.stop();
    }
}

View:
package com.test.drawhandler;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.view.View;

public class DrawView extends View 
{
    private boolean isRunning = false;
    private Context mContext = null;
    private RefreshHandler mRefreshHandler = null;
    private Drawable mIcon = null;
    private int mIconX = 0;
    private int mIconY = 0;
    
    public DrawView(Context context)
    {
        super(context);
        mContext = context;
        mIcon = context.getResources().getDrawable(R.drawable.icon);
        mRefreshHandler = new RefreshHandler();
    }
    
    public void start()
    {
        isRunning = true;
        update();
    }
    
    public void stop()
    {
        isRunning = false;
    }
    
    @Override
    protected void onDraw(Canvas canvas)
    {
        mIcon.setBounds(mIconX, mIconY, mIconX+48, mIconY+48);
        mIcon.draw(canvas);
    }
    
    private void update()
    {
        if(isRunning)
        {
            mIconX++;
            mIconY++;
            mRefreshHandler.sleep(20);
        }
    }
    
    class RefreshHandler extends Handler
    {
        @Override
        public void handleMessage(Message msg)
        {
            DrawView.this.update();
            DrawView.this.invalidate();
        }
        
        public void sleep(long delayMillis)
        {
            this.removeMessages(0);
            sendMessageDelayed(obtainMessage(0), delayMillis);
        }
    }
}

1 коментар:

Анонимен каза...

need to check