Como crear un video juego en realidad aumentada – Parte Uno

De Cero a Héroe en WebAR: Tu Primer Videojuego (Parte 1 – Controles e Interacción)

imagen con fondo gris, un automovil color azul y monedas color dorado cerca del carro, un titulo en color negro que dice tu primer video juego con realidad aumentada
How to create a videogame with augmented reality

Antes de comenzar con el post quiero agradecer a la comunidad de Sketchfab y a los usuarios que crearon los modelos 3D en formato .glb que utilice para este tutorial.

Ahora si comencemos con el tutorial para que aprendas como crear tu primer video juego en realidad aumentada (How to create an augmented reality videogame).

Hoy vamos a empezar un viaje para desmitificar el desarrollo en WebAR. En esta primera parte, construiremos los cimientos de nuestro juego: una interfaz funcional y un modelo 3D interactivo que cobra vida en tu propio entorno.

Usando un sencillo minijuego de un coche controlable como ejemplo, te guiaré a través de los conceptos clave. Al final de este tutorial, tendrás una base sólida y funcional, lista para que en la segunda parte le añadamos más elementos a nuestra app de RA.

¿Por Qué WebAR es el Futuro de las Experiencias Interactivas?

WebAR significa «Realidad Aumentada en la Web». Su mayor ventaja es la inmediatez. Los usuarios solo necesitan escanear la imagen que sirve como marcador de realidad aumentada o visitar un enlace con su móvil leyendo un código QR. Automáticamente, la cámara se activa y el mundo digital cobra vida sobre el entorno real. No hay barreras, no hay tiendas de aplicaciones, solo interacción pura. Esto es revolucionario para el marketing, la educación y, por supuesto, los videojuegos.

Nuestro Proyecto: Un carro Interactivo usando realidad aumentada y controles estilo video juego
Para ilustrar el proceso, hemos creado una base sólida para un minijuego. Al apuntar la cámara de tu móvil a un marcador de imagen aparecerá un coche de juguete en 3D que podrás controlar en tiempo real.

El marcador que vamos a utilizar para este tutorial será este:

Un carro de color azul con fondo de imagen de color gris y una monedas de color doradas cerca del carro. un texto en color negro que dice tu primer video juego con realidad aumentada y una direccion web que apunta ha blog.realidad-aumentada.com.co
How to create an augmented reality videogame

Al final de este tutorial debes de tener una app de realidad aumentada WebAR que se debe de ver así:

Tal vez te pueda interesar también:

Funcionalidades Clave de esta Primera Parte:

  • Reconocimiento de Marcador: La escena AR se ancla a una imagen del mundo real.
  • Modelo 3D Interactivo: Un coche con animaciones y efectos.
  • Controles Virtuales: Un joystick en pantalla para mover el coche y botones para acciones especiales.
  • Efectos Visuales: Sistemas de partículas para simular el humo del escape.
  • Animaciones: El coche puede realizar una acción predefinida al tocar un botón.
  • Todo esto funciona directamente en el navegador de cualquier smartphone moderno.

Las Herramientas que utilizaremos para nuestro proyecto RA:

Nuestro proyecto se apoya en un trío tecnológico fundamental que todo desarrollador WebAR debería conocer:

  • HTML (El Esqueleto): Es la base de nuestra aplicación. Define la estructura, como el contenedor donde vivirá la escena AR y los divs para nuestros controles (joystick y botones).
  • CSS (El Estilo): Se encarga de que todo se vea bien. Con CSS, posicionamos el joystick en la esquina inferior izquierda, los botones en la derecha, y les damos un aspecto atractivo y semitransparente para que no obstruyan la vista del mundo real.
  • JavaScript (La Magia): Aquí es donde ocurre toda la acción. Utilizamos dos librerías potentísimas:
  • Three.js: Es el motor 3D por excelencia para la web. Nos permite crear escenas, cargar modelos 3D (como nuestro coche en formato .glb), gestionar luces, cámaras y renderizar todo en pantalla.
  • MindAR.js: Esta es nuestra librería de realidad aumentada. Se integra con Three.js para hacer el trabajo pesado: acceder a la cámara, detectar el marcador de imagen y proyectar nuestra escena 3D sobre él en el lugar y ángulo correctos.

