Codificação baseada em dados
Tudo o que você menciona é algo que pode ser especificado nos dados. Por que você está carregando aspecificmap
? Como a configuração do jogo indica que é o primeiro nível quando um jogador inicia um novo jogo, ou porque esse é o nome do ponto de salvamento atual no arquivo de salvamento que ele acabou de carregar, etc.
Como você encontra aspecificmap
? Porque está em um arquivo de dados que lista os IDs do mapa e seus recursos em disco.
É necessário apenas um conjunto particularmente pequeno de recursos "essenciais" que sejam legitimamente rígidos ou impossíveis de evitar a codificação. Com um pouco de trabalho, isso pode ser limitado a um único nome de ativo padrão codificado como main.wad
ou semelhante. Este arquivo pode ser potencialmente alterado em tempo de execução, passando um argumento de linha de comando para o jogo, também conhecido como game.exe -wad mymain.wad
.
A escrita de código orientado a dados se baseia em alguns outros princípios. Por exemplo, é possível evitar que sistemas ou módulos solicitem um recurso específico e inverter essas dependências. Ou seja, não faça DebugDrawer
carregamento debug.font
no seu código de inicialização; em vez disso, DebugDrawer
use um identificador de recurso em seu código de inicialização. Esse identificador pode ser carregado a partir do arquivo de configuração principal do jogo.
Como exemplos concretos de nossa base de código, temos um objeto "dados globais" carregado do banco de dados de recursos (que por si só é a ./resources
pasta, mas pode ser sobrecarregado com um argumento de linha de comando). O ID do banco de dados de recurso desses dados globais é o único nome de recurso codificado necessário na base de código (temos outros porque às vezes os programadores ficam preguiçosos, mas geralmente acabamos corrigindo / removendo esses eventualmente). Esse objeto de dados global está cheio de componentes cujo único objetivo é fornecer dados de configuração. Um dos componentes é o componente Dados globais da interface do usuário, que contém identificadores de recursos para todos os principais recursos da interface do usuário (fontes, arquivos Flash, ícones, dados de localização etc.), entre vários outros itens de configuração. Quando um desenvolvedor de interface do usuário decide renomear o principal recurso da interface do usuário de /ui/mainmenu.swf
para/ui/lobby.swf
eles apenas atualizam essa referência de dados globais; nenhum código do mecanismo precisa mudar.
Usamos esses dados globais para tudo. Todos os personagens jogáveis, todos os níveis, interface do usuário, áudio, ativos principais, configuração de rede, tudo. (bem, não tudo , mas essas outras coisas são bugs a serem corrigidos.)
Essa abordagem tem muitas outras vantagens. Por um lado, torna a compactação e agregação de recursos parte integrante de todo o processo. Os caminhos de codificação embutida no mecanismo também tendem a significar que esses mesmos caminhos devem ser codificados em quaisquer scripts ou ferramentas que agrupam os ativos do jogo, e esses caminhos podem ficar fora de sincronia. Contando com um único núcleo de ativos e cadeias de referência a partir daí, podemos criar um pacote de ativos com um único comando bundle.exe -root config.data -out main.wad
e saber que ele incluirá todos os ativos necessários. Além disso, como o empacotador seguiria apenas as referências de recursos, sabemos que incluirá apenas os ativos necessários e pularemos toda a penugem restante que inevitavelmente se acumula durante a vida de um projeto (além disso, podemos gerar automaticamente listas dessa cotão para poda).
Um caso difícil dessa coisa toda está nos scripts. Tornar o mecanismo controlado por dados é fácil conceitualmente, mas já vi muitos projetos (hobby da AAA) em que os scripts são considerados dados e, portanto, "é permitido" usar apenas caminhos de recursos indiscriminadamente. Não faça isso. Se um arquivo Lua precisar de um recurso e chamar apenas uma função textures.lua("/path/to/texture.png")
, o pipeline de ativos terá muitos problemas ao saber que o script precisa /path/to/texture.png
operar corretamente e pode considerar que a textura não é usada e desnecessária. Os scripts devem ser tratados como qualquer outro código: todos os dados necessários, incluindo recursos ou tabelas, devem ser especificados em uma entrada de configuração que o mecanismo e o pipeline de recursos possam inspecionar quanto a dependências. Os dados que dizem "carregar script foo.lua
" devem dizer "foo.lua
e forneça esses parâmetros ", onde os parâmetros incluem todos os recursos necessários. Se um script gerar aleatoriamente inimigos, por exemplo, passe a lista de possíveis inimigos para o script a partir desse arquivo de configuração. O mecanismo poderá pré-carregar os inimigos com o nível ( pois ele conhece a lista completa de possíveis spawns) e o pipeline de recursos sabe agrupar todos os inimigos no jogo (já que eles são definitivamente referenciados pelos dados de configuração) .Se os scripts geram sequências de nomes de caminhos e apenas chama uma load
função, o mecanismo nem o pipeline de recursos têm como saber especificamente quais ativos o script pode tentar carregar.