Hi
Friends,
Welcome you all in the Android Game
Development Part 5.
In
the Part4 we were setting up the Game Architecture. We have created the
interfaces. In this part5 we will continue our Game Architecture setup.
Setting
Up the Game Architecture:-
In the previous
part we have completed till step 6. We will continue from there.
7)
Right click
on the src directory , New à Package.
Give the package name com.pocogame.framework.implementation
8)
Right click
on the package com.pocogame.framework.implementation and create the following
classes.
a. AndroidAudio.java
b. AndroidFileIO.java
c. AndroidGame.java
d. AndroidGraphics.java
e. AndroidImage.java
f. AndroidInput.java
g. AndroidMusic.java
h. AndroidSound.java
i.
AndoirdView.java
j. MultiTouchHandler.java
k. SingleTouchHandler.java
l.
TouchHandler.java
9)
Copy and
paste following codes into the respective java file.
a. /************ AndroidAudio.java ******************/
package com.pocogame.framework.implementation; import java.io.IOException; import android.app.Activity; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.media.AudioManager; import android.media.SoundPool; import com.pocogame.framework.Audio; import com.pocogame.framework.Music; import com.pocogame.framework.Sound; public class AndroidAudio implements Audio { AssetManager assets; SoundPool soundPool; public AndroidAudio(Activity activity) { activity.setVolumeControlStream(AudioManager.STREAM_MUSIC); this.assets = activity.getAssets(); this.soundPool = new SoundPool(20, AudioManager.STREAM_MUSIC, 0); } @Override public Music createMusic(String filename) { try { AssetFileDescriptor assetDescriptor = assets.openFd(filename); return new AndroidMusic(assetDescriptor); } catch (IOException e) { throw new RuntimeException("Couldn't load music '" + filename + "'"); } } @Override public Sound createSound(String filename) { try { AssetFileDescriptor assetDescriptor = assets.openFd(filename); int soundId = soundPool.load(assetDescriptor, 0); return new AndroidSound(soundPool, soundId); } catch (IOException e) { throw new RuntimeException("Couldn't load sound '" + filename + "'"); } } }
b. /************ AndroidFileIO.java ******************/
package com.pocogame.framework.implementation; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import android.content.Context; import android.content.SharedPreferences; import android.content.res.AssetManager; import android.os.Environment; import android.preference.PreferenceManager; import com.pocogame.framework.FileIO; public class AndroidFileIO implements FileIO { Context context; AssetManager assets; String externalStoragePath; public AndroidFileIO(Context context) { this.context = context; this.assets = context.getAssets(); this.externalStoragePath = Environment.getExternalStorageDirectory() .getAbsolutePath() + File.separator; } @Override public InputStream readAsset(String file) throws IOException { return assets.open(file); } @Override public InputStream readFile(String file) throws IOException { return new FileInputStream(externalStoragePath + file); } @Override public OutputStream writeFile(String file) throws IOException { return new FileOutputStream(externalStoragePath + file); } public SharedPreferences getSharedPref() { return PreferenceManager.getDefaultSharedPreferences(context); } }
c. /************ AndroidGame.java ******************/
package com.pocogame.framework.implementation; 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.pocogame.framework.Audio; import com.pocogame.framework.FileIO; import com.pocogame.framework.Game; import com.pocogame.framework.Graphics; import com.pocogame.framework.Input; import com.pocogame.framework.Screen; public abstract class AndroidGame extends Activity implements Game { AndroidView 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 isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; int frameBufferWidth = isPortrait ? 480: 800; int frameBufferHeight = isPortrait ? 800: 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 AndroidView(this, frameBuffer); graphics = new AndroidGraphics(getAssets(), frameBuffer); fileIO = new AndroidFileIO(this); audio = new AndroidAudio(this); input = new AndroidInput(this, renderView, scaleX, scaleY); screen = getInitScreen(); setContentView(renderView); PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "MyGame"); } @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; } }
d. /************ AndroidGraphics.java ******************/
package com.pocogame.framework.implementation; import java.io.IOException; import java.io.InputStream; import android.content.res.AssetManager; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; import com.pocogame.framework.Graphics; import com.pocogame.framework.Image; public class AndroidGraphics implements Graphics { AssetManager assets; Bitmap frameBuffer; Canvas canvas; Paint paint; Rect srcRect = new Rect(); Rect dstRect = new Rect(); public AndroidGraphics(AssetManager assets, Bitmap frameBuffer) { this.assets = assets; this.frameBuffer = frameBuffer; this.canvas = new Canvas(frameBuffer); this.paint = new Paint(); } @Override public Image newImage(String fileName, ImageFormat format) { Config config = null; if (format == ImageFormat.RGB565) config = Config.RGB_565; else if (format == ImageFormat.ARGB4444) config = Config.ARGB_4444; else config = Config.ARGB_8888; Options options = new Options(); options.inPreferredConfig = config; InputStream in = null; Bitmap bitmap = null; try { in = assets.open(fileName); bitmap = BitmapFactory.decodeStream(in, null, options); if (bitmap == null) throw new RuntimeException("Couldn't load bitmap from asset '" + fileName + "'"); } catch (IOException e) { throw new RuntimeException("Couldn't load bitmap from asset '" + fileName + "'"); } finally { if (in != null) { try { in.close(); } catch (IOException e) { } } } if (bitmap.getConfig() == Config.RGB_565) format = ImageFormat.RGB565; else if (bitmap.getConfig() == Config.ARGB_4444) format = ImageFormat.ARGB4444; else format = ImageFormat.ARGB8888; return new AndroidImage(bitmap, format); } @Override public void clearScreen(int color) { canvas.drawRGB((color & 0xff0000) >> 16, (color & 0xff00) >> 8, (color & 0xff)); } @Override public void drawLine(int x, int y, int x2, int y2, int color) { paint.setColor(color); canvas.drawLine(x, y, x2, y2, paint); } @Override public void drawRect(int x, int y, int width, int height, int color) { paint.setColor(color); paint.setStyle(Style.FILL); canvas.drawRect(x, y, x + width - 1, y + height - 1, paint); } @Override public void drawARGB(int a, int r, int g, int b) { paint.setStyle(Style.FILL); canvas.drawARGB(a, r, g, b); } @Override public void drawString(String text, int x, int y, Paint paint){ canvas.drawText(text, x, y, paint); } public void drawImage(Image Image, int x, int y, int srcX, int srcY, int srcWidth, int srcHeight) { srcRect.left = srcX; srcRect.top = srcY; srcRect.right = srcX + srcWidth; srcRect.bottom = srcY + srcHeight; dstRect.left = x; dstRect.top = y; dstRect.right = x + srcWidth; dstRect.bottom = y + srcHeight; canvas.drawBitmap(((AndroidImage) Image).bitmap, srcRect, dstRect, null); } @Override public void drawImage(Image Image, int x, int y) { canvas.drawBitmap(((AndroidImage)Image).bitmap, x, y, null); } public void drawScaledImage(Image Image, int x, int y, int width, int height, int srcX, int srcY, int srcWidth, int srcHeight){ srcRect.left = srcX; srcRect.top = srcY; srcRect.right = srcX + srcWidth; srcRect.bottom = srcY + srcHeight; dstRect.left = x; dstRect.top = y; dstRect.right = x + width; dstRect.bottom = y + height; canvas.drawBitmap(((AndroidImage) Image).bitmap, srcRect, dstRect, null); } @Override public int getWidth() { return frameBuffer.getWidth(); } @Override public int getHeight() { return frameBuffer.getHeight(); } }
e. /************ AndroidImage.java ******************/
package com.pocogame.framework.implementation; import android.graphics.Bitmap; import com.pocogame.framework.Image; import com.pocogame.framework.Graphics.ImageFormat; public class AndroidImage implements Image { Bitmap bitmap; ImageFormat format; public AndroidImage(Bitmap bitmap, ImageFormat format) { this.bitmap = bitmap; this.format = format; } @Override public int getWidth() { return bitmap.getWidth(); } @Override public int getHeight() { return bitmap.getHeight(); } @Override public ImageFormat getFormat() { return format; } @Override public void dispose() { bitmap.recycle(); } }
f. /************ AndroidInput.java ******************/
package com.pocogame.framework.implementation; import java.util.List; import android.content.Context; import android.os.Build.VERSION; import android.view.View; import com.pocogame.framework.Input; public class AndroidInput implements Input { TouchHandler touchHandler; public AndroidInput(Context context, View view, float scaleX, float scaleY) { if(Integer.parseInt(VERSION.SDK) < 5) touchHandler = new SingleTouchHandler(view, scaleX, scaleY); else touchHandler = new MultiTouchHandler(view, scaleX, scaleY); } @Override public boolean isTouchDown(int pointer) { return touchHandler.isTouchDown(pointer); } @Override public int getTouchX(int pointer) { return touchHandler.getTouchX(pointer); } @Override public int getTouchY(int pointer) { return touchHandler.getTouchY(pointer); } @Override public ListgetTouchEvents() { return touchHandler.getTouchEvents(); } } /* */
g. /************ AndroidMusic.java ******************/
package com.pocogame.framework.implementation; import java.io.IOException; import android.content.res.AssetFileDescriptor; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnPreparedListener; import android.media.MediaPlayer.OnSeekCompleteListener; import android.media.MediaPlayer.OnVideoSizeChangedListener; import com.pocogame.framework.Music; public class AndroidMusic implements Music, OnCompletionListener, OnSeekCompleteListener, OnPreparedListener, OnVideoSizeChangedListener { MediaPlayer mediaPlayer; boolean isPrepared = false; public AndroidMusic(AssetFileDescriptor assetDescriptor) { mediaPlayer = new MediaPlayer(); try { mediaPlayer.setDataSource(assetDescriptor.getFileDescriptor(), assetDescriptor.getStartOffset(), assetDescriptor.getLength()); mediaPlayer.prepare(); isPrepared = true; mediaPlayer.setOnCompletionListener(this); mediaPlayer.setOnSeekCompleteListener(this); mediaPlayer.setOnPreparedListener(this); mediaPlayer.setOnVideoSizeChangedListener(this); } catch (Exception e) { throw new RuntimeException("Couldn't load music"); } } @Override public void dispose() { if (this.mediaPlayer.isPlaying()){ this.mediaPlayer.stop(); } this.mediaPlayer.release(); } @Override public boolean isLooping() { return mediaPlayer.isLooping(); } @Override public boolean isPlaying() { return this.mediaPlayer.isPlaying(); } @Override public boolean isStopped() { return !isPrepared; } @Override public void pause() { if (this.mediaPlayer.isPlaying()) mediaPlayer.pause(); } @Override public void play() { if (this.mediaPlayer.isPlaying()) return; try { synchronized (this) { if (!isPrepared) mediaPlayer.prepare(); mediaPlayer.start(); } } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } @Override public void setLooping(boolean isLooping) { mediaPlayer.setLooping(isLooping); } @Override public void setVolume(float volume) { mediaPlayer.setVolume(volume, volume); } @Override public void stop() { if (this.mediaPlayer.isPlaying() == true){ this.mediaPlayer.stop(); synchronized (this) { isPrepared = false; }} } @Override public void onCompletion(MediaPlayer player) { synchronized (this) { isPrepared = false; } } @Override public void seekBegin() { mediaPlayer.seekTo(0); } @Override public void onPrepared(MediaPlayer player) { // TODO Auto-generated method stub synchronized (this) { isPrepared = true; } } @Override public void onSeekComplete(MediaPlayer player) { // TODO Auto-generated method stub } @Override public void onVideoSizeChanged(MediaPlayer player, int width, int height) { // TODO Auto-generated method stub } }
h. /************ AndroidSound.java ******************/
package com.pocogame.framework.implementation; import android.media.SoundPool; import com.pocogame.framework.Sound; public class AndroidSound implements Sound { int soundId; SoundPool soundPool; public AndroidSound(SoundPool soundPool, int soundId) { this.soundId = soundId; this.soundPool = soundPool; } @Override public void play(float volume) { soundPool.play(soundId, volume, volume, 0, 0, 1); } @Override public void dispose() { soundPool.unload(soundId); } }
i.
/************
AndoirdView.java ******************/
package com.pocogame.framework.implementation; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; import android.view.SurfaceHolder; import android.view.SurfaceView; public class AndroidView extends SurfaceView implements Runnable { AndroidGame game; Bitmap framebuffer; Thread renderThread = null; SurfaceHolder holder; volatile boolean running = false; public AndroidView(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) / 10000000.000f; startTime = System.nanoTime(); if (deltaTime > 3.15){ deltaTime = (float) 3.15; } game.getCurrentScreen().update(deltaTime); game.getCurrentScreen().paint(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 } } } }
j. /************ MultiTouchHandler.java ******************/
package com.pocogame.framework.implementation; import java.util.ArrayList; import java.util.List; import android.view.MotionEvent; import android.view.View; import com.pocogame.framework.Pool; import com.pocogame.framework.Input.TouchEvent; import com.pocogame.framework.Pool.PoolObjectFactory; public class MultiTouchHandler implements TouchHandler { private static final int MAX_TOUCHPOINTS = 10; boolean[] isTouched = new boolean[MAX_TOUCHPOINTS]; int[] touchX = new int[MAX_TOUCHPOINTS]; int[] touchY = new int[MAX_TOUCHPOINTS]; int[] id = new int[MAX_TOUCHPOINTS]; PooltouchEventPool; List */touchEvents = new ArrayList (); List touchEventsBuffer = new ArrayList (); float scaleX; float scaleY; public MultiTouchHandler(View view, float scaleX, float scaleY) { PoolObjectFactory factory = new PoolObjectFactory () { @Override public TouchEvent createObject() { return new TouchEvent(); } }; touchEventPool = new Pool (factory, 100); view.setOnTouchListener(this); this.scaleX = scaleX; this.scaleY = scaleY; } @Override public boolean onTouch(View v, MotionEvent event) { synchronized (this) { int action = event.getAction() & MotionEvent.ACTION_MASK; int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; int pointerCount = event.getPointerCount(); TouchEvent touchEvent; for (int i = 0; i < MAX_TOUCHPOINTS; i++) { if (i >= pointerCount) { isTouched[i] = false; id[i] = -1; continue; } int pointerId = event.getPointerId(i); if (event.getAction() != MotionEvent.ACTION_MOVE && i != pointerIndex) { // if it's an up/down/cancel/out event, mask the id to see if we should process it for this touch // point continue; } switch (action) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_DOWN: touchEvent = touchEventPool.newObject(); touchEvent.type = TouchEvent.TOUCH_DOWN; touchEvent.pointer = pointerId; touchEvent.x = touchX[i] = (int) (event.getX(i) * scaleX); touchEvent.y = touchY[i] = (int) (event.getY(i) * scaleY); isTouched[i] = true; id[i] = pointerId; touchEventsBuffer.add(touchEvent); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_CANCEL: touchEvent = touchEventPool.newObject(); touchEvent.type = TouchEvent.TOUCH_UP; touchEvent.pointer = pointerId; touchEvent.x = touchX[i] = (int) (event.getX(i) * scaleX); touchEvent.y = touchY[i] = (int) (event.getY(i) * scaleY); isTouched[i] = false; id[i] = -1; touchEventsBuffer.add(touchEvent); break; case MotionEvent.ACTION_MOVE: touchEvent = touchEventPool.newObject(); touchEvent.type = TouchEvent.TOUCH_DRAGGED; touchEvent.pointer = pointerId; touchEvent.x = touchX[i] = (int) (event.getX(i) * scaleX); touchEvent.y = touchY[i] = (int) (event.getY(i) * scaleY); isTouched[i] = true; id[i] = pointerId; touchEventsBuffer.add(touchEvent); break; } } return true; } } @Override public boolean isTouchDown(int pointer) { synchronized (this) { int index = getIndex(pointer); if (index < 0 || index >= MAX_TOUCHPOINTS) return false; else return isTouched[index]; } } @Override public int getTouchX(int pointer) { synchronized (this) { int index = getIndex(pointer); if (index < 0 || index >= MAX_TOUCHPOINTS) return 0; else return touchX[index]; } } @Override public int getTouchY(int pointer) { synchronized (this) { int index = getIndex(pointer); if (index < 0 || index >= MAX_TOUCHPOINTS) return 0; else return touchY[index]; } } @Override public List getTouchEvents() { synchronized (this) { int len = touchEvents.size(); for (int i = 0; i < len; i++) touchEventPool.free(touchEvents.get(i)); touchEvents.clear(); touchEvents.addAll(touchEventsBuffer); touchEventsBuffer.clear(); return touchEvents; } } // returns the index for a given pointerId or -1 if no index. private int getIndex(int pointerId) { for (int i = 0; i < MAX_TOUCHPOINTS; i++) { if (id[i] == pointerId) { return i; } } return -1; } } /*
k. /************ SingleTouchHandler.java ******************/
package com.pocogame.framework.implementation; import java.util.ArrayList; import java.util.List; import android.view.MotionEvent; import android.view.View; import com.pocogame.framework.Pool; import com.pocogame.framework.Input.TouchEvent; import com.pocogame.framework.Pool.PoolObjectFactory; public class SingleTouchHandler implements TouchHandler { boolean isTouched; int touchX; int touchY; PooltouchEventPool; List */touchEvents = new ArrayList (); List touchEventsBuffer = new ArrayList (); float scaleX; float scaleY; public SingleTouchHandler(View view, float scaleX, float scaleY) { PoolObjectFactory factory = new PoolObjectFactory () { @Override public TouchEvent createObject() { return new TouchEvent(); } }; touchEventPool = new Pool (factory, 100); view.setOnTouchListener(this); this.scaleX = scaleX; this.scaleY = scaleY; } @Override public boolean onTouch(View v, MotionEvent event) { synchronized(this) { TouchEvent touchEvent = touchEventPool.newObject(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: touchEvent.type = TouchEvent.TOUCH_DOWN; isTouched = true; break; case MotionEvent.ACTION_MOVE: touchEvent.type = TouchEvent.TOUCH_DRAGGED; isTouched = true; break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: touchEvent.type = TouchEvent.TOUCH_UP; isTouched = false; break; } touchEvent.x = touchX = (int)(event.getX() * scaleX); touchEvent.y = touchY = (int)(event.getY() * scaleY); touchEventsBuffer.add(touchEvent); return true; } } @Override public boolean isTouchDown(int pointer) { synchronized(this) { if(pointer == 0) return isTouched; else return false; } } @Override public int getTouchX(int pointer) { synchronized(this) { return touchX; } } @Override public int getTouchY(int pointer) { synchronized(this) { return touchY; } } @Override public List getTouchEvents() { synchronized(this) { int len = touchEvents.size(); for( int i = 0; i < len; i++ ) touchEventPool.free(touchEvents.get(i)); touchEvents.clear(); touchEvents.addAll(touchEventsBuffer); touchEventsBuffer.clear(); return touchEvents; } } } /*
l.
/************
TouchHandler.java ******************/
package com.pocogame.framework.implementation; import java.util.List; import android.view.View.OnTouchListener; import com.pocogame.framework.Input.TouchEvent; public interface TouchHandler extends OnTouchListener { public boolean isTouchDown(int pointer); public int getTouchX(int pointer); public int getTouchY(int pointer); public ListgetTouchEvents(); } /* */
In the next part we
are going to see how we can re-use our old java game code for android game
development.
Thanks for the tutorial waiting for the next....hope will publish soon
ReplyDeleteAndroidFileIO.java
ReplyDeletemethod SharedPreferences getSharedPref() --> is wrong name;
please check again your codes in your blog, there are many junk code appears.
But, it's good job. :)
Thanks +Habibie Ed Dien, but this time i am not agree with you.
DeleteI have crossed checked with my code backups, it is correct.
If you still facing any issues , please send me the screenshot of your error i will try to resolve it
Thanks for your valuable comments