Esta página descreve problemas comuns de desempenho e as melhores práticas para mitigá-los.
Cálculo de script
Operações caras no código Luau levam mais tempo para processar e, portanto, podem impactar a taxa de quadros. A menos que esteja sendo executado em paralelo, o código Luau é executado de forma síncrona e bloqueia a thread principal até encontrar uma função que cede a thread.
Problemas comuns
- Operações intensivas em estruturas de tabela - Operações complexas, como serialização, desserialização e clonagem profunda, incurram em um alto custo de desempenho, especialmente em grandes estruturas de tabela. Isso é particularmente verdadeiro se essas operações forem recursivas ou envolverem a iteração sobre estruturas de dados muito grandes.
- Eventos de alta frequência - Vincular operações caras a eventos baseados em quadros do RunService sem limitar a frequência significa que essas operações são repetidas a cada quadro, o que muitas vezes resulta em um aumento desnecessário no tempo de computação. Esses eventos incluem:
Mitigação
- Invocar código em eventos do RunService com moderação, limitando o uso a casos onde a invocação de alta frequência é essencial (por exemplo, atualizar a câmera). Você pode executar a maioria dos outros códigos em outros eventos ou com menos frequência em um loop.
- Divida tarefas grandes ou caras usando task.wait() para espalhar o trabalho por vários quadros.
- Identifique e otimize operações desnecessariamente caras e use multithreading para tarefas computacionalmente caras que não precisam acessar o modelo de dados.
- Certos scripts do lado do servidor podem se beneficiar da geração de código nativo, uma simples flag que compila um script em código de máquina em vez de bytecode.
- Se seus servidores ainda ficarem sem CPU após a otimização, você pode reservar mais com Serviços Estendidos para Computação.
Escopos do MicroProfiler
| Escopo | Cálculo associado |
| RunService.PreRender | Código executando no evento PreRender |
| RunService.PreSimulation | Código executando no evento Stepped |
| RunService.PostSimulation | Código executando no evento Heartbeat |
| RunService.Heartbeat | Código executando no evento Heartbeat |
Para mais informações sobre como depurar scripts usando o MicroProfiler, consulte a biblioteca debug, que inclui funções para marcar código específico e aumentar ainda mais a especificidade, como debug.profilebegin e debug.profileend. Muitos métodos da API do Roblox chamados por scripts também têm suas próprias tags associadas do MicroProfiler que podem fornecer sinais úteis.
Uso de memória do script
Vazamentos de memória podem ocorrer quando você escreve scripts que consomem memória que o coletor de lixo não consegue liberar adequadamente quando não está mais em uso. Vazamentos são especialmente persistentes no servidor, porque podem ficar online continuamente por muitos dias, enquanto uma sessão de cliente é muito mais curta.
Os seguintes valores de memória no Console do Desenvolvedor podem indicar um problema que precisa de mais investigação:
- LuaHeap - Consumo alto ou crescente sugere um vazamento de memória.
- InstanceCount - Números de instâncias consistentemente crescentes sugerem que referências a algumas instâncias em seu código não estão sendo coletadas pelo coletor de lixo.
- PlaceScriptMemory - Fornece uma análise de uso de memória script por script.
Problemas comuns
Deixar conexões conectadas - O mecanismo nunca coleta eventos conectados a uma instância e quaisquer valores referenciados dentro do callback conectado. Portanto, conexões ativas de eventos e código dentro das instâncias conectadas, funções conectadas e valores referenciados, estão fora do escopo para o coletor de lixo, mesmo após os eventos serem disparados.
Embora os eventos sejam desconectados quando a instância a que pertencem é destruída, um erro comum é assumir que isso se aplica a objetos Player. Após um usuário sair de um jogo, o mecanismo não destrói automaticamente seu objeto representativo Player e modelo de personagem, então conexões ao objeto Player e instâncias sob o modelo de personagem, como CharacterAdded, ainda consomem memória se você não desconectá-las em seus scripts. Isso pode resultar em vazamentos de memória muito significativos ao longo do tempo no servidor, à medida que centenas de usuários entram e saem do jogo.
Tabelas - Inserir objetos em tabelas, mas não removê-los quando não são mais necessários, causa consumo desnecessário de memória, especialmente para tabelas que rastreiam dados do usuário quando eles entram. Por exemplo, o seguinte exemplo de código cria uma tabela adicionando informações do usuário cada vez que um usuário entra:
Exemplolocal playerInfo = {}Players.PlayerAdded:Connect(function(player)playerInfo[player] = {} -- algumas informaçõesend)Se você não remover essas entradas quando não forem mais necessárias, a tabela continua a crescer em tamanho e consome mais memória à medida que mais usuários entram na sessão. Qualquer código que itere sobre essa tabela também se torna mais caro em termos de computação à medida que a tabela cresce em tamanho.
Mitigação
Para limpar todos os valores usados para prevenir vazamentos de memória:
Desconectar todas as conexões - Revise seu código e certifique-se de que cada conexão seja limpa por um dos seguintes caminhos:
- Desconectando manualmente usando a função Disconnect().
- Destruindo a instância a que o evento pertence com a função Destroy().
- Destruindo o objeto script ao qual a conexão remete.
Remover objetos e personagens de jogadores após a saída - Ative Workspace.PlayerCharacterDestroyBehavior para destruir automaticamente objetos de jogadores e modelos de personagens após um usuário sair. Se preferir, você pode limpá-los manualmente:
Exemplo de limpeza de jogador e personagemlocal Players = game:GetService("Players")Players.PlayerAdded:Connect(function(player)player.CharacterRemoving:Connect(function(character)task.defer(character.Destroy, character)end)end)Players.PlayerRemoving:Connect(function(player)task.defer(player.Destroy, player)end)
Cálculo de física
Simulação excessiva de física pode ser uma causa chave do aumento do tempo de computação por quadro, tanto no servidor quanto no cliente.
Problemas comuns
Frequência de passo de física excessiva - Por padrão, o comportamento de passo está em modo adaptativo, onde a física é calculada a 60 Hz, 120 Hz ou 240 Hz, dependendo da complexidade do mecanismo de física.
Um modo fixo com precisão melhorada da física também está disponível, que força todos os conjuntos de física a serem calculados a 240 Hz (quatro vezes por quadro). Isso resulta em significativamente mais computação a cada quadro.
Número excessivo de objetos simulados - Quanto mais montagens 3D forem simuladas, mais tempo as computações de física levarão a cada quadro. Muitas vezes, os jogos terão objetos sendo simulados que não precisam ser ou terão mecanismos que têm mais restrições e juntas do que precisam.
Detecção de colisão excessivamente precisa - Partes de malha têm uma propriedade CollisionFidelity para detectar colisões que oferece uma variedade de modos com diferentes níveis de impacto no desempenho. O modo de detecção de colisão precisa para partes de malha tem o custo de desempenho mais caro e leva mais tempo para o mecanismo calcular.
Mitigação
Ancore partes que não requerem simulação - Ancore todas as partes que não precisam ser movidas por física, como NPCs estáticos.
Use passos de física adaptativos - O passo adaptativo ajusta dinamicamente a taxa de cálculos de física para mecanismos de física, permitindo que as atualizações de física sejam feitas com menos frequência em alguns casos.
Reduza a complexidade do mecanismo
- Sempre que possível, minimize o número de restrições ou juntas de física em uma montagem.
- Reduza a quantidade de auto-colisão dentro de um mecanismo, como aplicando limites ou restrições de sem colisão aos membros de ragdoll para evitar que colidam entre si.
Reduza o uso de fidelidade de colisão precisa para malhas
Para objetos pequenos ou não interativos onde os usuários raramente notariam a diferença, use fidelidade de caixa.
Para objetos de tamanho pequeno a médio, use fidelidade de caixa ou casco, dependendo da forma.
Para objetos grandes e muito complexos, construa colisões personalizadas usando partes invisíveis sempre que possível.
Para objetos que não requerem colisões, desative colisões e use fidelidade de caixa ou casco, uma vez que a geometria de colisão ainda é armazenada na memória.
Você pode renderizar a geometria de colisão para fins de depuração no Studio ativando Fidelidade de colisão no widget de Opções de Visualização no canto superior direito da viewport 3D.
Alternativamente, você pode aplicar o filtro CollisionFidelity=PreciseConvexDecomposition ao Explorer, que mostra uma contagem de todas as partes de malha com a fidelidade precisa e permite que você as selecione facilmente.
Para um guia detalhado sobre como escolher uma opção de fidelidade de colisão que equilibre suas necessidades de precisão e desempenho, consulte Definir parâmetros de física e renderização.
Escopos do MicroProfiler
| Escopo | Cálculo associado |
| physicsStepped | Cálculo geral de física |
| worldStep | Passos discretos de física dados a cada quadro |
Uso de memória de física
O movimento de física e a detecção de colisão consomem memória. Partes de malha têm uma propriedade CollisionFidelity que determina a abordagem usada para avaliar os limites de colisão da malha.
Problema comum
Os modos de detecção de colisão padrão e precisa consomem significativamente mais memória do que os outros dois modos com formas de colisão de menor fidelidade.
Se você observar altos níveis de consumo de memória sob PhysicsParts, pode ser necessário explorar a redução da fidelidade de colisão dos objetos em seu jogo.
Como mitigar
Para reduzir a memória usada para fidelidade de colisão:
- Para partes que não precisam de colisões, desative suas colisões definindo BasePart.CanCollide, BasePart.CanTouch e BasePart.CanQuery como false.
- Reduza a fidelidade das colisões usando a configuração CollisionFidelity. Box tem a menor sobrecarga de memória, e Default e Precise são geralmente mais caros.
- É geralmente seguro definir a fidelidade de colisão de qualquer parte pequena ancorada como Box.
- Para malhas grandes e muito complexas, você pode querer construir sua própria malha de colisão a partir de objetos menores com fidelidade de colisão de caixa.
Humanos
Humanoid é uma classe que fornece uma ampla gama de funcionalidades para personagens jogáveis e não jogáveis (NPCs). Embora poderosa, uma Humanoid vem com um custo significativo de computação.
Problemas comuns
- Deixar todos os HumanoidStateTypes habilitados em NPCs - Há um custo de desempenho ao deixar certos HumanoidStateTypes habilitados. Desative qualquer um que não seja necessário para seus NPCs. Por exemplo, a menos que seu NPC vá escalar escadas, é seguro desativar o estado Climbing.
- Instanciar, modificar e renascer modelos com Humanoids ou MeshParts esqueletizados com frequência
- Isso pode ser intensivo para o mecanismo processar, particularmente se esses modelos usarem roupas em camadas. Isso também pode ser particularmente problemático em jogos onde avatares renascem com frequência.
- No MicroProfiler, tags longas de updateInvalidatedFastClusters (mais de 4 ms) são frequentemente um sinal de que a instância/modificação de avatar está acionando invalidações excessivas.
- Usar Humanos em casos onde não são necessários - NPCs estáticos que não se movem geralmente não têm necessidade da classe Humanoid.
- Reproduzir animações em um grande número de NPCs a partir do servidor - Animações de NPCs que rodam no servidor precisam ser simuladas no servidor e replicadas para o cliente. Isso pode ser uma sobrecarga desnecessária.
- Realizar mudanças desnecessárias de tamanho e escala - Mudanças de tamanho/escala fazem com que o FastCluster seja reconstruído. Tente reduzir isso durante o jogo se você estiver vendo problemas de desempenho relacionados ao FastCluster. Da mesma forma, outras mudanças de propriedades também podem causar a reconstrução do FastCluster, então, em geral, reduza essas mudanças o máximo possível.
Mitigação
- Reproduza animações de NPC no cliente - Em jogos com um grande número de NPCs, considere criar o Animator no cliente e executar as animações localmente. Isso reduz a carga no servidor e a necessidade de replicação desnecessária. Também torna possíveis otimizações adicionais (como reproduzir animações apenas para NPCs que estão próximos ao personagem).
- Use alternativas amigáveis ao desempenho em vez de Humanos - Modelos de NPC não precisam necessariamente conter um objeto humanoide.
- Para NPCs estáticos, use um simples AnimationController, porque eles não precisam se mover, mas apenas precisam reproduzir animações.
- Para NPCs em movimento, considere implementar seu próprio controlador de movimento e usar um AnimationController para animações, dependendo da complexidade de seus NPCs.
- Desative estados humanoides não utilizados - Use Humanoid:SetStateEnabled() para habilitar apenas os estados necessários para cada humanoide.
- Agrupe modelos de NPC com renascimento frequente - Em vez de destruir um NPC completamente, envie o NPC para um pool de NPCs inativos. Dessa forma, quando um novo NPC precisar renascer, você pode simplesmente reativar um dos NPCs do pool. Esse processo é chamado de pooling, que minimiza a quantidade de vezes que os personagens precisam ser instanciados.
- Apenas crie NPCs quando os usuários estiverem por perto - Não crie NPCs quando os usuários não estiverem ao alcance e elimine-os quando os usuários saírem de seu alcance.
- Evite fazer alterações na hierarquia do avatar após a instância - Certas modificações na hierarquia de um avatar têm implicações significativas de desempenho. Algumas otimizações estão disponíveis:
- Para animações procedurais personalizadas, não atualize as propriedades JointInstance.C0 e JointInstance.C1. Em vez disso, atualize a propriedade Motor6D.Transform.
Escopos do MicroProfiler
| Escopo | Cálculo associado |
| stepHumanoid | Controle e física do Humanoid |
| stepAnimation | Animação do Humanoid e do animador |
| updateInvalidatedFastClusters | Associado à instância ou modificação de um avatar |
Renderização
Uma parte significativa do tempo que o cliente gasta a cada quadro é na renderização da cena no quadro atual. O servidor não faz nenhuma renderização, então esta seção é exclusiva para o cliente.
Chamadas de desenho
Uma chamada de desenho é um conjunto de instruções do mecanismo para a GPU renderizar algo. Chamadas de desenho têm uma sobrecarga significativa. Geralmente, quanto menos chamadas de desenho por quadro, menos tempo computacional é gasto renderizando um quadro.
Você pode ver quantas chamadas de desenho estão ocorrendo atualmente com o item Estatísticas de Renderização ⟩ Tempos no Studio. Você pode visualizar Estatísticas de Renderização no cliente pressionando ShiftF2.
Quanto mais objetos precisam ser desenhados em sua cena em um determinado quadro, mais chamadas de desenho são feitas para a GPU. No entanto, o Motor Roblox utiliza um processo chamado instanciação para colapsar malhas idênticas com as mesmas características de textura em uma única chamada de desenho. Especificamente, várias malhas com o mesmo MeshContent são tratadas em uma única chamada de desenho quando:
- SurfaceAppearances são idênticas, se presentes; caso contrário, quando TextureContents são idênticas.
- Materiais são idênticos quando tanto SurfaceAppearance quanto MeshPart.TextureID não existem.
Outros problemas comuns
Densidade excessiva de objetos - Se um grande número de objetos estiver concentrado com alta densidade, renderizar essa área da cena requer mais chamadas de desenho. Se você perceber que sua taxa de quadros cai ao olhar para uma determinada parte do mapa, isso pode ser um bom sinal de que a densidade de objetos nessa área está muito alta.
Objetos como decalques, texturas e partículas não se agrupam bem e introduzem chamadas de desenho adicionais. Preste atenção extra a esses tipos de objetos em uma cena. Em particular, mudanças de propriedades em ParticleEmitters podem ter um impacto dramático no desempenho.
Oportunidades de instanciação perdidas - Muitas vezes, uma cena incluirá a mesma malha duplicada várias vezes, mas cada cópia da malha tem IDs de ativos de malha ou textura diferentes. Isso impede a instanciação e pode levar a chamadas de desenho desnecessárias.
Uma causa comum desse problema é quando uma cena inteira é importada de uma só vez, em vez de ativos individuais serem importados para o Roblox e, em seguida, duplicados após a importação para montar a cena.
Mesmo um script simples como este pode ajudá-lo a identificar partes de malha com o mesmo nome que usam IDs de malha diferentes:
for _,descendant in workspace:GetDescendants() doif descendant:IsA("MeshPart") thenprint(descendant.Name .. ", " .. descendant.MeshId)endendA saída (com Stack Lines habilitado) pode parecer algo assim. Linhas repetidas indicam reutilização da mesma malha, o que é bom. Linhas únicas não são necessariamente ruins, mas dependendo do seu esquema de nomenclatura, podem indicar malhas duplicadas em seu jogo:
LargeRock, rbxassetid://106420009602747 (x144) -- bomLargeRock, rbxassetid://120109824668127LargeRock, rbxassetid://134460273008628LargeRock, rbxassetid://139288987285823LargeRock, rbxassetid://71302144984955LargeRock, rbxassetid://90621205713698LargeRock, rbxassetid://113160939160788LargeRock, rbxassetid://135944592365226 -- todos possíveis duplicadosComplexidade excessiva de objetos - Embora não seja tão importante quanto o número de chamadas de desenho, o número de triângulos em uma cena influencia quanto tempo um quadro leva para renderizar. Cenas com um número muito grande de malhas muito complexas são um problema comum, assim como cenas com a propriedade MeshPart.RenderFidelity definida como Precise em muitas malhas.
Sombra excessiva - Lidar com sombras é um processo caro, e mapas que contêm um número alto e densidade de objetos de luz que projetam sombras (ou um número alto e densidade de pequenas partes influenciadas por sombras) podem ter problemas de desempenho.
Excesso de sobreposição de transparência - Colocar objetos com transparência parcial próximos uns dos outros força o mecanismo a renderizar os pixels sobrepostos várias vezes, o que pode prejudicar o desempenho. Para mais informações sobre como identificar e corrigir esse problema, consulte Excluir transparências em camadas.
Movimento desnecessário de MeshPart esqueletizados - MeshParts esqueletizados que fazem parte de um Modelo sem um Humanoid são agrupados usando FastClusters organizados espacialmente. Quando esses MeshParts se movem, eles devem ser continuamente adicionados e removidos desses clusters espaciais, forçando a reconstrução dos clusters e impactando o desempenho.
- Uma solução altamente eficaz é incorporar um Humanoid dentro do Modelo. A presença de um Humanoid substitui o comportamento padrão de agrupamento espacial, exigindo o uso de um único FastCluster unificado para todo o Modelo. Consequentemente, atualizações de posição não exigem mais reconstruções de clusters, mitigando assim o gargalo de desempenho. Essa técnica deve ser reservada exclusivamente para MeshParts com movimento esperado, pois pode introduzir sobrecarga de memória e anular os benefícios da otimização espacial. Recomendamos sempre perfilar seu jogo após fazer esse tipo de alteração. Consulte Dicas de desempenho do Humanoid para mais informações.
Demasiadas partes em um Model - Demasiadas partes em um Modelo podem causar reconstruções mais frequentes devido ao potencial de uma propriedade de parte mudar, levando a exigir uma reconstrução completa. Encontre o equilíbrio certo de partes em um Modelo quando ele estiver usando FastCluster.
Mitigação
- Instanciar malhas idênticas e reduzir a quantidade de malhas únicas - Se você garantir que todas as malhas idênticas tenham os mesmos IDs de ativos subjacentes, o mecanismo pode reconhecê-las e renderizá-las em uma única chamada de desenho. Certifique-se de fazer o upload de cada malha em um mapa apenas uma vez e, em seguida, duplicá-las no Studio para reutilização, em vez de importar grandes mapas como um todo, o que pode fazer com que malhas idênticas tenham IDs de conteúdo separados e sejam reconhecidas como ativos únicos pelo mecanismo. Pacotes são um mecanismo útil para reutilização de objetos.
- Culling - Culling descreve o processo de eliminar chamadas de desenho para objetos que não fazem parte do quadro renderizado final. Por padrão, o mecanismo ignora chamadas de desenho para objetos fora do campo de visão da câmera (culling de frustum) e partes, malhas e terrenos ocultos da vista por outros objetos (culling de oclusão). Em certos cenários, como ambientes internos, você pode ser capaz de implementar um sistema de sala ou portal e eliminar manualmente objetos para reduzir ainda mais chamadas de desenho ou carga computacional geral.
- Reduzir o nível de detalhe para modelos - Ative streaming de instâncias e defina a propriedade LevelOfDetail de seus modelos do mundo como SLIM para renderizar malhas SLIM leves otimizadas à medida que a distância da câmera aumenta.
- Reduzir o nível de detalhe para avatares - Ative o streaming de instâncias e defina Workspace.EnableSLIMAvatars para renderizar avatares de plataforma como representações SLIM leves otimizadas com suporte total a animações à medida que a distância da câmera aumenta.
- Reduzir a fidelidade de renderização - Defina MeshPart.RenderFidelity como Automatic ou Performance. Isso permite que as malhas voltem a alternativas menos complexas, o que pode reduzir o número de polígonos que precisam ser desenhados.
- Desativar a projeção de sombras em partes e objetos de luz apropriados - O mecanismo Roblox automaticamente degrada a qualidade das sombras à medida que o nível de qualidade gráfica do cliente diminui, eventualmente desativando sombras completamente em níveis de qualidade abaixo de 4. No entanto, você pode desativar seletivamente propriedades de projeção de sombras em objetos de luz e partes para melhorar o desempenho enquanto as sombras estão habilitadas e aumentar a probabilidade de que as sombras permaneçam habilitadas. Alguns exemplos de otimizações que você pode fazer, seja no tempo de edição ou dinamicamente em tempo de execução:
Use a propriedade BasePart.CastShadow para desativar a projeção de sombras em partes pequenas onde as sombras provavelmente não serão visíveis. Essa estratégia é particularmente eficaz quando aplicada a partes que estão longe da câmera do usuário.
Desative sombras em objetos em movimento sempre que possível.
Desative Light.Shadows em instâncias de luz onde o objeto não precisa projetar sombras.
Limite o alcance e o ângulo das instâncias de luz.
Use menos instâncias de luz.
Considere desativar luzes que estão fora de um alcance específico ou em uma base de sala por sala para ambientes internos.
Escopos do MicroProfiler
| Escopo | Cálculo associado |
| Prepare and Perform | Renderização geral |
| Perform/Scene/computeLightingPerform | Atualizações de grade de luz e sombra |
| LightGridCPU | Atualizações da grade de luz voxel |
| ShadowMapSystem | Mapeamento de sombras |
| Perform/Scene/UpdateView | Preparação para renderização e atualizações de partículas |
| Perform/Scene/RenderView | Renderização e pós-processamento |
Rede e replicação
Rede e replicação descrevem o processo pelo qual os dados são enviados entre o servidor e os clientes conectados. As informações são enviadas entre o cliente e o servidor a cada quadro, mas quantidades maiores de informações exigem mais tempo de computação.
Problemas comuns
Tráfego remoto excessivo - Enviar uma grande quantidade de dados através de objetos RemoteEvent ou RemoteFunction ou invocá-los com muita frequência pode levar a uma grande quantidade de tempo de CPU sendo gasto processando pacotes de entrada a cada quadro. Erros comuns incluem:
- Replicar dados a cada quadro que não precisam ser replicados.
- Replicar dados na entrada do usuário sem qualquer mecanismo para limitar isso.
- Despachar mais dados do que o necessário. Por exemplo, enviar todo o inventário do jogador quando ele compra um item, em vez de apenas os detalhes do item comprado.
Criação ou remoção de árvores de instâncias complexas - Quando uma mudança é feita no modelo de dados no servidor, ela é replicada para os clientes conectados. Isso significa que criar e destruir grandes hierarquias de instâncias, como mapas em tempo de execução, pode ser muito intensivo em rede.
Um culpado comum aqui é os dados de animação complexos salvos por plugins do Animation Editor em rigs. Se esses não forem removidos antes que o jogo seja publicado e o modelo animado for clonado regularmente, uma grande quantidade de dados será replicada desnecessariamente.
TweenService do lado do servidor - Se TweenService for usado para tween um objeto do lado do servidor, a propriedade tweened é replicada para cada cliente a cada quadro. Isso não apenas resulta em um tween instável à medida que a latência dos clientes flutua, mas causa muito tráfego de rede desnecessário.
Mitigação
Você pode empregar as seguintes táticas para reduzir a replicação desnecessária:
- Evite enviar grandes quantidades de dados de uma só vez através de eventos remotos. Em vez disso, envie apenas os dados necessários com uma frequência menor. Por exemplo, para o estado de um personagem, replique-o quando mudar, em vez de a cada quadro.
- Divida árvores de instâncias complexas como mapas e carregue-as em partes para distribuir o trabalho de replicação ao longo de vários quadros.
- Limpe os metadados de animação, especialmente o diretório de animação de rigs, após a importação.
- Limite a replicação desnecessária de instâncias, especialmente em casos onde o servidor não precisa ter conhecimento das instâncias sendo criadas. Isso inclui:
- Efeitos visuais, como uma explosão ou um feitiço mágico. O servidor só precisa saber a localização para determinar o resultado, enquanto os clientes podem criar visuais localmente.
- Modelos de itens em primeira pessoa.
- Tween objetos no cliente em vez do servidor.
Escopos do MicroProfiler
| Escopo | Cálculo associado |
| ProcessPackets | Processamento de pacotes de rede de entrada, como invocações de eventos e mudanças de propriedades |
| Allocate Bandwidth and Run Senders | Eventos de saída relevantes nos servidores |
Uso de memória de ativos
O mecanismo de maior impacto disponível para criadores para melhorar o uso de memória do cliente é habilitar streaming de instâncias.
Streaming de instâncias
O streaming de instâncias carrega seletivamente partes do modelo de dados que não são necessárias, o que pode levar a tempos de carregamento consideravelmente reduzidos e aumentar a capacidade do cliente de evitar falhas quando está sob pressão de memória.
Se você estiver enfrentando problemas de memória e tiver o streaming de instâncias desativado, considere atualizar seu jogo para suportá-lo, especialmente se seu mundo 3D for grande. O streaming de instâncias é baseado na distância no espaço 3D, então mundos maiores naturalmente se beneficiam mais disso.
Se o streaming de instâncias estiver habilitado, você pode aumentar a agressividade dele. Por exemplo, considere:
- Reduzir o uso de Enum.ModelStreamingMode.Persistent sempre que possível. Você pode precisar atualizar seus scripts se estiver usando isso como uma medida de compatibilidade.
- Reduzir o Workspace.StreamingMinRadius e o Workspace.StreamingTargetRadius.
Para mais informações sobre opções de streaming e seus benefícios, consulte propriedades de streaming.
Outros problemas comuns
Duplicação de ativos - Um erro comum é fazer o upload do mesmo ativo várias vezes, resultando em IDs de ativos diferentes. Isso pode levar ao mesmo conteúdo sendo carregado na memória várias vezes.
Volume excessivo de ativos - Mesmo quando os ativos não são idênticos, há casos em que oportunidades de reutilizar o mesmo ativo e economizar memória são perdidas.
Arquivos de áudio - Arquivos de áudio podem ser um contribuinte surpreendente para o uso de memória, particularmente se você carregar todos eles no cliente de uma só vez, em vez de carregar apenas o que você precisa para uma parte do jogo. Para estratégias, consulte Tempos de carregamento.
Texturas de alta resolução - O consumo de memória gráfica para uma textura não está relacionado ao tamanho da textura no disco; o número de pixels na textura determina o uso de memória. Por exemplo, uma textura de 1024x1024 pixels consome quatro vezes a memória gráfica de uma textura de 512x512 pixels.
Imagens carregadas no Roblox são transcodificadas para um formato fixo, então não há benefício de memória em fazer o upload de imagens em um modelo de cor associado a menos bytes por pixel. Da mesma forma, comprimir imagens antes do upload ou remover o canal alfa de imagens que não precisam dele pode diminuir o tamanho da imagem no disco, mas não melhora o uso de memória.
À medida que um jogo carrega, o mecanismo automaticamente começa com texturas de qualidade mais baixa e, em seguida, aumenta a qualidade com base na memória disponível do dispositivo, distância da câmera, quantidade de espaço na tela que a textura ocupa e outros fatores. Mesmo assim, dimensionar estrategicamente suas texturas pode melhorar o uso de memória em seu jogo.
Mitigação
Faça o upload de ativos apenas uma vez - Reutilize o mesmo ID de ativo em objetos e certifique-se de que os mesmos ativos, especialmente malhas e imagens, não sejam carregados separadamente várias vezes.
Encontre e corrija ativos duplicados - Procure partes de malha e texturas idênticas que foram carregadas várias vezes com IDs diferentes.
- Embora não haja uma API para detectar automaticamente a similaridade de ativos, você pode coletar todos os IDs de ativos de imagem em seu lugar (seja manualmente ou com um script), baixá-los e compará-los usando ferramentas de comparação externas.
- Para partes de malha, a melhor estratégia é pegar IDs de malha únicos e organizá-los por tamanho para identificar manualmente duplicatas.
- Em vez de usar texturas separadas para diferentes cores, faça o upload de uma única textura e use a propriedade SurfaceAppearance.Color para aplicar várias tonalidades a ela.
Importe ativos no mapa separadamente - Em vez de importar um mapa inteiro de uma só vez, importe e reconstrua ativos no mapa individualmente e reconstrua-os. O Importador não faz nenhuma desduplicação de malhas, então se você importar um grande mapa com muitos azulejos de piso separados, cada um desses azulejos será importado como um ativo separado (mesmo que sejam duplicatas). Isso pode levar a problemas de desempenho e memória no futuro, já que cada malha é tratada como individual e ocupa memória e chamadas de desenho.
Limite os pixels das imagens a não mais do que a quantidade necessária. A menos que uma imagem esteja ocupando uma grande quantidade de espaço físico na tela, geralmente precisa de no máximo 512x512 pixels. A maioria das imagens menores deve ser menor que 256x256 pixels.
Use folhas de recorte para garantir a máxima reutilização de texturas em mapas 3D. Para etapas e exemplos sobre como criar folhas de recorte, consulte Criar folhas de recorte.
Você também pode considerar usar folhas de sprites para carregar muitas imagens menores de UI como uma única imagem. Você pode então usar ImageLabel.ImageRectOffset e ImageLabel.ImageRectSize para exibir partes da folha.
Tempos de carregamento
Antes que um jogador possa começar a jogar, o cliente deve receber tudo em ReplicatedFirst e ReplicatedStorage, o conteúdo inicial de Workspace, e quaisquer ativos que seus scripts pré-carregam. Quanto mais conteúdo o cliente tiver que baixar e processar durante esse tempo, mais tempo os jogadores esperam. Para uma análise de cada etapa no processo de entrada, consulte Identificar problemas de desempenho.
Problemas comuns
Streaming de instâncias desativado - Sem streaming de instâncias, todo o Workspace deve replicar para o cliente antes que o jogador possa entrar, então jogos com muitas partes, modelos e ativos têm tempos de carregamento proporcionalmente mais longos.
Conteúdo excessivo em ReplicatedStorage - Tudo em ReplicatedStorage se replica para cada cliente antes que o jogador possa ver ou interagir com qualquer coisa, independentemente de o cliente precisar disso imediatamente. Modelos gratuitos e ativos herdados podem adicionar silenciosamente um volume significativo.
Conteúdo pesado em ReplicatedFirst - ReplicatedFirst se replica antes de qualquer outra coisa e existe principalmente para manter uma tela de carregamento personalizada. Cada ativo ou script adicional nele atrasa o início da fase principal de replicação.
Uso excessivo de PreloadAsync() - Muitos jogos implementam telas de carregamento personalizadas e usam ContentProvider:PreloadAsync() para solicitar ativos para que imagens, sons e malhas sejam baixados em segundo plano. Essa abordagem garante que partes importantes do seu jogo estejam totalmente carregadas sem pop-in, mas um erro comum é pré-carregar mais ativos do que realmente são necessários.
Um exemplo de uma prática ruim é carregar todo o Workspace. Embora isso possa evitar o pop-in de texturas, aumenta significativamente os tempos de carregamento.
Outra prática semelhante é utilizar ContentProvider.RequestQueueSize para garantir que todos os ativos solicitados tenham terminado de carregar. No entanto, isso apresenta o mesmo problema de tempos de carregamento significativamente aumentados, além de ser um método não confiável devido à sua natureza flutuante.
Scripts de inicialização caros - Execução pesada de scripts em LocalScripts que rodam durante o processo de entrada, como carregar estruturas de dados grandes de forma síncrona, atrasa a transição da replicação para um estado jogável.
Mitigação
Ative o streaming de instâncias - O streaming envia apenas a parte do seu mundo próxima ao ponto de spawn do jogador na entrada inicial, e depois transmite o resto progressivamente à medida que o jogador se move pelo seu jogo. Para jogos que ainda não o utilizam, o streaming é uma das maneiras mais eficazes de reduzir os tempos de carregamento. O streaming é habilitado por padrão para novos lugares. Para etapas de configuração, referência de propriedades e orientações de migração, consulte Streaming de instâncias.
Mantenha o ReplicatedStorage enxuto - Armazene apenas o que os clientes precisam no momento em que entram, como ModuleScripts compartilhados que LocalScripts requerem. Mova modelos grandes, áudio e outros ativos que não são necessários imediatamente para ServerStorage, e mova-os para o cliente apenas quando um jogador precisar deles. Audite regularmente o ReplicatedStorage para conteúdo que você não usa mais.
Mantenha o ReplicatedFirst enxuto - Limite ReplicatedFirst ao que sua tela de carregamento precisa, como elementos básicos de UI em vez de texturas grandes ou modelos 3D, e evite computação pesada em scripts que rodam a partir dele. Para um guia sobre como construir uma tela de carregamento personalizada, incluindo quando chamar ReplicatedFirst:RemoveDefaultLoadingScreen(), consulte Telasc de carregamento.
Pré-carregue apenas ativos necessários - Use ContentProvider:PreloadAsync() apenas em situações necessárias, que incluem:
- Imagens na tela de carregamento.
- Imagens importantes no menu do seu jogo, como fundos de botões e ícones.
- Ativos importantes na área de início ou spawn.
Se você precisar carregar um grande número de ativos, recomendamos que forneça um botão Pular Carregamento.
Deferir trabalho de inicialização não essencial - Carregue conteúdo que um jogador ainda não alcançou, como painéis de UI que eles ainda não abriram, apenas quando for necessário pela primeira vez. Para trabalhos de inicialização que devem acontecer cedo, espalhe-os por vários quadros em vez de executá-los todos de uma vez. Para técnicas, consulte Cálculo de script.