Un Vistazo al Código: ¿Cómo Funciona?
Aunque nuestro script tiene muchas líneas, la lógica se puede resumir en varios pasos clave. Gracias a los comentarios en el código que dejare en cada linea, puedes explorar cada detalle, pero aquí tienes una visión general:

  • Configuración de la Escena (Función setupAR)
    Primero, inicializamos MindAR y le decimos qué marcador debe buscar. Luego, creamos una escena básica de Three.js con luces para que nuestro modelo sea visible.
// Inicializamos MindAR y lo vinculamos al contenedor HTML.
mindarThree = new MindARThree({
container: document.querySelector("#ar-container"),
imageTargetSrc: "./targets.mind"
});

// Creamos un "ancla" en el marcador. Todo lo que añadamos aquí aparecerá sobre la imagen.
const anchor = mindarThree.addAnchor(0);
  • Carga del Modelo 3D (GLTFLoader)
    Usamos el GLTFLoader de Three.js para cargar nuestro archivo low-poly_cartoon_style_car_03.glb. Una vez cargado, lo añadimos al «ancla» que creamos antes.
const loader = new GLTFLoader();
loader.load("./low-poly_cartoon_style_car_03.glb", (gltf) => {
carModel = gltf.scene; // El modelo 3D.
// ...ajustamos su escala y rotación...
anchor.group.add(carModel); // ¡Lo añadimos a la escena!
});
  • Creación de Controles Interactivos
    Vinculamos los eventos táctiles (touchstart, touchmove) y del ratón (mousedown, mousemove) a nuestros elementos HTML. La lógica del joystick calcula la distancia del dedo desde el centro y la convierte en un vector de movimiento para el coche. La clave para que funcione en móviles es rastrear el identifier de cada toque, permitiendo así el multi-touch.
// Cuando un dedo toca el joystick, guardamos su ID único.
const joystickTouchStart = (e) => {
if (joystickTouchId !== null) return; // Si ya hay un dedo, ignoramos este.
const touch = e.changedTouches[0];
joystickTouchId = touch.identifier;
// ...
};
  • Dando Vida con Animaciones y Efectos
    Para la animación del botón B, usamos el AnimationMixer de Three.js. Al cargar el modelo, extraemos sus animaciones y preparamos una acción. El botón B simplemente la activa.
const pressButtonB = (e) => {
e.preventDefault();
if (carAnimationAction) {
carAnimationAction.reset().play(); // Reinicia y reproduce la animación.
}
};

Pasemos a la sección de código mas detallado.

Estructura HTML del video juego RA

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MindAR Car Controller - v22 FINAL</title>

<!-- Enlace a la hoja de estilos CSS -->
<link rel="stylesheet" href="style.css">

<!-- Import Map para gestionar las librerías de JavaScript -->
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/",
"mindar-image-three": "https://cdn.jsdelivr.net/npm/mind-ar@1.2.5/dist/mindar-image-three.prod.js"
}
}
</script>
</head>
<body>
<!-- Contenedor principal donde se renderizará la escena de Realidad Aumentada -->
<div id="ar-container"></div>

<!-- Contenedor para la interfaz de usuario (controles) que se superpone a la vista AR -->
<div id="ui-container">
<!-- Control de Joystick para el movimiento -->
<div id="joystick-container">
<div id="joystick-base">
<div id="joystick-handle"></div>
</div>
</div>

<!-- Contenedor para los botones de acción -->
<div id="buttons-container">
<div id="button-b" class="action-button">B</div>
<div id="button-a" class="action-button">A</div>
</div>
</div>

<!-- Enlace al archivo JavaScript principal, de tipo "module" para usar imports -->
<script src="script.js" type="module"></script>
</body>
</html>

Estilos CSS para la interfaz del juego

/* Estilos generales para el cuerpo del documento */
body {
margin: 0; /* Elimina el margen por defecto */
font-family: Arial, sans-serif; /* Establece una fuente estándar */
}

/* Contenedor principal para la vista de Realidad Aumentada */
#ar-container {
width: 100vw; /* Ocupa el 100% del ancho de la ventana */
height: 100vh; /* Ocupa el 100% de la altura de la ventana */
position: relative; /* Posicionamiento base para elementos hijos */
overflow: hidden; /* Oculta cualquier contenido que se desborde */
}

