← Blog

Lançamento de Setembro de 2026

Lançamento de Setembro de 2026

2026-10-01

Há um ano ou dois, decidi fazer um jogo usando apenas tecnologia que eu mesmo havia construído. Isso significava que eu precisava de uma engine de jogos. Eu queria trabalhar do jeito que estava acostumado, num editor no estilo Unity com ferramentas de designer fases, então a engine precisava de um studio. O studio precisava de uma GUI, que se tornou a Guinevere. No caminho, percebi que eu preferia construir sobre uma plataforma como o VSCode, que já fornece grande parte dessa base, do que escrever mais um editor de propósito único. Assim, o studio se tornou um host genérico para plugins, chamado Gaya, e a Turian se tornou um de seus plugins. Junto com o jogo, essa pilha é o MEGA4.

Alguns meses atrás, fiquei travado e fui explorar Zig. Fiz um grande progresso, e essa engine vive como TurianZ, e Voltando para casa no C# conta por que voltei. O retorno levou os últimos dois meses.

No post da 1.0 eu brinquei que não ficaria surpreso em ver a v23 em um par de meses. Meia dúzia de mudanças incompatíveis para chegar à v2. Duas das lacunas que listei naquele post, a ausência de undo no Studio e as cópias de prefab sem vínculo com sua origem, agora estão fechadas.

Turian Studio 2.0 with the main menu expanded across the application bar
The same Studio frame with the main menu collapsed into a single button

Por que 2.0, e por que hoje

A Turian segue o versionamento semântico: o primeiro número, a versão major, só sobe quando uma mudança pode quebrar seu projeto. É uma promessa em forma de número. Se você vê 2.3 depois de 2.0, pode atualizar sem ler nada. Se vê 3.0, leia o guia primeiro.

Seis das mudanças de setembro quebram essa promessa para projetos 1.x:

Mudança O que quebra Passos de migração
Camera rigs e UI in-game viraram bricks projetos que os usam precisam declará-los Bricks
Injeção de dependência substituiu serviços globais Input, Localization, RuntimeServices estáticos Serviços
Transform virou um valor imutável node.Transform.Position = … Transform
Atributos do Inspector mudaram para um pacote compartilhado nomes completos de atributos, [HideInEditor] Atributos
IdClass renomeado para IdObject plugins que usam a API de identidade IdObject
Barra do aplicativo e aparência migraram para o Gaya hosts e chrome customizados do Gaya Gaya

Marquei a 2.0.0 em 30 de setembro, assim que a primeira delas chegou, e removi a tag no mesmo dia. Mais trabalho incompatível já estava em andamento. Lançar isso como 2.0, 3.0 e 4.0 em uma semana teria dado a você três migrações pelo trabalho de um mês. Então as quebras saíram juntas hoje, com um único guia de migração. Arquivos de cena, prefab e data asset mantêm seus ids, então nenhum conteúdo seu precisa ser convertido. A partir daqui, a 2.x só recebe mudanças compatíveis. A próxima quebra, quando vier, será a 3.0.

Bricks: um formato aberto de pacotes

Turian 2.0.0, tickets #79, #74

A maior parte da 2.0 são os bricks. Um brick é um pacote: código, assets e ferramentas de editor numa única pasta, com versão, dependências e licença, que qualquer projeto pode instalar, atualizar ou remover. A Unity tem seu Package Manager e a Godot tem sua Asset Library. Os bricks são a nossa resposta, com uma diferença: eles não são amarrados à Turian.

Os bricks pertencem à Gaya, a plataforma sob o Studio, e seu formato é uma especificação aberta: um manifesto package.json, um lock file e um índice de registro, tudo documentado com schemas JSON. Qualquer engine, ou qualquer aplicação que nem seja uma engine, pode lê-los e publicá-los. A Turian é simplesmente a primeira usuária. É também como vamos distribuir coisas daqui em diante: projetos de exemplo, mods de jogos e extensões do Studio serão todos bricks, para que vocês possam empilhar e construir sobre o trabalho uns dos outros.

O painel de Bricks listando os bricks desta máquina, com o Inspector mostrando o manifesto de um brick

O que um brick pode carregar

