A Game Kitchen sobre 3 desafios técnicos na criação de The Stone of Madness

No início deste ano, The Game Kitchen lançou The Stone of Madness, um RPG tático onde os jogadores ajudam cinco prisioneiros a escapar de uma prisão inquisitorial. Neste post de convidado, três desenvolvedores do estúdio compartilham como enfrentaram desafios de renderização, interface do usuário e testes durante o desenvolvimento.
Nós somos The Game Kitchen, e recentemente lançamos The Stone of Madness no PC e consoles. Queremos compartilhar alguns dos desafios mais urgentes que enfrentamos durante o desenvolvimento do nosso último projeto, abordando-os de uma perspectiva técnica com exemplos práticos. Neste artigo colaborativo, nossa equipe de programação analisa as principais soluções que implementamos no Unity para otimizar tanto o desempenho quanto a eficiência no desenvolvimento.
Primeiro, Adrián de la Torre (programador de gráficos) explicará como projetamos e renderizamos o pipeline de arte do jogo para alcançar seu estilo visual distinto.
Em seguida, Alberto Martín (programador de UI) detalhará como aproveitamos o Noesis para agilizar o desenvolvimento de UI, melhorando o fluxo de trabalho com melhorias de UX baseadas no feedback dos usuários.
Finalmente, Raúl Martón (programador de gameplay) mostrará como externalizamos e automatizamos testes para ações complexas dentro do jogo em um servidor, garantindo que múltiplos casos extremos fossem tratados sem interromper a integração.
Fazendo a loucura parecer boa: Uma olhada no pipeline de renderização personalizado
Adrián de la Torre, Programador Gráfico, The Game Kitchen
A Pedra da Loucura combina visuais 2D com mecânicas de jogo 3D, o que apresenta um desafio técnico único. Enquanto os jogadores veem um mundo 2D, os sistemas subjacentes do jogo operam em um espaço tridimensional, criando uma dualidade distinta em seu design.
Para enfrentar esse desafio, nossa equipe de desenvolvimento criou um pipeline de renderização personalizado que efetivamente conecta as informações de jogabilidade 3D à representação visual 2D. Esta solução implementa múltiplas passagens de renderização e técnicas especializadas para manter a consistência visual enquanto preserva a profundidade de jogabilidade pretendida, permitindo a tradução perfeita de elementos 3D para o distinto estilo artístico 2D do jogo.
Em A Pedra da Loucura, há dois cenários principais que contribuem para a renderização de um quadro.
O primeiro cenário, que chamamos de Cenário Proxy, é composto por primitivas geométricas que calculam a iluminação do quadro final.

O segundo cenário é o Cenário de Canvas, que consiste em sprites que correspondem à forma e à posição da geometria do Proxy. A Tela é organizada em camadas para simular o espaço 3D e alcançar a ordenação Z adequada com elementos de jogo em movimento.
A seção a seguir detalha cada etapa em nosso pipeline gráfico para renderização de quadros.
1. Cone de visão
Sempre que um cone de visão ou habilidade de jogo é ativado, ele inicia o primeiro passo no pipeline. Posicionamos uma câmera no ponto de vista (PoV) do NPC para renderizar a profundidade dos proxies dentro de seu campo de visão (FoV).

Então, em outra textura de renderização, a câmera gera um gradiente da distância da origem do jogador no canal B, que é usado para efeitos de área de habilidade.

Usando a textura de renderização do PoV do NPC, a câmera do cone de visão renderiza um cone sobre a textura anterior nos canais R e G com informações sobre obstáculos e distância.

A passagem final renderiza ondas sonoras no canal Alpha.

Esta é a textura final criada nesta etapa, que será usada na etapa da Câmera do Canvas para renderizar os sprites da cena.

2. ID da Câmera de Renderização de Canvas
Cada proxy em nosso projeto tem um ID de Render associado (um valor float). O proxy e seu sprite relacionado compartilham o mesmo ID de Renderização. Neste passo, renderizamos o valor float do ID de Render em uma textura de renderização.

No passo seguinte, usamos essa textura para combinar as informações de iluminação calculadas no cenário proxy com os sprites no cenário Canvas.
3. Ilum
A iluminação em nosso jogo consiste em:
- Iluminação assada Luzes naturais que permanecem ativas permanentemente, como iluminação externa
- Iluminação mista: Luzes estáticas na cena que podem ser ligadas e desligadas, como velas.
- Iluminação em tempo real: Luz que se move por toda a cena e pode ser ligada e desligada (implementamos isso em apenas uma instância, a lamparina de óleo do Alfredo)
Usando a textura RenderID, criamos uma textura de renderização contendo as informações de iluminação da cena proxy.

