700.000 linhas de código, 20 anos e um desenvolvedor: Como o game Dwarf Fortress é construído

Dwarf Fortress é um daqueles projetos de paixão excêntricos que invadiram a consciência da Internet. É um jogo gratuito onde você joga com um aventureiro ou uma fortaleza cheia de anões em um mundo de fantasia gerado aleatoriamente.

A simulação é profunda, com novos jogos criando múltiplas civilizações com histórias, mitologias e artefatos. Falei com ele para ver como ele conseguiu uma única base de código crescente ao longo de mais de 15 anos, os perigos de rastrear e depurar gatos mortos. Nossa conversa abaixo foi editada para maior clareza.

 

Dwarf Fortress é um daqueles projetos de paixão excêntricos que invadiram a consciência da Internet. É um jogo gratuito onde você joga como um aventureiro ou uma fortaleza cheia de anões em um mundo de fantasia gerado aleatoriamente. A simulação é profunda, com novos jogos criando múltiplas civilizações com histórias, mitologias e artefatos. 

 

Tornou-se notório, e com razão. Anões individuais têm estados emocionais, joias favoritas e rancores. E tudo acontece em uma interface ASCII que parece imponente para os novatos, mas parece o rastreamento de texto em Matrix : artesão anão, rio, megabesta lendário. 

 

O jogo inteiro é produto de um desenvolvedor, Tarn Adams, também conhecido como Toady One, que tem trabalhado em Dwarf Fortress desde 2002. Nos primeiros quatro anos foi um projeto de meio período, mas desde 2006 tem sido em tempo integral. Ele mesmo escreve todo o código, embora seu irmão ajude no design e crie histórias baseadas no jogo. Até agora, ele dependia de doações para continuar, mas atualmente está trabalhando em uma versão com gráficos de pixel e uma interface de usuário renovada que estará disponível para compra no Steam. 

 

Falei com Tarn Adams para ver como ele conseguiu uma única base de código crescente ao longo de mais de 15 anos, os perigos de pathing e depuração de gatos mortos. Nossa conversa abaixo foi editada para maior clareza. 

 

P : Quais linguagens de programação e outras tecnologias você usa? Basicamente, qual é a sua pilha? Isso mudou ao longo dos 15-20 anos em que você está fazendo isso?

 

R : DF é uma combinação de C e C ++, não de algum tipo de obediência padrão, mas uma espécie de bagunça que se acumula com o tempo. Uso o Microsoft Visual Studio desde o MSVC 6, mas agora estou usando alguma versão da Comunidade do Visual Studio. 

 

Eu uso OpenGL e SDL para lidar com as questões do motor. Fomos com eles porque era mais fácil portá-los para OSX e Linux, embora eu ainda não fosse capaz de fazer isso sozinho, é claro. Não tenho certeza se usaria algo como Unity ou Unreal agora se tivesse escolha, já que não sei como usar nenhum deles. Mas lidar com seu próprio motor também é uma dor real, especialmente agora que estou fazendo algo além de gráficos de texto. Eu uso FMOD para som. 

 

Tudo isso tem sido constante ao longo do projeto, exceto que o SDL foi introduzido há alguns anos para que pudéssemos fazer as portas. No lado mecânico do jogo, não uso muitas bibliotecas externas, mas ocasionalmente peguei algumas coisas de geração de números aleatórios - coloquei um Mersenne Twister há muito tempo e, mais recentemente, adotei SplitMix64 , que foi destaque em uma palestra na última celebração Roguelike.

 

P : Quais são os desafios em desenvolver um único projeto por tanto tempo? Você acha que isso é mais fácil de fazer sozinho? Ou seja, como você escreveu cada linha, é mais fácil manter e alterar?

 

R : É fácil esquecer as coisas! Procurando por ';', que é um método flexível, mas próximo o suficiente, chegamos a 711.000 linhas, então simplesmente não é possível manter tudo na minha cabeça agora. Tento nomear minhas variáveis ​​e objetos de forma consistente e memorável, e deixo comentários suficientes para me lembrar do que está acontecendo quando chego a um ponto de código. Às vezes, são necessárias várias pesquisas para encontrar a linha exata que estou tentando puxar quando vou e revisito alguma peça do jogo que não toquei por uma década, o que acontece bastante. Eu diria que a maioria das mudanças está focada apenas em certas partes do jogo, então há uma espécie de núcleo fundido ativo sobre o qual tenho um conhecimento muito melhor. Existem algumas partes realmente difíceis que eu não olhei desde antes do primeiro lançamento em 2006.

 