Um brick pode conter tudo o que uma pasta de projeto contém:

  • Scripts, compilados sob sua própria assembly definition, de modo que o código de um brick fica isolado do seu.
  • Assets: modelos, texturas, prefabs, cenas, sons, DataAssets. Cada um vem com um arquivo .meta que carrega seu id permanente, então uma cena que usa o prefab de um brick continua apontando para ele entre atualizações.
  • Extensões do Studio: menus, painéis e páginas do Inspector. O brick com.acme.inventory do exemplo de produtor adiciona um menu Acme ao Studio no momento em que é instalado.
  • Código pré-compilado. turian-cli brick pack --precast compila os scripts do brick com antecedência, de modo que quem instala carrega assemblies prontas em vez de compilar seu código.

A engine come os próprios bricks

Os primeiros bricks são os da própria Turian. Os camera rigs Follow, Orbit, Free Fly e FPS saíram do núcleo da engine para org.mass4.turian.cameras, e a UI in-game (documentos .ui, style sheets .uss e tudo que os desenha) migrou para org.mass4.turian.ui. Ambos acompanham a Turian e novos projetos os instalam, mas um jogo que não precisa deles pode removê-los. Um jogo sem UI não embarca mais Guinevere nem Skia, e o executável perde libSkiaSharp, cerca de 13 MB por plataforma.

Quando um brick está ausente, nada é destruído. Seus componentes carregam como placeholders, seus assets são ignorados e seus arquivos .meta ficam intactos, então instalar o brick de novo traz tudo de volta. Projetos 1.x precisam declarar esses dois bricks: veja o guia de migração.

Instalando e gerenciando bricks

Um projeto lista seus bricks em Bricks/manifest.json. Ao lado, packages-lock.json registra exatamente o que foi resolvido, até o commit ou o hash do arquivo, para que toda máquina da equipe construa o mesmo jogo. Faça commit dos dois. Um brick pode vir de cinco lugares:

Origem Exemplo Use para
Integrado builtin:org.mass4.turian.cameras bricks que acompanham a engine
Pasta file:../bricks/com.acme.levels um brick que você desenvolve ao lado do seu jogo
Arquivo empacotado file:levels-1.0.0.brick um brick que alguém lhe enviou
Git git+https://github.com/acme/rules.git#v1.2.0 um brick num repositório, fixado a um commit
Registro ^1.0.0 o release compatível mais novo de um registro

Os bricks ficam armazenados uma vez por máquina, em ~/.gaya/bricks, e nunca são copiados para dentro dos projetos, então dez projetos usando o mesmo brick compartilham uma cópia no disco. Defina GAYA_BRICKS para manter o repositório em outro lugar, como num cache de CI.

No Studio, Project → Bricks… abre o painel de Bricks. Ele lista o que o projeto instala e por que cada brick está ali, isto é, o que depende dele, e instala, atualiza, remove ou embute bricks sem sair do editor. O Inspector mostra o manifesto do brick selecionado. O painel de Assets ganha uma pasta Bricks ao lado de Assets, para que você possa arrastar prefabs, texturas e scripts de um brick para cenas e campos do Inspector como qualquer outro asset. Tudo também funciona no terminal: turian-cli brick add, remove, list, update, search e restore --locked, que falha um build de CI quando o manifesto e o lock divergem.

Mudando o que você instalou

Bricks instalados são somente leitura, porque uma atualização apagaria suas edições. Em vez disso, há uma escada de formas de tornar um brick seu, da mais leve à mais pesada:

Passo Como Acompanha as atualizações do brick?
Usar como é referenciá-lo por id sim
Criar uma variante uma variante de prefab ou uma variante de data asset que sobrescreve alguns valores sim, exceto o que você sobrescreveu
Mudar como ele importa ProjectSettings/PackageImportOverrides.json sim
Copiar um asset Copy no painel de Assets, ou brick copy não: a cópia ganha um novo id
Bifurcar o brick inteiro brick embed você mescla com brick rebase

Um brick embutido é uma cópia gravável em Bricks/<id>/ que prevalece sobre o instalado. brick diff mostra o que você mudou, e brick rebase mescla o próximo release do original no seu fork, arquivo por arquivo, marcando conflitos onde vocês editaram as mesmas linhas, do jeito que o git faz.

Fazendo o seu

