Programación de Juegos en Android 5. Velocidad y animación de los sprites.

En este tutorial vamos a darle a nuestro sprite una velocidad "x" e "y" randómica. Con esto, el sprite arrancará con una dirección aleatoria, que cambiará cada vez que llegue a un borde.

package com.edu4java.android.killthemall;

import java.util.Random;

import android.graphics.Bitmap;

import android.graphics.Canvas;

import android.graphics.Rect;

 

public class Sprite {

       private static final int BMP_ROWS = 4;

       private static final int BMP_COLUMNS = 3;

       private int x = 0;

       private int y = 0;

       private int xSpeed = 5;

       private GameView gameView;

       private Bitmap bmp;

       private int currentFrame = 0;

       private int width;

       private int height;

       private int ySpeed;

 

       public Sprite(GameView gameView, Bitmap bmp) {

             this.gameView = gameView;

             this.bmp = bmp;

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

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

             Random rnd = new Random();

             xSpeed = rnd.nextInt(10)-5;

             ySpeed = rnd.nextInt(10)-5;

       }

 

       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 = 1 * 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);

       }

}

 

La imagen de nuestro sprite contiene 4 animaciones con 3 imágenes cada una:

 

Dependiendo en que dirección vaya el sprite tenemos que enseñar una animación diferente, por ejemplo;

Necesitamos una función que tenga como parámetros "xSpeed" y "ySpeed" y que devuelva la fila que tenemos que utilizar para la animación [0,1,2,3]

Utilizaremos la función Math.atan2(xSpeed, ySpeed) para calcular la dirección del sprite en tiempo de ejecución.

atan2(x,y) nos da el ángulo en radianes en double desde (-PI a PI), pero necesitamos el resultado en int de 0 a 3 para saber que animación utilizar.

Observemos el gráfico que tenemos a continuación. En él escribo las coordenadas (x,y) para cada dirección en la que tenemos una animación: Arriba (0,-1), derecha (1,0), abajo (0,1) e izquierda (0,-1).

Cuando el ángulo resultante está cerca de una de estas direcciones, queremos utilizar la animación correspondiente (up, down, left, right). Para esto utilizaremos la función Math.round.

Bueno, ahora viene la parte complicada que resolví con esta función a continuación y una constante array;

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

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

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

 

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

       }

En esta tabla muestro que he hecho en la función;

  1. con atan2(xSpeed,ySpeed) obtengo el angulo en radianes desde (-PI a PI)
  2. Divido el ángulo por PI/2 y obtengo un double de (-2 a 2)
  3. Sumo 2 para cambiar el rango de (0 a 4)
  4. Utilizo % (modulo) para reducir el rango de (0 a 3) (ver que 0 y el 4 son la misma dirección )
  5. Mapeo cada dirección a la animación correcta utilizando el array { 3, 1, 0, 2 }
x y atan2(x,y) atan2(x,y)/(PI/2) (atan2(x,y)/(PI/2)+2)%4 bmp row (from 0)
up 0 -1 PI or -PI 2 or -2 4 or 0 3
right 1 0 PI/2 1 3 2
down 0 1 0 0 2 0
left -1 0 -PI/2 -1 1 1

Nota: aqui "x" y "y" son xSpeed y ySpeed.

Si sigues leyendo, enhorabuena!!! eres un programador de juegos ;)

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 int x = 0;

       private int y = 0;

       private int xSpeed = 5;

       private GameView gameView;

       private Bitmap bmp;

       private int currentFrame = 0;

       private int width;

       private int height;

       private int ySpeed;

 

       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(System.currentTimeMillis());

             xSpeed = rnd.nextInt(50) - 5;

             ySpeed = rnd.nextInt(50) - 5;

       }

 

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

       }

 

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

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

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

       }

}

 

<< Nuestro primer Sprite Trabajar con múltiples Sprites >>