Blog dos Desenvolvedores | Carregando Inovação

Neste artigo da nossa série Blog dos Desenvolvedores, Mod Dunk explica quais são as mudanças necessárias para reproduzir movimento sem interrupções no novo cliente do RuneScape.


O RuneScape é um jogo enorme, cheio de personagens para conhecer, missões para completar e regiões para explorar. 15 anos de conteúdo pesam bastante no nosso cliente Java atual, e o NXT tem como objetivo trazer um mecanismo novo que lide bem com o tamanho do RuneScape – agora e no futuro.

O carregamento é um problema constante no desenvolvimento de jogos – principalmente se tratando de jogos de mundo aberto, como o RuneScape. Uma vez que o jogador pode ir para qualquer lugar, e a área é muito grande para ser armazenada na memória, o jogo tem que carregar constantemente os ativos sem interromper a jogabilidade.

Aqui você pode estar pensando, "Espere aí, mas o RuneScape não pausa o jogo para carregar?" – e a resposta é: sim, pausa. Na verdade, muitos de vocês devem conhecer a caixinha "Carregando - Aguarde...", que pausa o jogo quando o jogador está andando.

Essa caixinha deixará de ser necessária no NXT – e vamos explicar como fizemos isso!

Transmissão

O mundo do RuneScape é dividido em quadrados no mapa. Conforme você se move, os quadrados na frente do seu personagem são carregados, enquanto aqueles atrás do personagem são removidos para economizar memória. O cliente Java interrompe todo o jogo enquanto isso acontece, por isso temos a pausa.

Um dos principais objetivos do NXT era fazer com que o carregamento fosse realizado em segundo plano, sem nunca interromper a jogabilidade. Essa técnica é conhecida como "transmissão" ou "carregamento assíncrono", pois faz com que o processo de carregamento seja realizado paralelamente ao processo principal do jogo.

Para fazer isso, incluímos cada pedido de carregamento num objeto de "trabalho", e atribuímos cada trabalho para um de uma série de threads. Esses threads, por sua vez, processam os trabalhos individualmente e retornam os resultados quando terminam.

Infelizmente, isso exigia que todo o processamento de modelos 3D, animações, texturas, sistemas de partícula, objetos do script - tudo que compõe o jogo - fosse reescrito. Como você pode imaginar, tratava-se de uma tarefa hercúlea, mas o esforço valia a pena: o resultado era uma fluidez de movimentos que seria impossível no cliente Java!

Empacotamento

Para suportar a maior distância de renderização do NXT, é necessário carregar rapidamente diversos elementos do mundo do jogo.

A distância máxima de renderização do NXT é mais de 4 vezes maior do que a distância de renderização do cliente Java, o que significa que o NXT requer que um número aproximadamente 16 vezes maior de modelos seja carregado de uma vez. Carregar paralelamente é uma coisa, mas esse número significa também que temos que carregar cada modelo individual com mais rapidez.

Com cada modelo carregado pelo Java, são feitos vários processamentos para calcular a informação necessária para a sua renderização. Assim como acontece com qualquer jogo, os modelos 3D são compostos por triângulos que formam a sua superfície visível. Cada canto (ou vértice) de um triângulo contém informações como vetor normal de superfície, cor e coordenadas de textura.

No cliente Java, o código de carregamento executa algoritmos para calcular esses valores, o que torna o processo bem lento. Para o NXT, esses valores agora são pré-calculados numa fase de empacotamento externo; depois, basta lê-los diretamente na fase de execução. Isso agiliza significativamente os tempos de carregamento (embora aumente o tamanho do ativo baixado), e é a nossa abordagem preferida sempre que possível.

De forma semelhante, temos uma fase de empacotamento externo para a compressão de texturas, e isso resulta num número menor de dados a serem processados na fase de execução.

Distância de renderização no Java Distância de renderização no NXT

Acima: distância de renderização do Java. Abaixo: distância de renderização do NXT.

Pré-carregamento

No cliente Java, até mesmo para chegar na tela de login existe uma tela de carregamento. No NXT, o tempo de carregamento foi diminuído de forma significativa. Agora, fazemos uma parte muito maior do trabalho necessário enquanto o jogador está no lobby. Nesta fase, o computador não está fazendo nada além de renderizar o menu, por isso temos tempo para pré-carregar o máximo de conteúdo possível.

O trabalho principal que executamos aqui é o pré-carregamento do mundo. Se você estava em Lumbridge quando saiu do jogo pela última vez, por exemplo, o cliente fará o pré-carregamento desta área em segundo plano, pois é aí que você entrará no jogo novamente.

Outra coisa que fazemos nessa fase é o pré-carregamento dos shaders. Shaders são pequenos programas executados na placa de vídeo que executam cálculos de renderização. No NXT, mantemos uma lista de shaders usados com frequência e garantimos que eles sejam disponibilizados antes do jogo começar.

Nível de detalhe do quadrado do mapa

No NXT, otimizamos o carregamento de quadrados de mapas distantes construindo e renderizando apenas os objetos mais importantes. Por exemplo, as paredes de um edifício devem sempre ser visíveis à distância, mas os detalhes internos do edifício não precisam ser carregados até que o jogador chegue perto o suficiente para ver o que está dentro. Este sistema de "nível de detalhe" é muito eficaz na aceleração do carregamento e da renderização de distâncias mais longas.

Torre de Armadyl

Objetos grandes como a torre de Armadyl continuam sendo carregados de longe, mas pequenos detalhes como a grama ou as flores em torno da torre não são

Permutações do shader

Existem vários objetos no mundo do RuneScape, feitos de diversos materiais diferentes. Cada material usa uma combinação particular de efeitos visuais, como mapeamento do ambiente, texturas, iluminação especular, transparência alfa, luz emissiva e muito mais.

No NXT, introduzimos ainda mais recursos, incluindo animação de hardware, melhorias na névoa e mapeamento de sombras. Permitir que cada um desses recursos seja ativado ou desativado independentemente faz com que sejam geradas centenas, ou mesmo milhares, de permutações (ou variantes) de shader. Felizmente, conseguimos diminuir este número para 100, e codificamos uma redução ainda maior durante o desenvolvimento.

Isso foi atingido de algumas formas diferentes:

  • Identificamos os recursos de shader que estão produzindo o maior número de permutações, usando os resultados da depuração interna dos shaders.
  • Recursos destacados que exigem menos são disponibilizados a todo o momento.
  • Recursos destacados que exigem mais podem ser alterados para serem ativados ou desativados automaticamente dentro do próprio programa do shader.

Um grande problema dos mecanismos de jogo genéricos como o Unreal ou o Unity é que um usuário pode criar facilmente materiais diferentes que exigem milhares de permutações do shader, aumentando o tempo necessário para carregá-los. Uma vez que o NXT só precisa lidar com um conjunto específico (embora grande) de dados, podemos otimizar nosso código de renderização e de shader para executar os comandos de forma mais eficiente ao gerar menos permutações.

Todo esse trabalho foi feito em função de um só objetivo – garantir que o cliente do NXT possa mostrar muito mais do mundo do RuneScape e ainda assim ter melhores tempos de carregamento que o cliente Java.

Mod Dunk
Desenvolvedor de Mecanismo Sênior

Voltar ao início