Por ejemplo, el famoso juego Flappy Bird, o cualquier cosa que realmente clasifique, es el jugador (en este caso, el pájaro o la cámara, lo que prefiera) avanzando o el mundo entero retrocediendo (el pájaro solo cambia la posición Y y tiene un posición X constante)?
game-mechanics
platformer
Lendrit Ibrahimi
fuente
fuente
Respuestas:
Estoy un poco en desacuerdo con la respuesta de Philipp ; o al menos con cómo lo presentó. Da la impresión de que mover el mundo alrededor del jugador podría ser una mejor idea; cuando es exactamente lo contrario. Así que aquí está mi propia respuesta ...
Ambas opciones pueden funcionar, pero generalmente es una mala idea "invertir la física" moviendo el mundo alrededor del jugador en lugar del jugador alrededor del mundo.
Pérdida / desperdicio de rendimiento:
El mundo generalmente tendrá muchos objetos; muchos, si no la mayoría, estáticos o durmiendo. El jugador tendrá uno, o relativamente pocos, objetos. Mover a todo el mundo alrededor del jugador, significa mover todo en la escena excepto el jugador. Objetos estáticos, objetos dinámicos durmientes, objetos dinámicos activos, fuentes de luz, fuentes de sonido, etc. Todos tienen que ser trasladados.
Eso (obviamente) es considerablemente más caro que mover solo lo que realmente se está moviendo (el jugador, y tal vez algunas cosas más).
Mantenibilidad y extensibilidad:
Mover el mundo alrededor del jugador hace que el mundo (y todo lo que contiene) sea el punto donde las cosas están sucediendo más activamente. Cualquier error o cambio en el sistema significa que, potencialmente, todo cambia. Esta no es una buena manera de hacer las cosas; desea que los errores / cambios estén tan aislados como sea posible, para que no obtenga comportamientos inesperados en algún lugar donde no haya realizado cambios.
También hay muchos otros problemas con este enfoque. Por ejemplo, rompe muchas suposiciones de cómo se supone que las cosas funcionan en el motor. No podrá utilizar la dinámica
RigidBody
para otra cosa que no sea el reproductor, por ejemplo; como un objeto con un adjuntoRigidBody
no establecido en cinemática se comportará inesperadamente al establecer la posición / rotación / escala (que estaría haciendo cada cuadro, para cada objeto en la escena, excepto el jugador 😨)¡Entonces la respuesta es solo mover al jugador!
Bueno ... si y no . Como se menciona en la respuesta de Philipp, en un tipo de juego de corredor infinito (o cualquier juego con una gran área explorable sin fisuras), ir demasiado lejos del origen eventualmente introduciría FPPE ( errores de precisión de punto flotante) notables , y aún más, eventualmente, desbordar el tipo numérico, ya sea haciendo que tu juego se bloquee o, básicamente, haciendo que el mundo del juego se agriete ... ¡con esteroides! 😵 (porque en este punto, los FPPE harían que el juego ya esté en crack "normal")
La solución real :
¡No hagas nada y haz las dos cosas! Debes mantener el mundo estático y mover al jugador a su alrededor. Pero "vuelva a enraizar" tanto al jugador como al mundo, cuando el jugador comience a alejarse demasiado de la raíz (posición
[0, 0, 0]
) de la escena.Si mantienes la posición relativa de las cosas (jugador en el mundo que lo rodea) y haces este proceso en una sola actualización de cuadro, ¡el jugador (real) ni siquiera se dará cuenta!
Para hacerlo, tienes dos opciones principales:
Aquí hay un ejemplo de este proceso en acción.
¿Pero que lejos es muy lejos?
Si observa el código fuente de Unity, usan
1e-5
(0.00001
) como base para considerar dos valores de punto flotante "iguales", dentro deVector2
yVector3
(los tipos de datos responsables de las posiciones de los objetos, rotaciones y escalas [euler-]). Dado que la pérdida de precisión de punto flotante ocurre en ambos sentidos lejos de cero, es seguro asumir que cualquier cosa debajo de1e+5
(100000
) unidades lejos de la raíz / origen de la escena es seguro para trabajar.¡Pero! Ya que...
... entonces probablemente sea una buena idea volver a rootear mucho antes / con más frecuencia que esa marca de 100000 unidades. El video de ejemplo que proporcioné parece hacerlo cada 1000 unidades más o menos, por ejemplo.
fuente
Ambas opciones funcionan.
Pero si quieres que el corredor interminable sea verdaderamente interminable, tendrás que mantener al jugador inmóvil y mover el mundo. De lo contrario, finalmente alcanzará los límites de las variables que utiliza para almacenar la posición X. Un número entero eventualmente se desbordaría y una variable de punto flotante se volvería cada vez menos precisa, lo que haría que el juego fallara después de un tiempo. Pero puede evitar este problema utilizando un tipo lo suficientemente grande como para que nadie encuentre estos problemas dentro del intervalo de tiempo que uno podría jugar en una sesión (cuando un jugador mueve 1000 píxeles por segundo, un número entero de 32 bits se desbordará después de 49 días).
Así que haz lo que te parezca conceptualmente más intuitivo.
fuente
Partiendo de la respuesta de XenoRo , en lugar del método de re-rooting que describen, se podría hacer lo siguiente:
Cree un búfer circular de partes de su mapa infinito generado, a través del cual su personaje se moverá con la posición actualizada con el módulo aritmético (para que simplemente corra alrededor del búfer circular). Comienza a reemplazar partes de tu búfer tan pronto como tu personaje abandone el fragmento. La ecuación de actualización de los jugadores sería algo como:
player.position = (player.position + player.velocity) % worldBuffer.width;
Aquí hay un ejemplo pictoral de lo que estoy hablando:
Aquí hay un ejemplo de lo que sucede al envolver al final.
Con este método, nunca se encontrará con errores de precisión, pero es posible que necesite hacer un búfer muy grande si tiene la intención de hacerlo en 3D con una distancia de visión muy lejana (ya que aún tendrá que poder ver por delante de usted mismo) ) Si es un pájaro flappy, el tamaño de su fragmento probablemente solo sea tan grande como sea necesario para mantener una sola escena para una sola pantalla, y su búfer puede ser muy pequeño.
Tenga en cuenta que comenzará a obtener resultados repetidos con CUALQUIER prng, y el número máximo de secuencias no repetitivas de una generación PRNG es generalmente menor que la longitud del pow (2, número de bits utilizados internamente), con merzenne twister esto no es es un gran problema, ya que usa 2.5k de estado interno, pero si usa la variante pequeña, tiene 2 ^ 127-1 iteraciones máximas antes de repetir (o peor), sin embargo , este es un número astronómicamente grande . Puede solucionar problemas de períodos repetitivos incluso si su PRNG tiene un período corto a través de buenas funciones de mezcla de avalanchas para la semilla (a medida que agrega más estado implícitamente) repetidamente.
fuente
Como ya se preguntó y aceptó, realmente depende del alcance y el estilo de su juego, pero como no se mencionó: FlappyBird mueve el obstáculo a través de la pantalla, en lugar del jugador en todo el mundo.
Un generador está creando instancias de objetos fuera de la pantalla con una velocidad fija en la
Vector2.left
dirección.fuente