4. Canvas Camera
Após criar todas as texturas de renderização, uma câmera começa a renderizar os sprites com informações sobre iluminação, áreas de efeito de habilidades, cones de visão e ondas de ruído.
5. Pós-processamento
A correção de cor, vinhetas e outros efeitos são aplicados em uma etapa de pós-processamento.
6. UI
Finalmente, a interface do usuário está sobreposta.
Loucura no HUD: Acelerando processos de interface do usuário
Alberto Martín, Programador de UI, The Game Kitchen
A versão final de lançamento de The Stone of Madness apresenta mais de 50 interfaces de usuário. A razão por trás desse número é que este jogo tem muitos dados para mostrar ao usuário. Nosso trabalho de UI foi muito demorado, especialmente considerando o quão pequena a equipe era no início, e por isso estávamos continuamente otimizando nossos processos para garantir que estávamos alcançando bons resultados no menor tempo possível.
Nosso trabalho de UI abrangeu todo o projeto, então era importante que nossos designers de UI/UX entendessem claramente todas as funcionalidades que precisávamos implementar. Para garantir que nosso jogo proporcionasse uma boa experiência ao usuário e fosse divertido de jogar, tivemos o cuidado de manter uma linha de comunicação aberta entre as equipes de programação e design.
Para criar as melhores versões de todos os nossos componentes de interface, precisávamos remover os silos entre nossas equipes técnicas e nossas equipes criativas/pesquisa, para que todos estivessem ativamente envolvidos no desenvolvimento do jogo. Aqui está como abordamos este fluxo de trabalho em duas partes.
O papel da pesquisa e da criatividade no design de UI
Nossos designers de UI/UX são responsáveis por definir como os elementos de UI irão parecer no jogo final e garantir que ofereçamos uma experiência do usuário satisfatória. Com isso em mente, eles começaram criando cada elemento com carga técnica mínima e validando-o com usuários potenciais. Esse processo parecia assim:
- Requisitos: Compreender as necessidades do jogador e criar uma lista das necessidades do jogo e dos objetivos do usuário
- Investigação: Olhando para outros jogos para ver como eles lidaram com problemas semelhantes
- Wireframes Trabalhando nos esquemas e na estrutura (sem arte final neste momento)
- Maquete: Neste ponto, montamos a interface quase totalmente projetada com elementos previamente criados (botões, rolagens, quadros, etc.), permitindo-nos iterar sem muito esforço.
- Protótipo: Construímos um protótipo no Figma usando nosso mock-up, simulando interações com gamepads e teclado/mouse para mostrar como funcionará em um ambiente real.
- Teste do usuário: Usando nosso protótipo criado anteriormente, iniciamos um teste com usuários, validando as necessidades e objetivos que identificamos na Etapa 1.
- Fase de iteração: Se o teste do usuário atender às expectativas, ele é passado para os processos da parte técnica, faz mais iterações ou realiza mais testes se for conveniente.
Implementação técnica de UI
Como mencionado anteriormente, o número de elementos de interface do usuário em The Stone of Madness é enorme. Desenvolver um motor de interface é caro, então precisávamos usar um framework que fosse fácil de aprender, com ferramentas e fluxos de trabalho decentes. Após avaliar uma variedade de middleware, escolhemos o Noesis GUI, que segue o padrão Model-View-ViewModel (MVVM).
Escolhemos o Noesis porque é baseado no WPF (Windows Presentation Framework) e segue o modelo MVVM de uma maneira que podemos reutilizar a maior parte da documentação, bibliografia, entradas de fórum, e assim por diante para solucionar a maioria dos problemas. Este framework existe há um tempo – já se passaram 18 anos desde seu primeiro lançamento – e é familiar para um grande número de desenvolvedores de UI, o que dá ao nosso estúdio a opção de contratar de um pool de talentos comparativamente maior para implementar interfaces e ferramentas para nossos projetos. Outra coisa importante sobre a Noesis é que podemos usar as mesmas ferramentas do WPF.
Com XAML, nossa equipe criativa de UI esteve envolvida no trabalho de layout e no polimento de todos os elementos com mínima participação técnica. Graças à abordagem MVVM, nossos programadores de UI técnicos puderam se concentrar na funcionalidade e fornecer suporte às equipes criativas em certas áreas quando necessário.
Testando (ou, como não enlouquecer criando um jogo com um design sistêmico)
Raul Martón, Gameplay Programmer, Teku Studios
A jogabilidade em The Stone of Madness é baseada em três pilares fundamentais: Habilidades do jogador, IA de NPC e interações de cena. Cada um desses três sistemas está fundamentalmente entrelaçado, o que aumenta exponencialmente o número de situações que o jogador precisa controlar – e o número de cenários que precisamos testar.
Assim que começamos o projeto, percebemos que um sistema de QA tradicional seria insuficiente. Havia simplesmente muitos cenários que dependiam de várias peças interagindo entre si de uma maneira particular, criando uma situação incontrolável. Além disso, essas situações podem muito bem ocorrer em um intervalo de tempo que é simplesmente pequeno demais para uma equipe de QA testar confortavelmente.
Para resolver esses problemas, criamos um conjunto de testes automáticos. A ideia era que todos os possíveis cenários/situações que poderiam ocorrer para nossa equipe de desenvolvimento em relação a um determinado sistema pudessem ser contabilizados e testados automaticamente de forma muito mais eficiente em um ambiente de jogo simulado.
Para fornecer um exemplo, uma das personagens principais de The Stone of Madness, Amelia Exposito, tem a habilidade de batedora de carteira. Ao implementar essa habilidade, iniciamos uma série de testes para garantir:
- O funcionamento básico da habilidade estava correto: Ao roubar de um NPC, o mini-jogo de furto seria aberto e o jogo pausaria até que terminasse.
- Situações menos comuns também são abordadas: Se você tentar roubar de um NPC enquanto outro NPC (como um guarda) está te observando, ou se o NPC estiver correndo, a ação é impossível.

