Класс Sprite

от
Android   gamedev

В Android нет специальных классов для игровых объектов, как это было в Java ME, но зато в нём есть множество других не менее полезных классов, знание которых поможет упростить разработку игры.
Во-первых, в Android есть специальные классы для точек, прямоугольников, контуров и остальных объектов. Теперь не нужно иметь кучу переменных, чтобы оперировать координатами персонажей, достаточно задать класс Point (для координат типа int) или PointF (для координат типа float).
Во-вторых, возможности рисования в Android куда более богаты, чем в Java ME. Здесь тебе и рисование градиентов, и гибкая работа с изображениями Bitmap, и сглаживание, и поворот, и т.д.
Советую посмотреть на список классов пакета android.graphics.
Вот теперь, давайте напишем реализацию класса Sprite с блекджеком проверкой столкновений и фреймами анимации.

Создадим новый Android проект и MainActivity. У кого с этим трудности, добро пожаловать сюда.
Сразу создадим класс Sprite.
Он должен содержать картинку, которая будет отрисовываться. Но так как мы хотим использовать последовательность картинок, то придётся создать массив и текущий индекс кадра:
  1. private final Bitmap[] mFrames;
  2. private int mFrameIndex;
Позицию спрайта мы будем хранить не в int x, y, а в специальном классе Point. Также нам потребуется знать размеры спрайта, для корректной проверки столкновений, для этого подойдёт класс Rect:
  1. private final Point mPosition;
  2. private final Rect mBounds;

Это всё, что нам понадобится. Теперь перейдём к методам.
В Java ME у класса Sprite был метод move(int dx, int dy), позволяющий сдвигать спрайт с учётом указанных координат. По-сути, он прибавляет эти значения к текущей позиции спрайта. У класса Point есть метод offset, который как раз сдвигает позицию на заданные значения:
  1. public void move(int dx, int dy) {
  2.     mPosition.offset(dx, dy);
  3. }
Для установки координат, воспользуемся методом set(int x, int y):
  1. public void setPosition(int x, int y) {
  2.     mPosition.set(x, y);
  3. }

Теперь разберёмся с проверкой столкновений. В классе Rect есть статический метод intersects(Rect r1, Rect r2), позволяющий проверить, пересекаются ли прямоугольники. У нас есть позиция спрайта в mPoint, и есть его размеры в mBounds. Напомню, mBounds просто хранит ширину и высоту (0, 0, width, height). Чтобы получить Rect с реальными позициями спрайта, нужно сдвинуть прямоугольник mBounds на координату x, y. Это делается так же, как и в случае с Point: методом offset:
  1. private Rect getBoundsRect() {
  2.     Rect bounds = new Rect(mBounds);
  3.     bounds.offset(mPosition.x, mPosition.y);
  4.     return bounds;
  5. }
Напишем методы для проверки столкновений спрайта с другим спрайтом, прямоугольником и точкой:
  1. public boolean collidesWith(Sprite s) {
  2.     return Rect.intersects(getBoundsRect(), s.getBoundsRect());
  3. }
  4.  
  5. public boolean collidesWith(Rect rect) {
  6.     return Rect.intersects(getBoundsRect(), rect);
  7. }
  8.  
  9. public boolean collidesWith(Point point) {
  10.     return Rect.intersects(getBoundsRect(), new Rect(point.x, point.y, point.x, point.y));
  11. }
Как видно, не нужно проверять каждую координату - метод intersects всё делает сам.

И, наконец, отрисовка спрайта. Она выглядит так:
  1. public void paint(Canvas canvas, Paint paint) {
  2.     canvas.drawBitmap(mFrames[mFrameIndex], mPosition.x, mPosition.y, paint);
  3. }

Полный код класса Sprite.java

Теперь создадим GameView. Для примера, выведем несколько статичных спрайтов, которые будут анимировать при их выделении.
Хранить список спрайтов будем в ArrayList, а выделение в Rect.
Инициализация списка спрайтов:
  1. Bitmap pacman1 = BitmapFactory.decodeResource(context.getResources(), R.drawable.pacman_1);
  2. Bitmap pacman2 = BitmapFactory.decodeResource(context.getResources(), R.drawable.pacman_2);
  3. Bitmap[] pacman = new Bitmap[] { pacman1, pacman2 };
  4. mSprites = new ArrayList<Sprite>();
  5. Random rnd = new Random();
  6. final int size = rnd.nextInt(10) + 5;
  7. for (int i = 0; i < size; i++) {
  8.     Sprite spr = new Sprite(pacman);
  9.     spr.setPosition(rnd.nextInt(450), rnd.nextInt(800));
  10.     mSprites.add(spr);
  11. }
Здесь мы подгружаем из ресурсов два изображения, кладём их в массив pacman и потом передаём в класс Sprite, не забыв выставить новые координаты.

Теперь, отрисовывать несколько спрайтов можно вот таким небольшим кодом:
  1. for (Sprite sprite : mSprites) {
  2.     sprite.paint(canvas, mPaint);
  3. }

Анимировать будут лишь выделенные спрайты, для этого мы проверяем столкновение спрайта с прямоугольником и, если столкновение произошло, вызываем метод nextFrame():
  1. if (!mSelectionRect.isEmpty()) {
  2.     for (Sprite sprite : mSprites) {
  3.         if (sprite.collidesWith(mSelectionRect)) {
  4.             sprite.nextFrame();
  5.         }
  6.     }
  7. }

Вот результат:
  spritedemo.png

Готовый проект с исходниками:
   SpriteDemo.zip
+9   9   0
2287