Escena de RA (Realidad Aumentada) en Unity con Vuforia en la que varios personajes se muevan de forma autónoma por un escenario virtual superpuesto al mundo real, utilizando el sistema de navegación con NavMesh.
- Configurar un proyecto de Unity con Vuforia Engine
- Crear una base de datos de marcadores en el portal de Vuforia con al menos tres marcadores
- Construir un escenario virtual asociado a uno de los marcadores
- Asociar personajes 3D a los otros marcadores
- Configurar navegación con NavMesh para que los personajes se desplacen por el escenario
- Implementar interacción táctil para dirigir a los personajes hacia un punto del escenario
Se puede ver la demo del proyecto en demo_RApp.mp4. Cualquier problema con la versión entregada por Moodle (tanto del proyecto como del README) se puede usar el repositorio donde se encuentra alojada la práctica: https://github.com/solsolet/LaberintPrim.git.
Para probar la aplicación directamente en un dispositivo Android (SDK <= 35) se puede instalar la APK: RApp.apk o descargándola de GitHub.
Assets/
├── Material/ # Materiales de los objetos
│ ├── FlowerMaterial.mat
│ └── GradientMaterial.mat # Lo tienen todos los objetos
│
├── Models/
│ ├── abella.fbx # Personaje 1
│ ├── pilota.fbx # Personaje 2
│ ├── fence.fbx # Entorno
│ └── sunflower.fbx # Suelo
│
├── Scenes/
│ └── MainScene.unity # Escena única del juego
│
├── Resources/
│ └── VuforiaConfiguration.asset
│
└── Scripts/
├── NavMeshBaker.cs # Hornea el NavMesh en runtime al detectar el marcador del suelo y teleporta los personajes sobre él
└── TouchNavigation.cs # Detecta toques en pantalla, lanza un raycast y ordena a los agentes desplazarse al punto tocado
MainScene
├── Directional Light
├── GlobalVolume
├── ARCamera # Cámara Vuforia: captura el vídeo real y gestiona el tracking de marcadores
├── HelloKittyTarget # ImageTarget que detecta el marcador Hello Kitty
│ └── Abella # Personaje 1 (esfera); al detectar el marcador aparece sobre él
├── StarTarget # Detecta el marcador Estrela
│ └── Pilota # Personaje 2 (esfera); al detectar el marcador aparece sobre él
├── FootTarget # Detecta el marcador Peu; contiene el escenario completo y el NavMesh
│ ├── Floor # Plano transitable con NavMeshSurface, MeshCollider y MeshRenderer
│ ├── Fence # Decoración del entorno
│ ├── ...
│ ├── Sunflower
│ └── ...
└── GameManager # Objeto vacío que contiene el script TouchNavigation; conecta la entrada táctil con los NavMeshAgentsLa integración con Vuforia se realizó en tres pasos:
-
Cuenta y License Key: Se creó una cuenta en el Vuforia Engine Developer Portal y se generó una License Key desde la sección Plan & Licenses. La clave se pegó en Vuforia Configuration dentro de Unity (
Ctrl+Shift+V). -
Base de datos de marcadores: Desde el Target Manager del portal se creó la base de datos
RAppDBde tipo Device. Se añadieron tres imágenes como Image Targets: una etiqueta Hello Kitty, una estrella y una imagen de un pie. Cada imagen se evaluó por Vuforia, que le asigna un rating de 0 a 5 estrellas según su calidad para tracking. Finalmente se descargó el.unitypackagecon la opción Unity Editor y se importó en el proyecto. -
Instalación en Unity: Vuforia Engine se instaló desde la Unity Asset Store. Al importar el paquete, se aceptaron los cambios automáticos en Player Settings. La ARCamera se añadió desde
GameObject > Vuforia Engine > AR Camerasustituyendo a la Main Camera por defecto.
| Marcador | Imagen | Función | Ancho real (m) | Rating Vuforia |
|---|---|---|---|---|
| HelloKitty | Etiqueta Hello Kitty | Personaje 1 | ~0.06 | ⭐⭐⭐⭐⭐ |
| Estrela | Pegatina estrella | Personaje 2 | ~0.05 | ⭐⭐⭐⭐ |
| Peu | Imagen pie | Escenario | ~0.45 | ⭐⭐⭐⭐⭐ |
Ajustes realizados en Player Settings y el proyecto:
- Graphics API: Se eliminó Vulkan de la lista de APIs de Android y se dejó únicamente OpenGLES3. Vuforia 11.4 no soporta Vulkan completamente y causaba pantalla negra y fallos de inicialización.
- Active Input Handling: Se cambió de Input System Package (New) a Both, necesario para que
UnityEngine.Inputfuncione junto con Vuforia en Android. - Scripting Backend: IL2CPP con arquitectura ARM64.
- Minimum API Level: Android 8.0 (API 26).
- Paquetes instalados: AI Navigation (com.unity.ai.navigation) para el sistema de NavMesh en runtime.
- Agent Type personalizado: Se creó un Agent Type llamado
Small(radio 0.05, altura 0.3) en Project Settings > AI Navigation, ya que el escenario tiene escala reducida (~0.035) y el Humanoid por defecto (radio 0.5) es mayor que el propio suelo. - Play Mode: Configurado en modo Simulator para pruebas en el editor sin webcam.
Se adjunta al FootTarget. Al detectar el marcador del pie por primera vez, ejecuta una corrutina que:
- Llama a
surface.BuildNavMesh()para hornear la malla de navegación en runtime sobre el suelo real. - Espera 2 frames para que los datos se registren completamente.
- Desvincula cada personaje de su ImageTarget padre (
SetParent(null)) para que Vuforia deje de controlar su posición. - Usa
NavMesh.SamplePositionpara encontrar el punto más cercano del NavMesh y teleporta los agentes allí. - Activa los componentes
NavMeshAgentde los personajes.
Se adjunta al GameManager. En cada frame comprueba si se ha pulsado la pantalla usando Input.GetMouseButtonDown(0) (funciona tanto en editor como en Android con la configuración Both). Si hay pulsación, lanza un raycast desde la ARCamera hacia la escena. Si impacta con el Floor (que tiene MeshCollider), llama a agent.SetDestination(hit.point) en todos los agentes que estén sobre el NavMesh.
Problema: Al hacer el primer build en Android la cámara aparecía en negro y Vuforia mostraba el warning "Vulkan support is currently experimental" y el error "Failed to start Vuforia".
Solución: En Player Settings > Android > Other Settings > Graphics APIs se eliminó Vulkan y se dejó solo OpenGLES3. Vuforia 11.4 no tiene soporte completo para Vulkan en Android.
Problema: BuildNavMesh() se ejecutaba sin errores pero NavMesh.SamplePosition siempre fallaba, incluso con radio de búsqueda de 50 metros. El NavMesh generado estaba vacío.
Solución: El Agent Type por defecto (Humanoid) tiene radio 0.5 m y altura 2 m, pero el suelo escalado mide apenas ~0.35 m de ancho. El bake no genera superficie si ningún punto del suelo puede alojar al agente. Se creó un Agent Type Small con radio 0.05 y altura 0.3, asignado tanto al NavMeshSurface como a los NavMeshAgents.
Problema: Los agentes recibían el destino correctamente (visible en logcat: "Hit: Floor") pero no se movían visualmente. Vuforia reasignaba la posición de los hijos de un ImageTarget cada frame, cancelando el movimiento del NavMeshAgent.
Solución: Al teletransportar los personajes al NavMesh se llama primero a agent.transform.SetParent(null), desvinculándolos del ImageTarget. A partir de ese momento el NavMeshAgent controla libremente su posición.
Problema: El script de navegación usaba Input.GetTouch() pero nunca se disparaba en el dispositivo. Vuforia registra sus propios handlers de toque en Android y consume los eventos antes de que lleguen al sistema de Unity.
Solución: Reemplazar Input.GetTouch() por Input.GetMouseButtonDown(0), que en Unity mapea el primer toque táctil al botón 0 del ratón y no es interceptado por Vuforia.
Problema: El primer build tardaba ~30 minutos en el paso "Shader Universal Render Pipeline".
Solución: Este paso solo ocurre la primera vez; Unity cachea los shaders compilados en la carpeta Library/. Builds posteriores con Patch and Run tardan ~1 minuto. También se configuró Shader Stripping en modo Automatic en Project Settings > Graphics.
Problema: Al cambiar el script a Input.GetMouseButtonDown(0) apareció el error "You are trying to read Input using the UnityEngine.Input class, but you have switched active Input handling to Input System package".
Solución: En Player Settings > Other Settings > Active Input Handling se cambió de Input System Package (New) a Both, permitiendo usar tanto el Input System nuevo como la clase UnityEngine.Input clásica.
Problema: Al intentar probar en Android la aplicación tenía que buildearla y podría tardar mínimo media hora. La primera fase del desarrollo fue debatirme entre ir haciendo cambios y avanzando con la aplicación y encontrar la causa que tuviese que compilar 72497 shaders (a pesar que al principio usaba primitivas de unity y materiales planos), era incomprensible. Toqueteé la configuración del proyecto en cuanto a gráficos muchas veces intentando bajarle todo tipo de resolución, luces, sombras... para que fuese lo más básica posible pero a´ùn así tardaba lo mismo.
Solución: Se estaba usando la configuración gráfica de SampleSceneProfile en lugar de la que estaba toqueteando que era Mobile_RPAsset, por tanto mis cambios nos se veían reflejados por que donde estaban no se estaba usando. Al cambiar de archivo el tiempo bjó a minuto y poco.
Todos los objetos 3D del proyecto (suelo, paredes, personajes) fueron hechos con primitivas geométricas de Unity (Plane, Cube, Sphere) sin assets externos en primera instancia para probar el juego. En la versión final los modelos y los materiales están hechos todos por mi íntegramente.