avatar

Пишем игру под Android: Часть 4 - onTouchEvent и определение столкновений

Опубликовал в блог Android
Добрый день/вечер, в этой части мы будем рассматривать работу с сенсорным экраном и проверка столкновений координат касания пальца к экрану с координатами человечков (будем убивать наших славных ботов:)).

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

Если Вы уже прочитали эти статьи то прошу под кат, там Вы узнаете много нового (надеюсь).


onTouchEvent() — событие касания к экрану


Первое, что нужно сделать, это добавить метод onTouchEvent() в класс GameView, в целях обработки каждого касания по экрану. Для каждого спрайта в списке мы узнаем, существует ли событие нажатия на экран по координатам (х, у) и имеют ли они столкновения со спрайтами. Если столкновения не существует, мы удаляем спрайт из списка спрайтов и возвращаем назад список чтобы избежать ошибок в следующей итерации, когда мы удаляем спрайт.

GameView.java
/**Обработка косания по экрану*/
public boolean onTouchEvent(MotionEvent event) 
{
     for (int i = sprites.size()-1; i >= 0; i--) 
     {
         Sprite sprite = sprites.get(i);
          if (sprite.isCollition(event.getX(),event.getY())) 
          {
                      sprites.remove(sprite);
          }
      }
    return super.onTouchEvent(event);
}


Теперь метод onDraw() рисует спрайт из списка, удаленные спрайты рисоваться не будут. Результат обработки события onTouchEvent() является как бы раздавливае персонажа своим пальцем :) Мы очень жестоки…

Определение столкновений


Чтобы заставить его работать, мы должны реализовать метод isCollition() в классе Sprite. Этот метод должен вернуть true, если (х, у) координаты указывают на нашего человечка.

Sprite.java
/**Проверка на столкновения*/
public boolean isCollition(float x2, float y2) 
{
      return x2 > x && x2 < x + width && y2 > y && y2 < y + height;
}


Если х2 не больше х, это означает, что касание было слева от спрайта. если х2 больше, чем х + width, это означает, что касание было — справа от спрайта.

Если х2 > х & & 2 <х + width, что означает, что прикосновение в том же столбце, но все же мы должны проверить, если он находится в той же строке. у2> у & & 2 <у + height проверяет ряд столкновений таким же образом.

Запустим и глянем что происходит:


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

Ограничение по количеству кликов


Удаляются все человечки какие находятся по этим координатам, а мы хотим что бы удалялся только один из допустим трех. Для этого нам нужно добавить прерывании в нашу функцию определения нажатия на экран onTouchEvent().

GameView.java
for(int i = sprites.size()-1; i >= 0; i--) 
{
           Sprite sprite = sprites.get(i);
           if (sprite.isCollition(event.getX(),event.getY())) 
           {
                   sprites.remove(sprite);
                   break;
           }
}


Я забыл о синхронизации. События обрабатываются в потоке, не похожем на поток рисования и обновления. Это может привести к конфликту, если мы обновляем данные, которые были разработаны в одно и то же время. Решение довольно простое — обернуть весь код внутри onTouchEvent() с тем же объектом SurfaceHolder который мы использовали в классе GameManager.

Вспомним что мы использовали для синхронизации цикла:

GameManager.java
canv = view.getHolder().lockCanvas();
synchronized (view.getHolder()) 
{
      view.onDraw(canv);
}


А теперь мы добавляем вместо старого кода в onTouchEvent():

GameView.java
synchronized (getHolder()) 
{
        for (int i = sprites.size()-1; i > 0; i--) 
        {
                Sprite sprite = sprites.get(i);
                if (sprite.isCollition(event.getX(),event.getY())) 
                {
                     sprites.remove(sprite);
                     break;
                }
        }
}

при помощи этого мы можем избежать ошибки, которая появляется и очень раздражает. :)

Если мы запустим его снова, и увидим что проблема все еще не устранена. На этот раз проблема не в потоке и не синхронизации, проблема в том что событие вызывается сильно быстро и нам его нужно ограничить. Создаем метод который будет ограничивать количество кликов в 300 миллисекунд, тогда эта ошибка исчезнет и человечки будут убиваться по одному раз в 300 мс. Добавляем чуть выше кода в onTouchEvent().

GameView.java
if (System.currentTimeMillis() - lastClick > 300) 
{
      lastClick = System.currentTimeMillis()
//скобочка закрывается перед return true; иф - охватывает весь метод


Теперь запустите его снова, и вы увидите разницу.

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


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