/* Contenedor de la interfaz de usuario (UI) */
#ui-container {
position: absolute; /* Se superpone sobre el contenedor AR */
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10; /* Se asegura de que esté por encima de la vista AR */
pointer-events: none; /* Por defecto, no intercepta clics para permitir la interacción con la escena AR si fuera necesario */
}

/* Contenedor del Joystick */
#joystick-container {
position: absolute; /* Posicionamiento fijo en la pantalla */
bottom: 30px; /* Abajo */
left: 30px; /* A la izquierda */
width: 120px;
height: 120px;
pointer-events: auto; /* Permite que este elemento sí reciba clics y toques */
}

/* Base circular del Joystick */
#joystick-base {
position: relative;
width: 100%;
height: 100%;
background: rgba(128, 128, 128, 0.4); /* Fondo semitransparente */
border-radius: 50%; /* Lo hace un círculo perfecto */
border: 2px solid rgba(255, 255, 255, 0.5);
}

/* Palanca del Joystick (la parte que se mueve) */
#joystick-handle {
position: absolute;
width: 50px;
height: 50px;
background: rgba(255, 255, 255, 0.7);
border-radius: 50%;
left: 50%; /* Centrado horizontalmente */
top: 50%; /* Centrado verticalmente */
transform: translate(-50%, -50%); /* Ajuste fino para un centrado perfecto */
cursor: grab; /* Cambia el cursor para indicar que es movible */
}

/* Contenedor para los botones de acción (A y B) */
#buttons-container {
position: absolute;
bottom: 40px; /* Abajo */
right: 30px; /* A la derecha */
display: flex; /* Organiza los botones en una fila */
gap: 20px; /* Espacio entre los botones */
align-items: center; /* Alinea verticalmente los botones */
pointer-events: auto; /* Permite que los botones reciban clics y toques */
}

/* Estilo común para todos los botones de acción */
.action-button {
width: 60px;
height: 60px;
border-radius: 50%; /* Círculos perfectos */
border: 2px solid rgba(255, 255, 255, 0.7);
display: flex; /* Para centrar el texto dentro del botón */
justify-content: center; /* Centrado horizontal del texto */
align-items: center; /* Centrado vertical del texto */
font-size: 24px;
font-weight: bold;
color: white;
user-select: none; /* Evita que el texto del botón se pueda seleccionar */
cursor: pointer; /* Indica que es clickeable */
}

/* Color de fondo específico para el botón A */
#button-a {
background: rgba(220, 50, 50, 0.6); /* Rojo semitransparente */
}

/* Color de fondo específico para el botón B */
#button-b {
background: rgba(50, 150, 220, 0.6); /* Azul semitransparente */
}

Ahora pasemos al cerebro de la App de realidad aumentada (El Javascript).

// Importamos las librerías necesarias desde el "importmap" del HTML.
// Three.js es el motor 3D.
import * as THREE from 'three';
// MindARThree es el conector entre MindAR y Three.js para realidad aumentada basada en imágenes.
import { MindARThree } from 'mindar-image-three';
// GLTFLoader se usa para cargar modelos 3D en formato .glb o .gltf.
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

