Android Open Source - GameOfLife Game Loop View






From Project

Back to project page GameOfLife.

License

The source code is released under:

GNU General Public License

If you think the Android project GameOfLife listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*******************************************************************************
 * Copyright (c) 2011-2013 Pieter Pareit.
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version./* w ww.  java  2  s  . c  o  m*/
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contributors:
 *     Pieter Pareit - initial API and implementation
 ******************************************************************************/
package be.ppareit.android;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import be.ppareit.gameoflife.BuildConfig;

/**
 * This class contains all logic related to running a game loop.
 * <p>
 *
 * This class must be extended by your game. All game logic should happen in onUpdate().
 * All drawing should happen in onDraw(). The game loop is started with startGameLoop()
 * and stopped with pauseGameLoop(). When the game loop is paused, and the screen needs to
 * be refreshed, call invalidate().
 * <p>
 *
 * While part of GameOfLife, this class can be reused in other applications that need a
 * view containing a game loop and the related logic.
 *
 */
public abstract class GameLoopView extends SurfaceView implements SurfaceHolder.Callback {
    private static final String TAG = GameLoopView.class.getSimpleName();

    class AnimationThread extends Thread {

        private volatile boolean mRun;

        private long mLastTime = System.currentTimeMillis();

        private int mFrameSamplesCollected = 0;
        private int mFrameSampleTime = 0;
        private int mFrameNumber = 0; // keeping track of how many frames we've shown
        private int mFps = 0;

        private SurfaceHolder mSurfaceHolder;

        private Paint mFpsTextPaint;

        public AnimationThread(SurfaceHolder surfaceHolder) {
            mSurfaceHolder = surfaceHolder;

            mFpsTextPaint = new Paint();
            mFpsTextPaint.setARGB(255, 255, 0, 0);
            mFpsTextPaint.setTextSize(32);
        }

        @SuppressLint("WrongCall")
        @Override
        public void run() {
            Log.d(TAG, "AnimationThread.run'ing");

            // block until the surface is completely created in the main thread
            while (!surfaceCreatedCompleted) {
            }

            // run the gameloop
            while (mRun) {
                onUpdate();
                Canvas canvas = null;
                try {
                    canvas = mSurfaceHolder.lockCanvas();
                    synchronized (mSurfaceHolder) {
                        onDraw(canvas);
                        drawFps(canvas);
                    }
                } finally {
                    mSurfaceHolder.unlockCanvasAndPost(canvas);
                }
                sleepIfNeeded();
                mFrameNumber++;
                updateFps();
            }
            Log.d(TAG, "AnimationThread.run'ed");
        }

        private long mNextGameTick = System.currentTimeMillis();

        /**
         * Will only sleep if we need to limit the frame rate to a certain number.
         */
        private void sleepIfNeeded() {
            if (mTargetFps <= 0)
                return;

            mNextGameTick += 1000 / mTargetFps;
            long sleepTime = mNextGameTick - System.currentTimeMillis();
            if (sleepTime >= 0) {
                try {
                    sleep(sleepTime);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            } else if (mFrameNumber % 20 == 0) {
                Log.i(TAG, "Failed to reach expected FPS!");
            }
        }

        public void setRunning(boolean state) {
            mRun = state;
        }

        private void updateFps() {
            long currentTime = System.currentTimeMillis();

            int timeDifference = (int) (currentTime - mLastTime);
            mFrameSampleTime += timeDifference;
            mFrameSamplesCollected++;

            if (mFrameSamplesCollected == 10) {
                mFps = (10 * 1000) / mFrameSampleTime;

                mFrameSampleTime = 0;
                mFrameSamplesCollected = 0;
            }

            mLastTime = currentTime;
        }

        private void drawFps(Canvas canvas) {
            if (mDrawFps == true && mFps != 0) {
                int x = getWidth() - getWidth() / 8;
                int y = getHeight() - (int) mFpsTextPaint.getTextSize() - 5;
                canvas.drawText(mFps + " fps", x, y, mFpsTextPaint);
            }
        }
    }

    private AnimationThread mThread;
    private int mTargetFps = 0;
    private boolean mDrawFps = false;

    public GameLoopView(Context context, AttributeSet attrs) {
        super(context, attrs);

        getHolder().addCallback(this);

        setFocusable(true);
    }

    /**
     * Will start a seperate thread that runs the game loop. From within this will call
     * onUpdate() and onDraw().
     */
    public void startGameLoop() {
        Log.d(TAG, "startGameLoop'ing");
        // if thread exists, the gameloop is running
        if (mThread == null) {
            mThread = new AnimationThread(getHolder());
            mThread.setRunning(true);
            mThread.start();
        }
        if (BuildConfig.DEBUG == true) {
            mDrawFps = true;
        }
        Log.d(TAG, "startGameLoop'ed");
    }

    /**
     * Pauses the gameloop, this can be restared with startGameLoop().
     */
    public void pauseGameLoop() {
        // only pause a gameloop that is running
        if (mThread != null) {
            boolean retry = true;
            mThread.setRunning(false);
            while (retry) {
                try {
                    mThread.join();
                    retry = false;
                } catch (InterruptedException e) {
                    // swallow exception and retry joining thread
                }
            }
            mThread = null;
        }
    }

    /**
     * Set's the frame rate at which the game loop should run. Be conservative and
     * implement an efficient onUpate()/onDraw() so this frame rate can be maintaned.
     *
     * @param fps
     *            The frame rate at which the game loop should run, set to zero to run as
     *            fast as possible.
     */
    public void setTargetFps(int fps) {
        mTargetFps = fps;
    }

    /**
     * If set to true, the gameloop will display the fps in the bottom right corner.
     *
     * @param show
     *            Flag indicating wheter to show the fps or not.
     */
    public void setDrawFps(boolean show) {
        mDrawFps = show;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    /*
     * keeps track if the surface is completely created, the gameloop can only run if we
     * have a surface, so the gameloop thread has to block until so
     */
    private volatile boolean surfaceCreatedCompleted = false;

    @SuppressLint("WrongCall")
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.d(TAG, "surfaceCreated'ing");
        Canvas canvas = null;
        try {
            canvas = holder.lockCanvas();
            synchronized (holder) {
                onDraw(canvas);
            }
        } finally {
            holder.unlockCanvasAndPost(canvas);
        }
        surfaceCreatedCompleted = true;
        Log.d(TAG, "surfaceCreated'ed");
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

    @SuppressLint("WrongCall")
    @Override
    public void invalidate() {
        SurfaceHolder holder = getHolder();
        Canvas canvas = null;
        try {
            canvas = holder.lockCanvas();
            synchronized (holder) {
                onDraw(canvas);
            }
        } finally {
            holder.unlockCanvasAndPost(canvas);
        }
    }

    /**
     * Override this to implement the game logic.
     */
    abstract protected void onUpdate();

    /**
     * Override this to de the drawing.
     */
    @Override
    abstract protected void onDraw(Canvas canvas);

}




Java Source Code List

be.ppareit.android.GameLoopView.java
be.ppareit.gameoflife.GameOfLifeView.java
be.ppareit.gameoflife.GameOfLife.java
be.ppareit.gameoflife.UndoManager.java