
Opa ! apos algumas idas e vindas no codigo, as coisas começam a se arrumar hehehehe, nesta versão já esta resolvida a exibição de imagem a partir do nome da imagem, e execução de som no formato ogg, tanto no loop como um som a partir de um evento do usuário.
Apesar da recomendação do SDK seja de acessar os arquivos de midia através do resource ID criado na classe R, eu estou construindo as classes usando o nome do resource para poder expandir a programação do jogo para uma lingaguem de script, e usar o java como uma camada de abstração do jogo.
Vamos ao codigo e alguns comentarios sobre cada um :
GameActivity - ganhou uma nova chamada para determinar que volume o jogo vai usar, setVolumeControlStream(AudioManager.STREAM_MUSIC) assim o jogo fica com o mesmo volume que estiver acertado para a musica, alem disto o metodo onDestroy() chama o novo metodo release() da view que vai se encarregar de liberar recursos e em especial mandar os audios se calarem
package com.athanazio.android.showimage;
import android.app.Activity;
import android.media.AudioManager;
import android.os.Bundle;
import android.view.Window;
public class GameActivity extends Activity {
private GameView view;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
view = new GameView(this, getResources());
setContentView(view);
}
protected void onDestroy() {
super.onDestroy();
view.release();
}
}
GameView – usa as classes Sprite e Audio para exibir imagens e tocar audio, poucas mudanças comparando com a versão anterior.
package com.athanazio.android.showimage;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
public class GameView extends View implements Runnable {
private static final String TAG = "GAME VIEW";
private static final int INTERVAL = 10;
private Paint cPaint;
private boolean running = true;
FPSCounter fps;
private Sprite background;
private Sprite cloud;
private Audio soundtrack;
private Audio touch;
public GameView(Context context, Resources resources) {
super(context);
String packageName = getClass().getPackage().getName();
background = new Sprite(resources, packageName, "background");
cloud = new Sprite(resources, packageName, "cloud");
cloud.setCenterAtMiddle();
soundtrack = new Audio(resources, packageName, "soundtrack");
touch = new Audio(resources, packageName, "cowbell");
soundtrack.setLooping(true);
soundtrack.play();
cPaint = new Paint();
setFocusable(true);
setClickable(true);
setLongClickable(true);
fps = new FPSCounter();
// Set the background
this.setBackgroundColor(Color.WHITE);
Log.i(TAG, "game view created");
Thread monitorThread = new Thread(this);
monitorThread.setPriority(Thread.MIN_PRIORITY);
monitorThread.start();
}
public void draw(Canvas canvas) {
super.draw(canvas);
background.draw(canvas);
cloud.draw(canvas);
cPaint.setColor(Color.WHITE);
canvas.drawText(fps.getFPS(), 20, 20, cPaint);
}
public boolean onKeyUp(int keyCode, KeyEvent event) {
return super.onKeyUp(keyCode, event);
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
boolean handled = false;
Log.i(TAG, "key down");
float x = cloud.getX();
float y = cloud.getY();
if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
y = y - 5;
handled = true;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
y = y + 5;
handled = true;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
x = x - 5;
handled = true;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
x = x + 5;
handled = true;
}
cloud.move(x, y);
return handled;
}
public boolean onTouchEvent(MotionEvent event) {
cloud.setX(event.getRawX());
cloud.setY(event.getRawY());
Log.i(TAG, "on touch");
touch.play();
return super.onTouchEvent(event);
}
public void run() {
while (running) {
try {
Thread.sleep(INTERVAL);
} catch (InterruptedException e) {
Log.e(TAG, "main loop finished");
}
update();
postInvalidate();
}
}
private void update() {
fps.update();
}
public void release() {
running = false;
soundtrack.release();
touch.release();
}
}
Sprite – esta eh responsavel por exibir imagens na tela, uma atenção especial deve ser dada ao recurso de determinar o centro da imagem, isto eh muito util para responder eventos de mouse, e por exemplo posicionar a imagem centralizada onde o usuário clicou, o método setCenterAtMiddle() calcula a posição para desenhar baseado na largura e altura da imagem. e por default o centro da imagem eh no 0,0.
package com.athanazio.android.showimage;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.Log;
public class Sprite {
private static final String TAG = "GAME SPRITE";
private Bitmap bitmap;
private float x;
private float y;
private float centerX;
private float centerY;
private float drawX;
private float drawY;
private Paint paint;
public Sprite(Resources resources, String packageName, String name) {
String logFileName = packageName + " " + name;
Log.i(TAG, "loading image : " + logFileName);
int id = resources.getIdentifier(name, "drawable", packageName);
bitmap = BitmapFactory.decodeResource(resources, id);
centerX = 0;
centerY = 0;
setX(0);
setY(0);
this.paint = new Paint();
}
public void draw(Canvas canvas) {
canvas.drawBitmap(bitmap, drawX, drawY, paint);
}
public void move(float x, float y) {
setX(this.x + x);
setY(this.y + y);
}
public void setCenterAtMiddle(){
centerX = bitmap.getWidth() / 2F;
centerY = bitmap.getHeight() / 2F;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
drawX = this.x - centerX;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
drawY = this.y - centerY;
}
}
Audio – esta classe esconde a criação de um MediaPlayer para cada som que se deseja tocar, imagino que o melhor cenário seria ter uma classe para som em loop que usaria um MediaPlayer, e outra classe para efeitos sonoros que tocaria no maximo N efeitos ao mesmo tempo, mantendo menos objetos MediaPlayer na memória.
Mas a implementação atual mantém um MediaPlayer para cada som, observar que o resource ID é recuperado pelo nome do arquivo usando o metodo resources.getIdentifier(name, “raw”, packageName) e a partir do resource ID podemos recuperar o AssetFileDescriptor com a chamada resources.openRawResourceFd(id), of file descriptor é usado para inicializar o MediaPlayer com os detalhes necessários a execução do som.
package com.athanazio.android.showimage;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.media.MediaPlayer;
import android.util.Log;
/**
* Audio resources abstraction
*
* @author athanazio
* @see "http://developer.android.com/intl/fr/guide/topics/media/index.html"
* @see "http://developer.android.com/intl/fr/reference/android/media/MediaPlayer.html#Valid_and_Invalid_States"
*/
public class Audio {
private static final String TAG = "GAME AUDIO";
private MediaPlayer player;
private boolean ready;
private int id;
private AssetFileDescriptor afd;
/**
*
* @param resources
* @param context
* @param packageName
* @param name
*
*/
public Audio(Resources resources, String packageName, String name) {
String logFileName = packageName + " " + name;
Log.i(TAG, "loading audio : " + logFileName);
ready = false;
player = new MediaPlayer();
try {
this.id = resources.getIdentifier(name, "raw", packageName);
this.afd = resources.openRawResourceFd(id);
Log.i(TAG, "loading audio : " + logFileName + " id:" + id);
player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
player.setLooping(false);
player.prepare();
// reset the player after completion
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
try {
player.reset();
player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
player.prepare();
} catch (Exception e) {
Log.e(TAG, "error resetting the media player");
e.printStackTrace();
}
}
});
ready = true;
} catch (Exception e) {
Log.e(TAG, "ERROR loading audio : " + logFileName);
e.printStackTrace();
}
}
public void play() {
if (ready) {
player.start();
} else {
Log.i(TAG, "the player is not ready.");
}
}
public void setLooping(boolean b) {
player.setLooping(b);
}
public void release() {
player.stop();
player.release();
}
}