turian-cli brick new user.you.inventory cria um brick com manifesto, assembly definition e um primeiro script. Os ids são escritos em ordem DNS reversa, como pacotes Java e Android: use um domínio que você possui (com.acme.inventory), ou user.<name>.… se não tiver um. brick verify checa que cada asset traz um .meta único, e brick pack grava um arquivo .brick com seu hash. O empacotamento é reproduzível: as mesmas fontes sempre produzem os mesmos bytes, então qualquer um pode conferir que um brick publicado corresponde ao seu código-fonte. Um workflow reutilizável do GitHub, MASS4ORG/Turian/.github/workflows/brick.yml, verifica, empacota e publica um brick para você.

Três projetos de exemplo mostram o ciclo inteiro. example-06-plugin-producer cria três bricks, um deles com código de editor; example-05-plugins os instala; e example-07-team-split cobre equipes. Quando uma equipe constrói um brick e outra constrói um jogo sobre ele, brick stub gera um placeholder com os mesmos ids de asset e de tipo, para que o jogo referencie os ids reais desde o primeiro dia, e brick verify --against falha quando o stub e o brick real divergem.

Registros

Um registro é onde as pessoas publicam bricks para outros encontrarem. Um registro Turian é um site estático com um índice assinado, então GitHub Pages basta para hospedar um, sem servidor para rodar. Adicione um a um projeto com turian-cli brick registry add studio https://… --scope com.studio --key studio.pub, e um intervalo de versão simples como "com.studio.inventory": "^1.0.0" resolve para o release compatível mais novo.

Todo release é assinado. Publicadores assinam com chaves ed25519 (turian-cli brick publish --registry site/ --key registry-key), um esquema de assinatura moderno de curvas elípticas, do mesmo tipo de chave que o GitHub aceita para logins SSH, então a maioria dos desenvolvedores já tem uma. Um projeto só confia nas chaves que ele mesmo lista, e o lock file registra a versão, o hash do arquivo e a chave que o assinou. Se um registro for comprometido, um brick adulterado falha na instalação em vez de parar silenciosamente no seu jogo. Os nomes user.<name>.… pertencem a quem os publicar primeiro.

Traga um pacote da Unity (muito experimental)

turian-cli import unitypackage Crates.unitypackage --brick user.you.crates --out bricks/ lê um .unitypackage diretamente, sem Unity instalada, e o transforma em um brick. Texturas, modelos e áudio mantêm seus ids da Unity, e materiais e prefabs simples vêm junto. Trate como experimento, não como caminho de migração: scripts, shaders e animação não são convertidos, e qualquer coisa além de cenas simples vai exigir trabalho manual. O relatório lista o que foi ignorado. Verifique também a licença do que você importa: a ferramenta move arquivos, não muda seus termos.

Chega de globais: injeção de dependência

Turian 2.0.0, ticket #44

A Unity fez uma escolha em seus primeiros anos que a persegue até hoje: sua API é global. Input.GetKey, Time.deltaTime, Camera.main, GameObject.Find são chamadas estáticas que qualquer um pode fazer de qualquer lugar, e a maioria dos projetos Unity adiciona seus próprios singletons GameManager.Instance por cima. É maravilhosamente conveniente no dia um. Anos depois, é por isso que um componente Unity é difícil de testar isolado, por que desativar o domain reload para acelerar o Play Mode significa caçar cada campo estático que lembra a execução anterior, e por que a comunidade construiu Zenject e VContainer para contornar isso. A Unity nunca conseguiu remover seus estáticos, porque todo projeto do mundo depende deles.

A Turian 1.0 tinha a mesma conveniência, por trás de classes estáticas como Input e Localization. A 2.0 a remove enquanto ainda há poucos projetos para quebrar. Injeção de dependência significa que um componente não sai procurando o que precisa; ele declara, e a engine entrega. Cada sessão (o jogo exportado, cada execução de Play Mode, cada teste) recebe seu próprio contêiner de serviços, construído sobre o Microsoft.Extensions.DependencyInjection padrão do .NET:

public class Patrol : Component
{
    [InjectService, JsonIgnore]
    public IInputSource? Input { get; private set; }

