Войти
UnityСтатьи

Реализуем multi-touch жесты самостоятельно

Автор:

В этой статье будет рассмотрена реализация pan (перемещения) и pinch zoom (масштабирования двумя пальцами) изображения. В результате получится некий аналог приложения для просмотра фотографий.

Часть первая (с картинками). Подготовка
Часть вторая. Код
  Перемещение
  Масштабирование

Часть первая (с картинками). Подготовка


Создадим новый проект и настроим камеру на ортогональную проекцию (свойство Projection = Orthographic). Компоненты GUILayer, Flare Layer и Audio Listener можно отключить или вообще удалить за ненадобностью.

После этого сохраним нашу сцену (Ctrl+S), назвав её как-нибудь, например, MainScene.unity. В итоге экран редактора дожен выглядеть примерно так:

+ Показать

Теперь зададим режим отрисовки объектов (если в игре не будет ничего, кроме спрайтов):
Edit -> Project Settings -> Player -> Other settings -> Rendering -> Rendering Path = Vertex Lit

Нам нужно создать какой-то объект, который будет отображаться на экране, чтобы мы видели, как он двигается. Для этой цели могла бы подойти плоскость (Game Object ->Create Other -> Plane), но она состоит из 400 треугольников, что неоправданно много для отображения одного спрайта. Поэтому воспользуемся скриптом отсюда http://wiki.unity3d.com/index.php/CreatePlane

Создадим папку Editor в папке Assets и добавим туда новый файл CreatePlane.cs, текст которого можно найти по ссылке выше.

После этого в меню появится новый пункт GameObject -> Create Other -> Custom Plane... Создадим новую плоскость с такими параметрами:
2 | Реализуем multi-touch жесты самостоятельно
На экране появится розовый квадрат - это наш нетекстурированный объект.

Добавим какую-нибудь картинку в папку Assets, создадим для него новый материал и установим для него текстурой только что добавленное изображение. Для материала поставим шейдер Unlit/Texture:

+ Показать

Наконец, установим материал для объекта:

+ Показать

Теперь у нас есть неподвижный объект на экране, и нам нужно написать код, который будет двигать камеру.

Часть вторая. Код

Создадим новый файл с текстом скрипта под названием CameraScript и добавим его к MainCamera (перетянем в окно Hierarchy на MainCamera). Затем запишем в него такой текст:

+ Показать

Перемещение

Сначала рассмотрим вариант с перемещением. Для него нам нужен:
а) 1 флаг drag типа boolean, который будет говорить, что мы находимся в режиме перемещения камеры
б) 1 точка касания initialTouchPosition, которая хранит начальные экранные координаты точки касания экрана пальцем
в) 1 мировые координаты камеры initialCameraPosition в момент первоначального касания пальцем экрана

Код для перемещения камеры одним пальцем очень прост. Для начала проверяем, что экрана касается только один палец:

if (Input.touchCount == 1)
{
    ...
}
Внутри первым делом отменяем операцию масштабирования, чтобы она не мешала коду перемещения, и получаем экранные координаты точки касания:
zoom = false;
Touch touch0 = Input.GetTouch(0);
Если это первый кадр, который инициирует перемещение, то устанавливаем соответствующий флаг и запоминаем координаты точки касания и текущих координаты камеры:
if (!drag)
{
    initialTouchPosition = touch0.position;
    initialCameraPosition = this.transform.position;
    drag = true;
}
Если же это последующие после начала касания кадры, то мы определяем, насколько сместился палец относительно начальной точки касания и перемещаем камеру в обратную сторону, чтобы скомпенсировать это движение, и объект под пальцем остался в той же точке:
else
{
    Vector2 delta = camera.ScreenToWorldPoint(touch0.position) -
                              camera.ScreenToWorldPoint(initialTouchPosition);

    Vector3 newPos = initialCameraPosition;
    newPos.x -= delta.x;
    newPos.y -= delta.y;

    this.transform.position = newPos;
}

Масштабирование

Код для двуточечного масштабирования немного сложнее. Как и в прошлый раз, сначала проверяем, что экрана касается 2 пальца:

if (Input.touchCount == 2)
{
    ...
}
Затем отменяем операцию перемещения и получаем экранные координаты обеих точек касания:
drag = false;

Touch touch0 = Input.GetTouch(0);
Touch touch1 = Input.GetTouch(1);
Для первого кадра, в котором экрана касаются оба пальца одновременно, нужно запомнить 5 переменных:
а) 2 точки касания initialTouch0Position и initialTouch1Position
б) 1 мировые координаты камеры initialCameraPosition в момент первоначального касания пальцев экрана
в) 1 размер половины видимого объёма камеры initialOrthographicSize
г) 1 координаты точки initialMidPointScreen посередине точек касания

Каждый последующий кадр сбрасываем координаты камеры и её "размер" к значениям на момент первоначального касания обеих пальцев экрана для того, чтобы каждый следующий кадр не накапливались изменения в положении/размере камеры.

this.transform.position = initialCameraPosition;
camera.orthographicSize = initialOrthographicSize;
Затем находим, во сколько раз увеличивать или уменьшать размер области вывода камеры. Это значение есть отношение длины отрезка, образованного расположением пальцев на текущем кадре, к длине отрезка, образованного расположением пальцев на первом кадре.
float scaleFactor = GetScaleFactor(touch0.position,
                                            touch1.position,
                                            initialTouch0Position,
                                            initialTouch1Position);
Далее находим среднюю точку между двумя пальцами на текущем кадре:
Vector2 currentMidPoint = (touch0.position + touch1.position) / 2;
Следующие 4 строки вычисляют разницу в мировых координатах средней точки касания после изменения размера камеры, так как при изменении размера экранные координаты средней точки остаются неизменными, а мировые могут поменяться, так как масштабирование камеры всегда происходит относительно центра экрана:
Vector3 initialMidPointWorldBeforeZoom = camera.ScreenToWorldPoint(initialMidPointScreen);
Camera.main.orthographicSize = initialOrthographicSize / scaleFactor;
Vector3 initialMidPointWorldAfterZoom = camera.ScreenToWorldPoint(initialMidPointScreen);
Vector2 initialMidPointDelta = initialMidPointWorldBeforeZoom - initialMidPointWorldAfterZoom;
Наконец, вычисляем между мировыми координатами средней точки на текущем и первом
кадре:
Vector2 oldAndNewMidPointDelta =
                    camera.ScreenToWorldPoint(currentMidPoint) -
                    camera.ScreenToWorldPoint(initialMidPointScreen);
и корректируем положение камеры, учитывая все поправки:
Vector3 newPos = initialCameraPosition;
newPos.x -= oldAndNewMidPointDelta.x - initialMidPointDelta.x;
newPos.y -= oldAndNewMidPointDelta.y - initialMidPointDelta.y;

this.transform.position = newPos;

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

Хотя код прверялся только на Android, он должен работать без изменений и на iOS.

Исходный код можно скачать по адресу https://github.com/Skybladev2/Unity3D-pan-and-zoom-example
Готовое приложение для Android можно скачать отсюда: http://rghost.ru/41537997

#Android, #iOS, #multitouch, #pan, #pinch, #zoom

20 октября 2012 (Обновление: 29 ноя. 2012)

Комментарии [4]