Saltar al contenido principal

Multijugador

Todo lo de abajo solo está vivo cuando tu proyecto tiene el multijugador activado y el reproductor ha abierto una sala.

El resto del tiempo estas llamadas no hacen nada en silencio, y es a propósito: un script escrito para multijugador corre perfectamente en una vista previa de un solo jugador. No necesitas dos versiones.

ctx.net.id;        // el identificador de este participante
ctx.net.peers(); // todos los que están ahora en la sala
ctx.net.onJoin((peer) => {});
ctx.net.onLeave((peer) => {});

Qué se replica solo

Solo las posiciones de los cuerpos físicos marcados como compartidos. Todo lo demás lo envías tú.

Suena a poco, y es exactamente el poco correcto: una caja, una puerta o una pelota deberían ser un objeto en el que todos coincidan, mientras que un personaje de jugador no debe serlo; si lo fuera, una sola persona caminando movería el avatar de todos. → propiedad

Mensajes

Para eventos: alguien ha puntuado, ha empezado una ronda, se ha abierto una puerta.

ctx.net.send('score', { points: 10 });
ctx.net.on('score', ({ from, data }) => {
/* … */
});

Se manda y se olvida, a todos menos a ti.

Estado compartido

Para valores en los que todos deben coincidir. Es un mapa simple donde gana la última escritura, replicado a todo el mundo.

ctx.net.state.set(ctx.net.id, { name, colour, ready: true });
ctx.net.state.get(somePeerId);
ctx.net.state.all();
ctx.net.state.on(ctx.net.id, (value) => {});

Indexar por ctx.net.id es el patrón estándar para el estado por jugador: cada cual escribe su propia entrada y lee las de los demás.

Generar para todos

const id = ctx.net.spawn(props.bullet, { position, rotation });
ctx.net.despawn(id);

El identificador vuelve de inmediato, pero el objeto aparece cuando la sala lo confirma. Ese viaje de ida y vuelta es lo que mantiene el identificador idéntico en todos los clientes, que es lo que permite que la física se ponga de acuerdo sobre él después.

Solo quien generó algo puede eliminarlo, y salir de la sala se lleva tus generaciones contigo.

Dibujar a los demás con suavidad

Esta es la parte que más se tuerce, así que tiene su propia llamada.

ctx.net.follow(otherPlayerObject, { position, rotation });
ctx.net.unfollow(otherPlayerObject);

Llámala con cada posición que recibas —de diez a veinte veces por segundo es lo normal— y el runtime escribirá el transform en cada fotograma por ti, reproduciendo el búfer ligeramente por detrás para tener siempre dos posiciones conocidas entre las que moverse.

No escribas las posiciones entrantes directamente en un transform

Los paquetes llegan mucho menos a menudo que los fotogramas, así que el objeto se queda quieto y luego salta: los jugadores remotos parecen dar zancadas en vez de caminar. Suavizar hacia el paquete más reciente tampoco lo arregla: entre paquetes no hay nada por delante del momento actual hacia lo que suavizar, así que se atasca y luego da un tirón.

Volver a pasar la misma posición no cuesta nada, así que llamar a follow en cada fotograma con lo que tengas en ese momento es el uso previsto.

Un reparto que funciona

Si empiezas de cero, este reparto de tareas te llevará muy lejos:

  • cada jugador posee su propio personaje: un cuerpo local, movido por su entrada;
  • su posición sale por net.state o por un canal, de diez a veinte veces por segundo;
  • los personajes de los demás se dibujan con follow;
  • los objetos del mundo son cuerpos compartidos, así que la física los mantiene coherentes gratis;
  • la puntuación y el estado de la ronda viven en net.state, escritos por quien tenga la autoridad sobre ese evento.

Siguiente: Solución de problemas — cuando algo no se comporta bien.