Trabajar con varios sprites. Programación de Juegos en Android 6.

En este tutorial vamos a ver la creación de varios sprites. Cada sprite con su propia posición, velocidad y orientación.

 

En GameView vamos a cambiar la variable sprite por una Lista de sprites.

private List<Sprite> sprites = new ArrayList<Sprite>();

Copiamos las imágenes de los caracteres en el directorio de recursos como hicimos con bad1.pgn en el tutorial "Programación de Juegos en Android 4. Sprites 1". Ahora creamos un nuevo sprite en el constructor, con cada imagen.

             sprites.add(createSprite(R.drawable.bad1));

             sprites.add(createSprite(R.drawable.bad2));

             sprites.add(createSprite(R.drawable.bad3));

             sprites.add(createSprite(R.drawable.bad4));

             sprites.add(createSprite(R.drawable.bad5));

             sprites.add(createSprite(R.drawable.bad6));

             sprites.add(createSprite(R.drawable.good1));

             sprites.add(createSprite(R.drawable.good2));

             sprites.add(createSprite(R.drawable.good3));

             sprites.add(createSprite(R.drawable.good4));

             sprites.add(createSprite(R.drawable.good5));

             sprites.add(createSprite(R.drawable.good6));

    

utilizando el siguiente método

       private Sprite createSprite(int resouce) {

             Bitmap bmp = BitmapFactory.decodeResource(getResources(), resouce);

             return new Sprite(this,bmp);

       }

 

y actualizamos el método onDraw con

             for (Sprite sprite : sprites) {

                    sprite.onDraw(canvas);

             }

 

Si lo ejecutamos, vamos a ver un efecto no muy bonito, debido al hecho de que todos los sprites empiezan en el mismo punto.

Corregimos esto facilmente, añadiendo;

             x = rnd.nextInt(gameView.getWidth() - width);

             y = rnd.nextInt(gameView.getHeight() - height);

en el constructor del Sprite. Si se vuelve a ejecutar, aparece un error.

Si ponemos un break point en la línea que hemos añadido y ejecutamos la aplicación en modo debug, veremos lo siguiente;

Aquí inspeccionamos el valor de gameView.getWidth() y encontramos que el width= 0. ¿Qué ocurrió? El problema es que hemos pedido el view width antes de que el view esté listo.

Si se acuerdan, utilizamos el método surfaceCreated en el Callback listener para saber cuando está listo view.

Podemos mover la creación de los sprites a este método y solucionar nuestro problema.

package com.edu4java.android.killthemall;

import java.util.ArrayList;

import java.util.List;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.Canvas;

import android.graphics.Color;

import android.view.SurfaceHolder;

import android.view.SurfaceView;

 

public class GameView extends SurfaceView {

       private SurfaceHolder holder;

       private GameLoopThread gameLoopThread;

       private List<Sprite> sprites = new ArrayList<Sprite>();

      

       public GameView(Context context) {

             super(context);

             gameLoopThread = new GameLoopThread(this);

             holder = getHolder();

             holder.addCallback(new SurfaceHolder.Callback() {

 

                    @Override

                    public void surfaceDestroyed(SurfaceHolder holder) {

                           boolean retry = true;

                           gameLoopThread.setRunning(false);

                           while (retry) {

                                  try {

                                        gameLoopThread.join();

                                        retry = false;

                                  } catch (InterruptedException e) {

                                  }

                           }

                    }

 

                    @Override

                    public void surfaceCreated(SurfaceHolder holder) {

                           createSprites();

                           gameLoopThread.setRunning(true);

                           gameLoopThread.start();

                    }

 

                    @Override

                    public void surfaceChanged(SurfaceHolder holder, int format,

                                  int width, int height) {

                    }

             });

       }

      

       private void createSprites() {

             sprites.add(createSprite(R.drawable.bad1));

             sprites.add(createSprite(R.drawable.bad2));

             sprites.add(createSprite(R.drawable.bad3));

             sprites.add(createSprite(R.drawable.bad4));

             sprites.add(createSprite(R.drawable.bad5));

             sprites.add(createSprite(R.drawable.bad6));

             sprites.add(createSprite(R.drawable.good1));

             sprites.add(createSprite(R.drawable.good2));

             sprites.add(createSprite(R.drawable.good3));

             sprites.add(createSprite(R.drawable.good4));

             sprites.add(createSprite(R.drawable.good5));

             sprites.add(createSprite(R.drawable.good6));

       }

      

       private Sprite createSprite(int resouce) {

             Bitmap bmp = BitmapFactory.decodeResource(getResources(), resouce);

             return new Sprite(this,bmp);

       }

 

       @Override

       protected void onDraw(Canvas canvas) {

             canvas.drawColor(Color.BLACK);

             for (Sprite sprite : sprites) {

                    sprite.onDraw(canvas);

             }

       }

}

 

package com.edu4java.android.killthemall;

 

import java.util.Random;

import android.graphics.Bitmap;

import android.graphics.Canvas;

import android.graphics.Rect;

 

public class Sprite {

       // direction = 0 up, 1 left, 2 down, 3 right,

       // animation = 3 back, 1 left, 0 front, 2 right

       int[] DIRECTION_TO_ANIMATION_MAP = { 3, 1, 0, 2 };

       private static final int BMP_ROWS = 4;

       private static final int BMP_COLUMNS = 3;

       private static final int MAX_SPEED = 5;

       private GameView gameView;

       private Bitmap bmp;

       private int x = 0;

       private int y = 0;

       private int xSpeed;

       private int ySpeed;

       private int currentFrame = 0;

       private int width;

       private int height;

 

       public Sprite(GameView gameView, Bitmap bmp) {

             this.width = bmp.getWidth() / BMP_COLUMNS;

             this.height = bmp.getHeight() / BMP_ROWS;

             this.gameView = gameView;

             this.bmp = bmp;

 

             Random rnd = new Random();

             x = rnd.nextInt(gameView.getWidth() - width);

             y = rnd.nextInt(gameView.getHeight() - height);

             xSpeed = rnd.nextInt(MAX_SPEED * 2) - MAX_SPEED;

             ySpeed = rnd.nextInt(MAX_SPEED * 2) - MAX_SPEED;

       }

 

       private void update() {

             if (x >= gameView.getWidth() - width - xSpeed || x + xSpeed <= 0) {

                    xSpeed = -xSpeed;

             }

             x = x + xSpeed;

             if (y >= gameView.getHeight() - height - ySpeed || y + ySpeed <= 0) {

                    ySpeed = -ySpeed;

             }

             y = y + ySpeed;

             currentFrame = ++currentFrame % BMP_COLUMNS;

       }

 

       public void onDraw(Canvas canvas) {

             update();

             int srcX = currentFrame * width;

             int srcY = getAnimationRow() * height;

             Rect src = new Rect(srcX, srcY, srcX + width, srcY + height);

             Rect dst = new Rect(x, y, x + width, y + height);

             canvas.drawBitmap(bmp, src, dst, null);

       }

 

       private int getAnimationRow() {

             double dirDouble = (Math.atan2(xSpeed, ySpeed) / (Math.PI / 2) + 2);

             int direction = (int) Math.round(dirDouble) % BMP_ROWS;

             return DIRECTION_TO_ANIMATION_MAP[direction];

       }

}

 

<< Sprites velocidad y Animación Eventos de Touch y detección de colisiones de Sprite >>