¿Cómo se pueden crear juegos que tengan niveles infinitos como Temple Run? ¿Qué conceptos se utilizan en ellos?

En la mayoría de los juegos, un humano, en algún momento, especificó dónde va todo en un nivel y creó cada uno. En algunos otros, en su lugar, escribimos un algoritmo para crear los niveles para nosotros. Algunos de estos se encuentran en un área predefinida y funcionan más o menos como un nivel artesanal, aunque tal vez con menos inspiración y menos novedad. Otros pueden generar más sobre la marcha, que es de lo que se trata la pregunta.
Hay algunas técnicas que funcionan bien para un área fija pero no para un nivel interminable, por lo que no las discutiré aquí.

Clasificaría niveles infinitos en 2 categorías amplias; Lineal y abierto.

Lineal es simple, y el recorrido del templo cae en esta categoría. No estás creando un mundo tanto como una serie de obstáculos o desafíos para el jugador. Esto puede ser muy simple; tienes un conjunto de cosas que el jugador puede encontrar, ya sean obstáculos, enemigos, power-ups, pick-ups, etc. Luego los eliges al azar y los colocas en el mundo a medida que el jugador progresa.
Para hacer las cosas más interesantes, las probabilidades de estas cosas pueden cambiar con el tiempo. Al principio, los enemigos pueden ser escasos y los power-ups frecuentes, pero a medida que avanzas, los power-ups se vuelven más aterradores, y hay más enemigos, enemigos más difíciles, obstáculos más difíciles, etc. Esto crea una curva de dificultad para el juego.
También puede crear patrones más sofisticados; puedes dar pausas en la dificultad para dejar que el jugador recupere el aliento o lanzarles estiramientos más duros, variando un poco las cosas para hacerlo más interesante. Puedes usar patrones para crear una mejor estructura que cosas al azar, como cadenas de monedas o una cadena de monedas que sigue un camino a través de los obstáculos, o tal vez un buen poder en un lugar riesgoso. Podría decidir que algunos aspectos funcionarían bien en un patrón más regular, por ejemplo, las caídas de salud se distribuyen de manera más uniforme. También es una buena idea verificar que los obstáculos generados sean solucionables; que hay un camino que no te matará, por ejemplo.

Open-ende requiere técnicas más sofisticadas. En general, desea que el mundo sea consistente si regresa a un área antigua, y realmente se trata más de crear un mundo que una cadena de obstáculos.
Un método es usar una función de ruido, como el ruido perlin. Estos son patrones que son aleatorios, pero tienen una estructura y pueden calcularse en un punto sin conocer otros puntos. Por ejemplo, podría usar esto para generar el mapa de altura del mundo, y definiría cómo funcionan sus colinas. Siempre que estuvieras en el mismo punto, la tierra tendría la misma altura, y se generaría sin problemas, y cada vez que regresaras a un área antigua coincidiría perfectamente.
Algunos juegos tienen un enfoque que generará siempre la misma área de manera consistente, lo que ahorra espacio de almacenamiento, mientras que otros almacenarán lo que se ha generado y utilizarán métodos como los anteriores para garantizar que la generación sea coherente consigo misma.
Colocar detalles dentro del mundo puede funcionar de manera similar; puedes crear un mapa de ruido que te diga la densidad de los árboles en un área, y usarlo para determinar cuántos árboles debes engendrar, o cuántos enemigos, rocas o cualquier otro detalle que necesites.

También hay técnicas para generar una estructura. Un enfoque es construir la estructura en una cuadrícula; coloca varias habitaciones en cualquier nivel dado, luego agrega pasillos para encadenarlos. Otro enfoque subdivide el área en habitaciones, haciéndola mucho más densa.

Soy el desarrollador Polygon Run y ​​lo logré con esta estrategia:

Haga un seguimiento de estas 2 variables

– posición de jugador
– posición del objeto más alejado

Luego, para cada cuadro, ejecute esto:

if (posición del jugador + 300> posición del objeto más alejado) {
createMoreStuff ();
}

Y finalmente destruya los objetos que quedan para no tener problemas de rendimiento después de unos minutos de ejecución:

para cada objeto existente {
if (posición del objeto destruir (objeto)
}
}

La constante “300” cambiará según la escala de su mundo y la amplitud de su cámara. Puede encontrar el mejor valor con prueba y error.

El concepto principal es: creación de nivel de procedimiento. Eso significa que un sistema de software crea niveles válidos, de acuerdo con un procedimiento (algoritmo), utilizando activos conocidos por ser válidos.

Por ejemplo, si tiene una carpeta de activos artísticos, digamos piezas del camino del lado izquierdo, derecho y medio, que encajan perfectamente y un mapa que incluye un ligero aumento en el paisaje, puede escribir código para armar un Conjunto L / M / R de elementos de arte al azar justo encima de la colina y hacerlos visibles para el jugador. Mientras tanto, se está armando un nuevo trío de piezas, fuera de la vista, etc.

Puede hacerlo con arte isométrico 2D o con elementos de arte 3D. También se están llevando a cabo experimentos con música procesal, mapas de textura, clima, olas y paisajes con conjuntos enteros de plantas que se colocan y “crecen” a diferentes edades, para dar la impresión de un bosque naturalmente aleatorio.

Los gráficos son la parte fácil. El verdadero problema radica en mantener los niveles desafiantes incluso después de haber completado cientos de ellos.
En algún momento, el jugador ha acumulado tanta riqueza y experiencia (o cualquier medida de progreso que se use), que jugar otro nivel es tan desafiante (es decir, “divertido”), como masticar chicle sin morderse la lengua.
Una solución es simplemente generar monstruos / oposición más fuertes o más duros, pero nunca he visto que realmente funcione. Mientras su inteligencia o dificultad inherente permanezca igual, aún terminas con jugadores aburridos o frustrados (al enfrentar desafíos imposibles).
No tengo respuesta para esto, y si lo tuviera, ciertamente no te lo diría 😉

Psuedocode:

RenderDistance = x
GeneratorDistance = (y> = x)
WorldData = generate ()

Si bien es cierto {
If (WorldData.distance {generar ()}

Actualizar ()

Renderizar ()
}