// Esperamos a que todo el contenido del DOM (la página HTML) se haya cargado antes de ejecutar el script.
document.addEventListener('DOMContentLoaded', () => {
// --- DECLARACIÓN DE VARIABLES GLOBALES ---

// Variables para la escena de AR y el reloj para la animación.
let mindarThree = null, clock = new THREE.Clock();

// Variables para el modelo 3D del coche y sus sistemas de partículas (humo).
let carModel = null, smokeParticleSystem = null, smokeParticleGeometry = null, burstParticleSystem = null, burstParticlesData = [];

// Variables para controlar el movimiento y la velocidad del coche.
let moveVector = { x: 0, y: 0 }, moveSpeed = 0.3;

// Constantes para definir la cantidad de partículas en cada sistema de humo.
const smokeParticleCount = 50, burstParticleCount = 100;

// Variables para gestionar los controles táctiles del joystick.
let isDragging = false;
let joystickTouchId = null; // Guardará el ID único del dedo que controla el joystick.

// Variables para el sistema de animación del modelo 3D.
let mixer = null; // El "reproductor" de animaciones.
let carAnimationAction = null; // La acción de animación específica a reproducir.

// --- FUNCIÓN PRINCIPAL DE CONFIGURACIÓN DE LA REALIDAD AUMENTADA ---
const setupAR = async () => {
// Inicializamos MindAR, lo vinculamos al contenedor HTML y le decimos dónde encontrar el archivo del marcador.
mindarThree = new MindARThree({
container: document.querySelector("#ar-container"),
imageTargetSrc: "./targets.mind"
});

// Extraemos el renderizador, la escena y la cámara de la instancia de MindAR.
const { renderer, scene, camera } = mindarThree;

// Añadimos una luz ambiental para iluminar toda la escena de manera uniforme.
scene.add(new THREE.AmbientLight(0xffffff, 0.8));

// Añadimos una luz direccional (como el sol) para generar sombras y dar más realismo.
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 7.5);
scene.add(directionalLight);

// Creamos un "ancla" en el marcador de imagen (índice 0). Todo lo que añadamos a esta ancla aparecerá sobre el marcador.
const anchor = mindarThree.addAnchor(0);

// Creamos una instancia del cargador de modelos GLTF.
const loader = new GLTFLoader();
// Cargamos el modelo 3D del coche.
loader.load("./low-poly_cartoon_style_car_03.glb", (gltf) => {
// Cuando el modelo se carga, esta función se ejecuta.
carModel = gltf.scene; // La escena del archivo .glb es nuestro modelo.

// Ajustamos la rotación, escala y posición inicial del coche.
carModel.rotation.x = Math.PI / 2;
carModel.scale.set(0.3, 0.3, 0.3); // Escala inicial del modelo.
carModel.position.set(0, 0, 0);

// Añadimos el coche al ancla para que aparezca sobre el marcador.
anchor.group.add(carModel);

// Si el archivo .glb contiene animaciones, las configuramos.
if (gltf.animations && gltf.animations.length) {
mixer = new THREE.AnimationMixer(carModel); // Creamos el reproductor de animaciones para el coche.
const clip = gltf.animations[0]; // Seleccionamos la primera animación disponible.
carAnimationAction = mixer.clipAction(clip); // Creamos una acción controlable con esa animación.
carAnimationAction.setLoop(THREE.LoopOnce); // Hacemos que la animación se reproduzca solo una vez por clic.
carAnimationAction.clampWhenFinished = true; // Mantiene el estado final de la animación al terminar.
}

// Una vez cargado el coche, creamos los sistemas de partículas.
createContinuousSmoke();
createBurstSmoke();
});

// Creamos una instancia del cargador de texturas.
const textureLoader = new THREE.TextureLoader();
// Cargamos la imagen que usaremos para las partículas de humo.
const smokeTexture = await textureLoader.loadAsync('./Humo.png');

// Función para crear el sistema de humo continuo (el del escape).
const createContinuousSmoke = () => {
smokeParticleGeometry = new THREE.BufferGeometry();
const positions = new Float32Array(smokeParticleCount * 3);
for (let i = 0; i < smokeParticleCount * 3; i++) positions[i] = (Math.random() - 0.5) * 0.1;
smokeParticleGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const particleMaterial = new THREE.PointsMaterial({ map: smokeTexture, size: 400.0, transparent: true, blending: THREE.AdditiveBlending, depthWrite: false, sizeAttenuation: true });
smokeParticleSystem = new THREE.Points(smokeParticleGeometry, particleMaterial);
smokeParticleSystem.position.z = -1; // Posicionamos el emisor de humo detrás del coche.
carModel.add(smokeParticleSystem); // Lo añadimos como hijo del coche para que se mueva con él.
};

// Función para crear el sistema de ráfaga de humo (del botón A).
const createBurstSmoke = () => {
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(burstParticleCount * 3);
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const material = new THREE.PointsMaterial({ map: smokeTexture, size: 600.0, transparent: true, opacity: 0, depthWrite: false, sizeAttenuation: true });
burstParticleSystem = new THREE.Points(geometry, material);
burstParticleSystem.position.z = -1; // Lo posicionamos en el mismo lugar.
burstParticleSystem.visible = false; // Lo hacemos invisible hasta que se presione el botón A.
carModel.add(burstParticleSystem);
};

// Iniciamos el motor de MindAR (esto pide permiso para la cámara).
await mindarThree.start();

// --- BUCLE DE ANIMACIÓN (GAME LOOP) ---
// Esto se ejecuta en cada fotograma.
renderer.setAnimationLoop(() => {
const delta = clock.getDelta(); // Calcula el tiempo transcurrido desde el último fotograma.

// Si el reproductor de animaciones existe, lo actualizamos.
if (mixer) {
mixer.update(delta);
}
// Si el modelo del coche ya se cargó, actualizamos su posición y rotación.
if (carModel) {
carModel.position.x += moveVector.x * moveSpeed * delta;
carModel.position.y -= moveVector.y * moveSpeed * delta;
if (moveVector.x !== 0 || moveVector.y !== 0) {
carModel.rotation.y = Math.atan2(moveVector.x, moveVector.y);
}
}
// Si el sistema de humo continuo existe, animamos sus partículas.
if (smokeParticleSystem) {
const positions = smokeParticleGeometry.attributes.position.array;
for (let i = 0; i < smokeParticleCount * 3; i += 3) {
positions[i + 2] -= 0.005; // Movemos cada partícula hacia atrás.
if (positions[i + 2] < -0.6) { // Si una partícula se aleja demasiado...
positions[i + 2] = Math.random() * 0.05; // ...la reiniciamos cerca del origen.
positions[i] = (Math.random() - 0.5) * 0.05;
}
}
smokeParticleGeometry.attributes.position.needsUpdate = true; // Notificamos a Three.js que las posiciones han cambiado.
}
// Si la ráfaga de humo está visible, animamos sus partículas.
if (burstParticleSystem && burstParticleSystem.visible) {
const positions = burstParticleSystem.geometry.attributes.position.array;
for (let i = 0; i < burstParticleCount; i++) {
positions[i * 3] += burstParticlesData[i].velocity.x;
positions[i * 3 + 1] += burstParticlesData[i].velocity.y;
positions[i * 3 + 2] += burstParticlesData[i].velocity.z;
}
burstParticleSystem.material.opacity -= delta * 1.5; // Hacemos que se desvanezca.
if (burstParticleSystem.material.opacity <= 0) {
burstParticleSystem.visible = false; // La ocultamos cuando es invisible.
}
burstParticleSystem.geometry.attributes.position.needsUpdate = true;
}
// Renderizamos la escena con la cámara en cada fotograma.
renderer.render(scene, camera);
});
};

// Llamamos a la función principal para que todo comience.
setupAR();

// --- LÓGICA DE CONTROLES DE LA INTERFAZ DE USUARIO ---

// Obtenemos las referencias a los elementos HTML de los controles.
const joystickHandle = document.getElementById('joystick-handle');
const joystickBase = document.getElementById('joystick-base');
const buttonA = document.getElementById('button-a');
const buttonB = document.getElementById('button-b');
let joystickRadius = 0;

// Función para actualizar la posición del joystick y el vector de movimiento del coche.
const updateJoystickPosition = (clientX, clientY) => {
if (joystickRadius === 0) joystickRadius = joystickBase.offsetWidth / 2;
const baseRect = joystickBase.getBoundingClientRect();
const centerX = baseRect.left + joystickRadius;
const centerY = baseRect.top + joystickRadius;
let deltaX = clientX - centerX;
let deltaY = clientY - centerY;
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance > joystickRadius) {
deltaX = (deltaX / distance) * joystickRadius;
deltaY = (deltaY / distance) * joystickRadius;
}
joystickHandle.style.transform = `translate(-50%, -50%) translate(${deltaX}px, ${deltaY}px)`;
moveVector.x = deltaX / joystickRadius;
moveVector.y = deltaY / joystickRadius;
};

