En algún momento, un motor DEBE especializarse y saber cosas sobre el juego. Iré por una tangente aquí.
Tomar recursos en un RTS. Un juego puede tener Credits
y Crystal
otro Metal
yPotatoes
Debes usar los conceptos de OO correctamente y elegir max. reutilización de código. Está claro que Resource
aquí existe un concepto de .
Entonces decidimos que los recursos tienen lo siguiente:
- Un gancho en el bucle principal para aumentar / disminuir
- Una forma de obtener la cantidad actual (devuelve un
int
)
- Una forma de restar / sumar arbitrariamente (jugadores transfiriendo recursos, compras ...)
¡Tenga en cuenta que esta noción de a Resource
podría representar asesinatos o puntos en un juego! No es muy poderoso.
Ahora pensemos en un juego. Podemos tener moneda al negociar centavos y agregar un punto decimal a la salida. Lo que no podemos hacer son recursos "instantáneos". Como decir "generación de red eléctrica"
Digamos que agrega una InstantResource
clase con métodos similares. Ahora está (comenzando) a contaminar su motor con recursos.
El problema
Tomemos el ejemplo de RTS nuevamente. Supongamos que el jugador dona lo que sea Crystal
a otro jugador. Quieres hacer algo como:
if(transfer.target == engine.getPlayerId()) {
engine.hud.addIncoming("You got "+transfer.quantity+" of "+
engine.resourceDictionary.getNameOf(transfer.resourceId)+
" from "+engine.getPlayer(transfer.source).name);
}
engine.getPlayer(transfer.target).getResourceById(transfer.resourceId).add(transfer.quantity)
engine.getPlayer(transfer.source).getResourceById(transfer.resourceId).add(-transfer.quantity)
Sin embargo, esto es realmente bastante desordenado. Es de uso general, pero desordenado. ¡Aunque ya impone una resourceDictionary
que significa que ahora tus recursos tienen que tener nombres! Y es por jugador, por lo que ya no puede tener recursos del equipo.
Esta es una abstracción "demasiado" (no es un ejemplo brillante, lo admito), en su lugar, debe llegar a un punto en el que acepte que su juego tiene jugadores y cristal, y luego puede tener (por ejemplo)
engine.getPlayer(transfer.target).crystal().receiveDonation(transfer)
engine.getPlayer(transfer.source).crystal().sendDonation(transfer)
Con una clase Player
y una clase CurrentPlayer
donde CurrentPlayer
el crystal
objeto mostrará automáticamente las cosas en el HUD para la transferencia / envío de donaciones.
Esto contamina el motor con cristal, la donación de cristal, los mensajes en el HUD para los jugadores actuales y todo eso. Es más rápido y fácil de leer / escribir / mantener (lo cual es más importante, ya que no es significativamente más rápido)
Observaciones finales
El caso de los recursos no es brillante. Sin embargo, espero que todavía puedas ver el punto. En todo caso, he demostrado que "los recursos no pertenecen al motor", ya que lo que un juego específico necesita y lo que es aplicable a todas las nociones de recursos son cosas MUY diferentes. Lo que generalmente encontrarás son 3 (o 4) "capas"
- El "núcleo": esta es la definición del motor del libro de texto, es un gráfico de escena con ganchos de eventos, se trata de sombreadores y paquetes de red y una noción abstracta de jugadores
- El "GameCore": es bastante genérico para el tipo de juego, pero no para todos los juegos, por ejemplo, recursos en RTS o municiones en FPS. La lógica del juego comienza a filtrarse aquí. Aquí es donde estaría nuestra noción anterior de recursos. Hemos agregado estas cosas que tienen sentido para la mayoría de los recursos RTS.
- "GameLogic" MUY específico para el juego real que se está haciendo. Encontrarás variables con nombres como
creature
o ship
o squad
. El uso de la herencia que obtendrá clases que abarcan las 3 capas (por ejemplo, Crystal
es una Resource
la cual es un GameLoopEventListener
ejemplo)
- Los "activos" son inútiles para cualquier otro juego. Tomemos, por ejemplo, los scripts de AI combinados en Half Life 2, no se usarán en un RTS con el mismo motor.
Hacer un nuevo juego a partir de un motor viejo
Esto es MUY común. La fase 1 es extraer las capas 3 y 4 (y 2 si el juego es de un tipo TOTALMENTE diferente) Supongamos que estamos haciendo un RTS a partir de un viejo RTS. Todavía tenemos recursos, simplemente no cristal y demás, por lo que las clases base en las capas 2 y 1 todavía tienen sentido, todo ese cristal al que se hace referencia en 3 y 4 se puede descartar. Así lo hacemos Sin embargo, podemos verificarlo como referencia de lo que queremos hacer.
Contaminación en la capa 1
Esto puede suceder. La abstracción y el rendimiento son enemigos. UE4, por ejemplo, proporciona muchos casos de composición optimizados (por lo que si desea X e Y, alguien escribió un código que hace X e Y juntos muy rápido, sabe que está haciendo ambas cosas) y, como resultado, es REALMENTE bastante grande. Esto no es malo, pero lleva mucho tiempo. La capa 1 decidirá cosas como "cómo pasar los datos a los sombreadores" y cómo animar las cosas. Hacerlo de la mejor manera para su proyecto SIEMPRE es bueno. Simplemente intente y planifique para el futuro, reutilizar el código es su amigo, herede donde tenga sentido.
Clasificar capas
POR ÚLTIMO (lo prometo) no tengas demasiado miedo a las capas. Motor es un término arcaico de los viejos tiempos de las tuberías de funciones fijas donde los motores funcionaban casi de la misma manera gráfica (y como resultado tenían mucho en común) la tubería programable le dio vueltas y como tal la "capa 1" se contaminó con los efectos que los desarrolladores quisieran lograr. La IA era la característica distintiva (debido a la gran cantidad de enfoques) de los motores, ahora es la IA y los gráficos.
Su código no debe archivarse en estas capas. Incluso el famoso motor de Unreal tiene MUCHAS versiones diferentes, cada una específica para un juego diferente. Hay pocos archivos (aparte de las estructuras de datos similares) que no habrían cambiado. ¡Esto esta bien! Si quieres hacer un juego nuevo desde otro, te llevará más de 30 minutos. La clave es planificar, saber qué bits copiar y pegar y qué dejar atrás.