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”