Esta página descreve problemas comuns de desempenho e melhores práticas para mitigá-los.
Cálculo de scripts
Operações custosas no código Luau demoram mais para serem processadas e podem impactar a taxa de quadros. A menos que esteja sendo executado em paralelo, o código Luau roda 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, geram 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 - Atar operações custosas 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 nos eventos do RunService com moderação, limitando o uso a casos em que 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.
- Quebre tarefas grandes ou custosas usando task.wait() para espalhar o trabalho através de múltiplos quadros.
- Identifique e otimize operações desnecessariamente custosas e use multithreading para tarefas computacionais caras que não precisam acessar o modelo de dados.
- Certos scripts do lado do servidor podem se beneficiar de geração de código nativo, uma simples flag que compila um script para código de máquina em vez de bytecode.
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 depuração de scripts usando o MicroProfiler, consulte a biblioteca debug, que inclui funções para marcar código específico e aumentar a especificidade ainda mais, como debug.profilebegin e debug.profileend. Muitos métodos da API Roblox chamados por scripts também têm suas próprias tags associadas do MicroProfiler que podem fornecer sinais úteis.
Uso de memória de scripts
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 especificamente persistentes no servidor, porque podem ficar online continuamente por muitos dias, enquanto uma sessão do 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 no seu código não estão sendo coletadas pelo lixo.
- PlaceScriptMemory - Fornece um detalhamento do uso de memória script a 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 do coletor de lixo da memória, mesmo após os eventos serem disparados.
Embora os eventos sejam desconectados quando a instância à qual pertencem é destruída, um erro comum é assumir que isso se aplica a objetos do 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, portanto, conexões ao objeto Player e instâncias sob o modelo de personagem, como CharacterAdded, ainda consomem memória se você não desconectá-los 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 de usuários quando eles entram. Por exemplo, o seguinte exemplo de código cria uma tabela adicionando informações de 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 são mais necessárias, a tabela continua a crescer em tamanho e consome mais memória à medida que mais usuários se juntam à sessão. Qualquer código que itera sobre essa tabela também se torna mais computacionalmente caro à 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 - Percorra seu código e assegure-se de que cada conexão seja limpa por uma das seguintes rotas:
- Desconectando manualmente usando a função Disconnect().
- Destruindo a instância à qual 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 no servidor e no cliente.
Problemas comuns
Frequência excessiva de passos de física - Por padrão, o comportamento de stepping está em modo adaptativo, onde as etapas de física são feitas a 60 Hz, 120 Hz ou 240 Hz, dependendo da complexidade do mecanismo de física.
Um modo fixo com precisão melhorada de física também está disponível, que força todas as assemblies de física a operar a 240 Hz (quatro vezes por quadro). Isto resulta em significativamente mais computação a cada quadro.
Número excessivo de objetos simulados - Quanto mais assemblies 3D estão sendo simulados, mais tempo as computações físicas levam a cada quadro. Muitas vezes, os jogos têm 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ão 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 ser computado pelo mecanismo.
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 stepping de física adaptativa - O stepping adaptativo ajusta dinamicamente a taxa de cálculos de física para mecanismos de física, permitindo que atualizações de física sejam feitas com menos frequência em alguns casos.
Reduza a complexidade do mecanismo
- Onde possível, minimize o número de restrições ou juntas de física em uma assembly.
- Reduza a quantidade de autocolisão dentro de um mecanismo, como aplicando limites ou restrições de sem colisão em membros de ragdoll para evitar que eles 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 fidelidades de caixa ou casco, dependendo da forma.
Para objetos muito grandes e complexos, construa colisões personalizadas usando partes invisíveis sempre que possível.
Para objetos que não requerem colisões, desative as colisões e use fidelidade de caixa ou casco, já 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 no Explorer que mostra uma contagem de todas as partes de malha com a fidelidade precisa e permite que você as selecione facilmente.
Para um passeio detalhado sobre como escolher uma opção de fidelidade de colisão que equilibre seus requisitos de precisão e desempenho, consulte Defina parâmetros de física e renderização.
Escopos do MicroProfiler
| Escopo | Cálculo associado |
| physicsStepped | Cálculo geral de física |
| worldStep | Etapas discretas de física tomadas 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. As 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 precisar explorar a redução da fidelidade de colisão de objetos no seu jogo.
Como mitigar
Para reduzir a memória usada pela 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 menos sobrecarga de memória, e Default e Precise são geralmente mais caros.
- É geralmente seguro definir a fidelidade de colisão de qualquer parte ancorada pequena como Box.
- Para malhas muito grandes e 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 jogadores e NPCs. Embora poderosa, uma Humanoid tem um custo significativo de computação.
Problemas comuns
- Deixar todos os HumanoidStateTypes habilitados em NPCs - Há um custo de desempenho por 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 reaparecer modelos com Humanoids ou MeshParts skinned frequentemente
- Isso pode ser intensivo para o mecanismo processar, especialmente se esses modelos usarem roupas em camadas. Isso também pode ser problemático em jogos onde avatares reaparecem com frequência.
- No MicroProfiler, tags longas 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 são executadas no servidor precisam ser simuladas no servidor e replicadas para o cliente. Isso pode ser uma sobrecarga desnecessária.
- Realizando mudanças desnecessárias de tamanho e escala - Mudanças no 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, portanto, em geral, reduza essas mudanças tanto quanto 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 perto do personagem).
- Use alternativas amigáveis ao desempenho em vez de Humanos - Modelos NPC não precisam necessariamente conter um objeto humanoide.
- Para NPCs estáticos, use um simples AnimationController, pois eles não precisam se mover, 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 reaparecimento frequente - Em vez de destruir completamente um NPC, envie o NPC para um pool de NPCs inativos. Dessa forma, quando um novo NPC for necessário para reaparecer, 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.
- Spawn NPCs apenas quando usuários estiverem por perto - Não spawn NPCs quando os usuários não estiverem ao alcance, e elimine-os quando os usuários saírem do alcance deles.
- Evite fazer mudanças 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 humanoide |
| stepAnimation | Animação humanoide e 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, portanto, esta seção é exclusiva do cliente.
Chamadas de desenho
Uma chamada de desenho é um conjunto de instruções do motor para a GPU renderizar algo. Chamadas de desenho possuem 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 Render Stats ⟩ Timing no Studio. Você pode visualizar Render Stats no cliente pressionando ShiftF2.
Quanto mais objetos precisam ser desenhados em sua cena em um dado quadro, mais chamadas de desenho são feitas para a GPU. No entanto, o mecanismo Roblox utiliza um processo chamado instancing 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ênticos, se presentes; caso contrário, quando TextureContents são idênticos.
- 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 quando olha para uma parte específica 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 agrupam bem e introduzem chamadas de desenho adicionais. Preste atenção especial a esses tipos de objetos em uma cena. Em particular, alterações de propriedades em ParticleEmitters podem ter um impacto dramático no desempenho.
Oportunidades de instancing perdidas - Muitas vezes, uma cena incluirá a mesma malha duplicada um número de vezes, mas cada cópia da malha possui IDs de ativos de malha ou textura diferentes. Isso impede a instância e pode levar a chamadas de desenho desnecessárias.
Uma causa comum desse problema é quando uma cena inteira é importada de uma 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 com 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 no seu jogo:
LargeRock, rbxassetid://106420009602747 (x144) -- bomLargeRock, rbxassetid://120109824668127LargeRock, rbxassetid://134460273008628LargeRock, rbxassetid://139288987285823LargeRock, rbxassetid://71302144984955LargeRock, rbxassetid://90621205713698LargeRock, rbxassetid://113160939160788LargeRock, rbxassetid://135944592365226 -- todas possíveis duplicatasComplexidade 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 leva para renderizar um quadro. 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.
Excesso de lançamento de sombras - Lidar com sombras é um processo caro, e mapas que contêm um número alto e denso de objetos de luz que lançam sombras (ou um número elevado e denso de pequenas partes influenciadas por sombras) podem ter problemas de desempenho.
Alta transparência em excesso - 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 skinned - MeshParts skinned que são parte de um Model sem um Humanoid são agrupados usando FastClusters organizados espacialmente. Quando esses MeshParts se movem, eles devem ser constantemente adicionados e removidos desses clusters espaciais, obrigando os clusters a serem reconstruídos e impactando o desempenho.
- Uma solução altamente eficaz é embutir um Humanoid dentro do Model. 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 Model. Consequentemente, atualizações de posição não necessitam reconstruções de cluster, 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 perfilá-lo após fazer esse tipo de mudança. Consulte dicas de desempenho para humanos para obter informações adicionais.
Demasiadas partes em um Model - Muitas partes em um Model podem causar reconstruções mais frequentes devido ao potencial de alteração das propriedades de uma parte exigindo uma reconstrução completa. Encontre o equilíbrio certo de partes em um Model quando 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 carregar 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 terreno ocultos por outros objetos (culling de oclusão). Em certos cenários, como ambientes internos, você pode implementar um sistema de sala ou portal e realizar culling manual de objetos para reduzir ainda mais as chamadas de desenho ou a carga computacional geral.
Reduzindo o nível de detalhamento para modelos - Ative streaming de instâncias e defina o LevelOfDetail dos modelos do seu mundo como SLIM para renderizar malhas SLIM otimizadas e leves conforme aumenta a distância da câmera.
Reduzindo o nível de detalhamento para avatares - Ative o streaming de instâncias e defina a propriedade Workspace.EnableSLIMAvatars para renderizar modelos de avatares de plataforma com qualquer número de acessórios ou camadas de roupas como representações otimizadas e leves de avatares com suporte total para animações à medida que aumenta a distância da câmera.
Reduzindo a fidelidade de renderização - Defina MeshPart.RenderFidelity para Automatic ou Performance. Isso permite que as malhas voltem a opções 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 reduz automaticamente a qualidade das sombras à medida que o nível de qualidade gráfica do cliente diminui, eventualmente desativando completamente as sombras 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 ativadas e aumentar a probabilidade de que as sombras permaneçam ativas. 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 seriam visíveis. Essa estratégia é particularmente eficaz quando aplicada a peças que estão longe da câmera do usuário.
Desative sombras em objetos em movimento quando 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 intervalo específico ou com base em cômodo 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 de 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 requerem mais tempo computacional.
Problemas comuns
Tráfego remoto excessivo - Enviar grandes quantidades de dados através de objetos RemoteEvent ou RemoteFunction ou invocá-los muito frequentemente pode resultar em uma grande quantidade de tempo de CPU sendo gasta 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 desacelerá-los.
- 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 detalhes do item comprado.
Criação ou remoção de árvores de instâncias complexas - Quando uma alteração é 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 são 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 só resulta em um tween sendo instável à medida que a latência dos clientes flutua, mas também 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 vez através de eventos remotos. Em vez disso, envie apenas os dados necessários com menor frequência. 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 pedaços 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 que estão 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 de visão 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 propriedade |
| 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 é ativar 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 estiver 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 ativado, 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 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 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 apenas carregar 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 de 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 para o Roblox são transcodificadas para um formato fixo, portanto, não há benefício de memória em carregar imagens em um modelo de cor associado a menos bytes por pixel. Da mesma forma, compactar 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 inferior e, em seguida, aumenta a qualidade com base na memória disponível do dispositivo, na distância da câmera, na quantidade de espaço na tela que a textura ocupa e em outros fatores. Mesmo assim, ajustar estrategicamente o tamanho das suas texturas pode melhorar o uso de memória em seu jogo.
Mitigação
Carregue ativos apenas uma vez - Reutilize o mesmo ID de ativo em objetos e assegure-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 a similaridade de ativos automaticamente, você pode coletar todos os IDs de ativos de imagem no 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 exclusivos e organizá-los por tamanho para identificar duplicatas manualmente.
- Em vez de usar texturas separadas para diferentes cores, carregue 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 os ativos do mapa individualmente. O Importer não faz nenhuma deduplicação de malhas, então se você importar um grande mapa com muitos pisos separados, cada um desses pisos será importado como um ativo separado (mesmo que sejam duplicatas). Isso pode levar a problemas de desempenho e memória mais adiante, já que cada malha é tratada como individual e ocupa memória e chamadas de desenho.
Limite os pixels das imagens para 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 trim sheets para garantir a máxima reutilização de texturas em mapas 3D. Para etapas e exemplos sobre como criar trim sheets, veja Criar trim sheets.
Você também pode considerar usar sprite sheets para carregar muitas imagens de UI menores como uma única imagem. Você pode usar ImageLabel.ImageRectOffset e ImageLabel.ImageRectSize para exibir partes da folha.
Tempos de carregamento
Muitos jogos implementam telas de carregamento personalizadas e usam o método ContentProvider:PreloadAsync() para solicitar ativos, de modo que imagens, sons e malhas sejam baixados em segundo plano.
A vantagem dessa abordagem é que ela permite garantir que partes importantes do seu jogo estejam totalmente carregadas sem pop-in. No entanto, um erro comum é utilizar excessivamente esse método para 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 prevenir pop-in de texturas, aumenta significativamente o tempo 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 pouco confiável devido à sua natureza flutuante.
Em vez disso, 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 aparição.
Se você precisar carregar um grande número de ativos, recomendamos que você forneça um botão Pular Carregamento.