2010年3月25日 星期四

遊戲樣版程式改造教學之一


在3月26日我們曾介紹Google Android 遊戲樣版程式----2D Android Game Template,我們將它修改成小精靈控制程式,其修改的程式以粗體字顯示:

package eu.MrSnowflake.android.gametemplate;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
* View that draws, takes keystrokes, etc. for a simple LunarLander game.
*
* Has a mode which RUNNING, PAUSED, etc. Has a x, y, dx, dy, ... capturing the
* current ship physics. All x/y etc. are measured with (0,0) at the lower left.
* updatePhysics() advances the physics based on realtime. draw() renders the
* ship, and does an invalidate() to prompt another draw() as soon as possible
* by the system.
*/
class GameView extends SurfaceView implements SurfaceHolder.Callback {
class GameThread extends Thread {
/*
* State-tracking constants
*/
public static final int STATE_LOSE = 1;
public static final int STATE_PAUSE = 2;
public static final int STATE_READY = 3;
public static final int STATE_RUNNING = 4;
public static final int STATE_WIN = 5;

private float x;
private float y;

private static final int SPEED = 100;
private boolean dRight;
private boolean dLeft;
private boolean dUp;
private boolean dDown;

private int mCanvasWidth;
private int mCanvasHeight;

private long mLastTime;
private Bitmap[] mSnowflake;
/** Message handler used by thread to post stuff back to the GameView */
private Handler mHandler;

/** The state of the game. One of READY, RUNNING, PAUSE, LOSE, or WIN */
private int mMode;
/** Indicate whether the surface has been created & is ready to draw */
private boolean mRun = false;
/** Handle to the surface manager object we interact with */
private SurfaceHolder mSurfaceHolder;

private int mDirect=0;


public GameThread(SurfaceHolder surfaceHolder, Context context,
Handler handler) {
// get handles to some important objects
mSurfaceHolder = surfaceHolder;
mHandler = handler;
mContext = context;

x = 10;
y = 10;

mSnowflake = new Bitmap[8];
mSnowflake[0] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.packman_right);
mSnowflake[1] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.packman_rightt2);
mSnowflake[2] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.packman_left);
mSnowflake[3] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.packman_left2);
mSnowflake[4] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.packman_up);
mSnowflake[5] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.packman_up2);
mSnowflake[6] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.packman_down);
mSnowflake[7] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.packman_down2);
}

/**
* Starts the game, setting parameters for the current difficulty.
*/
public void doStart() {
synchronized (mSurfaceHolder) {
// Initialize game here!

x = 10;
y = 10;

mLastTime = System.currentTimeMillis() + 100;
setState(STATE_RUNNING);
}
}

/**
* Pauses the physics update & animation.
*/
public void pause() {
synchronized (mSurfaceHolder) {
if (mMode == STATE_RUNNING)
setState(STATE_PAUSE);
}
}

@Override
public void run() {
while (mRun) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if (mMode == STATE_RUNNING)
updateGame();
doDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}

/**
* Used to signal the thread whether it should be running or not.
* Passing true allows the thread to run; passing false will shut it
* down if it's already running. Calling start() after this was most
* recently called with false will result in an immediate shutdown.
*
* @param b true to run, false to shut down
*/
public void setRunning(boolean b) {
mRun = b;
}

/**
* Sets the game mode. That is, whether we are running, paused, in the
* failure state, in the victory state, etc.
*
* @see #setState(int, CharSequence)
* @param mode one of the STATE_* constants
*/
public void setState(int mode) {
synchronized (mSurfaceHolder) {
setState(mode, null);
}
}

/**
* Sets the game mode. That is, whether we are running, paused, in the
* failure state, in the victory state, etc.
*
* @param mode one of the STATE_* constants
* @param message string to add to screen or null
*/
public void setState(int mode, CharSequence message) {
synchronized (mSurfaceHolder) {
mMode = mode;
}
}

/* Callback invoked when the surface dimensions change. */
public void setSurfaceSize(int width, int height) {
// synchronized to make sure these all change atomically
synchronized (mSurfaceHolder) {
mCanvasWidth = width;
mCanvasHeight = height;
}
}

/**
* Resumes from a pause.
*/
public void unpause() {
// Move the real time clock up to now
synchronized (mSurfaceHolder) {
mLastTime = System.currentTimeMillis() + 100;
}
setState(STATE_RUNNING);
}