Em relação à relativa facilidade de fazer as coisas sozinho, certamente para mim, que não tenho experiência em trabalhar em um grande projeto com várias pessoas, este é o caminho a percorrer! As pessoas obviamente ficam boas em fazer isso de outra maneira, por exemplo, no contexto de jogos AAA, e claramente vários engenheiros são necessários lá para fazer as coisas a tempo. Eu hesitaria em dizer que posso entrar e mudar as coisas mais rápido do que eles, necessariamente, já que não trabalhei nesse contexto antes, mas é verdade que não tenho nenhum obstáculo burocrático ou orientado para a equipe para pular através quando eu quero fazer uma alteração. Eu posso simplesmente ir e fazer isso. Mas também tenho que fazer isso sozinho.

 

P : Qual foi a maior refatoração / mudança que você teve que fazer?

 

R : Houve alguns refatores que duraram meses, refazendo certas estruturas de dados e assim por diante, embora eu não tenha certeza de que alguma coisa seja um refator estritamente aqui, já que sempre há oportunidades de empurrar a mecânica para frente simultaneamente e faz sentido fazer então, quando o conhecimento do código é recente. 

 

Adicionar a coordenada Z para tornar o jogo mecanicamente 3D (embora ainda seja texto) foi outro, e realmente a coisa mais entorpecente que eu provavelmente já fiz. Apenas semanas e semanas e semanas tomando chamadas lógicas e de função que dependiam de X e Y e vendo como um Z se encaixa ali.

 

Tornar o sistema de itens polimórfico foi, em última análise, um erro, mas foi um grande erro. 

 

P : Por que isso foi um erro?

 

R : Quando você declara uma classe que é um tipo de item, ela o prende a essa estrutura com muito mais firmeza do que se você apenas tivesse elementos de membro. É bom poder usar funções virtuais e esse tipo de coisa, mas as desvantagens são demais. Comecei a usar um item de "ferramenta" na hierarquia, que começou a obter várias funcionalidades e agora pode suportar qualquer coisa, desde uma escada até uma colmeia e um almofariz (e pilão, separadamente, ha ha), e parece mais flexível, e eu gostaria que todos os itens criados no jogo estivessem sob esse guarda-chuva. 

 

Fazemos muita geração procedural e, se quisermos, digamos, gerar um item que atue parcialmente como uma coisa e parcialmente como outra, é muito mais difícil fazer isso quando você está bloqueado em uma hierarquia de classes. Adicionar coisas como dependências de diamante e tudo isso acaba te deixando em nós quando existem maneiras mais limpas de fazer isso. Se diferentes componentes puderem ser simplesmente desligados e ligados, é mais fácil e permite que você faça mais. 

 

Acho que alguns desenvolvedores de jogos se referem a isso como um sistema de componente de entidade, embora seja meu entendimento que as pessoas otimistas mais radicais pensam nisso como outra coisa em que você está realmente quebrando as coisas por campos individuais. Usar um único objeto com diferentes subobjetos alocados é quase certamente pior para perdas de cache, o que é outra coisa, mas os benefícios de organização, flexibilidade e extensibilidade simplesmente não podem ser ignorados, e os diferentes subcampos do item de ferramenta não são t usado com tanta frequência que se torna um problema de otimização.

 

P : Você teve problemas ao mudar de 32 bits para 64 bits? Parece uma daquelas coisas que eram enormes na época, mas se tornou bastante aceita.

 

R : De forma alguma! Estou lutando para pensar em um único problema. Felizmente para nós, já tínhamos nossos tamanhos de bytes sob controle muito bem, uma vez que ele vem salvando e carregando os mundos; o formato precisava ser corrigido quando o configuramos, especialmente porque tivemos que lidar com coisas endian entre sistemas operacionais e tudo mais. E não fazemos nenhuma operação de ponteiro ou outras coisas que possam ter nos colocado em problemas. Acabou sendo um código muito bom para conversão de 64 bits devido às nossas outras práticas, inteiramente por acidente. O principal problema era conseguirmos tempo juntos para fazer a mudança, e então não acabou demorando tanto quanto eu pensei que demoraria.

 

P : Já vi outros jogos semelhantes ao DF morrerem em seus algoritmos de pathfinding. O que você usa e como o mantém eficiente?

 

