Neste tutorial faremos os últimos ajustes no nosso jogo antes de publica-lo no Google Play (irei mostrar como em outro tutorial). Iremos adicionar uma música ambiente, sons de interação com os Pinks e Golds, sons de vitória ao passar de nível e som de derrota ao dar Game Over. Também iremos salvar o High Score, ou seja, a maior pontuação em todo o jogo, assim se cria um desafio a ser passado toda vez que o jogador jogar o jogo.
Acompanhe o desenvolvimento por aqui ou baixe o código usado aqui ou o .apk aqui.
Primeiro vamos adicionar os sons e músicas. Baixe-os aqui, e extraia a pasta raw em res (todo aquivo de som deve ficar na pasta res/raw). Dê um refresh no seu projeto e você já poderá utilizar os sons como recursos. Para maiores informações sobre músicas e sons no Android acesse esse tutorial: Manipulando música e sons no Android
Abra o MainGameView e adicione os seguintes atributos (em negrito) a classe:
...
private int alivePinks; |
public MediaPlayer musica; |
public SoundPool sound; |
private int soundIdPinkHit, soundIdGoldHit, soundIdWin; |
public MainGameView(Context context) { |
...
Dado esses atributos precisamos instancia-los e carrega-los. No onLoad adicione isso:
...
newStage(); |
musica = MediaPlayer.create(context, R.raw.background_music); |
musica.setLooping(true); |
musica.start(); |
sound = new SoundPool(4, AudioManager.STREAM_MUSIC, 0); |
soundIdPinkHit = sound.load(context, R.raw.pink_hit, 1); |
soundIdGoldHit = sound.load(context, R.raw.gold_hit, 1); |
soundIdWin = sound.load(context, R.raw.win, 1); |
} |
...
Simples, não? Esse código apenas instancia (e manda executar no caso da música) e carrega os sons. Note que se a gente não mandar explicitamente a música parar ela ira executar "para sempre", mesmo após o aplicativo ser fechado. Para evitar isso vamos fazer uma mudança no nosso MainGameActivity:
package com.tutoriandroid.games.smash; |
import android.app.Activity; |
import android.os.Bundle; |
public class MainGameActivity extends Activity { |
private MainGameView gameView; |
@Override |
public void onCreate(Bundle savedInstanceState) { |
super.onCreate(savedInstanceState); |
setContentView(gameView = new MainGameView(this)); |
} |
|
@Override |
protected void onPause() { |
super.onPause(); |
gameView.musica.stop(); |
} |
@Override |
protected void onDestroy() { |
super.onDestroy(); |
gameView.sound.release(); |
} |
@Override |
protected void onResume() { |
super.onResume(); |
if(gameView.musica != null) |
gameView.musica.start(); |
} |
} |
Assim sempre que a aplicação for pausada a música será, e quando a activity for destruída a área de memória usada pelos sons deve ser liberada. Vamos fazer o sons serem tocados quando os eventos ocorrerem. Para isso vamos alterar nosso TouchEvents do MainGameView:
@Override
public void TouchEvents(MotionEvent event) {
if((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN){
if(alivePinks > 0){
float x = event.getX(), y = event.getY();
for(Gold gold : golds)
if(x > gold.x && y > gold.y && x < gold.x + gold.width && y < gold.y + gold.height){
sound.play(soundIdGoldHit, 1f, 1f, 0, 0, 1f);
((Activity) context).finish();
Intent intent = new Intent(context, GameOverActivity.class);
intent.putExtra("SCORE", score);
context.startActivity(intent);
}
for(Pink pink : pinks)
if(!pink.isDead() && x > pink.x && y > pink.y && x < pink.x + pink.width && y < pink.y + pink.height){
sound.play(soundIdPinkHit, 1f, 1f, 0, 0, 1f);
pink.kill();
int add;
score += add = (int) Math.max(100 - level*3 - (System.currentTimeMillis() - startTime)/500, 1);
alivePinks--;
if(alivePinks == 0){
nextLevelSprite.visible = true;
sound.play(soundIdWin, 1f, 1f, 0, 0, 1f);
}
Log.d("Score", "Valeu: " + add);
}
}else{
newStage();
}
}
}
public void TouchEvents(MotionEvent event) {
if((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN){
if(alivePinks > 0){
float x = event.getX(), y = event.getY();
for(Gold gold : golds)
if(x > gold.x && y > gold.y && x < gold.x + gold.width && y < gold.y + gold.height){
sound.play(soundIdGoldHit, 1f, 1f, 0, 0, 1f);
((Activity) context).finish();
Intent intent = new Intent(context, GameOverActivity.class);
intent.putExtra("SCORE", score);
context.startActivity(intent);
}
for(Pink pink : pinks)
if(!pink.isDead() && x > pink.x && y > pink.y && x < pink.x + pink.width && y < pink.y + pink.height){
sound.play(soundIdPinkHit, 1f, 1f, 0, 0, 1f);
pink.kill();
int add;
score += add = (int) Math.max(100 - level*3 - (System.currentTimeMillis() - startTime)/500, 1);
alivePinks--;
if(alivePinks == 0){
nextLevelSprite.visible = true;
sound.play(soundIdWin, 1f, 1f, 0, 0, 1f);
}
Log.d("Score", "Valeu: " + add);
}
}else{
newStage();
}
}
}
Agora toda vez que um Pink for morto ouvirá um som, quando acertar um Gold ouvirá outro e quando passar de nível ouvirá um som de incentivo. Vamos colocar um som de Game Over, no GameOverView em onLoad adicione o seguinte:
...
score = ((Activity) context).getIntent().getIntExtra("SCORE", 0); |
MediaPlayer musica = MediaPlayer.create(context, R.raw.gameover); |
musica.start(); |
} |
...
Pronto. Aqui a gente não precisa se preocupar em fazer a música para pois ela é bastante curta e não possui loop, então eventualmente irá parar.
Agora iremos implementar o High Score, que é bastante simples. Para isso usaremos SharedPreferences, se quiser saber mais sobre acesse este tutorial: Criando uma tela de configurações usando SharedPreferences. Primeiramente queremos que o high score seja exibido na tela inicial, assim o jogador sabe o desafio que o aguarda. Então vamos alterar nosso TitleGameView para guardar um inteiro highScore, carrega-lo e exibi-lo na tela:
...
int highScore;
...
@Override |
protected void onLoad() { |
Resources res = getResources(); |
Sprite title; |
mSprites.add(title = new Sprite(BitmapFactory.decodeResource(res, R.drawable.title))); |
title.x = getWidth()/2 - title.width/2; |
title.y = 20; |
paintText = new Paint(); |
paintText.setColor(Color.WHITE); |
paintText.setTextSize(25); |
highScore = PreferenceManager.getDefaultSharedPreferences(context).getInt("HIGH_SCORE", 0); |
} |
@Override |
protected void onDraw(Canvas canvas) { |
super.onDraw(canvas); |
canvas.drawText(context.getString(R.string.iniciar_jogo), 50, getHeight()*0.6f, paintText); |
canvas.drawText(context.getString(R.string.highscore)+" "+highScore, 40, getHeight()*0.8f, paintText); |
} |
...
Com apenas uma linha de código recuperamos nosso high score e com outra linha desenhamos. Agora vamos para a GameOverView salvar (caso o score tenha sido maior) o novo high score e exibir o ultimo para o jogador:
...
private int score, highScore;
...
@Override |
protected void onLoad() { |
Resources res = getResources(); |
Sprite gameover; |
mSprites.add(gameover = new Sprite(BitmapFactory.decodeResource(res, R.drawable.gameover))); |
gameover.x = getWidth()/2 - gameover.width/2; |
gameover.y = (int) (getHeight()*0.2f); |
paintText = new Paint(); |
paintText.setColor(Color.WHITE); |
paintText.setTextSize(25); |
score = ((Activity) context).getIntent().getIntExtra("SCORE", 0); |
MediaPlayer musica = MediaPlayer.create(context, R.raw.gameover); |
musica.start(); |
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); |
highScore = prefs.getInt("HIGH_SCORE", 0); |
if(score > highScore) |
prefs.edit().putInt("HIGH_SCORE", score).commit(); |
} |
@Override |
protected void onDraw(Canvas canvas) { |
super.onDraw(canvas); |
canvas.drawText(context.getString(R.string.score) + " " + score, 50, getHeight()*0.6f, paintText); |
canvas.drawText(context.getString(R.string.iniciar_jogo), 50, getHeight()*0.82f, paintText); |
canvas.drawText(context.getString(R.string.highscore)+" "+highScore, 50, getHeight()*0.68f, paintText); |
} |
...
Pronto. No onLoad simplesmente pegamos o high score, comparamos com o atual e se for maior salvamos. E mostramos o ultimo high score na tela.
Agora nosso jogo já está "completo". Claro que você pode melhora-lo em vários outros aspectos ainda, mas para fim de tutoriais ele está finalizado. Irei utiliza-lo para mostrar como adicionar AdMob no seu aplicativo e como publicar no Google Play.
Até mais.
16 comentários:
Gostei muito desse tuto!
Mas não sei como fazer pra um Sprite ficar em cima de uma imagem (estilo Super Mario), pode fazer um pequeno tutorial sobre isso? tbm como usar Physics Box2d
Obrigado!
Não entendi sua dúvida, poderia explicar melhor? E essa é a primeira vez que ouço falar do Physics Box2d... Interessante. Valeu!
Physics Box2d, pelo que pesquisei é um sistema de física open source utilizado na maioria dos jogos como Angry Birds.
Quero fazer um personagem(Sprite) andar sobre uma linha da mesma forma q jogos como do Mario, entendeu agora?
Entendi... vou pensar em um tutorial sobre isso. =]
Pronto Henrique, fiz um tutorial sobre exatamente sua dúvida: http://tutoriandroid.blogspot.com.br/2012/05/como-criar-um-jogo-estilo-plataforma.html
tem como disponibilizar o projeto pra download? aqui ta dando erros.
Aqui está: http://tutoriandroid-game-smash.googlecode.com/files/Smash-source-v3.rar
Finalmente um super tutorial! vlw cara! bora fazer outro tuto mostrando fazer um jogo? sensacional cara parábens!
Cara,
muito legal esse tutorial!!
Parabens!!!
Valeu Gustavo!
Se quiser verificar o resultado do meu aprendizado:
https://play.google.com/store/apps/details?id=com.jrso.game.asteroids
Parabéns!
Att.
Ricardo Oliveira.
@Ricardo: Parabéns pela publicação! Algumas detalhes que notei:
- O ícone no meu aparelho está aparecendo o default (android verde), provavelmente pois você não colocou (ou não deletou) o ícone correto na pasta drawable-xhdpi
- Ao "pausar" o jogo (teclando home, por exemplo) e retornar, a música não retorna.
- Percebi que uma nova onda de asteroids só aparecem quando toda a anterior tiver sido destruída, na minha opinião isso não é legal pois o jogador não precisa destruir rápido, precisa apenas destruir no intervalo de tempo, enquanto que se os novos asteroids não esperassem o jogador teria que destruir cada vez mais rápido.
Continue melhorando!
java.lang.NoClassDefFoundError: com.tutoriandroid.games.smash.TitleGameView
aqui retorna esse erro
vc podia fazer um video eu acho que ficaria mais fazio de entender...obrigado
Ola Gustavo eu me chamo Alan eu não consegui colocar o anuncio no meu APP tem como você colocar o anúncio pra mim mas eu queria o anúncio da startapp eu tenho a SDK dela se VC poder fazer esse favor pra mim eu agradeço
Tem como eu mudar o som?
Postar um comentário