Недавно мы сделали браузерную игру «Пирамида»: в ней нужно ставить блоки друг на друга, а что выходит за границы — обрезается. Цель игры — набрать как можно больше очков.
Проблема в том, что на компьютере всё работает отлично, а на мобильниках играть не получается: при запуске игры сразу появляется сообщение, что мы промахнулись. Перезапустить игру тоже не выходит, нужно перезагружать страницу целиком.
Сегодня мы исправим обе этих ошибки.
Игрок сразу проигрывает при запуске
Когда мы на мобилке прочитали условия и нажимаем на экран для старта игры, тут же появляется сообщение, что мы промахнулись и игра окончена. Странно, ведь мы даже не успели нажать на экран второй раз, чтобы уронить блок.
Всё дело в том, что мы перестраховались и добавили поддержку тач-экранов сразу после обработки нажатия мыши:
// обрабатываем нажатие мыши
window.addEventListener("mousedown", eventHandler);
// обрабатываем касание экрана
window.addEventListener("touchstart", eventHandler);
Кажется, что всё правильно, но вот как это работает в жизни:
- Скрипт понимает, что ему нужно отслеживать два нажатия — мыши и касание экрана.
- Мы открываем игру на телефоне, читаем инструкцию и касаемся экрана, чтобы запустить игру.
- В момент касания страницы телефон по умолчанию преобразует все нажатия на экран как клик мышкой.
- Сразу же срабатывает обработчик нажатия мыши, и мы запускаем игру.
- Одновременно с этим в момент касания браузер видит, что в скрипте есть отдельный обработчик именно тач-экранов и передаёт ему информацию о касании.
- Срабатывает тач-обработчик, он делает то же, что и обработчик нажатия мыши, — и в этот момент игра получает второй клик и сразу роняет блок мимо пирамиды.
Получается, проблема в том, что у нас два обработчика, которые делают одно и то же, но на компьютере не срабатывает тач, а на мобильнике срабатывают оба.
Добавляем проверку типа устройства
Чтобы исправить такое, нам нужно научить скрипт понимать, на каком устройстве он работает — смартфон или компьютер. Есть ещё совсем экзотика, когда мы запускаем игру на Windows-планшете и тоже управляем касаниями, но сюда лезть пока не будем.
Для проверки устройства мы будем использовать параметр userAgent — в нём хранится название браузера или тип устройства, на котором открыта страница. Логика будет такая:
- Собираем все возможные мобильные браузеры и платформы.
- Проверяем userAgent на совпадение по этому списку.
- Если совпадение есть, значит, игра работает на мобильном устройстве и нужно добавить обработчик тач-интерфейса.
- Если совпадения нет, значит, добавляем обычное нажатие мыши.
Меняем в коде два предыдущих обработчика на такую конструкцию:
// если игра запущена на мобильном устройстве
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(navigator.userAgent)) {
// то добавляем отслеживание события нажатия на экран
window.addEventListener("touchstart", eventHandler);
} else {
// иначе, если это ПК, добавляем отслеживание нажатия мыши
window.addEventListener("mousedown", eventHandler);
}
Теперь всё работает как нужно: не срабатывает два раза при одном нажатии и даёт поиграть на смартфонах.
Добавляем обновление страницы по свайпу
Во время игры на телефоне перед нами не появляется экранная клавиатура, поэтому мы не можем нажать R для перезапуска. Нам нужно обойти это ограничение и придумать что-то с мышкой.
Двойной клик не подходит — игра его обработает как два одиночных нажатия. Можно подключить сторонние библиотеки работы с жестами и мультитачем, но придётся переписывать и добавлять значительную часть кода.
Наша задача — сделать это максимально просто и без внешних скриптов. Используем для этого ещё одно событие тач-интерфейса: touchmove. Оно срабатывает, когда мы нажали на экран и не отрывая ведём палец в любую сторону. Похоже на свайп, но работает в любом направлении.
На это событие мы повесим функцию запуска игры — это как раз то поведение, что нам нужно. После этого игра будет работать так:
- В любой момент игры мы делаем свайп одним пальцем — нажимаем и проводим им по экрану, например влево или вправо.
- Во время нажатия срабатывает обработчик события touchstart — мы тут же роняем блок, при этом неважно куда.
- Если после нажатия палец продолжает двигаться по экрану, то срабатывает обработчик события touchmove и вызывает перезапуск игры.
Поместим эту строчку туда же, где лежит обработчик touchstart:
window.addEventListener("touchmove", startGame);
Чтобы новая механика стала известна пользователю, добавим описание в сообщение в конце игры:
<p>Вы промахнулись.</p>
<p>Для перезапуска игры нажмите R или проведите пальцем по экрану.</p>
Поиграть в адаптированную версию «Пирамиды» на странице проекта.