Working with Multiple Sprites. Android Game Programming 6.

In this tutorial we are going to see the creation of several sprites. Each sprite with its own position, speed, and orientation moving.

We are going to change in GameView the variable sprite for a List of sprites.

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

We need to copy the characters images in the resource directory as we did with bad1.pgn in the "Android Game Programming 4. Sprites 1". Then we are going to create a new sprite with each image in the contructor.

             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));

    

using this method

       private Sprite createSprite(int resouce) {

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

             return new Sprite(this,bmp);

       }

 

and update the onDraw method with

             for (Sprite sprite : sprites) {

                    sprite.onDraw(canvas);

             }

 

if we run this we are going to notice an unpleasant efect due to the fact that all sprites start at the same point.

We can fix this easily adding

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

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

in the Sprite constructor. But if you run it again you are going to get an error.

If we put a break point in the line we just added and run the application in debug mode, we can get this

here I inspect the value of gameView.getWidth() and I found that the width is 0. What happen here? The problem is that we ask for the view width before the view is ready.

If you remember, we use the surfaceCreated method in the Callback listener to know when the view is ready. We can move the sprites creation to this method and fix our problem.

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: Speed and Animation Touch Events and Sprite Collision Detection >>