    public override void OnUpdate(float deltaTime)
    {
        if (Input?.IsKeyDown(Key.W) == true)
            Node.Position += Vector3.UnitZ * deltaTime;
    }
}
Unity Turian 2.0
Ler entrada Input.GetKey(KeyCode.W) de qualquer lugar injetar um IInputSource
Seus próprios serviços singletons, ou Zenject ou VContainer IEngineServiceModule, integrado
Isolamento do Play Mode campos estáticos sobrevivem sem domain reload cada sessão tem seus próprios serviços
Testar um componente isolado substituir globais à mão dar a ele um contêiner de teste
Serviços do designer singletons ScriptableObject por convenção um DataAsset, compartilhado por sessão

[InjectService(Optional = true)] permite que um componente rode onde um serviço está ausente, como uma cena pré-visualizada no Studio sem entrada de input roteada. Jogos e bricks registram seus próprios serviços implementando IEngineServiceModule, e a Turian descobre os módulos em cada assembly que o jogo carrega. SceneTicker.TimeScale chega junto: ele escala o tempo do jogo, 0 o congela, e o tempo não escalado continua rodando para menus e efeitos. Atualizar da 1.x exige algumas substituições, listadas no guia de migração.

A última linha da tabela é o lado do designer da mesma história, e merece sua própria seção.

DataAssets: a espinha dorsal do designer

Turian 1.0 a 2.0, tickets #145, #146, #147, #43, #197

Um DataAsset é a versão da Turian do ScriptableObject da Unity: uma classe C# cujas instâncias vivem como arquivos no seu projeto, editadas no Inspector em vez de em código. Um asset WeaponData guarda o dano e a velocidade de uma espada; um LevelData guarda as ondas de inimigos de um nível. Programadores escrevem a classe uma vez, e designers criam, ajustam e equilibram quantos assets quiserem sem tocar num script.

Em 2017, Ryan Hipple mostrou na Unite Austin até onde essa ideia vai, numa palestra chamada Game Architecture with Scriptable Objects. Em vez de conectar sistemas em código, você os liga por meio de assets: a vida do jogador é um asset FloatVariable que a barra de vida, o áudio e o sistema de save leem, e "o jogador morreu" é um asset GameEvent que qualquer um pode ouvir. Designers religam o jogo arrastando assets no Inspector. A abordagem ficou conhecida como SOAP, a ScriptableObject Architecture Pattern, e é uma das formas mais amigáveis ao designer de construir um jogo. A Turian está dobrando a aposta: DataAssets serão a espinha dorsal de como os designers trabalham, e setembro lançou os fundamentos.

ScriptableObject da Unity DataAsset da Turian
Uma instância por asset sim sim, compartilhada por sessão
Mudanças no Play Mode persistem depois do fim do Play Mode descartadas; o arquivo autoral nunca é tocado
Variantes não, sem ferramentas de terceiros sim, como variantes de prefab
Configurações do projeto arquivos especiais em ProjectSettings/ os próprios DataAssets
Referenciar objetos da cena silenciosamente perdidas ao salvar recusado em tempo de compilação, com sugestão
  • Uma instância compartilhada. Todo campo, cena e serviço que referencia um DataAsset recebe o mesmo objeto, então um DataAsset GameManager funciona como serviço: mude suas moedas numa cena e a próxima cena vê. Uma segunda sessão, como outra execução de Play Mode ou um teste, recebe sua própria cópia.
  • Dados autorais permanecem autorais. O Play Mode parte dos valores salvos toda vez, e nada do que um jogo em execução faz é escrito de volta ao arquivo. Na Unity, mudanças que um jogo em execução faz num ScriptableObject sobrevivem ao Play Mode, forma clássica de perder uma tarde de balanceamento. DataAsset.Instantiate dá a um script uma cópia privada quando precisa.
  • Variantes. Um DataAsset pode ser variante de outro, guardando só os valores que sobrescreve, como uma variante de prefab funciona. Uma "Espada de Fogo" pode ser variante de "Espada" com mais dano, e quando você rebalanceia "Espada", toda variante acompanha. Crie uma no Studio ou com turian-cli variant.
  • Configurações são DataAssets. As configurações de Input, Graphics, Player e Localization do projeto são DataAssets, criadas em New → Settings e editadas no Inspector como todo o resto. Isso significa que configurações podem ser variantes, viver em bricks e ser lidas pelos mesmos serviços que os seus próprios dados.
  • Endurecimento. Serializadores são gerados em tempo de compilação em vez de descobertos por reflexão em runtime, então DataAssets carregam mais rápido e podem ser pré-carregados em segundo plano. Variantes recusam tudo o que mudaria silenciosamente o que elas são: um override não pode substituir o id ou o tipo do asset, uma variante deve manter o tipo da sua base, e sintaxe desconhecida falha alto em vez de ser ignorada. Um DataAsset que tenta guardar uma referência a um objeto de cena é reportado pelo compilador (TUR0001), com uma sugestão do que usar no lugar.

