Реализация тач-управления мобильной игры (тапы, свайпы, жесты)
Unity на мобильных устройствах обрабатывает касания через Input.GetTouch() или новый InputSystem — и между ними пропасть. Старый API не различает тап и начало свайпа до тех пор, пока палец не поднялся. Для игр, где реакция на касание должна быть мгновенной, это неприемлемо.
Где ломается большинство реализаций
Самая частая ошибка — распознавание жестов на основе TouchPhase.Ended. Разработчик сравнивает позицию Began и Ended, считает вектор — и получает свайп. Работает на девайсе с 60 FPS. На 30 FPS с throttling из-за тепла (типичная картина для бюджетных Android-устройств) дельта между кадрами растёт, и короткий тап по таймеру классифицируется как свайп.
Правильный подход — отслеживать движение в Stationary и Moved фазах, добавлять порог по расстоянию (sqrMagnitude > threshold) и пороговое время (Time.time - touchStartTime < tapMaxDuration). Unity не предоставляет это из коробки — нужен собственный GestureRecognizer.
В проекте на Godot 4 с InputEventScreenTouch и InputEventScreenDrag ситуация немного лучше: движок разделяет события на уровне API. Но мультитач — отдельная история. index у InputEventScreenTouch даёт порядковый номер пальца, и при быстром снятии одного пальца index-ы могут перемапиться, что ломает логику двухпальцевых жестов.
Как строим систему жестов
Для Unity пишем TouchInputManager как singleton MonoBehaviour, который на каждом кадре в Update() итерирует Input.touches и раскладывает касания по конечным автоматам состояний — по одному FSM на каждый активный fingerId. Состояния: Idle → Pressing → Tapping/Swiping/Holding. Переходы — по дистанции и времени.
На выходе — события: OnTap(Vector2 position), OnSwipe(Vector2 direction, float velocity), OnHold(Vector2 position, float duration), OnPinch(float delta). Игровые системы подписываются на эти события через C# delegates или UnityEvent, не зная ничего про Input.GetTouch.
Для Flutter-игр на flame engine используем TapDetector, PanDetector, ScaleDetector из пакета flame. Они корректно работают поверх Flutter GestureArena — каждый детектор участвует в конкурсе жестов, и победитель определяется по приоритету. Важно: ScaleDetector и PanDetector конфликтуют, если не настроить behavior: HitTestBehavior.opaque на родительском виджете.
Реальный кейс: свайп-атаки в RPG
В одном проекте (top-down RPG, Unity 2022.3 LTS) нужны были направленные свайп-атаки с восемью направлениями. Простая нормализация вектора давала нестабильный результат — диагональные направления срабатывали реже, потому что пользователи редко проводят строго под 45°. Решение: зоны допуска ±30° вместо стандартных ±22.5°, плюс взвешивание по скорости свайпа — быстрые свайпы менее точны, медленные — точнее. После этого процент корректного распознавания вырос с 78% до 94% по данным Firebase Analytics (custom event gesture_recognized).
Дополнительно — визуальный фидбек через LineRenderer, который рисует след свайпа с затуханием. Без него игрок не понимает, принял ли игра его жест.
Мультитач и edge cases
Пинч-зум реализуется через расстояние между двумя Touch с разными fingerId. Главный подводный камень — когда третий палец касается экрана во время пинча. Без обработки этого сценария fingerId смещаются и зум начинает прыгать. Решение: фиксировать fingerId двух пальцев в момент начала пинча и игнорировать все новые касания до завершения жеста.
На Android дополнительно проверяем MotionEvent.ACTION_POINTER_DOWN и ACTION_POINTER_UP через нативный плагин, если нужен доступ к давлению (pressure) и площади касания (size) — Unity Input.GetTouch эти данные не отдаёт в полном объёме.
Сроки и объём работ
Базовая система (тап, свайп, холд) для одной платформы: 2-4 дня. Полная система с мультитачем, пинчем, кастомными жестами и интеграцией с игровыми механиками: 1-2 недели. Стоимость рассчитывается индивидуально после анализа требований к проекту.







