avatar

Пишем игру под Android: Часть 3 - Спрайтовая анимация, работа с несколькими спрайтами

Опубликовал в блог Android
Сегодня мы будем устанавливать нашим спрайтам случайную скорость по х и у. При этом, спрайт будет начинать движение со случайных координат, которое будет меняться каждый раз как он будет достигает границы экрана. Для тех кто не читал предыдущие статьи — желательно прочитать сперва их, ниже приведены ссылки:
  1. Пишем игру под Android: Часть 1 — Рисуем картинки на SurfaceView
  2. Пишем игру под Android: Часть 2 — Создаем первый спрайт

Если Вы прочли эти статьи, значит Вы можете продолжать дальше, начнем с первой части названия статьи.

Спрайтовая анимация

Открываем наш проект и пишем следующий код:

Sprite.java

import java.util.Random;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
 
public class Sprite 
{
       /**Рядков в спрайте = 4*/
    private static final int BMP_ROWS = 4;
    
    /**Колонок в спрайте = 3*/
    private static final int BMP_COLUMNS = 3;
    
    /**Объект класса GameView*/
    private GameView gameView;
    
    /**Картинка*/
    private Bitmap bmp;
    
    /**Позиция по Х=0*/
    private int x = 5;
    
    /**Позиция по У=0*/
    private int y = 0;
    
    /**Скорость по Х=5*/
    private int xSpeed = 5;
    
    private int ySpeed = 5;
    
    /**Текущий кадр = 0*/
    private int currentFrame = 0;
    
    /**Ширина*/
    private int width;
    
    /**Ввыоста*/
    private int height;

    /**Конструктор*/
     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 = 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);
     }
}


Наш спрайт содержит 4 анимации с 3 кадрами в каждой строке.

В зависимости от направления мы должны показать разную анимацию, например:

Нам нужна функция, которая получает в качестве параметров xSpeed ​​и ySpeed ​​и возвращает строку, которую мы будем использовать для создания анимации [0,1,2,3]

Мы собираемся использовать функцию Math.atan2(xSpeed​​, ySpeed​​) для расчета направления спрайтов во время выполнения функции.

atan2(х, у) дает угол радиана в double из (-PI до PI), но мы просто хотим получить результат в int — от 0 до 3, чтобы узнать, какую анимацию мы должны использовать.

Давайте рассмотрим следующий рисунок, я описываю координатами (х, у) каждое направление на спрайте, что у нас имеется: вверх (0, -1), вправо (1,0), вниз (0,1) и влево (0, -1).

Когда результат равен углу одного из заданных направлений спрайта мы будем использовать соответствующее движение — вверх, вправо, вниз или влево. Для этого я использую Math.round функции.

Хорошо, теперь самая сложная часть, я решил это с помощью этой функции и одной константы массива:

Sprite.java
// 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];
}


Ниже я пытаюсь объяснить Вам, что я делаю в этой функции:

  1. с atan2(xSpeed​​, ySpeed​​) Я получаю радиан угла из (-PI до PI)
  2. Я делю угол PI / 2 и получаю вдвое больше по сравнению с (-2 до 2)
  3. Я добавляю 2 для изменения диапазона от (0 до 4)
  4. Я использую %, чтобы уменьшить диапазон (от 0 до 3) (0 и 4, были в том же направлении)
  5. Задаем на карте каждой строке — направление, к нужной анимации с использованием массива {3, 1, 0, 2}

Добавляем в конец класса Sprite.java код который я написал выше и переходим к следующей части названия статьи.

Работа с несколькими спрайтами


В этой части статьи мы будем создавать несколько спрайтов. Каждый спрайт будет иметь собственные позиции, скорости и ориентации движения.

Для начал изменим наш главный класс рисования — GameView.java для внесения в него списка спрайтов. Добавим в начало класса:
private List<Sprite> sprites = new ArrayList<Sprite>();

Нам нужно скопировать все спрайты которые Вы создали в каталоге drawable, как мы делали с bad1.png во втором уроке. Затем мы создадим новый спрайт с каждым изображением.

GameView.java
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));


Создаем новый метод который будет возвращать изображение спрайта:

GameView.java
private Sprite createSprite(int resouce) 
{
 Bitmap bmp = BitmapFactory.decodeResource(getResources(), resouce);
 return new Sprite(this,bmp);
}

и обновляем onDraw():

GameView.java
for(Sprite sprite : sprites) 
{
 sprite.onDraw(canvas);
}


Если мы запустим игру то увидим что все спрайты расползаются с одной точки, это наверно нужно исправить :)


Добавляем простые две строчки в конструктор Sprite.java:

Sprite.java
x = rnd.nextInt(gameView.getWidth() - width);
y = rnd.nextInt(gameView.getHeight() - height);


Но если Вы сейчас запустите проект то получите сообщение об ошибке потому что width у нас равен 0. Если Вы помните, мы используем метод в surfaceCreated, который знает когда запущена сцена и когда можно рисовать. Для исправления проблемы нам нужно записать в surfaceCreated() метод создания спрайтов, т.е. createSprites();

GameView.java
public void surfaceCreated(SurfaceHolder holder) 
{
 createSprites();
 gameLoopThread.setRunning(true);
 gameLoopThread.start();
}


Все, проблема решена, игра работает. Запускаем и смотрим как человечки бегают по экрану :)

Исходные коды


По традиции рекламирую свой блог — dajver.blogspot.com/. Заходим не стесняемся :)
0 комментариев RSS
Нет комментариев
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.