[Перевод] Реализация последовательностей жестов в Unity 3D с помощью библиотеки TouchScript
Для многих игровых приложений, особенно работающих на небольших экранах мобильных устройств, очень важно уменьшить область, занимаемую элементами управления, чтобы максимально расширить часть экрана, предназначенную для отображения основного контента. Для этого можно настроить цели касания (touch targets) так, чтобы они обрабатывали различные комбинации жестов. Таким образом, количество целей касания на экране сократится до минимума. Например, два элемента интерфейса, один из которых заставляет пушку стрелять, а второй — вращаться, можно заменить на один, позволяющий выполнять оба действия одним непрерывным касанием.В этой статье я расскажу о том, как настроить сцену для управления контроллером от первого лица при помощи целей касания. Прежде всего необходимо настроить цели касания для базовой позиции контроллера и вращения, а затем расширить набор их функций. Последнего можно достичь за счет существующих элементов интерфейса, не добавляя новые объекты. Сцена, которая у нас получится, продемонстрирует широкие возможности Unity 3D в ОС Windows* 8 как платформы для обработки различных последовательностей жестов.Настройка сцены в Unity* 3D Для начала необходимо настроить сцену. Для этого импортируем в Unity* 3D ресурс ландшафта в формате .fbx с горами и деревьями, экспортированный из Autodesk 3D Studio Max*. Поместим контроллер в центре ландшафта.Установим показатель глубины основной камеры (она входит в состав контроллера) на уровень -1. Создадим отдельный элемент интерфейса камеры с поддержкой ортогональной проекции, шириной 1 и высотой 0,5, а также флагами Don«t Clear. Затем создадим слой GUIWidget и сделаем его маской интерфейса камеры.Расположим основные элементы интерфейса, управляющие контроллером, на сцене в поле обзора ортогональной камеры. Добавим сферу для каждого пальца левой руки. Сфера мизинца заставляет контроллер двигаться влево, сфера безымянного пальца — вперед, среднего пальца — вправо, а указательного пальца — назад. Сфера большого пальца позволяет прыгать и запускать сферические снаряды под углом 30 градусов по часовой стрелке.Для элемента интерфейса правой руки создадим куб (квадрат в ортогональной проекции). Настроим для этого куба поддержку жеста сдвига и привяжем его к скрипту MouseLook.cs. Этот элемент интерфейса обеспечивает такие же возможности, как и сенсорная панель устройства Ultrabook.Эти элементы интерфейса расположим вне поля обзора основной камеры, а в качестве слоя установим GUIWidget. На рис. 1 можно увидеть, как элементы интерфейса позволяют запускать снаряды и управлять позицией контроллера на сцене.Рисунок 1. Сцена контроллера от первого лица с ландшафтом и запущенными сферическими снарядами.
На этой сцене снаряды, запущенные из контроллера, пролетают сквозь деревья. Чтобы это исправить, необходимо добавить сетку или коллайдер к каждому дереву. Другая проблема на этой сцене — низкая скорость движения вперед — возникает при попытке посмотреть вниз с помощью сенсорной панели одновременно с движением вперед с помощью сферы безымянного пальца. Чтобы устранить эту неполадку, можно ограничить угол обзора при взгляде вниз, когда удерживается кнопка движения вперед.
Множественные касания На базовой сцене находится контроллер от первого лица, который запускает снаряды под определенным углом по направлению от центра (см. Рисунок 1). По умолчанию установлен угол в 30 градусов по часовой стрелке.Настроим сцену для поддержки множественных касаний, выполняемых чаще, чем установленный период, изменим угол запуска снарядов и попробуем запустить снаряд. В этом случае можно настроить угол на возрастание в геометрической прогрессии в зависимости от числа касаний при помощи переменных типа float в скрипте для сферы большого пальца слева. Эти переменные регулируют угол и время с момента запуска последнего снаряда: private float timeSinceFire = 0.0f; private float firingAngle = 30.0f; Далее настроим цикл Update в скрипте для сферы большого пальца так, чтобы угол запуска снарядов снижался, если касания сферы большого пальца выполняются чаще, чем раз в полсекунды. В том случае, если касания последуют реже, чем раз в полсекунды, или угол запуска снарядов снизится до 0 градусов, величина угла запуска снарядов вернется к показателю 30 градусов. Получится следующий код: timeSinceFire += Time.deltaTime;
if (timeSinceFire <= 0.5f) { firingAngle += -l.0f; } else { firingAngle = 30.0f; } timeSinceFire = 0.0f;
if (firingAngle <= 0) { firingAngle = 30; }
projectileSpawnRotation = Quaternion.AngleAxis (firingAngle, CH.transform.up); Такой код произведет эффект обстрела, при котором непрерывные касания приведут к запуску снарядов под постоянно уменьшающимся углом (см. рис. 2). Этот эффект можно позволить настраивать пользователям либо сделать доступным при соблюдении определенных условий в игре или режиме симуляции.Рисунок 2. Непрерывные касания приводят к изменению направления запуска снарядов
Масштабирование прокруткой Мы настроили квадрат в правой нижней части экрана на рис. 1 на работу в режиме, аналогичном сенсорной панели на клавиатуре. При жесте сдвига квадрат не двигается, а поворачивает основную камеру сцены вверх, вниз, влево и вправо с помощью скрипта контроллера MouseLook. Аналогичным образом жест масштабирования (схожий со растягиванием/сжатием на других платформах) приводит не к масштабированию квадрата, а к изменению поля зрения основной камеры, благодаря которому пользователь может приблизить или отдалить объект на основной камере (см. Рисунок 3). Настроим контроллер таким образом, чтобы жест сдвига сразу после масштабирования возвращал поле зрения камеры к значению по умолчанию 60 градусов.Для этого нужно запрограммировать логическую переменную (panned) и переменную типа float, чтобы они отмечали время, прошедшее от последнего жеста масштабирования: private float timeSinceScale; private float timeSincePan; private bool panned; Установим для переменной timeSinceScale значение 0.0f при выполнении жеста масштабирования, а для переменной panned — значение True при выполнении жеста сдвига. Поле зрения основной камеры сцены настраивается в цикле Update, как можно увидеть в скрипте для прямоугольника-сенсорной панели: timeSinceScale += Time.deltaTime; timeSincePan += Time.deltaTime;
if (panned && timeSinceScale >= 0.5f && timeSincePan >= 0.5f) { fieldOfView += 5.0f; panned = false; }
if (panned && timeSinceScale <= 0.5f) { fieldOfView = 60.0f; panned = false; }
Camera.main.fieldOfView = fieldOfView; Рассмотрим функции onScale и onPan. Обратите внимание на то, что переменная float timeSincePan не позволяет полю зрения постоянно увеличиваться, когда для управления камерой используется сенсорная панель: private void onPanStateChanged (object sender, GestureStateChangeEventArgs e) { switch (e.State) { case Gesture.GestureState.Began: case Gesture.GestureState.Changed: var target = sender as PanGesture; Debug.DrawRay (transform.position, target.WorldTransformPlane.normal); Debug.DrawRay (transform.position, target.WorldDeltaPosition.normalized);
var local = new Vector3(transform.InverseTransformDirection (target.WorldDeltaPosition).x, transform.InverseTransformDirection (target.WorldDeltaPosition).y, 0); targetPan += transform.InverseTransformDirection (transform.TransformDirection (local));
//if (transform.InverseTransformDirection (transform.parent.TransformDirection (targetPan -startPos)).y < 0) targetPan = startPos; timeSincePan = 0.0f; panned = true; break; } } private void onScaleStateChanged(object sender, GestureStateChangeEventArgs e) { switch (e.State) { case Gesture.GestureState.Began: case Gesture.GestureState.Changed: var gesture = (ScaleGesture)sender;
if (Math.Abs (gesture.LocalDeltaScale) > 0.01) { fieldOfView *= gesture.LocalDeltaScale;
if (fieldOfView >= 170){fieldOfView = 170;} if (fieldOfView <= 1){fieldOfView = 1;}
timeSinceScale = O.Of; } break; } }
Рисунок 3. Основная камера сцены с изображением, приближенным при помощи прямоугольника сенсорной панели справа.
Нажатие, отпускание с щелчком Если нажать и отпустить сферу мизинца, а затем в течение полусекунды провести по ней, можно увеличить горизонтальную скорость контроллера.Для поддержки этой функции добавим переменную типа float и логическую переменную, которые позволят отмечать время с жестов отпускания сферы мизинца и жеста щелчка по ней: private float timeSinceRelease; private bool flicked; При первоначальной установке сцены я предоставил скрипту сферы мизинца доступ к скрипту контроллера InputController, чтобы сфера мизинца слева могла заставлять контроллер двигаться влево. Переменная, управляющая горизонтальной скоростью контроллера, находится не в скрипте InputController, а в скрипте CharacterMotor. Передать скрипт сферы левого мизинца скрипту CharacterMotor можно аналогичным образом: CH = GameObject.Find («First Person Controller»); CHFPSInputController = (FPSInputController)CH.GetComponent («FPSInputController»); CHCharacterMotor = (CharacterMotor)CH.GetComponent («CharacterMotor»); Функция onFlick в нашем скрипте лишь позволяет установить для логической переменной flicked значение, равное True.Функция Update в скрипте вызывается один раз за кадр и изменяет движение контроллера по горизонтали следующим образом: if (flicked && timeSinceRelease <= 0.5f) { CHCharacterMotor.movement.maxSidewaysSpeed += 2.0f; flicked = false; }
timeSinceRelease += Time.deltaTime; } Благодаря этому коду можно увеличивать скорость движения по горизонтали. Для этого нужно нажать и отпустить сферу мизинца, а затем провести по ней в течение полсекунды. Настроить замедление движения по горизонтали можно различными способами. Например, для этого можно использовать нажатие и отпускание сферы указательного пальца, а затем щелчок по ней. Обратите внимание на то, что способ CHCharacterMotor.movement содержит не только параметр maxSidewaysSpeed, но также gravity, maxForwardsSpeed, maxBackwardsSpeed, и другие. Использование разнообразных жестов библиотеки TouchScript и объектов, которые обрабатывают жесты, в сочетании с этими параметрами обеспечивает широкие возможности и различные стратегии разработки сенсорных интерфейсов в сценах Unity 3D. При создании сенсорных интерфейсов для таких приложений можно пробовать различные варианты и выбирать наиболее эффективные и эргономичные.Проблемы с последовательностями жестов Последовательности жестов, которые мы использовали в этой статье, существенно зависят от функции Time.deltaTime. Я применяю эту функцию в сочетании с разными жестами до и после того, как функция определит действие. Две основные проблемы, с которыми я столкнулся при настройке этих случаев, заключаются в величине временного интервала и характере жестов.Временной интервал При написании этой статьи я использовал интервал в полсекунды. Если я выбирал интервал равный одной десятой секунды, устройство не могло распознать последовательности жестов. Хотя мне казалось, что темп касаний был достаточно высок, это не приводило к нужным действиям на экране. Возможно, это связано с задержками в работе устройства и ПО, поэтому рекомендую при разработке последовательностей жестов учитывать производительность целевой платформы.Жесты При работе над этим примером я изначально собирался использовать жесты масштабирования и сдвига, а затем касания и щелчка. Масштабирование и сдвиг работали правильно, но перестали, как только я добавил жест касания. Хотя мне удалось настроить последовательности из жестов масштабирования и сдвига, она не очень удобна для пользователя. Более удачным вариантом будет настроить другую цель касания в элементе интерфейса на обработку жестов касания и проведения после масштабирования и сдвига.В этом примере я использую интервал времени в полсекунды, чтобы определять, выполнено действие или нет. Также можно настроить несколько временных интервалов, хотя это и приведет к усложнению интерфейса. Например, последовательность жестов нажатия и отпускания и следующего в течение полсекунды за ними проведения может увеличивать скорость движения по горизонтали, а аналогичная последовательность с интервалом от полсекунды до секунды — снижать скорость. Используя временные интервалы таким образом, можно не только более гибко настроить пользовательский интерфейс, но и добавить на сцену скрытые секреты.Заключение В этой статье я настроил сцену с различными последовательностями жестов в Unity* 3D с помощью библиотеки TouchScript на устройстве Ultrabook с ОС Windows 8. Цель реализации этих последовательностей — сократить область экрана, с помощью которой пользователь управляет приложением. В этом случае можно выделить большую площадь экрана для показа привлекательного контента.В тех случаях, когда последовательности жестов не работали правильно, нам удавалось найти подходящее альтернативное решение. Одной из задач настройки было добиться правильной работы последовательности действий с помощью функции Time.deltaTime на имеющемся устройстве. Таким образом, сцена, которую мы создали в Unity 3D для этой статьи, подтверждает жизнеспособность ОС Windows 8 на устройствах Ultrabook как платформы для разработки приложений с использованием последовательностей жестов.