- RollerCoaster Tycoon, lançado em 1999, é um jogo de simulação escrito quase inteiramente em assembly que manteve desempenho estável mesmo processando milhares de visitantes em tempo real
- O desenvolvedor Chris Sawyer escolheu controle de baixo nível em vez de linguagens de alto nível, criando uma das últimas grandes gerações de jogos em assembly com eficiência máxima de CPU
- Por meio do projeto de fãs OpenRCT2, os padrões precisos de otimização e as técnicas de economia de memória do original foram analisados por engenharia reversa
- O jogo usa operações de deslocamento de bits e detalhamento de tipos de dados para aumentar a velocidade de cálculo e a eficiência de cache, além de tornar possível uma simulação em larga escala com limites na profundidade de pathfinding e remoção de cálculos de colisão
- Essa estrutura é um exemplo clássico de otimização que usa restrições técnicas de forma criativa e ainda hoje mostra a importância de eliminar cálculos desnecessários já na fase de projeto
Análise da estrutura de otimização de RollerCoaster Tycoon
- RollerCoaster Tycoon (RCT), lançado em 1999, foi escrito quase inteiramente em assembly (Assembly) e é avaliado como um jogo que conseguiu manter frames estáveis no hardware da época enquanto simulava milhares de agentes em tempo real
- Com base no conteúdo abordado no podcast alemão sobre jogos Stay Forever, o texto analisa em detalhes como Chris Sawyer alcançou um nível extremo de otimização
- O código-fonte original não foi publicado, mas o projeto feito por fãs OpenRCT2 permite verificar por engenharia reversa a estrutura do código e as técnicas de otimização
-
Maximização de desempenho com base em assembly
- RCT foi escrito em assembly em vez de C ou C++, o que permitiu um controle de desempenho muito mais detalhado do que em outros jogos da época
- Por exemplo, Doom (1993) foi escrito majoritariamente em C, enquanto RCT foi implementado quase inteiramente em assembly
- Essa abordagem já era rara no fim dos anos 1990, e RCT é considerado um dos últimos grandes jogos em assembly
- Como a otimização automática dos compiladores ainda era limitada na época, a otimização manual fazia grande diferença no desempenho
-
Análise de código por meio do OpenRCT2
- OpenRCT2 é um projeto open source em que fãs reimplementaram completamente o jogo original, usando os mesmos assets e mantendo 100% de compatibilidade
- As versões iniciais reproduziam um comportamento quase idêntico ao do código original, e depois várias melhorias foram adicionadas
- Esse projeto permitiu observar os padrões minuciosos de otimização do código original
-
Detalhamento de tipos de dados — economia de memória
- RCT armazena dados monetários em tamanhos diferentes conforme o contexto
- Ex.: o valor total do parque usa uma variável de 4 bytes, enquanto o preço de uma loja usa uma variável de 1 byte
- Esse detalhamento foi feito para economizar memória e melhorar a eficiência de cache, mas como em CPUs modernas a diferença de desempenho é quase nula, no OpenRCT2 isso foi unificado em uma única variável de 8 bytes
-
Otimização de operações matemáticas com deslocamento de bits
- No código, a operação
NewValue = OldValue >> substitui divisões por potências de 2
- Como essa otimização só é possível quando multiplicações e divisões envolvem potências de 2, parece que as próprias fórmulas do jogo foram projetadas para atender a essa condição
- Em outras palavras, a estrutura matemática já considerava a eficiência de CPU desde a etapa de design do jogo
-
Design de jogo orientado a desempenho
- Como Chris Sawyer participou de RCT como programador e único game designer, foi possível criar uma estrutura que já levava desempenho em conta desde o início do projeto
- Um exemplo representativo é o sistema de visitantes (pathfinding)
- Na maioria dos jogos de simulação, os visitantes definem um destino e calculam uma rota, mas em RCT os visitantes andam aleatoriamente e encontram atrações por acaso
- Essa abordagem evita cálculos de pathfinding em larga escala e permite processar milhares de visitantes ao mesmo tempo
- Quando o pathfinding é necessário (ex.: um mecânico indo até uma atração quebrada), há um limite na profundidade da busca para evitar queda de frames
- Visitantes comuns procuram apenas até 5 cruzamentos, mecânicos até 8
- Visitantes que compraram um mapa têm o limite de busca aumentado para 7
- Essas limitações não são apenas um compromisso técnico, mas uma estrutura de otimização integrada naturalmente ao gameplay
-
Processamento de multidões e omissão de desvio de colisão
- RCT remove completamente os cálculos de colisão e desvio entre visitantes
- Milhares de visitantes podem compartilhar o mesmo tile de caminho
- Em vez disso, o jogo acompanha a densidade populacional ao redor e faz com que a felicidade dos visitantes diminua quando o nível de lotação é alto
- O jogador ainda precisa gerenciar aglomerações, mas o volume de cálculo é muito menor
- Essa abordagem é considerada um exemplo representativo de como remover cálculos físicos complexos sem prejudicar a experiência de jogo
-
Lições para o desenvolvimento moderno
- A otimização de RCT é vista como um caso de uso criativo de restrições técnicas
- Ainda hoje, essa abordagem é possível, mas exige colaboração estreita entre programadores e designers
- Em alguns casos, em vez de resolver um problema técnico, eliminar o próprio problema já na etapa de design pode trazer ganhos de desempenho ainda maiores
2 comentários
Por favor, deem muito amor ao RollerCoaster Tycoon.
Comentários no Hacker News
Warcraft 1, 2 e StarCraft usavam mapas com tamanho em potências de 2
Isso permitia ganhar desempenho em CPUs 386/486 lentas usando operações de shift em vez de divisão e multiplicação
Renderização de mapa, sprites, fontes e efeitos de neblina eram tratados com milhares de linhas de assembly, e o restante era código altamente portável escrito em C
No caso de Blackthorne, as versões de SNES, Genesis e DOS foram portadas manualmente com assembly diferente para cada uma, e a versão de PC gerou por macro 100 mil linhas de código de renderização para VGA Mode X
Essa experiência levou a Blizzard à lição de que “assembly consome tempo demais de desenvolvimento”
Comanche: Maximum Overkill era um simulador de helicóptero baseado em voxels escrito inteiramente em assembly, mas a migração para modo protegido era tão difícil que as versões seguintes passaram para renderização por polígonos
A EA abriu o código-fonte da série Command & Conquer, mas Tiberian Sun e Red Alert 2 ficaram de fora
Seria bom se StarCraft também fosse aberto por uma questão de preservação histórica
Foi aí que fiquei completamente fascinado por jogos
Também fico curioso se ela participou da demoscene
Eu estive por pouco tempo como consultor perto do lançamento de WC3
Como o texto menciona, parece que os resultados mais impressionantes surgem quando designer e programador são a mesma pessoa
Mesmo com a estrutura hierárquica de grandes empresas, dá para empurrar algo com bastante gente, mas resultados realmente criativos muitas vezes nascem completos na cabeça de uma pessoa só
Também fico pensando se o futuro das ferramentas de desenvolvimento com IA pode acabar sendo um retorno a essa “era do desenvolvimento solo”
Designers de jogos ainda precisam considerar características numéricas
Quanto melhor o designer, mais ele entende que fatores como eficiência computacional e precisão influenciam o balanceamento do jogo
Hoje em dia há muitos desenvolvedores que ignoram isso, mas às vezes esse é um motivo oculto para a queda de qualidade do jogo
Hoje uma soma leva 1 ciclo, uma multiplicação 3 ciclos e uma divisão 12 ciclos, e várias operações são processadas em paralelo ao mesmo tempo
Na era do Pentium antigo, eram 46 ciclos e clock de 100 MHz
Hoje o layout de memória é muito mais importante — um único cache miss pode custar de 100 a 1000 ciclos
Fazer operações com
int[]é muito mais rápido do que comMonster[]Há trade-offs entre velocidade, precisão, tamanho de armazenamento e complexidade
Artigos como Toward an API for the Real Numbers propõem abordagens graduais para resolver esse tipo de problema
Existem vários métodos, como erro de ponto flutuante, aritmética intervalar e cálculo simbólico, mas no fim, se você não entender os trade-offs, o problema vai te engolir
Por exemplo, Fumito Ueda considerou com muito cuidado a viabilidade técnica em Shadow of the Colossus, e Doom também foi uma combinação de criatividade com tecnologia
Vale ver esta entrevista relacionada
Bugs, consistência da história e imersão são mais importantes
Claro, quedas severas de frame prejudicam a qualidade, mas se roda de forma fluida no hardware-alvo, o foco da melhoria deve ir para outros pontos
Ajustei para que constantes pudessem ser carregadas imediatamente com instruções Thumb-2, evitando stalls de memória
Com isso, foi possível usar números aleatórios de forma rápida e eficiente, e todos os testes passaram
Mas depois o projeto mudou para Cortex-M0 e esse código acabou descartado
Achei interessante a parte de transformar restrições técnicas em elementos de gameplay
Isso me fez lembrar do sistema de Blood Moon de The Legend of Zelda
A explicação de que “usar
>>3em vez de/8economiza uma divisão” não é verdadeira em compiladores modernosSe o tipo estiver correto, o compilador otimiza automaticamente para operação de shift
signed), entram operações extras para respeitar a regra de arredondamento do padrão CFiquei surpreso ao ver a explicação de que “em binário, shift à esquerda dobra o valor”
É curioso como em 2026 esse conceito tão básico pode parecer quase estranho de novo
Dizer que “o compilador não troca multiplicações por potências de 2 por shifts” é uma piada antiga
Já nos anos 2000 os compiladores bocejavam diante desse tipo de código e otimizavam isso automaticamente
Gostei da leitura. Sobre RCT, também recomendo estes materiais
Fico pensando se, em grandes estúdios, ainda dá para transformar restrições técnicas em características do jogo
Assim como obstáculos deixam a narrativa mais interessante, um desenvolvedor solo pode converter limitações técnicas em elementos criativos
Dá para imaginar abordagens como reinterpretar bugs ou glitches como minigames
Ao ver a parte sobre pathfinding em RCT, lembrei do vídeo do Marcel Vos no YouTube
Ele publica muitos vídeos explorando a fundo o funcionamento interno de RCT