// Función para reiniciar el estado del joystick.
const resetJoystick = () => {
isDragging = false;
joystickHandle.style.transform = `translate(-50%, -50%)`;
joystickHandle.style.cursor = 'grab';
moveVector = { x: 0, y: 0 };
};

// Lógica para el control con el Mouse (PC).
const joystickStart = (e) => { e.preventDefault(); isDragging = true; joystickHandle.style.cursor = 'grabbing'; };
const joystickMove = (e) => { if (!isDragging) return; updateJoystickPosition(e.clientX, e.clientY); };
const joystickEnd = () => { if (isDragging) resetJoystick(); };

// Lógica para el control Táctil (Móviles), que soporta Multi-Touch.
const joystickTouchStart = (e) => {
if (joystickTouchId !== null) return; // Si ya hay un dedo en el joystick, no hacer nada.
e.preventDefault();
const touch = e.changedTouches[0];
joystickTouchId = touch.identifier; // Guardamos el ID de este dedo.
isDragging = true;
joystickHandle.style.cursor = 'grabbing';
};
const joystickTouchMove = (e) => {
if (joystickTouchId === null) return;
e.preventDefault();
// Buscamos entre los dedos que se mueven cuál es el que controla el joystick.
for (let i = 0; i < e.changedTouches.length; i++) {
const touch = e.changedTouches[i];
if (touch.identifier === joystickTouchId) {
updateJoystickPosition(touch.clientX, touch.clientY);
break;
}
}
};
const joystickTouchEnd = (e) => {
if (joystickTouchId === null) return;
// Comprobamos si el dedo que se levantó es el que controlaba el joystick.
for (let i = 0; i < e.changedTouches.length; i++) {
const touch = e.changedTouches[i];
if (touch.identifier === joystickTouchId) {
resetJoystick();
joystickTouchId = null; // Liberamos el control para que otro dedo pueda usarlo.
break;
}
}
};

