Cómo crear un juego multiusuario en tiempo real desde cero

 

 

 

  • SmashingConf UX y diseño, Amberes 2024
  • Implemente rápidamente. Implementar inteligentemente

  • Índice
    1. Primer prototipo funcional (terrible)
      1. Validar la idea, deshacerse del prototipo
    2. Configuración del proyecto
      1. Estado de juego centralizado y sincronizado con Colyseus
      2. Creando un esquema
      3. Configuración de varias habitaciones (“Match-Making”)
      4. Aplicación Física en un Colyseus
      5. Ciclo vital
    3. Aplicación del lado del cliente
      1. ¿Por qué SvelteKit?
      2. Crear y almacenar PIN de juegos
      3. Accediendo al juego
      4. Conexión con Colyseus y actualización del estado
    4. Molestias menores
      1. Errores y advertencias con SvelteKit

    Este artículo destaca el proceso, las decisiones técnicas y las lecciones aprendidas detrás de la creación del juego en tiempo real Autowuzzler. Aprenda a compartir el estado del juego entre varios clientes en tiempo real con Colyseus, realice cálculos físicos con Matter.js, almacene datos en Supabase.io y cree la interfaz con SvelteKit.

     

    Mientras la pandemia persistía, el equipo con el que trabajo, repentinamente remoto, se vio cada vez más privado de futbolín . Pensé en cómo jugar al futbolín en un entorno remoto, pero estaba claro que simplemente reconstruir las reglas del futbolín en una pantalla no sería muy divertido.

    Lo divertido es patear una pelota usando carritos de juguete, algo que me di cuenta mientras jugaba con mi hijo de 2 años. Esa misma noche me propuse construir el primer prototipo de un juego que se convertiría en Autowuzzler .

    La idea es simple : los jugadores conducen autos de juguete virtuales en una arena de arriba hacia abajo que se asemeja a una mesa de futbolín. Gana el primer equipo que marque 10 goles.

    Por supuesto, la idea de utilizar coches para jugar al fútbol no es única, pero dos ideas principales deberían diferenciar a Autowuzzler : quería reconstruir parte de la apariencia de jugar en una mesa de futbolín física, y quería asegurarme de que así fuera. lo más fácil posible para invitar a amigos o compañeros de equipo a un juego casual rápido.

    En este artículo, describiré el proceso detrás de la creación de Autowuzzler , qué herramientas y marcos elegí, y compartiré algunos detalles de implementación y lecciones que aprendí.

    Autowuzzler (beta) con seis jugadores simultáneos en dos equipos. ( Vista previa grande )

    Primer prototipo funcional (terrible)

    El primer prototipo se construyó utilizando el motor de juegos de código abierto Phaser.js , principalmente por el motor de física incluido y porque ya tenía algo de experiencia con él. La etapa del juego estaba integrada en una aplicación Next.js , nuevamente porque ya tenía un conocimiento sólido de Next.js y quería centrarme principalmente en el juego.

    Como el juego necesita admitir varios jugadores en tiempo real , utilicé Express como intermediario de WebSockets. Sin embargo, aquí es donde se vuelve complicado.

    Dado que los cálculos de física se realizaron en el cliente en el juego Phaser, elegí una lógica simple, pero obviamente defectuosa: el primer cliente conectado tenía el dudoso privilegio de hacer los cálculos de física para todos los objetos del juego, enviando los resultados al servidor express, que a su vez transmitía las posiciones, ángulos y fuerzas actualizados a los clientes del otro jugador. Luego, los otros clientes aplicarían los cambios a los objetos del juego.

    Esto llevó a la situación en la que el primer jugador pudo ver la física sucediendo en tiempo real (después de todo, está sucediendo localmente en su navegador), mientras que todos los demás jugadores estaban retrasados ​​al menos 30 milisegundos (la velocidad de transmisión que elegí). ), o, si la conexión de red del primer jugador era lenta, considerablemente peor.

    Si esto le parece una mala arquitectura, tiene toda la razón. Sin embargo, acepté este hecho a favor de conseguir rápidamente algo jugable para descubrir si el juego es realmente divertido .

    Validar la idea, deshacerse del prototipo

    A pesar de lo defectuosa que fue la implementación, era lo suficientemente jugable como para invitar a amigos a una primera prueba de manejo. Los comentarios fueron muy positivos y la principal preocupación fue, como era de esperar, el rendimiento en tiempo real. Otros problemas inherentes incluyeron la situación en la que el primer jugador (recuerde, el que está a cargo de todo ) abandonó el juego: ¿quién debería hacerse cargo? En ese momento solo había una sala de juegos, por lo que cualquiera podía unirse al mismo juego. También estaba un poco preocupado por el tamaño del paquete que introdujo la biblioteca Phaser.js.

     

    Había llegado el momento de deshacerse del prototipo y empezar con una nueva configuración y un objetivo claro.

    Configuración del proyecto

    Claramente, el enfoque de “el primer cliente gobierna todo” necesitaba ser reemplazado por una solución en la que el estado del juego reside en el servidor . En mi investigación, me encontré con Colyseus , que parecía la herramienta perfecta para el trabajo.

    Para los otros componentes principales del juego elegí:

    • Matter.js como motor de física en lugar de Phaser.js porque se ejecuta en Node y Autowuzzler no requiere un marco de juego completo.
    • SvelteKit como marco de aplicación en lugar de Next.js, porque acababa de entrar en versión beta pública en ese momento. (Además: me encanta trabajar con Svelte ).
    • Supabase.io para almacenar PIN de juegos creados por el usuario.

    Veamos esos bloques de construcción con más detalle.

    Estado de juego centralizado y sincronizado con Colyseus

    Colyseus es un marco de juego multijugador basado en Node.js y Express. En esencia, proporciona:

    • Sincronizar el estado entre clientes de manera autorizada;
    • Comunicación eficiente en tiempo real utilizando WebSockets enviando únicamente datos modificados;
    • Configuraciones de varias habitaciones;
    • Bibliotecas cliente para JavaScript, Unity, Defold Engine, Haxe, Cocos Creator, Construct3;
    • Enlaces del ciclo de vida, por ejemplo, creación de una sala, entrada de un usuario, salida del usuario y más;
    • Enviar mensajes, ya sea como mensajes de difusión a todos los usuarios de la sala o a un solo usuario;
    • Un panel de monitoreo incorporado y una herramienta de prueba de carga.

    Nota : Los documentos de Colyseus facilitan el inicio con un servidor Colyseus básico al proporcionar un npm initscript y un repositorio de ejemplos .

    Creando un esquema

    La entidad principal de una aplicación Colyseus es la sala de juegos, que contiene el estado de una instancia de una sola sala y todos sus objetos de juego. En el caso de Autowuzzler , se trata de una sesión de juego con:

    • dos equipos,
    • una cantidad finita de jugadores,
    • una bola.

    Es necesario definir un esquema para todas las propiedades de los objetos del juego que deben sincronizarse entre los clientes . Por ejemplo, queremos que la pelota se sincronice y por eso necesitamos crear un esquema para la pelota:

    class Ball extends Schema { constructor() { super(); this.x = 0; this.y = 0; this.angle = 0; this.velocityX = 0; this.velocityY = 0; }}defineTypes(Ball, { x: "number", y: "number", angle: "number", velocityX: "number", velocityY: "number"});

    En el ejemplo anterior, se crea una nueva clase que amplía la clase de esquema proporcionada por Colyseus; en el constructor, todas las propiedades reciben un valor inicial. La posición y el movimiento de la pelota se describen utilizando las cinco propiedades: x, y, angle, velocityX, velocityY. Además, debemos especificar los tipos de cada propiedad . Este ejemplo utiliza la sintaxis de JavaScript, pero también puede utilizar la sintaxis de TypeScript, un poco más compacta .

     

    Los tipos de propiedad pueden ser tipos primitivos:

    • string
    • boolean
    • number(así como tipos enteros y flotantes más eficientes)

    o tipos complejos:

    • ArraySchema(similar a Matriz en JavaScript)
    • MapSchema(similar a Mapa en JavaScript)
    • SetSchema(similar a Establecer en JavaScript)
    • CollectionSchema(similar a ArraySchema, pero sin control sobre los índices)

    La Ballclase anterior tiene cinco propiedades de tipo number: sus coordenadas ( x, y), su corriente angley el vector de velocidad ( velocityX, velocityY).

    El esquema para jugadores es similar, pero incluye algunas propiedades más para almacenar el nombre del jugador y el número del equipo, que deben proporcionarse al crear una instancia de jugador:

    class Player extends Schema { constructor(teamNumber) { super(); this.name = ""; this.x = 0; this.y = 0; this.angle = 0; this.velocityX = 0; this.velocityY = 0; this.teamNumber = teamNumber; }}defineTypes(Player, { name: "string", x: "number", y: "number", angle: "number", velocityX: "number", velocityY: "number", angularVelocity: "number", teamNumber: "number",});

    Finalmente, el esquema de Autowuzzler Room conecta las clases definidas previamente: una instancia de sala tiene varios equipos (almacenados en un ArraySchema). También contiene una sola bola, por lo tanto creamos una nueva instancia de Ball en el constructor de RoomSchema. Los jugadores se almacenan en un MapSchema para una recuperación rápida utilizando sus ID.

    class RoomSchema extends Schema { constructor() { super(); this.teams = new ArraySchema(); this.ball = new Ball(); this.players = new MapSchema(); }}defineTypes(RoomSchema, { teams: [Team], // an Array of Team ball: Ball, // a single Ball instance players: { map: Player } // a Map of Players});

    Configuración de varias habitaciones (“Match-Making”)

    Cualquiera puede unirse a un juego de Autowuzzler si tiene un PIN de juego válido. Nuestro servidor Colyseus crea una nueva instancia de Sala para cada sesión de juego tan pronto como el primer jugador se une y descarta la sala cuando el último jugador la abandona.

    El proceso de asignar jugadores a la sala de juego deseada se llama "emparejamiento". Colyseus hace que sea muy fácil de configurar utilizando el filterBymétodo al definir una nueva habitación:

     

    gameServer.define("autowuzzler", AutowuzzlerRoom).filterBy(['gamePIN']);

    Ahora, cualquier jugador que se una al juego con el mismo gamePIN(veremos cómo “unirse” más adelante) terminará en la misma sala de juego. Cualquier actualización de estado y otros mensajes de transmisión están limitados a los jugadores en la misma sala.

    Aplicación Física en un Colyseus

    Colyseus ofrece muchas funciones listas para usar para comenzar a funcionar rápidamente con un servidor de juegos autorizado, pero deja en manos del desarrollador la creación de la mecánica real del juego, incluida la física. Phaser.js, que utilicé en el prototipo, no se puede ejecutar en un entorno que no sea un navegador, pero el motor de física integrado de Phaser.js, Matter.js, se puede ejecutar en Node.js.

    Con Matter.js, defines un mundo físico con ciertas propiedades físicas como su tamaño y gravedad. Proporciona varios métodos para crear objetos de física primitiva que interactúan entre sí adhiriéndose a leyes de la física (simuladas), incluida la masa, las colisiones, el movimiento con fricción, etc. Puedes mover objetos aplicando fuerza , tal como lo harías en el mundo real.

    Un “mundo” de Matter.js se encuentra en el corazón del juego Autowuzzler ; define qué tan rápido se mueven los autos, qué tan rebotante debe ser la pelota, dónde están ubicadas las porterías y qué sucede si alguien dispara a una portería.

    let ball = Bodies.circle( ballInitialXPosition, ballInitialYPosition, radius, { render: { sprite: { texture: '/assets/ball.png', } }, friction: 0.002, restitution: 0.8 });World.add(this.engine.world, [ball]);

    Código simplificado para agregar un objeto de juego de “pelota” al escenario en Matter.js.

    Una vez definidas las reglas, Matter.js puede ejecutarse con o sin representar algo en una pantalla. Para Autowuzzler , estoy utilizando esta función para reutilizar el código del mundo de la física tanto para el servidor como para el cliente, con varias diferencias clave:

    Mundo de la física en el servidor :

    • recibe la entrada del usuario (eventos de teclado para conducir un automóvil) a través de Colyseus y aplica la fuerza adecuada sobre el objeto del juego (el automóvil del usuario);
    • hace todos los cálculos físicos para todos los objetos (los jugadores y el balón), incluida la detección de colisiones;
    • comunica el estado actualizado de cada objeto del juego a Colyseus, quien a su vez lo transmite a los clientes;
    • se actualiza cada 16,6 milisegundos (= 60 fotogramas por segundo), activado por nuestro servidor Colyseus.

    El mundo de la física en el cliente :

    • no manipula los objetos del juego directamente;
    • recibe el estado actualizado de cada objeto del juego de Colyseus;
    • aplica cambios en posición, velocidad y ángulo después de recibir el estado actualizado;
    • envía entradas del usuario (eventos de teclado para conducir un automóvil) a Colyseus;
    • carga sprites del juego y utiliza un renderizador para dibujar el mundo de la física en un elemento de lienzo;
    • omite la detección de colisiones (usando isSensorla opción para objetos);
    • actualizaciones usando requestAnimationFrame, idealmente a 60 fps.

    Unidades lógicas principales de la arquitectura Autowuzzler: Physics World se comparte entre el servidor Colyseus y la aplicación cliente SvelteKit. ( Vista previa grande )
    Aviation Questions and Answers

     

    Ahora, con toda la magia sucediendo en el servidor, el cliente solo maneja la entrada y dibuja el estado que recibe del servidor en la pantalla. Con una excepcion:

    Interpolación en el cliente

    Dado que estamos reutilizando el mismo mundo físico de Matter.js en el cliente, podemos mejorar el rendimiento experimentado con un simple truco. En lugar de solo actualizar la posición de un objeto del juego, también sincronizamos la velocidad del objeto . De esta manera, el objeto sigue moviéndose en su trayectoria incluso si la próxima actualización del servidor tarda más de lo habitual. Entonces, en lugar de mover objetos en pasos discretos desde la posición A a la posición B, cambiamos su posición y hacemos que se muevan en una dirección determinada.

    Ciclo vital

    La clase Autowuzzler Room es donde se maneja la lógica relacionada con las diferentes fases de una sala Colyseus. Colyseus proporciona varios métodos de ciclo de vida:

    • onCreate: cuando se crea una nueva sala (normalmente cuando se conecta el primer cliente);
    • onAuth: como gancho de autorización para permitir o denegar la entrada a la habitación;
    • onJoin: cuando un cliente se conecta a la sala;
    • onLeave: cuando un cliente se desconecta de la habitación;
    • onDispose: cuando se descarta la habitación.

    La sala Autowuzzler crea una nueva instancia del mundo de la física (consulte la sección “ Física en una aplicación Colyseus ”) tan pronto como se crea ( onCreate) y agrega un jugador al mundo cuando un cliente se conecta ( onJoin). Luego actualiza el mundo de la física 60 veces por segundo (cada 16,6 milisegundos) usando el setSimulationIntervalmétodo (nuestro bucle principal del juego):

    // deltaTime is roughly 16.6 millisecondsthis.setSimulationInterval((deltaTime) = this.world.updateWorld(deltaTime));

    Los objetos de física son independientes de los objetos de Colyseus, lo que nos deja con dos permutaciones del mismo objeto del juego (como la pelota), es decir, un objeto en el mundo de la física y un objeto de Colyseus que se puede sincronizar.

    Tan pronto como el objeto físico cambia, sus propiedades actualizadas deben aplicarse nuevamente al objeto Colyseus. Podemos lograrlo escuchando el afterUpdateevento de Matter.js y estableciendo los valores desde allí:

    Events.on(this.engine, "afterUpdate", () = { // apply the x position of the physics ball object back to the colyseus ball object this.state.ball.x = this.physicsWorld.ball.position.x; // ... all other ball properties // loop over all physics players and apply their properties back to colyseus players objects})

    Hay una copia más de los objetos que debemos cuidar: los objetos del juego en el juego orientado al usuario .

    Autowuzzler mantiene tres copias de cada objeto físico, una versión autorizada (objeto Colyseus), una versión en el mundo físico de Matter.js y una versión en el cliente. ( Vista previa grande )

     

    Aplicación del lado del cliente

    Ahora que tenemos una aplicación en el servidor que maneja la sincronización del estado del juego para múltiples salas, así como también los cálculos físicos, centrémonos en crear el sitio web y la interfaz del juego real . La interfaz de Autowuzzler tiene las siguientes responsabilidades:

    • permite a los usuarios crear y compartir PIN de juegos para acceder a salas individuales;
    • envía los PIN del juego creados a una base de datos Supabase para su persistencia;
    • proporciona una página opcional "Unirse a un juego" para que los jugadores ingresen el PIN del juego;
    • valida los PIN del juego cuando un jugador se une a un juego;
    • aloja y presenta el juego real en una URL que se puede compartir (es decir, única);
    • se conecta al servidor Colyseus y maneja las actualizaciones de estado;
    • proporciona una página de destino (“marketing”).

    Para la implementación de esas tareas, elegí SvelteKit en lugar de Next.js por las siguientes razones:

    ¿Por qué SvelteKit?

    He querido desarrollar otra aplicación usando Svelte desde que construí neolightsout . Cuando SvelteKit (el marco de aplicación oficial de Svelte) entró en versión beta pública, decidí construir Autowuzzler con él y aceptar cualquier dolor de cabeza que surja al usar una versión beta nueva: la alegría de usar Svelte claramente lo compensa.

    Estas características clave me hicieron elegir SvelteKit en lugar de Next.js para la implementación real de la interfaz del juego:

    • Svelte es un marco de interfaz de usuario y un compilador y, por lo tanto, incluye un código mínimo sin un tiempo de ejecución del cliente;
    • Svelte tiene un lenguaje de plantillas expresivo y un sistema de componentes (preferencia personal);
    • Svelte incluye tiendas globales, transiciones y animaciones listas para usar, lo que significa: no hay fatiga en la toma de decisiones al elegir un conjunto de herramientas de gestión de estado global y una biblioteca de animaciones;
    • Svelte admite CSS con ámbito en componentes de un solo archivo;
    • SvelteKit admite SSR, enrutamiento basado en archivos simple pero flexible y rutas del lado del servidor para crear una API;
    • SvelteKit permite que cada página ejecute código en el servidor, por ejemplo, para recuperar datos que se utilizan para representar la página;
    • Diseños compartidos entre rutas;
    • SvelteKit se puede ejecutar en un entorno sin servidor.

    Crear y almacenar PIN de juegos

    Antes de que un usuario pueda comenzar a jugar, primero debe crear un PIN del juego. Al compartir el PIN con otras personas, todos podrán acceder a la misma sala de juegos.

    Inicie un nuevo juego copiando el PIN del juego generado o comparta el enlace directo a la sala de juegos. ( Vista previa grande )

    Este es un excelente caso de uso para los puntos finales del lado del servidor de SvelteKits junto con la función onMount de Svelte : el punto final /api/createcodegenera un PIN del juego, lo almacena en una base de datos Supabase.io y genera el PIN del juego como respuesta . Esta respuesta se obtiene tan pronto como se monta el componente de página de la página "crear":

     

    Los PIN de juego se crean en el terminal, se almacenan en una base de datos de Supabase.io y se muestran en la página "Crear". ( Vista previa grande )

    Almacenamiento de PIN de juegos con Supabase.io

    Supabase.io es una alternativa de código abierto a Firebase. Supabase hace que sea muy fácil crear una base de datos PostgreSQL y acceder a ella a través de una de sus bibliotecas cliente o mediante REST.

    Para el cliente JavaScript, importamos la createClientfunción y la ejecutamos usando los parámetros supabase_urlque supabase_keyrecibimos al crear la base de datos. Para almacenar el PIN del juego que se crea en cada llamada al createcodepunto final, todo lo que necesitamos hacer es ejecutar esta insertconsulta simple:

    import { createClient } from '@supabase/supabase-js'const database = createClient( import.meta.env.VITE_SUPABASE_URL, import.meta.env.VITE_SUPABASE_KEY);const { data, error } = await database .from("games") .insert([{ code: 123456 }]);

    Nota : Los supabase_urly supabase_keyse almacenan en un archivo .env. Debido a Vite, la herramienta de compilación en el corazón de SvelteKit, es necesario anteponer las variables de entorno con VITE_ para que sean accesibles en SvelteKit.

    Accediendo al juego

    Quería que unirse a un juego de Autowuzzler fuera tan fácil como seguir un enlace. Por lo tanto, cada sala de juegos necesitaba tener su propia URL basada en el PIN del juego creado previamente , por ejemplo, https://autowuzzler.com/play/12345 .

    En SvelteKit, las páginas con parámetros de ruta dinámica se crean poniendo las partes dinámicas de la ruta entre corchetes al nombrar el archivo de la página: client/src/routes/play/[gamePIN].svelte. El valor del gamePINparámetro estará disponible en el componente de la página (consulte los documentos de SvelteKit para obtener más detalles). En la playruta, necesitamos conectarnos al servidor Colyseus, crear una instancia del mundo de la física para representarlo en la pantalla, manejar las actualizaciones de los objetos del juego, escuchar la entrada del teclado y mostrar otra interfaz de usuario como la partitura, etc.

    Conexión con Colyseus y actualización del estado

    La biblioteca cliente de Colyseus nos permite conectar un cliente a un servidor Colyseus. Primero, creemos uno nuevo Colyseus.Clientapuntándolo al servidor Colyseus ( ws://localhost:2567en desarrollo). Luego únete a la sala con el nombre que elegimos anteriormente ( autowuzzler) y el gamePINdel parámetro de ruta. El gamePINparámetro garantiza que el usuario se una a la instancia de sala correcta (consulte “ emparejamiento ” más arriba).

    let client = new Colyseus.Client("ws://localhost:2567");this.room = await client.joinOrCreate("autowuzzler", { gamePIN });

    Dado que SvelteKit procesa páginas en el servidor inicialmente, debemos asegurarnos de que este código solo se ejecute en el cliente después de que la página haya terminado de cargarse. Nuevamente, usamos la onMountfunción de ciclo de vida para ese caso de uso. (Si está familiarizado con React, onMountes similar al useEffectgancho con una matriz de dependencia vacía).

     

    onMount(async () = { let client = new Colyseus.Client("ws://localhost:2567"); this.room = await client.joinOrCreate("autowuzzler", { gamePIN });})

    Ahora que estamos conectados al servidor del juego Colyseus, podemos comenzar a escuchar cualquier cambio en los objetos del juego.

    A continuación se muestra un ejemplo de cómo escuchar a un jugador unirse a la sala ( onAdd) y recibir actualizaciones de estado consecutivas para este jugador:

    this.room.state.players.onAdd = (player, key) = { console.log(`Player has been added with sessionId: ${key}`); // add player entity to the game world this.world.createPlayer(key, player.teamNumber); // listen for changes to this player player.onChange = (changes) = { changes.forEach(({ field, value }) = { this.world.updatePlayer(key, field, value); // see below }); };};

    En el updatePlayermétodo del mundo de la física, actualizamos las propiedades una por una porque Colyseus onChangeentrega un conjunto de todas las propiedades modificadas.

    Nota : Esta función sólo se ejecuta en la versión cliente del mundo de la física, ya que los objetos del juego sólo se manipulan indirectamente a través del servidor Colyseus.

    updatePlayer(sessionId, field, value) { // get the player physics object by its sessionId let player = this.world.players.get(sessionId); // exit if not found if (!player) return; // apply changes to the properties switch (field) { case "angle": Body.setAngle(player, value); break; case "x": Body.setPosition(player, { x: value, y: player.position.y }); break; case "y": Body.setPosition(player, { x: player.position.x, y: value }); break; // set velocityX, velocityY, angularVelocity ... }}

    El mismo procedimiento se aplica a los demás objetos del juego (pelota y equipos): escuche sus cambios y aplique los valores modificados al mundo físico del cliente.

    Hasta ahora, ningún objeto se mueve porque todavía necesitamos escuchar la entrada del teclado y enviarla al servidor . En lugar de enviar eventos directamente en cada keydownevento, mantenemos un mapa de las teclas presionadas actualmente y enviamos eventos al servidor Colyseus en un bucle de 50 ms. De esta manera, podemos soportar presionar varias teclas al mismo tiempo y mitigar la pausa que ocurre después del primer keydownevento y los consecutivos cuando la tecla permanece presionada:

    let keys = {};const keyDown = e = { keys[e.key] = true;};const keyUp = e = { keys[e.key] = false;};document.addEventListener('keydown', keyDown);document.addEventListener('keyup', keyUp);let loop = () = { if (keys["ArrowLeft"]) { this.room.send("move", { direction: "left" }); } else if (keys["ArrowRight"]) { this.room.send("move", { direction: "right" }); } if (keys["ArrowUp"]) { this.room.send("move", { direction: "up" }); } else if (keys["ArrowDown"]) { this.room.send("move", { direction: "down" }); } // next iteration requestAnimationFrame(() = { setTimeout(loop, 50); });}// start loopsetTimeout(loop, 50);

    Ahora el ciclo está completo: escuche las pulsaciones de teclas, envíe los comandos correspondientes al servidor Colyseus para manipular el mundo de la física en el servidor. Luego, el servidor Colyseus aplica las nuevas propiedades físicas a todos los objetos del juego y propaga los datos al cliente para actualizar la instancia del juego orientada al usuario.

    Molestias menores

    En retrospectiva, me vienen a la mente dos cosas de la categoría que nadie me lo dijo, pero alguien debería haberlo dicho :

    • Es beneficioso tener una buena comprensión de cómo funcionan los motores de física . Dediqué una cantidad considerable de tiempo a ajustar las propiedades y restricciones físicas. Aunque antes creé un juego pequeño con Phaser.js y Matter.js, hubo muchas pruebas y errores para lograr que los objetos se movieran de la forma que yo imaginaba.
    • El tiempo real es difícil , especialmente en los juegos basados ​​en la física. Los retrasos menores empeoran considerablemente la experiencia y, si bien la sincronización del estado entre clientes con Colyseus funciona muy bien, no puede eliminar los retrasos en el cálculo y la transmisión.

    Errores y advertencias con SvelteKit

    Dado que usé SvelteKit cuando recién salió del horno beta, hubo algunas trampas y advertencias que me gustaría señalar:

    • Me tomó un tiempo descubrir que las variables de entorno deben tener el prefijo VITE_ para poder usarlas en SvelteKit. Esto ahora está debidamente documentado en las preguntas frecuentes .
    • Para usar Supabase, tuve que agregar Supabase a las listas dependenciesy devDependenciesde package.json. Creo que este ya no es el caso.
    • ¡La función SvelteKits loadse ejecuta tanto en el servidor como en el cliente!
    • Para habilitar el reemplazo completo del módulo activo (incluida la preservación del estado), debe agregar manualmente una línea de comentario !-- @hmr:keep-all --en los componentes de su página. Consulte las preguntas frecuentes para obtener más detalles.

    Muchos otros marcos también habrían sido excelentes, pero no me arrepiento de haber elegido SvelteKit para este proyecto. Me permitió trabajar en la aplicación cliente de una manera muy eficiente, principalmente porque Svelte en sí es muy exp






    Tal vez te puede interesar:

    1. ¿Deberían abrirse los enlaces en ventanas nuevas?
    2. 24 excelentes tutoriales de AJAX
    3. 70 técnicas nuevas y útiles de AJAX y JavaScript
    4. Más de 45 excelentes recursos y repositorios de fragmentos de código

    Cómo crear un juego multiusuario en tiempo real desde cero

    Cómo crear un juego multiusuario en tiempo real desde cero

    SmashingConf UX y diseño, Amberes 2024 Implemente rápidamente. Implementar inteligentemente Índice Prim

    programar

    es

    https://pseint.es/static/images/programar-como-crear-un-juego-multiusuario-en-tiempo-real-desde-cero-1123-0.jpg

    2024-04-04

     

    Cómo crear un juego multiusuario en tiempo real desde cero
    Cómo crear un juego multiusuario en tiempo real desde cero

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20