Prefabs que mantêm o vínculo

Turian 1.1.0, tickets #84, #185

Um prefab é um pedaço reutilizável de cena, como um poste de luz, um inimigo ou uma casa inteira, que você constrói uma vez e coloca muitas vezes. Na 1.0, um prefab colocado era uma cópia: mudasse o original, e as cópias não notavam. Agora cada instância é um vínculo, e o fluxo é o que usuários de Unity conhecem:

  • Overrides são rastreados. Uma instância lembra o que muda no seu prefab: valores, componentes e nós adicionados, e removidos. A Scene Tree e o Inspector os marcam.
  • Apply e revert funcionam por valor, por componente ou para a instância inteira. Um apply escreve no prefab que possui o valor, do mais interno para fora, então uma edição num prefab aninhado vai parar onde pertence.
  • Prefabs aninhados e variantes. Um prefab pode conter instâncias de outros prefabs: um prefab de casa cheio de prefabs de poste. Um prefab cuja raiz é instância de outro é variante dele, como uma "Casa Vermelha" que herda tudo de "Casa" menos a cor.
  • Modo prefab. Abra o prefab de uma instância a partir da cena, edite-o e volte pela breadcrumb. Instâncias na cena aberta se atualizam assim que o prefab muda.
  • Unpack rompe o vínculo e mantém a hierarquia como nós simples, opcionalmente desempacotando instâncias aninhadas também.

Todas essas operações são desfaçáveis pelo undo. Arquivos de cena ficam pequenos também: uma instância é salva como um vínculo ao seu prefab mais o que ela sobrescreve, adiciona e remove, em vez de uma cópia completa do prefab em cada cena que o usa.

Undo, redo e nada perdido

Turian 1.1.0, ticket #81

O Studio agora tem undo e redo para toda operação de editor, e funciona como na Unity: um histórico para o projeto inteiro, onde cada passo pertence a um documento. Um documento é uma cena aberta ou um asset que o Inspector está editando. Desfazer um passo traz seu documento para frente primeiro, para que você sempre veja o que mudou.

O undo rastreia o que você quis, não cada evento minúsculo. Arrastar um objeto com o gizmo é um passo, não importa quantos frames dure, e arrastar um campo numérico se mescla num único passo. Salvar sela o passo, então a edição depois de um save sempre desfaz separadamente. Mudanças além da cena também contam: renomear ou mover arquivos no painel de Assets, ou reescrever um prefab com Apply. O menu Edit nomeia o passo que está prestes a desfazer ou refazer. Enquanto o Play Mode mostra o jogo rodando, o histórico fica travado, porque essas mudanças serão descartadas de qualquer forma.

Sair do Studio ou trocar de projeto com trabalho não salvo agora pergunta antes.

Desempenho

Turian 2.0.0, tickets #32, #132, #58

Uma palavra aparece em toda tabela abaixo: alocação. Em C#, todo objeto criado no heap é memória que o coletor de lixo depois precisa encontrar e liberar. Um jogo que aloca a cada frame eventualmente pausa para coletar, e o jogador vê um engasgo. Zero alocações por frame significa sem engasgos vindos da engine.

Um transform que parou de alocar

Começou como uma pergunta: a Turian deveria dividir seu Node em tipos 2D, 3D e UI especializados, como algumas engines fazem? Isso ecoaria por componentes, serialização, prefabs e ferramentas, então medi antes de decidir.

A resposta estava em outro lugar. O Transform de cada nó (posição, rotação e escala) era uma classe com notificações de mudança: toda edição disparava um evento, e toda leitura de posição mundial percorria a hierarquia e alocava. Na 2.0, Transform é uma struct imutável, um pequeno valor guardado dentro do nó em vez de um objeto separado, e cada nó faz cache do seu transform mundial até que ele ou um pai se mova.

