Реалізація VR-взаємодії через Gaze (напрямок погляду) у мобільному додатку

TRUETECH займається розробкою, підтримкою та обслуговуванням мобільних додатків iOS, Android, PWA. Маємо великий досвід та експертизу для публікації мобільних додатків до популярних маркетів Google Play, App Store, Amazon, AppGallery та інші.

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1735 послуг
Реалізація VR-взаємодії через Gaze (напрямок погляду) у мобільному додатку
Середній
~3-5 днів
Часті запитання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    495

VR Gaze Interaction у мобільних додатках

У мобільному VR без контролерів gaze-інтерфейс — єдиний спосіб взаємодіяти зі світом. Здається простим: дивись на кнопку, вона натискається. На практиці неправильно реалізований gaze розсердить користувача швидше, ніж будь-якої інший UI-паттерн.

Gaze Raycast

Gaze визначається напрямком взгляду камери. Ray пускається з позиції камери вперед по Camera.main.transform.forward:

void FixedUpdate() {
    Ray gazeRay = new Ray(Camera.main.transform.position,
                          Camera.main.transform.forward);

    if (Physics.Raycast(gazeRay, out RaycastHit hit, maxGazeDistance, interactableLayer)) {
        var target = hit.collider.GetComponent<IGazeTarget>();
        if (target != null) {
            HandleGazeHit(target, hit.point);
        } else {
            HandleGazeMiss();
        }
    } else {
        HandleGazeMiss();
    }
}

FixedUpdate() замість Update() — стабільна частота вызовів не прив'язана до FPS. На слабких пристроях при просадках до 30 FPS Update() дає нерівномірний відклик.

interactableLayer — обов'язково. Raycast по всій сцені дорого, і користувач не повинен випадково активувати невидимі коллайдери.

Reticle (Курсор взгляду)

Reticle — візуальний індикатор точки взгляду. Розміщується у world space на поверхні об'єкта під взглядом. Відстань динамічна: reticle "прилипає" до hit point.

void UpdateReticle(Vector3 hitPoint, Vector3 hitNormal) {
    reticleTransform.position = hitPoint + hitNormal * RETICLE_OFFSET;
    reticleTransform.rotation = Quaternion.LookRotation(-hitNormal);

    // Масштаб постійний в кутових одиницях (constant apparent size)
    float dist = Vector3.Distance(Camera.main.transform.position, hitPoint);
    reticleTransform.localScale = Vector3.one * dist * ANGULAR_SIZE;
}

Коли об'єкта під взглядом нема — reticle на дефолтній відстані (3–5 метрів). Не ховайте його: користувач завжди повинен бачити, куди дивиться.

Dwell-активація та progress-індикатор

Користувач дивиться на об'єкт N секунд — відбувається активація. Оптимальний час dwell: 1,2–2,0 секунди. Менше 1 секунди — випадкові активації при огляді сцени. Більше 2 секунд — утомлює.

Progress повинен бути помітним. Заповнюючееся кільце навколо reticle — стандарт:

public class GazeDwellController : MonoBehaviour {
    [SerializeField] private float dwellTime = 1.5f;
    [SerializeField] private Image progressRing;

    private float dwellProgress = 0f;
    private IGazeTarget currentTarget;
    private bool isActivated = false;

    public void OnGazeEnter(IGazeTarget target) {
        currentTarget = target;
        dwellProgress = 0f;
        isActivated = false;
        progressRing.gameObject.SetActive(true);
    }

    public void OnGazeStay() {
        if (isActivated) return;
        dwellProgress += Time.deltaTime / dwellTime;
        progressRing.fillAmount = dwellProgress;

        if (dwellProgress >= 1f) {
            isActivated = true;
            currentTarget?.OnGazeActivate();
            StartCoroutine(ResetAfterDelay(0.5f));
        }
    }

    public void OnGazeExit() {
        currentTarget = null;
        progressRing.gameObject.SetActive(false);
        dwellProgress = 0f;
    }
}

Після активації — коротка cooldown перед наступною активацією того ж об'єкта (0,5–1,0 сек). Інакше користувач не встигає убрати взгляд і кнопка "натискується" двічі.

Hover-стан: зворотній зв'язок до активації

Коли користувач дивиться на об'єкт, але dwell ще не завершений, потрібен негайний візуальний зворотній зв'язок. Об'єкт повинен якось відреагувати при OnGazeEnter — до закінчення часу активації. Варіанти:

  • Підсвітка: зміна emission кольору матеріалу
  • Масштаб: об'єкт трохи збільшується (0,05f достатньо)
  • Анімація: іконка реагує на взгляд
  • Звуковий сигнал: короткий click при початку dwell

Без цього користувач не розуміє, "бачить" ли його додаток.

Cardboard button як підтвердження

У Cardboard є фізична кнопка (магнітний триггер). Додаємо її як альтернативний метод активації замість dwell — для продвинутих користувачів це швидше та зручніше:

// Cardboard SDK trigger event
void Update() {
    if (CardboardInput.GetButtonDown()) {
        TriggerCurrentGazeTarget();
    }
}

Кнопка — не замена dwell, а доповнення. Не всі корпуси Cardboard мають робочу магнітну кнопку.

Типові помилки реалізації

Занадто маленький коллайдер у інтерактивного об'єкта — користувач "промахується" повз кнопку. Коллайдер повинен бути на 10–20% більший за видимий об'єкт.

Активація срабатує при будь-якому русі взгляду повз об'єкт — не лише при намаганій фіксації. Вирішується мінімальним порогом кутової швидкості голови при старті dwell.

Reticle трясється від дрижання рук — gyro-fusion від Cardboard SDK згладжує це, але додатковий Lerp на позицію reticle (~20ms) убирає залишковий дрок.

Робочий процес

Аналіз інтерактивних елементів: типи об'єктів, сценарії взаємодії.

Реалізація raycast-системи з правильними шарами та коллайдерами.

Reticle у world space з constant apparent size.

Dwell controller з progress-індикатором, hover-станом, cooldown.

Cardboard button як альтернативний триггер.

Тестування комфорту: час dwell, розміри кнопок, зворотній зв'язок.

Оцінка часу

Базова gaze interaction система з reticle та dwell — 3–5 днів. Повнофункціональна система з кількома типами інтерактивних об'єктів, анімаціями, звуком та налаштовуваними параметрами — 1–2 тижні.