// Función que se ejecuta al presionar el botón A.
const pressButtonA = (e) => {
e.preventDefault();
if (!carModel || !burstParticleSystem || burstParticleSystem.visible) return;
const positions = burstParticleSystem.geometry.attributes.position.array;
burstParticlesData = [];
// Creamos las partículas de la ráfaga con una velocidad inicial.
for (let i = 0; i < burstParticleCount; i++) {
positions[i * 3] = 0; positions[i * 3 + 1] = 0; positions[i * 3 + 2] = 0;
burstParticlesData.push({ velocity: new THREE.Vector3((Math.random() - 0.5) * 0.01, (Math.random() - 0.5) * 0.01, (Math.random() - 0.5) * 0.05 - 0.05) });
}
burstParticleSystem.geometry.attributes.position.needsUpdate = true;
burstParticleSystem.material.opacity = 1.0;
burstParticleSystem.visible = true; // Hacemos visible la ráfaga.
};

// Función que se ejecuta al presionar el botón B.
const pressButtonB = (e) => {
e.preventDefault();
// Si la acción de animación existe, la reinicia y la reproduce.
if (carAnimationAction) {
carAnimationAction.reset().play();
}
};

// --- ASIGNACIÓN DE EVENTOS A LOS ELEMENTOS ---

// Eventos para el control con Mouse.
joystickHandle.addEventListener('mousedown', joystickStart);
document.addEventListener('mousemove', joystickMove);
document.addEventListener('mouseup', joystickEnd);
buttonA.addEventListener('mousedown', pressButtonA);
buttonB.addEventListener('mousedown', pressButtonB);

// Eventos para el control Táctil en dispositivos móviles.
joystickHandle.addEventListener('touchstart', joystickTouchStart, { passive: false });
document.addEventListener('touchmove', joystickTouchMove, { passive: false });
document.addEventListener('touchend', joystickTouchEnd);
document.addEventListener('touchcancel', joystickTouchEnd); // En caso de que el toque se interrumpa.
buttonA.addEventListener('touchstart', pressButtonA, { passive: false });
buttonB.addEventListener('touchstart', pressButtonB, { passive: false });
});

¿Por Qué Deberías Aprender a Desarrollar en WebAR?
Aprender a manejar estas herramientas no solo te permite crear videojuegos geniales, sino que te abre las puertas a una industria en plena expansión. Las marcas buscan nuevas formas de conectar con sus clientes a través de probadores virtuales, manuales interactivos y publicidad inmersiva. El sector educativo está adoptando la AR para visualizar desde moléculas hasta monumentos históricos.

Dominar WebAR te posiciona en la vanguardia de la creación de contenido digital, una habilidad cada vez más demandada y con un potencial creativo ilimitado.

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Scroll al inicio
0
Would love your thoughts, please comment.x
()
x