Numa árvore de 100.000 nós, movendo cada nó e lendo cada posição mundial:

Tempo de frame Alocado por frame Memória por nó
1.x: Transform classe 75,9 ms 10 MB 556 bytes
2.0: Transform struct 6,5 ms 0 342 bytes
Protótipo de armazenamento 3D plano 0,60 ms 0 85 bytes
Protótipo de armazenamento 2D plano 0,36 ms 0 53 bytes

Isso é 11,7× mais rápido sem nenhum lixo. As duas últimas linhas são protótipos que guardam os dados de cada nó em arrays planos. Elas mostram onde a divisão 2D/3D realmente valeria a pena, então ficam como alvo de um major futuro, e Node permanece um único tipo por enquanto. Atualizar código que escreve transforms é basicamente buscar e substituir; veja o guia de migração.

Uma lista de render que fica parada

O renderer reconstruía sua lista do que desenhar percorrendo a cena inteira todo frame. Agora ele mantém a lista, atualiza só o que mudou e a ordena por material, para que a GPU troque de estado menos vezes. Na amostra Bistro, a cena de rua da Amazon Lumberyard, ao longo de 300 frames:

Bistro, 300 frames Render mediano Alocado por frame
Antes 7,2 ms 797 KiB
Depois 2,1 ms 0,1 KiB

Medindo o que não adotar