/**
* Handles a key-down event.
*
* @param keyCode the key that was pressed
* @param msg the original event object
* @return true
*/
boolean doKeyDown(int keyCode, KeyEvent msg) {
boolean handled = false;
synchronized (mSurfaceHolder) {
if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT){
dRight = true;
handled = true;
mDirect = (mDirect+1) %2; }
if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT){
dLeft = true;
handled = true;
mDirect = (mDirect+1) %2+2; }
if (keyCode == KeyEvent.KEYCODE_DPAD_UP){
dUp = true;
handled = true;
mDirect = (mDirect+1) %2+4; }
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN){
dDown = true;
handled = true;
mDirect = (mDirect+1) %2+6; }
return handled;
}
}

/**
* Handles a key-up event.
*
* @param keyCode the key that was pressed
* @param msg the original event object
* @return true if the key was handled and consumed, or else false
*/
boolean doKeyUp(int keyCode, KeyEvent msg) {
boolean handled = false;
synchronized (mSurfaceHolder) {
if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT){
dRight = false;
handled = true;

}
if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT){
dLeft = false;
handled = true;
}
if (keyCode == KeyEvent.KEYCODE_DPAD_UP){
dUp = false;
handled = true;
}
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN){
dDown = false;
handled = true;
}
return handled;
}
}

/**
* Draws the ship, fuel/speed bars, and background to the provided
* Canvas.
*/
private void doDraw(Canvas canvas) {
// empty canvas
canvas.drawARGB(255, 192, 192, 192);

canvas.drawBitmap(mSnowflake[mDirect], x, y, new Paint());

}

/**
* Updates the game.
*/
private void updateGame() {
////
long now = System.currentTimeMillis();
// Do nothing if mLastTime is in the future.
// This allows the game-start to delay the start of the physics
// by 100ms or whatever.
if (mLastTime > now)
return;
double elapsed = (now - mLastTime) / 1000.0;
mLastTime = now;
////


/*
* Why use mLastTime, now and elapsed?
* Well, because the frame rate isn't always constant, it could happen your normal frame rate is 25fps
* then your char will walk at a steady pace, but when your frame rate drops to say 12fps, without elapsed
* your character will only walk half as fast as at the 25fps frame rate. Elapsed lets you manage the slowdowns
* and speedups!
*/

if (dUp)
y -= elapsed * SPEED;
if (dDown)
y += elapsed * SPEED;
if (y < 0)
y = 0;
else if (y >= mCanvasHeight - mSnowflake[0].getHeight())
y = mCanvasHeight - mSnowflake[0].getHeight();
if (dLeft)
x -= elapsed * SPEED;
if (dRight)
x += elapsed * SPEED;
if (x < 0)
x = 0;
else if (x >= mCanvasWidth - mSnowflake[0].getWidth())
x = mCanvasWidth - mSnowflake[0].getWidth();
}
}

/** Handle to the application context, used to e.g. fetch Drawables. */
private Context mContext;

/** The thread that actually draws the animation */
private GameThread thread;

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

// register our interest in hearing about changes to our surface
SurfaceHolder holder = getHolder();
holder.addCallback(this);

// create thread only; it's started in surfaceCreated()
thread = new GameThread(holder, context, new Handler() {
@Override
public void handleMessage(Message m) {
// Use for pushing back messages.
}
});

setFocusable(true); // make sure we get key events
}

/**
* Fetches the animation thread corresponding to this LunarView.
*
* @return the animation thread
*/
public GameThread getThread() {
return thread;
}

/**
* Standard override to get key-press events.
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent msg) {
return thread.doKeyDown(keyCode, msg);
}

/**
* Standard override for key-up. We actually care about these, so we can
* turn off the engine or stop rotating.
*/
@Override
public boolean onKeyUp(int keyCode, KeyEvent msg) {
return thread.doKeyUp(keyCode, msg);
}

/**
* Standard window-focus override. Notice focus lost so we can pause on
* focus lost. e.g. user switches to take a call.
*/
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
if (!hasWindowFocus)
thread.pause();
}

/* Callback invoked when the surface dimensions change. */
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
thread.setSurfaceSize(width, height);
}

/*
* Callback invoked when the Surface has been created and is ready to be
* used.
*/
public void surfaceCreated(SurfaceHolder holder) {
// start the thread here so that we don't busy-wait in run()
// waiting for the surface to be created
thread.setRunning(true);
thread.start();
}

/*
* Callback invoked when the Surface has been destroyed and must no longer
* be touched. WARNING: after this method returns, the Surface/Canvas must
* never be touched again!
*/
public void surfaceDestroyed(SurfaceHolder holder) {
// we have to tell thread to shut down & wait for it to finish, or else
// it might touch the Surface after we return and explode
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
}

沒有留言:

張貼留言