Unity
- Utilizo como plataforma WebGL, que como sabéis significa Web Graphics Library, es una API (interfaz de programación de aplicaciones) utilizada para renderizar gráficos 3D y 2D en navegadores web sin necesidad de plugins y permite a los desarrolladores crear gráficos interactivos y animaciones utilizando JavaScript y el estándar de gráficos OpenGL ES. La mayoria de los navegadores web ya incorporan este framework por defecto.
- Compression Format: a Gzip.
- Data Caching: a check.
- Decompression Fallback: a check.
Archivos de la Build en Unity
static/Build/ ├── Builds.data ├── Builds.framework.js ├── Builds.loader.js └── Builds.wasm |
static/TemplateData/
├── style.css
└── [assets visuales]
|
index.html |
void HandleJoystickInput(string jsonData)
void HandleButtonInput(string jsonData)
void HandleInputMethod(string jsonData)
void HandleJoystickInput(string jsonData)
{
if (GameManager.instancia.inputRemote)
{
JoystickData data = JsonUtility.FromJson(jsonData);
movement = new Vector3(data.x, 0, data.y) * moveSpeed;
animator.SetFloat("Horizontal", data.x);
animator.SetFloat("Vertical", data.y);
}
}
void HandleButtonInput(string jsonData)
{
if (GameManager.instancia.inputRemote)
{
ButtonData data = JsonUtility.FromJson(jsonData);
if (data.state == "pressed")
{
switch (data.action)
{
case "up":
{
movement = Vector3.forward * moveSpeed;
animator.SetFloat("Horizontal", movement.x);
animator.SetFloat("Vertical", movement.y);
break;
}
case "down":
{
movement = Vector3.back * moveSpeed;
animator.SetFloat("Horizontal", movement.x);
animator.SetFloat("Vertical", movement.y);
break;
}
case "left":
{
movement = Vector3.left * moveSpeed;
animator.SetFloat("Horizontal", movement.x);
animator.SetFloat("Vertical", movement.y);
break;
}
case "right":
{
movement = Vector3.right * moveSpeed;
animator.SetFloat("Horizontal", movement.x);
animator.SetFloat("Vertical", movement.y);
break;
}
case "A":
{
break;
}
case "B":
{
// Iniciar el desplazamiento a la siguiente esquina
GameManager.instancia.indiceEsquinaActual = (GameManager.instancia.indiceEsquinaActual + 1) % GameManager.instancia.esquinas.Length;
GameManager.instancia.offset = GameManager.instancia.esquinas[GameManager.instancia.indiceEsquinaActual];
break;
}
}
}
else if (data.state == "released")
{
if (data.action != "A" && data.action != "B")
{
movement = Vector3.zero;
}
}
}
}
void HandleInputMethod(string jsonData)
{
InputMethodData data = JsonUtility.FromJson(jsonData);
switch (data.state)
{
case "on": GameManager.instancia.inputRemote = true; break;
case "off": GameManager.instancia.inputRemote = false; break;
}
}
[System.Serializable]
public class JoystickData
{
public float x;
public float y;
}
[System.Serializable]
public class ButtonData
{
public string action;
public string state;
}
[System.Serializable]
public class InputMethodData
{
public string state;
}
Objeto importante en Unity
Python y JavaScript
Componentes Principales
Servidor WebSocket (app.py)
Cliente Controlador (controller.js)
Cliente Unity (websocket.js)
|
Partes del código que cabe resaltar
handleGameControl(data) {
if (!this.gameInstance) {
console.warn('Game instance not ready, retrying connection...');
setTimeout(() => this.socket.connect(), 1000);
return;
}
// Send the control data to Unity
switch(data.type) {
case 'joystick':
this.gameInstance.SendMessage('MaribelController', 'HandleJoystickInput',
JSON.stringify(data.data));
break;
case 'button':
this.gameInstance.SendMessage('MaribelController', 'HandleButtonInput',
JSON.stringify(data.data));
break;
case 'inputMethod':
this.gameInstance.SendMessage('MaribelController', 'HandleInputMethod',
JSON.stringify(data.data));
break;
}
}
Error en la versión de producción (Deployments)
@app.route('/static/Build/')
def serve_build(filename):
try:
response = send_from_directory('static/Build', filename)
if filename.endswith('.wasm'):
response.headers['Content-Type'] = 'application/wasm'
response.headers['Cross-Origin-Embedder-Policy'] = 'require-corp'
response.headers['Cross-Origin-Opener-Policy'] = 'same-origin'
response.headers['Cross-Origin-Resource-Policy'] = 'cross-origin'
response.headers['Cache-Control'] = 'no-cache'
response.headers['Accept-Ranges'] = 'bytes'
# Asegurarse de que no haya compresión
response.direct_passthrough = True
if 'Content-Encoding' in response.headers:
del response.headers['Content-Encoding']
if 'Content-Length' in response.headers:
del response.headers['Content-Length']
return response
except Exception as e:
app.logger.error(f'Error serving {filename}: {str(e)}')
return str(e), 500
Técnica empleada y herramientas adicionales🔧
- Replit
- Unity versión 2022.3
- Copilot como asistente en programación C#
- Información adicional en https://github.com/jmcaneda/MobileJoystickControllerDef
Asset Store 🏬
He utilizado los siguientes packages de Asset Store, casi siempre en su versión free o con mínima inversión.
Elementos necesarios 🔑
- La app requiere un dispositivo móvil, con capacidad para el escaneo de código QR y navegador web, compatible o habilitado para JavaScript (la mayoría ya lo tienen configurado por defecto).
- La aplicación comenzará con el linkado desde el navegador web, compatible con WebGL (la mayoría ya lo son por defecto). Recomiendo que se realice desde equipo de sobremesa o portátil.
- Desde el dispositivo móvil escanearemos el código QR y dispondremos el mismo en apaisado. Es importante que veamos el "connected" en verde, esto es indicativo que podemos hacer uso del dispositivo.
- El foco debe estar puesto en el navegador, esto lo podemos conseguir posicionando el ratón en el mismo. Cambiaremos el input mode, en el dispositivo móvil, de Off a On y ya está, podremos manejar a nuestro personaje "Maribel", en su recorrido por las calles, escuchando los mensajes y procurando no ser convertida por los "UrbanZombies", de dos maneras: como Joystick o por medio de los botones de desplazamiento. El botón "B", esta programado para el cambio de posición de la cámara.
- Requiere de una conexión a internet.
_
✨ C.A.C. (Soho Málaga), supuso para nosotros (los de entonces), un lugar de encuentro.