Criando um teste de integração
Cada teste de integração que criamos exigiu configuração com base nos seguintes requisitos:
1. Uma cena especialmente preparada para criar esta situação particular
Para testar a habilidade de furto, criamos uma cena com dois guardas e um jogador. Posicionamos cada personagem de forma que eles estejam voltados na direção necessária para que a situação seja testada com precisão (lembre-se, o jogador não pode usar o furto se estiver dentro do campo de visão de um guarda).
Além disso, a cena deve incluir apenas os componentes mínimos necessários para testar o cenário, pois elementos extraneous podem adicionar ruído à medição. É por isso que nossa cena de exemplo não tem HUD, sistema de entrada manual, efeitos sonoros, e assim por diante.
- Esta etapa requer que a estrutura do jogo esteja bem compartimentada, o que pode exigir algum esforço, mas, uma vez alcançado, vale muito a pena! 😉
2. Um código de teste capaz de forçar a situação a ser testada
Muitas das situações que precisávamos testar podem ser difíceis e demoradas para criar manualmente e precisam de um envio de código para iniciar.
Por exemplo, se quisermos criar um cenário de teste para garantir que nossos NPCs nunca pisem em armadilhas para camundongos, a menos que o NPC esteja se movendo, a cadeia de instruções seria:
- Lançar a cena
- Espere um segundo
- Coloque uma armadilha para ratos embaixo do NPC
- Espere mais um segundo
- Comande o NPC para começar a andar em qualquer direção
Esta parte do projeto é muito sensível a quaisquer mudanças durante o desenvolvimento (dependente de fatores como mudanças nas especificações do jogo e vários cenários inesperados), portanto, é fundamental que tanto o código de teste quanto o feedback resultante sejam o mais claros possível.
Não há nada pior do que um teste que falha sem fornecer informações claras sobre o que está realmente dando errado.
3. Uma maneira confiável de saber se o cenário está funcionando como pretendido, ou se o teste detectou um erro na lógica.
Os testes automatizados ainda requerem supervisão. O aumento do número de testes com maior especificidade sobre o que está sendo testado pode se tornar difícil de monitorar, ou os cenários acabam não sendo testados por tempo suficiente para serem estatisticamente significativos. Para contornar esses problemas, criamos ferramentas personalizadas.
Por exemplo, alguns de nossos testes envolveram interações combinadas entre vários NPCs em uma cena. Para monitorar esses casos adequadamente, criamos um sistema para registrar os diferentes estados de IA pelos quais os NPCs passam durante o teste.

Também precisávamos de uma boa API que nos desse visibilidade sobre o estado atual do jogo (um NPC foi nocauteado?) Um NPC entrou em um estado roteado? Quantas vezes? Qual personagem jogador foi capturado? E assim por diante).
4. Um sistema para poder lançar todos esses testes rapidamente:
Ao contrário dos testes unitários, os testes automatizados devem ser realizados com o jogo em execução em tempo real. Isso pode tornar a execução desses testes muito lenta.
Nessas circunstâncias, conseguimos aproveitar o fato de que nosso jogo não utiliza o sistema de atualizações padrão da Unity. Em vez disso, todos os nossos componentes usam uma função Tick(), que simula atualizações do Unity, mas lançadas de uma maneira controlada pelo nosso mecanismo de jogo.
Isso nos ajudou a alcançar alguns objetivos diferentes com nossos testes:
- Primeiro, poderíamos acelerar a execução deles com uma função de forçamento que executa vários quadros de código para cada quadro do jogo.
- Em segundo lugar, como esses testes são realizados em tempo real, eles são muito suscetíveis a variações causadas pelas taxas de quadros no computador que executa o cenário de teste. Ao convertê-los para uma taxa de quadros controlada, evitamos essa variação: Se um teste passar em uma máquina, ele passará em todas as máquinas, e vice-versa.
E este seria o resultado.
Como testes seguros nos ajudam a evitar builds quebrados
Com a criação deste conjunto de testes, também precisávamos implementar uma salvaguarda que interrompesse automaticamente a mesclagem de um branch se ele contivesse bugs. Para garantir isso, criamos um script de mesclagem automática que é acionado toda vez que uma alteração é confirmada no branch principal do projeto.
Este script garante que todos esses testes sejam executados e monitora seus resultados. Se algum teste falhar, ele retorna com uma detecção de erro e interrompe a mesclagem.
Com este sistema, podemos evitar situações em que uma mudança em um sistema aparentemente isolado quebra outras mecânicas com as quais interage.
Obrigado ao The Game Kitchen por compartilhar este olhar nos bastidores do desenvolvimento de The Stone of Madness. Explore mais jogos Made With Unity em nossa página de Curador no Steam e obtenha mais insights de desenvolvedores na página de Recursos da Unity.