Saindo do desenvolvimento de jogos com Rust após 3 anos
(loglog.games)- Sobre a alegação de que todos os problemas desaparecem quando você se acostuma com Rust
- Mesmo se acostumando com Rust, os problemas fundamentais não desaparecem
- Jogos são máquinas de estado complexas e, como os requisitos mudam o tempo todo, isso não combina com a natureza estática e excessivamente verificadora do Rust
- O problema de precisar refatorar o código o tempo todo é autoinfligido
- Problemas de grandes refatorações causados pelo borrow checker
- Rust força refatorações com mais frequência do que outras linguagens
- Em situações em que os requisitos do jogo mudam com frequência, você acaba brigando com o borrow checker e precisa reestruturar o código
- É difícil concordar com a ideia de que refatoração gera código bom. Código bom surge ao iterar sobre ideias e testá-las
- A indireção resolve apenas alguns problemas e reduz a produtividade no desenvolvimento
- Ao usar indireção para resolver problemas do borrow checker, a lógica do código fica separada e mais complexa
- Em jogos, há eventos conectados, timing específico e muito gerenciamento de estado, e a indireção torna isso mais difícil
- Em Rust, até código simples precisa ser escrito de forma verbosa por causa da indireção
- ECS (Entity-Component-System) resolve o problema errado
- As vantagens do ECS são, na verdade, vantagens da generational arena
- O ECS é visto sob várias perspectivas: composição dinâmica, structure of arrays, solução para o borrow checker do Rust, generational arenas criadas dinamicamente etc.
- Mas é questionável se ECS é realmente a abordagem mais adequada para desenvolvimento de jogos. Mais importante do que performance ou composição é focar na lógica do jogo
- Sistemas generalizados não levam a uma jogabilidade divertida
- Usar sistemas generalizados demais acaba gerando uma jogabilidade sem graça
- Bons jogos exigem projetar cuidadosamente interações detalhadas, sincronizar VFX, repetir playtests e experimentos e lançar rápido para receber feedback
- Rust persegue valores que não são adequados para prototipagem rápida e iteração
- No desenvolvimento de jogos, o importante é prototipar e iterar rapidamente, mas os valores do Rust vão na direção oposta
- O essencial no desenvolvimento de jogos é o jogo em si e a experiência do jogador, não código sem crashes
- A manutenibilidade do código de jogo não é um valor tão importante em jogos indie. O que importa é a velocidade de iteração
- Rust está tão obcecado em evitar problemas que acaba deixando de lado o que realmente importa
- Procedural macros ficam muito aquém de reflection
- No desenvolvimento de jogos, é preciso escrever código para sistemas, gameplay, UI, VFX, áudio, ferramentas e várias outras áreas
- Em Rust, até algo simples como "imprimir um objeto" exige escrever código manualmente ou criar procedural macros
- Procedural macros são muito mais limitadas que macros declarativas e também aumentam bastante o tempo de compilação
- Reflection em C# é muito mais fácil de usar e permite desenvolver rapidamente quando performance não é crítica
- Hot reloading tem papel importante no aumento da velocidade de iteração
- Hot reloading é muito útil para UI/desenho em modo imediato, depuração e ajuste fino de constantes de gameplay
- Rust também tem
hot-lib-reloader, mas ele não é perfeito e exige planejamento e antecipação, o que limita usos mais criativos - Desenvolvedores de jogos querem ferramentas de nível mais alto do que simples recarregamento de
struct
- Abstração não é opcional, é obrigatória
- Exemplo real) Quando é preciso ter comportamentos diferentes conforme o estado da UI, em Rust acaba sendo necessário refatorar ou abusar de clones
- Muitas vezes o código é alterado não porque a lógica de negócio mudou, mas para satisfazer o compilador
- Rust não tem um sistema de tipos estruturais como "um tipo com estes campos", então ao refatorar é preciso modificar vários trechos do código
- A situação de GUI em Rust é péssima
- O importante em UI de jogos não é data binding nem atualizações reativas, e sim personalizar a aparência
- O que a UI de jogos precisa é de GUI bonita, sprites customizados, animações, vector shapes, partículas, efeitos, flashes etc.
- Atualmente não existe uma biblioteca Rust especializada em GUI para jogos
- A orphan rule deveria ser opcional
- A orphan rule reduz muito a produtividade de desenvolvimento em nome da segurança
- Em código de aplicação, e não de biblioteca, deveria ser possível desativar a orphan rule
- O tempo de compilação melhorou, mas ainda é lento quando se usam proc macros
- Proc macros como
serdeaumentam muito o tempo de compilação - Em builds incrementais que levam mais de 20 a 30 segundos, ajustar detalhes finos fica muito difícil
- Proc macros como
- O ecossistema de desenvolvimento de jogos em Rust parece supervalorizado
- Não há tantas pessoas realmente criando jogos
- Muitos projetos com sites ou READMEs chamativos e aparência famosa, na prática, não são tudo isso
- Se você quer realmente fazer jogos, a recomendação é
godot-rust. Em vez de insistir em uma solução puramente em Rust, é melhor contar com a ajuda de um engine maduro
- Como jogos são single-threaded, a justificativa para o incômodo com estado global está errada
- Em Rust, usar estado global é muito incômodo (
static mut,AtomicRefCell,Rcetc.) - Mas, diferentemente de serviços backend, jogos costumam ser single-threaded, então esse nível de incômodo não seria necessário
- O autor considera que foi um erro o Bevy introduzir sistemas paralelos. Foi feito por performance, mas acabou só aumentando a inconveniência, exigindo que o usuário especifique ordem o tempo todo
- Em Rust, usar estado global é muito incômodo (
- Dynamic borrow checking causa crashes inesperados após refatorações
- Ao usar
hecspor 2 anos, o autor viu com frequência casos em que queries se sobrepunham e causavam crashes - O mesmo ocorre com
RefCell: se.borrow_mut()é chamado em dois lugares, acontecem crashes inesperados - Não é tanto que o código seja ruim, e sim que Rust força refatorações desnecessárias
RefCellé útil em jogos, mas Rust torna seu uso difícil demais
- Ao usar
- Falta flexibilidade aos objetos de contexto
- Como em Rust é difícil usar estado global, usa-se o padrão de reunir referências em um objeto de contexto e passá-lo adiante
- Mas, ao emprestar apenas alguns campos, surgem erros de compilação por causa de partial borrow
- Dizem para quebrar o Context em partes e mudar a estrutura, mas o que se quer fazer é lógica de jogo; mudar a estrutura apenas para satisfazer o compilador é perda de tempo
Vantagens do Rust
- Quando compila, normalmente funciona bem. É especialmente útil para ferramentas CLI, processamento de dados e escrita de algoritmos
- Entrega boa performance sem grande esforço. O autor relata experiência de ser cerca de 1,5 a 2,5 vezes mais rápido que C#
enumé muito bem implementado- A usabilidade de IDE melhorou bastante graças ao Rust analyzer
- O sistema de
traité bom. Se a orphan rule fosse um pouco mais flexível, seria ainda mais útil
Opinião do GN⁺
-
Embora a visão sobre Rust seja em geral negativa, parece importante levá-la a sério, já que é uma conclusão a que o autor chegou após diversas tentativas. Especialmente no domínio de desenvolvimento de jogos, faz sentido a observação de que praticidade e velocidade de desenvolvimento são muito importantes, e que o Rust ainda deixa a desejar nesses pontos.
-
Concordo que a segurança buscada pelo Rust às vezes pode reduzir severamente a produtividade. Ao escrever código em Rust, muitas vezes se gasta mais tempo tentando satisfazer o compilador do que trabalhando na própria lógica, e isso parece vir do fato de Rust ser, no fundo, uma linguagem especializada em programação de sistemas
-
Por outro lado, desenvolvimento de jogos costuma acontecer em ambientes single-threaded, onde prototipagem rápida e iteração são fundamentais, então verificações de segurança excessivas podem acabar sendo mais prejudiciais do que úteis. Especialmente no início do Rust, muita gente tentou fazer jogos com a linguagem e acabou frustrada; pelo visto, esses problemas fundamentais ainda não melhoraram tanto.
-
É verdade que engines de jogo mais recentes, como Bevy, melhoraram a conveniência do desenvolvimento de jogos em Rust, mas ainda parecem muito distantes do nível de engines maduras como Unity ou Godot. Como o texto sugere, em vez de construir tudo de forma puramente em Rust, usar Rust junto com engines existentes pode ser uma alternativa mais realista
-
As vantagens mencionadas pelo autor ficam muito mais evidentes quando se usa Rust em áreas fora dos jogos. Em domínios como programação de sistemas ou desenvolvimento de servidores web, a segurança e a alta performance do Rust são grandes trunfos, mas esses benefícios não parecem ter tanto peso assim no desenvolvimento de jogos
-
No fim das contas, Rust não é uma linguagem universal, e sim uma linguagem especializada em certos domínios, então vale considerar com cuidado se ela realmente combina com a natureza do seu projeto
13 comentários
Acho que
rusté uma linguagem pensada para resolver problemas causados por diversos tipos de falhas de desenvolvedores, numa realidade em que há cada vez menos desenvolvedores capazes de escrever código íntegro no nível do core.Como ela é mais adequada para desenvolver com precisão do que com rapidez,
não é apropriada para projetos em que requisitos adicionais dos usuários surgem com frequência e precisam ser executados.
Mesmo assim, acho que o desejo pelo surgimento de uma biblioteca de UI
vem da expectativa de que, mesmo com uma UI pequena e simples funcionando sobre um código robusto, sua utilidade melhoraria bastante.
Como nunca desenvolvi jogos, não sei ao certo,
mas parece que escolheram a linguagem errada desde o começo, ou, se a velocidade de iteração no desenvolvimento era importante, deveriam ter escolhido uma linguagem de nível script...
ou então faltou uma compreensão mais profunda do sistema.
Acho que, mesmo se tivessem escolhido C++, teriam enfrentado problemas parecidos.
Isso mesmo. Bem, se eu tivesse escolhido C++, haveria a opção da Unreal, é claro...
Hoje em dia, tenho a impressão de que linguagens nativas como Rust e C++ parecem mais adequadas para o desenvolvimento de middleware, como engines de jogo, do que para o desenvolvimento de clientes de jogos.
Assim como quase não há casos de desenvolvimento web full stack com linguagens nativas.
Desde o início, era uma linguagem feita para esse propósito. Como substituta do C++.
Acho que o texto acabou virando mais uma crítica à linguagem.
Isso mesmo. Acho que originalmente começou para melhorar o motor interno do Firefox?
Pela vibe do texto, parece aquele caso de tentar desenvolver com Rust em uma área que não combina muito bem com a linguagem, como se fosse uma panaceia, e acabar sofrendo por isso kkk
Concordo 100%. Para a lógica de jogos, uma linguagem com boa produtividade parece mais adequada ao caso de uso.
Estou começando com Rust, então consigo me identificar um pouco.
É doloroso ter que modificar código demais por causa de mudanças no borrowing.
Diferente de Python e JavaScript, que têm muitas expressões implícitas, Rust tende a ser explícito, então às vezes o código fica verboso e, como obriga o programador a fazer muitas escolhas, também pode ser cansativo.
Mesmo assim, os conceitos que não existem em outras linguagens são novos e divertidos.
Se não fosse pelo Rust Analyzer e pelo GitHub Copilot, acho que eu teria desistido bem antes.
Como era de se esperar, desenvolvimento de jogos é hardcore mesmo...
Em vez de fazer tudo em Rust, do core ao front-end,
que tal escrever o core em Rust, como no Unity ou em outras engines de jogo, e usar em conjunto uma linguagem mais produtiva?
Acho que não tem muito como evitar, já que ele tende a impor a estrutura de forma bem rígida.
Comentários do Hacker News
Compartilha a experiência de desenvolver um cliente de metaverso e aponta problemas do Rust
asyncse espalhar até para áreas desnecessáriasComo desenvolvedor de jogos com 20 anos de experiência, considera que Rust não é adequado para desenvolvimento de jogos
O ecossistema de desenvolvimento de jogos em Rust depende de hype exagerado
Compara a experiência de desenvolvimento de jogos com Bevy, Unity e Godot
A linguagem Nim pode ser mais adequada para desenvolvimento de jogos do que Rust
Rust parece uma linguagem dogmática que força uma forma específica de programar
A demo de tooling de Allen Blomquist mostra que o loop de feedback no desenvolvimento é importante
O problema fundamental do Rust é abandonar orientação a objetos e preferir programação funcional
Em desenvolvimento de jogos, Rust é a pior escolha
Ao programar em D, percebeu que é muito mais fácil mudar o layout dos dados do que em C