Android游戏之屏幕类TestScreen和游戏类AndroidGame设计
1、基础知识:
A. WakeLock
http://developer.android.com/reference/android/os/PowerManager.WakeLock.html
B. requestWindowFeature
http://developer.android.com/reference/android/app/Activity.html#requestWindowFeature(int)
C. getWindow().setFlags
http://developer.android.com/reference/android/app/Activity.html#getWindow()
http://developer.android.com/reference/android/view/Window.html#setFlags(int, int)
D. getResources().getConfiguration()
http://developer.android.com/reference/android/content/res/Configuration.html
E. Bitmap.createBitmap
http://developer.android.com/reference/android/graphics/Bitmap.html
F. getWindowManager().getDefaultDisplay()
http://developer.android.com/reference/android/view/WindowManager.html
http://developer.android.com/reference/android/view/Display.html
G. powerManager.newWakeLock
http://developer.android.com/reference/android/os/PowerManager.html#newWakeLock(int, java.lang.String)
H.BufferedReader
http://developer.android.com/reference/java/io/BufferedReader.html
I.BufferedWriter
http://developer.android.com/reference/java/io/BufferedWriter.html
J.Thread
http://developer.android.com/reference/java/lang/Thread.html
2、设计屏幕接口Screen和游戏接口Game
package com.badlogic.androidgames.framework;
public abstract class Screen {
protected final Game game;
public Screen(Game game) {
this.game = game;
}
public abstract void update(float deltaTime);
public abstract void present(float deltaTime);
public abstract void pause();
public abstract void resume();
public abstract void dispose();
}
package com.badlogic.androidgames.framework;
public interface Game {
public Input getInput();
public FileIO getFileIO();
public Graphics getGraphics();
public Audio getAudio();
public void setScreen(Screen screen);
public Screen getCurrentScreen();
public Screen getStartScreen();
}
3. 实现屏幕类TestScreen和游戏类AndroidGame
package com.badlogic.androidgames.framework;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.List;
import android.graphics.Color;
import android.util.Log;
import com.badlogic.androidgames.framework.Graphics.PixmapFormat;
import com.badlogic.androidgames.framework.Input.KeyEvent;
import com.badlogic.androidgames.framework.Input.TouchEvent;
class TestScreen extends Screen {
long startTime = System.nanoTime();
int frames;
Pixmap bob;
Pixmap bobAlpha;
Sound sound;
Music music;
public TestScreen(Game game) {
super(game);
bob = game.getGraphics().newPixmap("bobrgb888.png", PixmapFormat.RGB565);
bobAlpha = game.getGraphics().newPixmap("bobargb8888.png", PixmapFormat.ARGB4444);
music = game.getAudio().newMusic("music.ogg");
music.setLooping(true);
music.setVolume(0.5f);
music.play();
sound = game.getAudio().newSound("music.ogg");
try {
BufferedReader in = new BufferedReader(new InputStreamReader(game.getFileIO().readAsset("test.txt")));
String text = in.readLine();
in.close();
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(game.getFileIO().writeFile("test.txt")));
out.write("This is a freaking test");
out.close();
in = new BufferedReader(new InputStreamReader(game.getFileIO().readFile("test.txt")));
String text2 = in.readLine();
in.close();
Log.d("MrNom", text + ", " + text2 );
} catch(Exception ex) {
ex.printStackTrace();
}
}
@Override
public void update(float deltaTime) {
}
@Override
public void present(float deltaTime) {
Graphics g = game.getGraphics();
Input inp = game.getInput();
g.clear(Color.RED);
g.drawLine(0,0,320, 480, Color.BLUE);
g.drawRect(20,20,100,100, Color.GREEN);
g.drawPixmap(bob, 100, 100);
g.drawPixmap(bobAlpha, 100, 200);
g.drawPixmap(bob, 200, 200, 0, 0, 64, 64);
for(int i=0; i < 2; i++) {
if(inp.isTouchDown(i)) {
g.drawPixmap(bob, inp.getTouchX(i), inp.getTouchY(i), 0, 0, 64, 64);
}
}
g.drawPixmap(bob, (int)(inp.getAccelX() * 10) + 160 - 16, (int)(inp.getAccelY() * 10) + 240 - 16, 0, 0, 32, 32 );
List<KeyEvent> keyEvents = inp.getKeyEvents();
int len = keyEvents.size();
for(int i = 0; i < len; i++) {
Log.d("MrNom", keyEvents.get(i).toString());
}
List<TouchEvent> touchEvents = inp.getTouchEvents();
len = touchEvents.size();
for(int i = 0; i < len; i++) {
Log.d("MrNom", touchEvents.get(i).toString());
if(touchEvents.get(i).type == TouchEvent.TOUCH_UP)
sound.play(1);
}
frames++;
if(System.nanoTime() - startTime > 1000000000l) {
Log.d("MrNom", "fps: " + frames + ", delta: " + deltaTime);
frames = 0;
startTime = System.nanoTime();
}
}
@Override
public void pause() {
Log.d("MrNom", "pause");
}
@Override
public void resume() {
Log.d("MrNom", "resume");
}
@Override
public void dispose() {
Log.d("MrNom", "dispose");
music.dispose();
}
}
package com.badlogic.androidgames.framework.impl;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.view.Window;
import android.view.WindowManager;
import com.badlogic.androidgames.framework.Audio;
import com.badlogic.androidgames.framework.FileIO;
import com.badlogic.androidgames.framework.Game;
import com.badlogic.androidgames.framework.Graphics;
import com.badlogic.androidgames.framework.Input;
import com.badlogic.androidgames.framework.Screen;
public abstract class AndroidGame extends Activity implements Game {
AndroidFastRenderView renderView;
Graphics graphics;
Audio audio;
Input input;
FileIO fileIO;
Screen screen;
WakeLock wakeLock;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
int frameBufferWidth = isLandscape ? 480 : 320;
int frameBufferHeight = isLandscape ? 320 : 480;
Bitmap frameBuffer = Bitmap.createBitmap(frameBufferWidth,
frameBufferHeight, Config.RGB_565);
float scaleX = (float) frameBufferWidth
/ getWindowManager().getDefaultDisplay().getWidth();
float scaleY = (float) frameBufferHeight
/ getWindowManager().getDefaultDisplay().getHeight();
renderView = new AndroidFastRenderView(this, frameBuffer);
graphics = new AndroidGraphics(getAssets(), frameBuffer);
fileIO = new AndroidFileIO(getAssets());
audio = new AndroidAudio(this);
input = new AndroidInput(this, renderView, scaleX, scaleY);
screen = getStartScreen();
setContentView(renderView);
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GLGame");
}
@Override
public void onResume() {
super.onResume();
wakeLock.acquire();
screen.resume();
renderView.resume();
}
@Override
public void onPause() {
super.onPause();
wakeLock.release();
renderView.pause();
screen.pause();
if (isFinishing())
screen.dispose();
}
@Override
public Input getInput() {
return input;
}
@Override
public FileIO getFileIO() {
return fileIO;
}
@Override
public Graphics getGraphics() {
return graphics;
}
@Override
public Audio getAudio() {
return audio;
}
@Override
public void setScreen(Screen screen) {
if (screen == null)
throw new IllegalArgumentException("Screen must not be null");
this.screen.pause();
this.screen.dispose();
screen.resume();
screen.update(0);
this.screen = screen;
}
public Screen getCurrentScreen() {
return screen;
}
}
4.游戏类AndroidGame用到了快速重绘视图类AndroidFastRenderView
package com.badlogic.androidgames.framework.impl;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class AndroidFastRenderView extends SurfaceView implements Runnable {
AndroidGame game;
Bitmap framebuffer;
Thread renderThread = null;
SurfaceHolder holder;
volatile boolean running = false;
public AndroidFastRenderView(AndroidGame game, Bitmap framebuffer) {
super(game);
this.game = game;
this.framebuffer = framebuffer;
this.holder = getHolder();
}
public void resume() {
running = true;
renderThread = new Thread(this);
renderThread.start();
}
public void run() {
Rect dstRect = new Rect();
long startTime = System.nanoTime();
while(running) {
if(!holder.getSurface().isValid())
continue;
float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f;
startTime = System.nanoTime();
game.getCurrentScreen().update(deltaTime);
game.getCurrentScreen().present(deltaTime);
Canvas canvas = holder.lockCanvas();
canvas.getClipBounds(dstRect);
canvas.drawBitmap(framebuffer, null, dstRect, null);
holder.unlockCanvasAndPost(canvas);
}
}
public void pause() {
running = false;
while(true) {
try {
renderThread.join();
break;
} catch (InterruptedException e) {
// retry
}
}
}
}
5. 另外基于openGL ES的游戏类GLGame(之前的游戏类AndroidGame是基于普通绘图类Graphics)
package com.badlogic.androidgames.framework.impl;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.app.Activity;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.view.Window;
import android.view.WindowManager;
import com.badlogic.androidgames.framework.Audio;
import com.badlogic.androidgames.framework.FileIO;
import com.badlogic.androidgames.framework.Game;
import com.badlogic.androidgames.framework.Graphics;
import com.badlogic.androidgames.framework.Input;
import com.badlogic.androidgames.framework.Screen;
public abstract class GLGame extends Activity implements Game, Renderer {
enum GLGameState {
Initialized,
Running,
Paused,
Finished,
Idle
}
GLSurfaceView glView;
GLGraphics glGraphics;
Audio audio;
Input input;
FileIO fileIO;
Screen screen;
GLGameState state = GLGameState.Initialized;
Object stateChanged = new Object();
long startTime = System.nanoTime();
WakeLock wakeLock;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
glView = new GLSurfaceView(this);
glView.setRenderer(this);
setContentView(glView);
glGraphics = new GLGraphics(glView);
fileIO = new AndroidFileIO(getAssets());
audio = new AndroidAudio(this);
input = new AndroidInput(this, glView, 1, 1);
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GLGame");
}
public void onResume() {
super.onResume();
glView.onResume();
wakeLock.acquire();
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
glGraphics.setGL(gl);
synchronized(stateChanged) {
if(state == GLGameState.Initialized)
screen = getStartScreen();
state = GLGameState.Running;
screen.resume();
startTime = System.nanoTime();
}
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
}
@Override
public void onDrawFrame(GL10 gl) {
GLGameState state = null;
synchronized(stateChanged) {
state = this.state;
}
if(state == GLGameState.Running) {
float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f;
startTime = System.nanoTime();
screen.update(deltaTime);
screen.present(deltaTime);
}
if(state == GLGameState.Paused) {
screen.pause();
synchronized(stateChanged) {
this.state = GLGameState.Idle;
stateChanged.notifyAll();
}
}
if(state == GLGameState.Finished) {
screen.pause();
screen.dispose();
synchronized(stateChanged) {
this.state = GLGameState.Idle;
stateChanged.notifyAll();
}
}
}
@Override
public void onPause() {
synchronized(stateChanged) {
if(isFinishing())
state = GLGameState.Finished;
else
state = GLGameState.Paused;
while(true) {
try {
stateChanged.wait();
break;
} catch(InterruptedException e) {
}
}
}
wakeLock.release();
glView.onPause();
super.onPause();
}
public GLGraphics getGLGraphics() {
return glGraphics;
}
@Override
public Input getInput() {
return input;
}
@Override
public FileIO getFileIO() {
return fileIO;
}
@Override
public Graphics getGraphics() {
throw new IllegalStateException("We are using OpenGL!");
}
@Override
public Audio getAudio() {
return audio;
}
@Override
public void setScreen(Screen screen) {
if (screen == null)
throw new IllegalArgumentException("Screen must not be null");
this.screen.pause();
this.screen.dispose();
screen.resume();
screen.update(0);
this.screen = screen;
}
@Override
public Screen getCurrentScreen() {
return screen;
}
}
本文浅述了“beginning-android-games”书本的相关基础知识和相关类的设计分析,
书本和源码:http://download.csdn.net/detail/yangzhenping/8420261
本篇中的类实现来自“beginning-android-games\ch07-gl-basics”