R : Sim, o algoritmo básico é apenas parte dele. Usamos A * , que é rápido, é claro, mas não é bom o suficiente por si só. Não podemos tirar proveito de algumas das inovações nisso (por exemplo , ponto de salto) já que nosso mapa muda muito. Geralmente, as pessoas usam abordagens que adicionam várias estruturas maiores no topo do mapa para cortar cantos e, por causa da mudança do mapa, elas demoram muito para manter ou são um incômodo. Portanto, nossa abordagem tem sido apenas rastrear os componentes conectados que podem ser acessados ​​caminhando. Eles são muito fáceis de atualizar, mesmo quando o mapa muda rapidamente, embora envolva algumas inundações. Por exemplo, se a água cortar a fortaleza pela metade, ela precisa inundar de um lado e atualizar uma metade inteira da fortaleza para um novo índice, mas uma vez feito isso, é bom, em geral. Isso nos permite cortar quase todas as chamadas A * com falha do jogo - nossos agentes só precisam consultar os números dos componentes e, se os números dos componentes forem iguais, eles sabem que a chamada será bem-sucedida.

 

É rápido de manter, mas a desvantagem é que os índices dos componentes são mantidos apenas para caminhadas. Isso significa que criaturas voadoras, por exemplo, não têm inteligência descobridora global que seja diferente de um caminhante. No entanto, em combate e em algumas outras situações, usamos preenchimentos de inundação de curto alcance com sua lógica real para dar a eles algumas vantagens. Mas não é ideal para eles.

 

Não tenho certeza se tentaremos outras estruturas aqui para fazer com que funcione melhor. Para os tamanhos de nossos mapas, todos falharam, incluindo algumas tentativas externas. Claro, pode ser possível com um esforço realmente concentrado, e eu vi outros jogos que conseguiram, por exemplo, algumas sobreposições retangulares e assim por diante que parecem promissoras, mas não tenho certeza de quão voláteis ou grandes seus mapas eram . 

 

A ideia mais simples seria algo como adicionar um novo índice para aviadores, mas isso é uma grande perda de memória e velocidade, já que precisaríamos manter dois índices de uma vez, e um é ruim o suficiente. Sobreposições mais específicas podem rastrear suas propriedades de caminho (e então você percorre as sobreposições em vez dos blocos), mas são difíceis e lentos de manter à medida que o mapa muda. Existem várias outras ideias flutuando por aí, como rastrear escadas ou fazer algum cache de caminho limitado, e provavelmente há alguns ganhos a serem obtidos nisso. Certamente estamos no limite do que podemos oferecer atualmente em termos de agentes e complexidade de mapas, então algo terá de ceder se quisermos tirar mais proveito disso

 

P : Nessa nota, você está simulando muitas coisas ao mesmo tempo - como você gerencia tantos atores de forma assíncrona (ou não)?

 

R: Se estamos falando de assíncrono como em multithreading, então não, não fazemos nada disso, além da própria exibição gráfica. Há muita promessa aqui, mesmo com microthreading, com o qual a comunidade me ajudou, mas não tive tempo para mergulhar. Eu não tenho nenhuma experiência e é uma coisa sujeita a insetos.

 

P : Você tentou outros projetos / tecnologias junto com o DF?

 

R : Claro! A pasta de projeto lateral que é migrada entre computadores nos últimos dez anos ou mais tem cerca de 90 projetos nela. Alguns deles duraram dias, alguns vários anos. São principalmente outros jogos, quase sempre em outros gêneros, mas também existem alguns projetos auxiliares do DF, como o protótipo do gerador de mitos. Nada perto de ver a luz do dia, mas é divertido brincar.

 

P : Com seus ~ 90 projetos paralelos, você explorou alguma outra linguagem de programação? Se sim, algum favorito?

 

R : Ha ha, não! Eu sou mais um noodler no lado do design, ao invés da tecnologia. Tenho certeza de que algumas coisas realmente acelerariam a realização de meus designs, então eu provavelmente deveria pelo menos aprender alguns scripts e brincar mais com threading. As pessoas até foram gentis o suficiente para fornecer algumas bibliotecas e coisas para ajudar lá fora, mas é difícil bloquear o tempo limite do projeto paralelo para aprendizado de tecnologia quando o tempo do meu projeto paralelo é para relaxar.

 

P : Você tem as notas de lançamento mais interessantes. Qual é o seu bug favorito e o que o causou?

 

R : Provavelmente é chato para mim dizer, mas simplesmente não consigo vencer o inseto do gato bêbado. Alguns vídeos foram feitos sobre isso até este ponto. Era aquele em que os gatos apareciam mortos por todo o chão da taverna e descobriram que estavam ingerindo álcool derramado ao limpar as patas. Um número estava errado no código ingerir durante a limpeza e os enviou através de todos os sintomas de envenenamento por álcool (que adicionamos quando enfeitamos criaturas venenosas).

 

Se você quiser experimentar Dwarf Fortress por si mesmo, você pode baixá-lo de seu site.

 

 

Nós levamos a sua privacidade a sério. Cadastre-se no Avance Network a verdadeira comunidade criptografada de extremo a extremo.


Strong

5178 בלוג פוסטים

הערות