Recientemente surgió una discusión acerca de cómo crear un juego multijugador de desplazamiento lateral en 2D que pueda tener un diseño de nivel de bucle (Piense en Starbound y cómo están girando sus mundos).
Pensé que la forma más simple sería tener un mapa rectangular con zonas de activación que pudieran teletransportar a los jugadores de un lado a otro. Sin embargo, el problema obvio con este enfoque es el caso de tener varios jugadores en el borde del mapa a la vez. No solo quieres teletransportar a los jugadores uno frente al otro y necesitarías una forma de transportar jugadores sin que otros jugadores desaparezcan.
Para agregar esta idea y solucionar el problema, se me ocurrió lo siguiente: tener una zona de activación (cuadro rojo en la imagen) donde los jugadores podrán ver una "zona de clonación" (cuadro verde). En este cuadrado verde, los objetos del lado opuesto de la zona de activación se copiarían en su zona de clonación correspondiente (se puede ver con las formas A y B). Cuando un jugador llega al borde inicial de la "zona de clonación", se teletransporta al otro lado del mapa.
En este ejemplo, el Jugador 2 pensaría que están viendo al Jugador 1, sin embargo, en realidad estarían viendo su clon y viceversa.
Esto parecía un poco extremo y complejo para el problema en cuestión. Mi pregunta ahora es saber si esta solución es un buen enfoque para abordar el problema, ¿o hay una forma más sencilla de resolver este problema?
fuente
Respuestas:
Este sistema con todos estos disparadores suena demasiado complicado y propenso a errores.
Podrías envolver la posición del jugador usando el módulo con algo como
playerPositionX = playerPositionX % mapWidth
De esta manera, cuando su jugador llegue
playerPosition == mapWidth
alplayerPosition
reiniciará a 0.Esta solución podría ampliarse con todo el sistema de renderizado.
fuente
pos - map_width
).La solución canónica es usar portales . En su ejemplo, solo hay un nivel, excepto que hay un portal que conecta los extremos izquierdo y derecho.
Cualquier cosa que se mueva a través de ese portal tendrá sus coordenadas traducidas al otro extremo del portal, de modo que si algo se mueve a la izquierda a través del portal, reaparecerá en el lado derecho del nivel y viceversa.
Tu cámara también necesita soportar los portales; Si el portal está dentro de la cámara, debe representar partes del nivel a cada lado del portal. Si está familiarizado con los editores de imágenes para gráficos de mosaico sin costura, aquí es el mismo trato.
La parte tediosa es que todo lo relacionado con la distancia o la ruta también tendrá que soportar portales. Esto incluye IA, algos de línea de visión, atenuación de sonido, etc.
Sin embargo, lo bueno de los portales es que es muy poderoso. El motor de compilación lo usó para simular niveles de varios pisos, a pesar de que no es un "verdadero" motor 3D. Algunos motores modernos también usan portales para crear espacios no euclidianos; Portal y Antichamber son ejemplos notables en 3D.
fuente
Recuerde que lo que muestra en la pantalla y lo que hay en la memoria son dos cosas totalmente diferentes. Imagine que tiene una ventana que necesita llenar con datos sobre el mundo. Rellena la ventana de izquierda a derecha. Mientras analiza sus datos para llenar el mundo, si llega al fin del mundo, simplemente regrese al comienzo de sus datos. Usar una operación de módulo es ideal. Recuerda que debes hacer esto para todo . Proyectiles, rayos, jugadores, física; todos necesitan tener sus posiciones envueltas al cruzar los límites del mundo.
Cada jugador comparte datos, pero tiene su propia perspectiva de los datos. Sus ventanas están pobladas de manera diferente dependiendo de dónde se encuentren en el mundo.
Esto significa que no hay necesidad de crear clones o teletransportarse a nadie. Básicamente, está creando clones, simplemente representando caracteres en las pantallas de los demás.
fuente
Desconecte el renderizado del mundo y puede hacer un renderizado envolvente y correcto sin recurrir a ningún artefacto de clonación o teletransportación.
Primero, en tu mundo tienes un mundo de tamaño fijo, desde
0
hastaWidth
. Cada vez que un objeto cae por debajo de 0, lo envuelve hasta el final, y cada vez que un objeto se sobrepasaWidth
al inicio. Esto significa que todos los objetos lógicos en su mundo siempre están dentro del rango0...Width
.Segundo, para renderizar harás un módulo en la posición. Entonces, el lado izquierdo de la pantalla es "Base" y el lado derecho es "Base + Tamaño". Entonces buscas en tu mundo cualquier cosa dentro de ese rango. En realidad, buscará el rango del módulo, que lo asigna nuevamente
0...Width
.El truco durante la búsqueda es devolver la posición del objeto en relación con el
Base
lado izquierdo. Esto se convierte en coordenadas locales de la pantalla para que el renderizador en sí no tenga que preocuparse por el módulo, solo lo hace la búsqueda.No necesita clonar nada, ya que cada renderizador solo trata con el objeto en una ubicación.
Si su mundo se produce en segmentos, o usando estructuras 3D, tendrá que segmentarlo. Por lo tanto, no es un bloque constante, pero se puede mover para acomodar esta representación. No necesita muchos bloques, como mínimo 2.
fuente
Creo que el único enfoque razonable sería implementar su mundo envuelto en una estructura de datos subyacente completamente transparente para el juego y el usuario. Entonces, en algunos de bajo nivel, tiene una función mapCoordinate () que ajusta sus coordenadas reales a su recurso de mapa subyacente ...
Entonces, si tu mundo real tiene solo 10 unidades de ancho, el jugador y el juego no lo sabrán. Para el jugador, el mundo es infinito, y si el juego pregunta qué está en la posición 15, la función subyacente traducirá esta solicitud, módulo 10 y le dará al paquete el artículo en la posición 5.
Entonces, para toda la lógica del juego y todo lo demás, es como si tuvieras un mundo infinito y grande, donde solo hay copias de todo.
fuente
No es lo mismo, pero implementé algo similar en un atasco de juego. El juego tenía jugadores moviéndose en un pequeño nivel circular, envuelto cuando el jugador alcanzaba una posición 'x' de pi. Renderizar fue fácil porque acabamos de renderizar todo y luego giramos una cámara offset para rastrear lo que estaba sucediendo. Podría implementar algo similar, como se ha sugerido anteriormente:
fuente