terça-feira, 17 de abril de 2012

Desenvolvendo jogos para Android - Parte 3 - Sons e High Score

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 event.getX()event.getY();
        for(Gold gold golds)
          if(gold.&& gold.&& gold.gold.width && gold.gold.height){
            sound.play(soundIdGoldHit1f1f001f);
            ((Activitycontext).finish();
            Intent intent new Intent(contextGameOverActivity.class);
            intent.putExtra("SCORE"score);
            context.startActivity(intent);
          }
        for(Pink pink pinks)
          if(!pink.isDead(&& pink.&& pink.&& pink.pink.width && pink.pink.height){
            sound.play(soundIdPinkHit1f1f001f);
            pink.kill();
            int add;
            score += add (intMath.max(100 level*(System.currentTimeMillis(startTime)/5001);
            alivePinks--;
            if(alivePinks == 0){
              nextLevelSprite.visible true;
              sound.play(soundIdWin1f1f001f);
            }

            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:

Henrique at 22 de maio de 2012 às 17:19 disse...

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

Gustavo Carvalho at 22 de maio de 2012 às 23:38 disse...

Obrigado!

Não entendi sua dúvida, poderia explicar melhor? E essa é a primeira vez que ouço falar do Physics Box2d... Interessante. Valeu!

Henrique at 23 de maio de 2012 às 19:01 disse...

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?

Gustavo Carvalho at 24 de maio de 2012 às 11:04 disse...

Entendi... vou pensar em um tutorial sobre isso. =]

Gustavo Carvalho at 29 de maio de 2012 às 11:33 disse...

Pronto Henrique, fiz um tutorial sobre exatamente sua dúvida: http://tutoriandroid.blogspot.com.br/2012/05/como-criar-um-jogo-estilo-plataforma.html

Anônimo at 15 de agosto de 2012 às 16:02 disse...

tem como disponibilizar o projeto pra download? aqui ta dando erros.

Gustavo Carvalho at 16 de agosto de 2012 às 15:51 disse...

Aqui está: http://tutoriandroid-game-smash.googlecode.com/files/Smash-source-v3.rar

Anônimo at 20 de agosto de 2012 às 17:44 disse...

Finalmente um super tutorial! vlw cara! bora fazer outro tuto mostrando fazer um jogo? sensacional cara parábens!

André Kunde at 29 de novembro de 2012 às 16:31 disse...

Cara,

muito legal esse tutorial!!

Parabens!!!

Unknown at 17 de março de 2013 às 19:29 disse...

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.

Gustavo Carvalho at 19 de março de 2013 às 11:30 disse...

@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!

Unknown at 11 de junho de 2013 às 21:49 disse...

java.lang.NoClassDefFoundError: com.tutoriandroid.games.smash.TitleGameView

aqui retorna esse erro

Anônimo at 17 de fevereiro de 2014 às 17:02 disse...

vc podia fazer um video eu acho que ficaria mais fazio de entender...obrigado

alan at 14 de janeiro de 2015 às 16:58 disse...
Este comentário foi removido pelo autor.
alan at 14 de janeiro de 2015 às 17:00 disse...

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

Jhonatan Trindade at 29 de novembro de 2015 às 20:55 disse...

Tem como eu mudar o som?

Postar um comentário

 
© 2011 Tutoriandroid | Recode by Ardhiansyam | Based on Android Developers Blog