Codificación basada en datos
Todo lo que mencionas es algo que se puede especificar en los datos. ¿Por qué estás cargando?aspecificmap
? Porque la configuración del juego dice que es el primer nivel cuando un jugador inicia un nuevo juego, o porque ese es el nombre del punto de guardado actual en el archivo de guardado del jugador que acaba de cargar, etc.
Cómo lo encuentras aspecificmap
? Porque está en un archivo de datos que enumera identificadores de mapas y sus recursos en disco.
Solo debe existir un conjunto particularmente pequeño de recursos "básicos" que sean legítimamente difíciles o imposibles de evitar. Con un poco de trabajo, esto puede limitarse a un solo nombre de activo predeterminado codificado comomain.wad
o similar. Este archivo puede modificarse potencialmente en tiempo de ejecución pasando un argumento de línea de comandos al juego, también conocido como game.exe -wad mymain.wad
.
Escribir código basado en datos se basa en algunos otros principios. Por ejemplo, se puede evitar que los sistemas o módulos soliciten un recurso en particular y en su lugar inviertan esas dependencias. Es decir, no DebugDrawer
cargue debug.font
en su código de inicialización; en cambio, tenerDebugDrawer
tome un controlador de recursos en su código de inicialización. Ese identificador podría cargarse desde el archivo de configuración del juego principal.
Como ejemplos concretos de nuestra base de código, tenemos un objeto de "datos globales" que se carga desde la base de datos de recursos (que por sí solo es la./resources
carpeta pero se puede sobrecargar con un argumento de línea de comandos). El ID de la base de datos de recursos de estos datos globales es el único nombre de recurso codificado necesario en la base de código (tenemos otros porque a veces los programadores se vuelven vagos, pero generalmente terminamos arreglando / eliminando esos eventualmente). Este objeto de datos global está lleno de componentes cuyo único propósito es proporcionar datos de configuración. Uno de los componentes es el componente UI Global Data, que contiene identificadores de recursos para todos los recursos principales de la IU (fuentes, archivos Flash, iconos, datos de localización, etc.) entre una serie de otros elementos de configuración. Cuando un desarrollador de IU decide cambiar el nombre del activo de IU principal de /ui/mainmenu.swf
a/ui/lobby.swf
simplemente actualizan esa referencia de datos global; ningún código de motor necesita cambiar en absoluto.
Utilizamos estos datos globales para todo. Todos los personajes jugables, todos los niveles, UI, audio, activos principales, configuración de red, todo. (bueno, no todo , pero esas otras cosas son errores que se deben corregir).
Este enfoque tiene muchas otras ventajas. Por un lado, hace que el empaquetado y agrupamiento de recursos sea parte integral de todo el proceso. Las rutas de codificación dura en el motor también tienden a significar que esas mismas rutas tienen que estar codificadas en cualquier script o herramienta que empaquete los activos del juego, y esas rutas pueden salir de la sincronización. Confiando en cambio en un único activo principal y cadenas de referencia a partir de ahí, podemos construir un paquete de activos con un solo comando como bundle.exe -root config.data -out main.wad
y saber que incluirá todos los activos que necesitamos. Además, dado que el paquete solo estaría siguiendo referencias de recursos, sabemos que incluirá solo los activos que requerimos y omitirá toda la pelusa sobrante que inevitablemente se acumula durante la vida de un proyecto (además, podemos generar automáticamente listas de eso pelusa para poda).
Un caso complicado de todo esto está en los guiones. Hacer que el motor se base en datos es fácil desde el punto de vista conceptual, pero he visto muchos proyectos (afición a AAA) en los que los scripts se consideran datos y, por lo tanto, se les "permite" usar rutas de recursos de manera indiscriminada. No hagas eso. Si un archivo Lua necesita un recurso y solo llama a una función como textures.lua("/path/to/texture.png")
entonces, la canalización de activos tendrá muchos problemas para saber que el script requiere /path/to/texture.png
funcionar correctamente y podría considerar que esa textura no se usa y es innecesaria. Las secuencias de comandos deben tratarse como cualquier otro código: cualquier dato que necesiten, incluidos los recursos o las tablas, debe especificarse en una entrada de configuración que el motor y la canalización de recursos puedan inspeccionar en busca de dependencias. Los datos que dicen "script de carga foo.lua
" deberían decir "foo.lua
y darle estos parámetros "donde los parámetros incluyen los recursos necesarios. Si un script genera enemigos aleatoriamente, por ejemplo, pasa la lista de posibles enemigos al script desde ese archivo de configuración. El motor puede precargar a los enemigos con el nivel ( ya que conoce la lista completa de posibles engendros) y el canal de recursos sabe agrupar a todos los enemigos con el juego (ya que están referenciados definitivamente por los datos de configuración). Si los scripts generan cadenas de nombres de ruta y simplemente llaman a una load
función, entonces ninguno el motor o la canalización de recursos tienen alguna forma de saber específicamente qué activos puede intentar cargar el script.
De la misma forma que evita la codificación en funciones generales.
Pasa parámetros y mantiene su información en archivos de configuración.
En esa situación, no hay absolutamente ninguna diferencia en la ingeniería de software entre escribir un motor y escribir una clase.
Luego, su código de cliente lee un archivo de configuración "maestro" ( este está codificado o se pasa como un argumento de línea de comando) que contiene la información que indica dónde están los archivos de activos y qué mapa contienen.
A partir de ahí, todo está controlado por el archivo de configuración "maestro".
fuente
Me gustan las otras respuestas, así que voy a ser un poco contrario. ;)
No puede evitar codificar el conocimiento sobre sus datos en su motor. De donde sea que provenga la información, el motor debe saber buscarla. Sin embargo, puede evitar codificar la información real en su motor.
Un enfoque "puro" basado en datos le permitiría iniciar el ejecutable con los parámetros de línea de comando necesarios para que cargue la configuración inicial, pero el motor tendrá que estar codificado para saber cómo interpretar esa información. Por ejemplo, si sus archivos de configuración son JSON, debe codificar las variables que busca, por ejemplo, el motor tendrá que saber buscar
"intro_movies"
y"level_list"
etcétera.Sin embargo, un motor "bien construido" puede funcionar para muchos juegos diferentes simplemente intercambiando los datos de configuración y los datos a los que hace referencia.
Por lo tanto, el mantra no es tanto para evitar la codificación rígida, sino para garantizar que pueda realizar cambios con la menor cantidad de esfuerzo posible.
Para contrastar con el enfoque de los archivos de datos (que apoyo de todo corazón), puede ser que esté bien compilando los datos en su motor. Si el "costo" de hacerlo es menor, entonces no hay daño real; Si usted es el único que trabaja en ello, puede diferir el manejo de archivos para una fecha posterior y no necesariamente atornillarse. Mis primeros proyectos de juego tenían grandes tablas de datos codificados en el juego en sí, por ejemplo, una lista de armas y su variedad de datos:
Por lo tanto, coloca estos datos en un lugar fácil de referencia y es fácil de editar según sea necesario. Lo ideal sería poner estas cosas en un archivo de configuración de algún tipo, pero luego debe analizar y traducir todo ese jazz, además de conectar referencias entre estructuras puede convertirse en un dolor adicional que realmente no desea tratar con.
fuente