ZLinq, uma substituição popular sem alocação para o LINQ (a sintaxe de consulta do C# para coleções), era candidata para consultas de cena. Benchmarks com 50.000 nodes disseram não:

Consulta de gameplay, 50.000 nodes LINQ padrão ZLinq Loops simples
Contar inimigos perto do jogador 1,8 ms 3,3 ms 1,7 ms
Achar um nó por nome 1,5 ms 1,9 ms 0,35 ms
Pickup mais próximo 1,9 ms 4,5 ms 1,7 ms
Os 10 inimigos mais fracos 2,0 ms, 40 KiB 4,6 ms 1,6 ms, 0 KiB

Uma vez que a caminhada pela cena parou de alocar, loops simples venceram toda consulta, sem nova dependência. A Turian ganhou em vez disso overloads como GetComponentsInChildren<T>(List<T>) que preenchem uma lista que você passa, para que seu código de gameplay consulte a cada frame sem alocar. A Guinevere passou pelo mesmo exercício no seu motor de layout, com seus próprios números abaixo.

Formulários que se escrevem sozinhos: Autoformers

Turian 1.2.0 e 2.0.0, tickets #28, #29, #194

Escreva uma classe C#, e o Inspector do Studio mostra seus campos como um formulário: um slider para um [Range], uma caixa de seleção para um bool, um seletor de cor para uma cor. A Turian 1.2 tornou essa maquinaria extensível com property drawers, que mudam como um tipo ou atributo é desenhado, junto com [Tooltip] e [InspectorOrder].

Então percebi que nada nessa maquinaria era sobre jogos. Qualquer ferramenta construída sobre o Gaya quer formulários gerados de classes C#. Então, na 2.0, o gerador de formulários migrou para a Guinevere como Autoformers, e os atributos foram para um pacote MASS4.Attributes compartilhado. Outras aplicações GUI agora podem usá-los, e o Gaya já o faz: seu painel de Settings é gerado automaticamente de classes de configuração C#, incluindo apresentação e validação customizadas. O Inspector do Studio e o Inspector de Bricks também são formulários Autoformer. A Turian fica só com os atributos que dizem respeito a jogos. Nomes de atributos mudaram um pouco no caminho; veja o guia de migração.

Um Studio com menos chrome

O menu principal do Studio agora se recolhe num único botão numa barra de aplicativo, que ele divide com o seletor de projetos e os controles de play. Essa barra também pode substituir a barra de título do sistema operacional: com decorações customizadas, o espaço vazio na barra arrasta a janela, seus próprios botões minimizam, maximizam e fecham, e a janela redimensiona por cada borda e canto.

Uma nova página de Aparência no Gaya reúne tema, tamanho de texto, zoom e decorações de janela, e o dropdown de temas e o menu View → Themes compartilham a escolha salva. O Studio vem com cinco temas: Dark, Light, Dark Contrast, Darcula e Catppuccin.

Studio's Appearance settings and View, Themes menu in the Catppuccin theme
The same Studio frame in the Light theme

Hosts e plugins do Gaya customizados que contribuem chrome de janela têm duas pequenas mudanças a fazer; veja o guia de migração.

As entranhas da engine

Turian 2.0.0, tickets #63, #197

Bricks tornam o conteúdo composável, e isso levantou uma pergunta: o que mantém um jogo estável quando seu conteúdo vem de muitos lugares? A 2.0 escreve as respostas, em código e em testes.

  • Content overlays. Pacotes de conteúdo montam em ordem de dependência. Conflitos, ciclos e requisitos ausentes são reportados antes do jogo começar, e um jogo em execução mantém o conteúdo com que começou. É o chão sobre o qual os mods vão ficar.
  • Saves em runtime. O novo RuntimeSave grava um snapshot versionado de um jogo em execução: seu tick, seu estado aleatório e seu estado de jogo. Antes de restaurar, ele checa o snapshot contra o conteúdo do jogo, então um save feito com outro conjunto de mods é capturado em vez de carregado errado. Upgrades de saves antigos rodam numa cópia, então um que falha deixa o save intacto.

Guinevere: fundamentos mais sólidos

O Studio é desenhado pela biblioteca de GUI Guinevere, então a maior parte do que você vê no Studio é trabalho da Guinevere, e a Guinevere teve um mês infernal: da 1.6 em 9 de setembro à 5.6 em 2 de outubro. Seus pacotes agora são publicados como MASS4.Guinevere.*. Os destaques:

  • Controles Excalibur. Uma biblioteca de controles dedicada sobre o núcleo, publicada como seu próprio pacote: tree views virtualizadas que desenham só as linhas visíveis, sliders, campos numéricos arrastáveis no estilo Unity, abas, toasts, tooltips, barras de menu, menus de contexto, flyouts em cascata, diálogos modais e um navegador de arquivos. Cores e tamanhos são tematizáveis valor por valor.

  • Docking. gui.DockSpace com grupos de abas, divisões aninhadas e janelas flutuantes, arrastar para ancorar, reordenar e destacar abas, mais divisores entre quaisquer dois painéis. O layout inteiro do Studio é construído sobre isso.

  • Autoformers. O gerador de formulários da Turian encontrou um novo lar na Guinevere; veja acima.

  • Um motor de layout mais rápido. Tamanhos agora são expressões composáveis: pixels, porcentagens, proporções, expansão e ajuste-ao-conteúdo podem ser somados, escalados e animados entre si, então um painel pode crescer suavemente de "200 pixels" para "70% da janela". Ao mesmo tempo, o layout parou de alocar e escala linearmente em árvores profundas:

    Cenário de 1.000 nós Antes Depois Alocado por layout
    Quebra de linha larga 0,035 ms 0,011 ms 45 KB → 0
    Ajuste ao conteúdo aninhado 0,132 ms 0,036 ms 55 KB → 0
    Medições de texto 0,035 ms 0,011 ms 52 KB → 0
  • Eventos e cursores. Eventos de mouse e teclado percorrem a interface como num navegador web, descendo até o alvo e voltando, e qualquer handler pode interrompê-los. Widgets declaram seus próprios formatos de cursor, e o ponteiro pode ser escondido ou travado, que é o que um viewport 3D precisa para mouse-look.

  • Capacidades de plataforma. Integrações (OpenGL, Vulkan, OpenTK e Raylib) declaram o que podem fazer, como formatos de cursor, travamento do ponteiro, movimento de janela ou veto ao fechamento. Gaya e Turian usam Vulkan, o mais moderno dos quatro.

A barra de aplicativo da Guinevere mudou de forma no caminho; código que a usa diretamente deve seguir o guia de migração.

Studio's Appearance settings and View, Themes menu in the Catppuccin theme
The same Studio frame in the Light theme

Olhando para frente

Há muito mais correções, ajustes e melhorias não listadas aqui. Para a lista completa, veja o changelog.

A página de releases tem a Turian 2.0.0 para Windows e Linux, o guia de migração conduz um projeto 1.x pela atualização, e a documentação tem uma nova página sobre bricks. Se algo quebrar, me avise no GitHub. Se você quiser ajudar a financiar o próximo release, me encontre no Patreon ou no Ko-fi.

#release#engine#studio#dotnet