Алгоритм для жування тачскріна

Не так давно вийшла моя перша особиста гра для мобільних. Суть полягає в тому, що ворогів треба пережовувати пальцями. Алгоритм не унікальний, але зустрічається рідко. На перший погляд, потрібно тільки прослухати зум-рух двох пальців, начебто нічого складного, проте в процесі розробки гри виявляються різні підводні камені.

  • Обчислення часу на стиснення щелеп;
  • Поєднання жування з управлінням персонажем;
  • Зміна параметрів по ходу тестів.
Весь код написаний на мові с# для движка Unity3D, для 2Д гри. Перейдемо безпосередньо до коду. У методі Update обчислюємо кількість тачей, і проводимо відповідні дії. Рухаємо персонажа у випадку одного дотику:

//Якщо один дотик
if (Input.touchCount == 1) {
//Якщо щелепи не стискаються або не розтискаються, персонаж рухається до місця дотику
if (!compressing && !decompressing) { 
Touch singleTouch = Input.GetTouch(0);
Vector3 targetPoint = Camera.main.ScreenToWorldPoint (singleTouch.position);
targetPoint = new Vector3 (targetPoint.x, targetPoint.y, 0);
transform.position = Vector3.MoveTowards (transform.position, targetPoint, movementSpeed * Time.deltaTime);
}
}

Тут нічого складного, можна рухатися далі. Код обробки двох торкань. Якщо немає стискання/розтиснення щелеп, то персонаж переміщується між двох пальців.

if (Input.touchCount > 1) {
//Робота з першими двома дотиками.
Touch touch1 = Input.GetTouch(0);
Touch touch2 = Input.GetTouch(1);
//Якщо щелепи не працюють, пересуваємо персонажа між пальцями
if (!compressing && !decompressing) {
Vector3 targetPoint = Camera.main.ScreenToWorldPoint ((touch1.position + touch2.position) / 2);
targetPoint = new Vector3 (targetPoint.x, targetPoint.y, 0);
transform.position = Vector3.MoveTowards (transform.position, targetPoint, movementSpeed * Time.deltaTime);
}

float currentDistance = Vector2.Distance(touch1.position, touch2.position);
if(pastFingersDistance == 0) {
//Обнулення минулого відстані, якщо перший раз засечено два тача
pastFingersDistance = currentDistance; 
}else if(currentDistance < pastFingersDistance - fingersMunchDetectionMin) { 
//Метод включення стиснення щелеп. Керування графікою, у кожного індивідуально.
SetCompression(); 
}else if(currentDistance > pastFingersDistance + fingersMunchDetectionMin) {
//Метод включення розтиснення щелеп. Керування графікою, у кожного індивідуально.
SetDecompression(); 
}
}
//Обнулення змінної, для того щоб обчислювати необхідність жування щодо нової позиції пальців.
if(Input.touchCount < 2) pastFingersDistance = 0;
//Якщо торкань стало менше двох, а щелепи стиснуті - вони автоматично розтискаються.
if(Input.touchCount < 2 && isCompressed) SetDecompression(); 

fingersMunchDetectionMin — змінна, що визначає яку відстань достатньо для того, щоб почати жування. Досить довго налаштовував з допомогою кількох друзів. У кожного виявилося різне сприйняття, вивів щось середнє. У ході тестів також з'ясувалося, що постійно жувати пальцями користувачеві просто незручно. Виникла необхідність зробити стискання щелеп по простому тапу і метод, викладений вище, має наступний вигляд:

if (Input.touchCount > 1) {
//Робота з першими двома дотиками.
Touch touch1 = Input.GetTouch(0);
Touch touch2 = Input.GetTouch(1);
//Перевірка якщо щелепи не працюють
if (!compressing && !decompressing) { 
float touch1Time = 0;
float touch2Time = 0;
//Обчислюється скільки часу активний тач 1
if (tapsHash.Contains (touch1.fingerId)) {
float startTouch1Time = (float) tapsHash [touch1.fingerId];
touch1Time = Time.time - startTouch1Time;
}
//Обчислюється скільки часу активний тач 2
if (tapsHash.Contains (touch2.fingerId)) {
float startTouch2Time = (float) tapsHash [touch2.fingerId];
touch2Time = Time.time - startTouch2Time;
}
//Якщо час відведений на тап вже перевищено для двох пальців, персонаж пересувається між пальцями.
if (touch1Time > SECONDS_FOR_TAP && touch2Time > SECONDS_FOR_TAP) {
Vector3 targetPoint = Camera.main.ScreenToWorldPoint ((touch1.position + touch2.position) / 2);
targetPoint = new Vector3 (targetPoint.x, targetPoint.y, 0);
transform.position = Vector3.MoveTowards (transform.position, targetPoint, movementSpeed * Time.deltaTime);
}
}
float currentDistance = Vector2.Distance(touch1.position, touch2.position);
if(pastFingersDistance == 0) {
//Обнулення минулого відстані, якщо перший раз засечено два тача
pastFingersDistance = currentDistance; 
}else if(currentDistance < pastFingersDistance - fingersMunchDetectionMin) {
//Метод включення стиснення щелеп. Керування графікою, у кожного індивідуально.
SetCompression(); 
}else if(currentDistance > pastFingersDistance + fingersMunchDetectionMin) {
//Метод включення розтиснення щелеп. Керування графікою, у кожного індивідуально.
SetDecompression();
}
}
//Обнулення змінної, для того щоб обчислювати необхідність жування щодо нової позиції пальців.
if(Input.touchCount < 2) pastFingersDistance = 0;
//Якщо торкань стало менше двох, а щелепи стиснуті - вони автоматично розтискаються.
if(Input.touchCount < 2 && isCompressed) SetDecompression(); 
//Метод, який відповідає за здійснення жування по тапу.
SetTapAttackListener ();

Константа SECONDS_FOR_TAP — час, відведений на тап, як і відстань на жування, досить довго тестувалася і налаштовувалася. Ну і власне останні методи, які здійснюють жування по простому тапу:

void SetTapAttackListener() {
if (Input.touchCount > 0) {
foreach (Touch touch in Input.touches) {
//Обробка активного тача
DetectOneTouchTap (touch); 
}
}
}

void DetectOneTouchTap(Touch touch) {
if (touch.phase == TouchPhase.Began) {
//У разі якщо тач тільки почався, він записується в хеш-таблицю для обробки.
//Ключ - ідентифікатор тача, значення - початок дотику.
tapsHash.Add (touch.fingerId, Time.time); 
} else if(touch.phase == TouchPhase.Ended) {
float startTouchTime = (float) tapsHash [touch.fingerId];
float timeOfTouch = Time.time - startTouchTime;
//Здійснення стискання і розтискання щелеп, якщо тач був тапом
if (timeOfTouch <= SECONDS_FOR_TAP) {
SetCompression();
SetDecompression();
}
tapsHash.Remove (touch.fingerId);
}
}

На початку намагався знайти цей алгоритм на просторах інтернету, не для копіпаста, а для перевірки свого ходу думок. Однак нічого не знайшов і вирішив викласти його в допомогу колегам. Зараз дуже добре бачу, що код дещо хаотичний, ну а в іншому — чекаю коментарів.

Update 1:
Демонстрація роботи алгоритму:


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.