Questa pagina descrive i problemi di prestazioni comuni e le migliori pratiche per mitigarli.
Computazione degli script
Operazioni costose nel codice Luau richiedono più tempo per essere elaborate e possono quindi influenzare il frame rate. A meno che non venga eseguito in parallelo, il codice Luau viene eseguito in modo sincrono e blocca il thread principale fino a quando non incontra una funzione che cede il thread.
Problemi comuni
- Operazioni intensive sulle strutture delle tabelle - Operazioni complesse come la serializzazione, la deserializzazione e la clonazione profonda comportano un alto costo di prestazioni, specialmente su strutture di tabelle grandi. Questo è particolarmente vero se queste operazioni sono ricorsive o coinvolgono l'iterazione su strutture di dati molto grandi.
- Eventi ad alta frequenza - Legare operazioni costose a eventi basati sui frame di RunService senza limitare la frequenza significa che queste operazioni vengono ripetute ogni frame, il che spesso porta a un aumento non necessario del tempo di calcolo. Questi eventi includono:
Mitigazione
- Invoca il codice sugli eventi di RunService con parsimonia, limitando l'uso ai casi in cui l'invocazione ad alta frequenza è essenziale (ad esempio, aggiornando la telecamera). Puoi eseguire la maggior parte degli altri codici in altri eventi o meno frequentemente in un ciclo.
- Suddividi compiti grandi o costosi utilizzando task.wait() per distribuire il lavoro su più frame.
- Identifica e ottimizza operazioni inutilmente costose e utilizza il multithreading per compiti computazionalmente costosi che non necessitano di accedere al modello dati.
- Alcuni script lato server possono beneficiare della generazione di codice nativo, un semplice flag che compila uno script in codice macchina piuttosto che in bytecode.
- Se i tuoi server esauriscono ancora la CPU dopo aver ottimizzato, puoi riservarne di più con Servizi Estesi per il Calcolo.
Scopi del MicroProfiler
| Ambito | Computazione associata |
| RunService.PreRender | Codice in esecuzione sull'evento PreRender |
| RunService.PreSimulation | Codice in esecuzione sull'evento Stepped |
| RunService.PostSimulation | Codice in esecuzione sull'evento Heartbeat |
| RunService.Heartbeat | Codice in esecuzione sull'evento Heartbeat |
Per ulteriori informazioni sul debug degli script utilizzando il MicroProfiler, consulta la libreria debug, che include funzioni per contrassegnare codice specifico e aumentare ulteriormente la specificità, come debug.profilebegin e debug.profileend. Molti metodi API di Roblox chiamati dagli script hanno anche i propri tag MicroProfiler associati che possono fornire segnali utili.
Utilizzo della memoria degli script
Le perdite di memoria possono verificarsi quando scrivi script che consumano memoria che il garbage collector non può rilasciare correttamente quando non è più in uso. Le perdite sono particolarmente diffuse sul server, poiché possono rimanere online per molti giorni, mentre una sessione client è molto più breve.
I seguenti valori di memoria nel Developer Console possono indicare un problema che necessita di ulteriori indagini:
- LuaHeap - Un consumo elevato o in crescita suggerisce una perdita di memoria.
- InstanceCount - Numeri di istanze in crescita costante suggeriscono che i riferimenti ad alcune istanze nel tuo codice non vengono raccolti dal garbage collector.
- PlaceScriptMemory - Fornisce una suddivisione dell'utilizzo della memoria script per script.
Problemi comuni
Lasciare le connessioni collegate - Il motore non raccoglie mai i garbage eventi collegati a un'istanza e qualsiasi valore referenziato all'interno del callback collegato. Pertanto, le connessioni attive di eventi e il codice all'interno delle istanze collegate, delle funzioni collegate e dei valori referenziati, sono fuori portata per il garbage collector della memoria, anche dopo che gli eventi sono stati attivati.
Anche se gli eventi vengono disconnessi quando l'istanza a cui appartengono viene distrutta, un errore comune è assumere che questo si applichi agli oggetti Player. Dopo che un utente lascia un gioco, il motore non distrugge automaticamente il loro oggetto rappresentativo Player e il modello del personaggio, quindi le connessioni all'oggetto Player e alle istanze sotto il modello del personaggio, come CharacterAdded, continuano a consumare memoria se non le disconnetti nei tuoi script. Questo può portare a perdite di memoria molto significative nel tempo sul server mentre centinaia di utenti entrano e escono dal gioco.
Tabelle - Inserire oggetti nelle tabelle ma non rimuoverli quando non sono più necessari causa un consumo di memoria non necessario, specialmente per le tabelle che tracciano i dati degli utenti quando si uniscono. Ad esempio, il seguente campione di codice crea una tabella aggiungendo informazioni sugli utenti ogni volta che un utente si unisce:
Esempiolocal playerInfo = {}Players.PlayerAdded:Connect(function(player)playerInfo[player] = {} -- alcune informazioniend)Se non rimuovi queste voci quando non sono più necessarie, la tabella continua a crescere in dimensione e consuma più memoria man mano che più utenti si uniscono alla sessione. Qualsiasi codice che itera su questa tabella diventa anche più costoso computazionalmente man mano che la tabella cresce in dimensione.
Mitigazione
Per ripulire tutti i valori utilizzati per prevenire perdite di memoria:
Disconnetti tutte le connessioni - Controlla il tuo codice e assicurati che ogni connessione venga pulita tramite uno dei seguenti percorsi:
- Disconnettere manualmente utilizzando la funzione Disconnect().
- Distruggere l'istanza a cui appartiene l'evento con la funzione Destroy().
- Distruggere l'oggetto script a cui la connessione fa riferimento.
Rimuovi oggetti e personaggi dei giocatori dopo l'uscita - Abilita Workspace.PlayerCharacterDestroyBehavior per distruggere automaticamente gli oggetti dei giocatori e i modelli dei personaggi dopo che un utente esce. Se preferisci, puoi invece pulirli manualmente:
Esempio di pulizia di giocatori e personaggilocal 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)
Computazione fisica
Una simulazione fisica eccessiva può essere una causa chiave dell'aumento del tempo di calcolo per frame sia sul server che sul client.
Problemi comuni
Frequenza del passo fisico eccessiva - Per impostazione predefinita, il comportamento di passo è in modalità adattiva, dove la fisica si svolge a 60 Hz, 120 Hz o 240 Hz, a seconda della complessità del meccanismo fisico.
È disponibile anche una modalità fissa con una maggiore accuratezza della fisica, che costringe tutti gli assemblaggi fisici a eseguire il passo a 240 Hz (quattro volte per frame). Questo comporta un calcolo significativamente maggiore per ogni frame.
Numero e complessità eccessivi di oggetti simulati - Più assemblaggi 3D vengono simulati, più a lungo richiedono i calcoli fisici per ogni frame. Spesso, i giochi avranno oggetti simulati che non necessitano di esserlo o avranno meccanismi che hanno più vincoli e giunti di quanto necessario.
Rilevamento delle collisioni eccessivamente preciso - I pezzi mesh hanno una proprietà CollisionFidelity per rilevare le collisioni che offre una varietà di modalità con diversi livelli di impatto sulle prestazioni. La modalità di rilevamento delle collisioni precisa per i pezzi mesh ha il costo di prestazioni più elevato e richiede più tempo per essere calcolata dal motore.
Mitigazione
Ancorare i pezzi che non richiedono simulazione - Ancorare tutti i pezzi che non devono essere guidati dalla fisica, come per NPC statici.
Utilizzare il passo fisico adattivo - Il passo adattivo regola dinamicamente il tasso di calcoli fisici per i meccanismi fisici, consentendo aggiornamenti fisici meno frequenti in alcuni casi.
Ridurre la complessità del meccanismo
- Dove possibile, minimizza il numero di vincoli fisici o giunti in un assemblaggio.
- Riduci la quantità di auto-collisione all'interno di un meccanismo, ad esempio applicando limiti o vincoli di non collisione agli arti del ragdoll per impedire loro di collidere tra loro.
Ridurre l'uso della fedeltà di collisione precisa per le mesh
Per oggetti piccoli o non interattivi dove gli utenti noterebbero raramente la differenza, utilizza la fedeltà a scatola.
Per oggetti di dimensioni piccole-medie, utilizza fedeltà a scatola o a scafo, a seconda della forma.
Per oggetti grandi e molto complessi, costruisci collisioni personalizzate utilizzando parti invisibili quando possibile.
Per oggetti che non richiedono collisioni, disabilita le collisioni e utilizza la fedeltà a scatola o a scafo, poiché la geometria di collisione è comunque memorizzata in memoria.
Puoi visualizzare la geometria di collisione per scopi di debug in Studio attivando Fidelità di collisione dal widget Opzioni di Visualizzazione nell'angolo in alto a destra della vista 3D.
In alternativa, puoi applicare il filtro CollisionFidelity=PreciseConvexDecomposition all'Esplora che mostra un conteggio di tutte le parti mesh con la fedeltà precisa e ti consente di selezionarle facilmente.
Per una guida approfondita su come scegliere un'opzione di fedeltà di collisione che bilanci le tue esigenze di precisione e prestazioni, consulta Imposta parametri fisici e di rendering.
Scopi del MicroProfiler
| Ambito | Computazione associata |
| physicsStepped | Computazione fisica complessiva |
| worldStep | Passi fisici discreti eseguiti ogni frame |
Utilizzo della memoria fisica
Il movimento fisico e il rilevamento delle collisioni consumano memoria. I pezzi mesh hanno una proprietà CollisionFidelity che determina l'approccio utilizzato per valutare i limiti di collisione della mesh.
Problema comune
Le modalità di rilevamento delle collisioni predefinite e precise consumano significativamente più memoria rispetto alle altre due modalità con forme di collisione a fedeltà inferiore.
Se noti alti livelli di consumo di memoria sotto PhysicsParts, potresti dover esplorare la riduzione della fedeltà di collisione degli oggetti nel tuo gioco.
Come mitigare
Per ridurre la memoria utilizzata per la fedeltà di collisione:
- Per i pezzi che non necessitano di collisioni, disabilita le loro collisioni impostando BasePart.CanCollide, BasePart.CanTouch e BasePart.CanQuery su false.
- Riduci la fedeltà delle collisioni utilizzando l'impostazione CollisionFidelity. Box ha il minor sovraccarico di memoria, e Default e Precise sono generalmente più costosi.
- È generalmente sicuro impostare la fedeltà di collisione di qualsiasi piccolo pezzo ancorato su Box.
- Per mesh molto complesse e grandi, potresti voler costruire la tua mesh di collisione utilizzando oggetti più piccoli con fedeltà di collisione a scatola.
Umani
Humanoid è una classe che fornisce una vasta gamma di funzionalità ai personaggi giocatori e non giocatori (NPC). Sebbene potente, un Humanoid comporta un costo di calcolo significativo.
Problemi comuni
- Lasciare abilitati tutti i HumanoidStateTypes sugli NPC - C'è un costo di prestazioni per lasciare abilitati alcuni HumanoidStateTypes. Disabilita quelli che non sono necessari per i tuoi NPC. Ad esempio, a meno che il tuo NPC non debba arrampicarsi su scale, è sicuro disabilitare lo stato Climbing.
- Istanziamento, modifica e respawn di modelli con Humanoids o MeshParts skinned frequentemente
- Questo può essere intensivo per il motore da elaborare, in particolare se questi modelli utilizzano abbigliamento a strati. Questo può anche essere particolarmente problematico nei giochi in cui gli avatar respawnano spesso.
- Nel MicroProfiler, i lunghi tag updateInvalidatedFastClusters (oltre 4 ms) sono spesso un segnale che l'istanziamento/modifica dell'avatar sta attivando invalidazioni eccessive.
- Utilizzare Humanoids in casi in cui non sono richiesti - NPC statici che non si muovono generalmente non hanno bisogno della classe Humanoid.
- Riproduzione di animazioni su un gran numero di NPC dal server - Le animazioni degli NPC che vengono eseguite sul server devono essere simulate sul server e replicate al client. Questo può essere un sovraccarico non necessario.
- Eseguire cambiamenti di dimensione e scala non necessari - I cambiamenti di dimensione/scala causano la ricostruzione di FastCluster. Cerca di ridurre questo durante il gameplay se noti problemi di prestazioni legati a FastCluster. Allo stesso modo, altre modifiche alle proprietà potrebbero anche causare la ricostruzione di FastCluster, quindi in generale riduci queste modifiche il più possibile.
Mitigazione
- Riproduci animazioni NPC sul client - Nei giochi con un gran numero di NPC, considera di creare il Animator sul client e di eseguire le animazioni localmente. Questo riduce il carico sul server e la necessità di replicazione non necessaria. Consente anche ulteriori ottimizzazioni (come riprodurre solo animazioni per NPC che sono vicini al personaggio).
- Utilizza alternative a basso costo per le prestazioni agli Humanoids - I modelli NPC non devono necessariamente contenere un oggetto umanoide.
- Per NPC statici, utilizza un semplice AnimationController, poiché non devono muoversi ma solo riprodurre animazioni.
- Per NPC in movimento, considera di implementare il tuo controller di movimento e utilizzare un AnimationController per le animazioni, a seconda della complessità dei tuoi NPC.
- Disabilita stati umanoidi non utilizzati - Utilizza Humanoid:SetStateEnabled() per abilitare solo gli stati necessari per ogni umanoide.
- Pool di modelli NPC con respawn frequente - Invece di distruggere completamente un NPC, invia l'NPC a un pool di NPC inattivi. In questo modo, quando è necessario respawnare un nuovo NPC, puoi semplicemente riattivare uno degli NPC dal pool. Questo processo è chiamato pooling, che minimizza il numero di volte in cui i personaggi devono essere istanziati.
- Spawnare NPC solo quando gli utenti sono vicini - Non spawnare NPC quando gli utenti non sono a portata, e rimuovili quando gli utenti escono dalla loro portata.
- Evita di apportare modifiche all'gerarchia dell'avatar dopo che è stata istanziata - Alcune modifiche a un'gerarchia dell'avatar hanno implicazioni significative sulle prestazioni. Sono disponibili alcune ottimizzazioni:
- Per animazioni procedurali personalizzate, non aggiornare le proprietà JointInstance.C0 e JointInstance.C1. Invece, aggiorna la proprietà Motor6D.Transform.
Scopi del MicroProfiler
| Ambito | Computazione associata |
| stepHumanoid | Controllo e fisica umanoide |
| stepAnimation | Animazione umanoide e animatore |
| updateInvalidatedFastClusters | Associato all'istanziamento o alla modifica di un avatar |
Rendering
Una parte significativa del tempo che il client trascorre ogni frame è dedicata al rendering della scena nel frame corrente. Il server non esegue alcun rendering, quindi questa sezione è esclusiva per il client.
Chiamate di disegno
Una chiamata di disegno è un insieme di istruzioni dal motore alla GPU per rendere qualcosa. Le chiamate di disegno hanno un sovraccarico significativo. In generale, meno chiamate di disegno per frame, meno tempo computazionale viene speso per il rendering di un frame.
Puoi vedere quante chiamate di disegno stanno attualmente avvenendo con l'elemento Render Stats ⟩ Timing in Studio. Puoi visualizzare le Render Stats nel client premendo ShiftF2.
Più oggetti devono essere disegnati nella tua scena in un dato frame, più chiamate di disegno vengono effettuate alla GPU. Tuttavia, il motore Roblox utilizza un processo chiamato istanza per ridurre in un'unica chiamata di disegno le mesh identiche con le stesse caratteristiche di texture. In particolare, più mesh con lo stesso MeshContent vengono gestite in un'unica chiamata di disegno quando:
- SurfaceAppearances sono identiche se presenti, altrimenti quando TextureContents sono identiche.
- I materiali sono identici quando sia SurfaceAppearance che MeshPart.TextureID non esistono.
Altri problemi comuni
Densità eccessiva di oggetti - Se un gran numero di oggetti è concentrato con un'alta densità, il rendering di quest'area della scena richiede più chiamate di disegno. Se noti che il tuo frame rate diminuisce quando guardi una certa parte della mappa, questo può essere un buon segnale che la densità degli oggetti in quest'area è troppo alta.
Oggetti come decalcomanie, texture e particelle non si raggruppano bene e introducono ulteriori chiamate di disegno. Fai particolare attenzione a questi tipi di oggetti in una scena. In particolare, le modifiche alle proprietà di ParticleEmitters possono avere un impatto drammatico sulle prestazioni.
Opportunità di istanza mancate - Spesso, una scena includerà la stessa mesh duplicata un certo numero di volte, ma ogni copia della mesh ha ID di asset mesh o texture diversi. Questo impedisce l'istanza e può portare a chiamate di disegno non necessarie.
Una causa comune di questo problema è quando un'intera scena viene importata in una sola volta, piuttosto che importare singoli asset in Roblox e poi duplicarli dopo l'importazione per assemblare la scena.
Anche un semplice script come questo può aiutarti a identificare le parti mesh con lo stesso nome che utilizzano ID mesh diversi:
for _,descendant in workspace:GetDescendants() doif descendant:IsA("MeshPart") thenprint(descendant.Name .. ", " .. descendant.MeshId)endendL'output (con Stack Lines abilitato) potrebbe apparire in questo modo. Le righe ripetute indicano il riutilizzo della stessa mesh, il che è positivo. Le righe uniche non sono necessariamente negative, ma a seconda del tuo schema di denominazione, potrebbero indicare mesh duplicate nel tuo gioco:
LargeRock, rbxassetid://106420009602747 (x144) -- buonoLargeRock, rbxassetid://120109824668127LargeRock, rbxassetid://134460273008628LargeRock, rbxassetid://139288987285823LargeRock, rbxassetid://71302144984955LargeRock, rbxassetid://90621205713698LargeRock, rbxassetid://113160939160788LargeRock, rbxassetid://135944592365226 -- tutti possibili duplicatiComplessità eccessiva degli oggetti - Sebbene non sia importante quanto il numero di chiamate di disegno, il numero di triangoli in una scena influisce su quanto tempo impiega a essere renderizzata. Scene con un numero molto elevato di mesh molto complesse sono un problema comune, così come le scene con la proprietà MeshPart.RenderFidelity impostata su Precise su troppe mesh.
Eccessiva proiezione delle ombre - Gestire le ombre è un processo costoso, e le mappe che contengono un numero elevato e una densità di oggetti luminosi che proiettano ombre (o un numero elevato e una densità di piccoli pezzi influenzati dalle ombre) possono avere problemi di prestazioni.
Eccessivo overdraw di trasparenza - Posizionare oggetti con trasparenza parziale vicini tra loro costringe il motore a rendere più volte i pixel sovrapposti, il che può danneggiare le prestazioni. Per ulteriori informazioni su come identificare e risolvere questo problema, consulta Elimina trasparenze a strati.
Movimento non necessario di MeshPart skinned - I MeshPart skinned che fanno parte di un Modello senza un Humanoid sono raggruppati utilizzando FastClusters organizzati spazialmente. Quando questi MeshPart si muovono, devono essere continuamente aggiunti e rimossi da questi cluster spaziali, costringendo i cluster a essere ricostruiti e impattando le prestazioni.
- Una soluzione altamente efficace è incorporare un Humanoid all'interno del Modello. La presenza di un Humanoid sovrascrive il comportamento di clustering spaziale predefinito, imponendo l'uso di un singolo FastCluster unificato per l'intero Modello. Di conseguenza, gli aggiornamenti di posizione non richiedono più ricostruzioni dei cluster, mitigando così il collo di bottiglia delle prestazioni. Questa tecnica dovrebbe essere riservata esclusivamente ai MeshPart con movimento previsto, poiché potrebbe introdurre un sovraccarico di memoria e annullare i benefici dell'ottimizzazione spaziale. Ti consigliamo di profilare sempre il tuo gioco dopo aver apportato questo tipo di modifiche. Consulta Suggerimenti sulle prestazioni degli umanoidi per ulteriori informazioni.
Troppi pezzi in un Model - Troppi pezzi in un Modello potrebbero causare ricostruzioni più frequenti a causa del potenziale cambiamento delle proprietà di un pezzo che richiede una ricostruzione completa. Trova il giusto equilibrio di pezzi in un Modello quando utilizza FastCluster.
Mitigazione
- Istanziamento di mesh identiche e riduzione del numero di mesh uniche - Se ti assicuri che tutte le mesh identiche abbiano gli stessi ID di asset sottostanti, il motore può riconoscerle e renderizzarle in un'unica chiamata di disegno. Assicurati di caricare ogni mesh in una mappa solo una volta e poi duplicarle in Studio per il riutilizzo piuttosto che importare grandi mappe nel loro insieme, il che potrebbe causare che mesh identiche abbiano ID di contenuto separati e siano riconosciute come asset unici dal motore. I pacchetti sono un meccanismo utile per il riutilizzo degli oggetti.
- Culling - Il culling descrive il processo di eliminazione delle chiamate di disegno per oggetti che non influiscono sul frame renderizzato finale. Per impostazione predefinita, il motore salta le chiamate di disegno per oggetti al di fuori del campo visivo della telecamera (culling del frustum) e parti, mesh e terreno occlusi dalla vista da altri oggetti (culling di occlusione). In determinate situazioni, come ambienti interni, potresti essere in grado di implementare un sistema di stanze o portali e di eseguire manualmente il culling degli oggetti per ridurre ulteriormente le chiamate di disegno o il carico computazionale complessivo.
- Riduzione del livello di dettaglio per i modelli - Abilita lo streaming delle istanze e imposta la proprietà LevelOfDetail dei tuoi modelli del mondo su SLIM per renderizzare mesh SLIM leggere ottimizzate man mano che aumenta la distanza dalla telecamera.
- Riduzione del livello di dettaglio per gli avatar - Abilita lo streaming delle istanze e imposta Workspace.EnableSLIMAvatars per renderizzare gli avatar della piattaforma come rappresentazioni SLIM leggere ottimizzate con supporto completo per l'animazione man mano che aumenta la distanza dalla telecamera.
- Riduzione della fedeltà di rendering - Imposta MeshPart.RenderFidelity su Automatic o Performance. Questo consente alle mesh di tornare a alternative meno complesse, il che può ridurre il numero di poligoni che devono essere disegnati.
- Disabilitare la proiezione delle ombre su parti e oggetti luminosi appropriati - Il motore Roblox degrada automaticamente la qualità delle ombre man mano che il livello di qualità grafica del client diminuisce, disabilitando infine completamente le ombre a livelli di qualità inferiori a 4. Tuttavia, puoi disabilitare selettivamente le proprietà di proiezione delle ombre su oggetti luminosi e parti per migliorare le prestazioni mentre le ombre sono abilitate e aumentare la probabilità che le ombre rimangano abilitate. Alcuni esempi di ottimizzazioni che puoi apportare sia in fase di modifica che dinamicamente durante l'esecuzione:
Utilizza la proprietà BasePart.CastShadow per disabilitare la proiezione delle ombre su piccoli pezzi dove le ombre sono improbabili da vedere. Questa strategia è particolarmente efficace quando applicata a pezzi lontani dalla telecamera dell'utente.
Disabilita le ombre su oggetti in movimento quando possibile.
Disabilita Light.Shadows su istanze luminose dove l'oggetto non deve proiettare ombre.
Limita la portata e l'angolo delle istanze luminose.
Utilizza meno istanze luminose.
Considera di disabilitare le luci che si trovano al di fuori di un intervallo specifico o su base stanza per stanza per ambienti interni.
Scopi del MicroProfiler
| Ambito | Computazione associata |
| Prepare and Perform | Rendering complessivo |
| Perform/Scene/computeLightingPerform | Aggiornamenti della griglia luminosa e delle ombre |
| LightGridCPU | Aggiornamenti della griglia luminosa voxel |
| ShadowMapSystem | Mapping delle ombre |
| Perform/Scene/UpdateView | Preparazione per il rendering e aggiornamenti delle particelle |
| Perform/Scene/RenderView | Rendering e post-elaborazione |
Networking e replicazione
Networking e replicazione descrivono il processo mediante il quale i dati vengono inviati tra il server e i client connessi. Le informazioni vengono inviate tra il client e il server ogni frame, ma quantità maggiori di informazioni richiedono più tempo di calcolo.
Problemi comuni
Traffico remoto eccessivo - Inviare una grande quantità di dati tramite oggetti RemoteEvent o RemoteFunction o invocarli molto frequentemente può portare a un grande consumo di tempo CPU per elaborare i pacchetti in arrivo ogni frame. Errori comuni includono:
- Replicare dati ogni frame che non necessitano di essere replicati.
- Replicare dati in base all'input dell'utente senza alcun meccanismo per limitare la frequenza.
- Inviare più dati di quanto necessario. Ad esempio, inviare l'intero inventario del giocatore quando acquista un oggetto piuttosto che solo i dettagli dell'oggetto acquistato.
Creazione o rimozione di alberi di istanze complessi - Quando viene apportata una modifica al modello dati sul server, viene replicata ai client connessi. Ciò significa che creare e distruggere grandi gerarchie di istanze come mappe durante il runtime può essere molto intensivo a livello di rete.
Un colpevole comune qui è il complesso dato di animazione salvato dai plugin Animation Editor nei rig. Se questi non vengono rimossi prima che il gioco venga pubblicato e il modello animato viene clonato regolarmente, una grande quantità di dati verrà replicata inutilmente.
TweenService lato server - Se TweenService viene utilizzato per tweenare un oggetto lato server, la proprietà tweenata viene replicata a ciascun client ogni frame. Non solo questo porta a un tween che è instabile man mano che la latenza dei client fluttua, ma causa anche un sacco di traffico di rete non necessario.
Mitigazione
Puoi adottare le seguenti tattiche per ridurre la replicazione non necessaria:
- Evita di inviare grandi quantità di dati contemporaneamente tramite eventi remoti. Invece, invia solo i dati necessari a una frequenza inferiore. Ad esempio, per lo stato di un personaggio, replicalo quando cambia piuttosto che ogni frame.
- Dividi alberi di istanze complessi come mappe e caricali a pezzi per distribuire il lavoro di replicazione su più frame.
- Pulisci i metadati delle animazioni, in particolare la directory delle animazioni dei rig, dopo l'importazione.
- Limita la replicazione non necessaria delle istanze, specialmente nei casi in cui il server non ha bisogno di avere conoscenza delle istanze create. Questo include:
- Effetti visivi come un'esplosione o un incantesimo. Il server ha solo bisogno di conoscere la posizione per determinare l'esito, mentre i client possono creare visivi localmente.
- Modelli di visualizzazione degli oggetti in prima persona.
- Tweenare oggetti sul client piuttosto che sul server.
Scopi del MicroProfiler
| Ambito | Computazione associata |
| ProcessPackets | Elaborazione dei pacchetti di rete in arrivo, come invocazioni di eventi e modifiche alle proprietà |
| Allocate Bandwidth and Run Senders | Eventi in uscita rilevanti sui server |
Utilizzo della memoria degli asset
Il meccanismo di maggiore impatto disponibile per i creatori per migliorare l'utilizzo della memoria del client è abilitare lo streaming delle istanze.
Streaming delle istanze
Lo streaming delle istanze carica selettivamente parti del modello dati che non sono richieste, il che può portare a tempi di caricamento notevolmente ridotti e aumentare la capacità del client di prevenire crash quando si trova sotto pressione di memoria.
Se stai riscontrando problemi di memoria e hai disabilitato lo streaming delle istanze, considera di aggiornare il tuo gioco per supportarlo, in particolare se il tuo mondo 3D è grande. Lo streaming delle istanze si basa sulla distanza nello spazio 3D, quindi i mondi più grandi beneficiano naturalmente di più.
Se lo streaming delle istanze è abilitato, puoi aumentare l'aggressività di esso. Ad esempio, considera:
- Ridurre l'uso di Enum.ModelStreamingMode.Persistent dove possibile. Potresti dover aggiornare i tuoi script se lo stai utilizzando come misura di compatibilità.
- Ridurre il Workspace.StreamingMinRadius e il Workspace.StreamingTargetRadius.
Per ulteriori informazioni sulle opzioni di streaming e sui loro benefici, consulta le proprietà di streaming.
Altri problemi comuni
Duplicazione degli asset - Un errore comune è caricare lo stesso asset più volte, risultando in ID di asset diversi. Questo può portare allo stesso contenuto caricato in memoria più volte.
Volume eccessivo di asset - Anche quando gli asset non sono identici, ci sono casi in cui si perdono opportunità di riutilizzare lo stesso asset e risparmiare memoria.
File audio - I file audio possono essere un sorprendente contributore all'utilizzo della memoria, in particolare se carichi tutti contemporaneamente nel client piuttosto che solo ciò di cui hai bisogno per una parte del gioco. Per strategie, consulta Tempi di caricamento.
Texture ad alta risoluzione - Il consumo di memoria grafica per una texture non è correlato alla dimensione della texture su disco; il numero di pixel nella texture determina l'utilizzo della memoria. Ad esempio, una texture di 1024x1024 pixel consuma quattro volte la memoria grafica di una texture di 512x512 pixel.
Le immagini caricate su Roblox vengono transcodificate in un formato fisso, quindi non c'è alcun beneficio di memoria nel caricare immagini in un modello di colore associato a meno byte per pixel. Allo stesso modo, comprimere le immagini prima del caricamento o rimuovere il canale alfa da immagini che non ne hanno bisogno può ridurre la dimensione dell'immagine su disco, ma non migliora l'utilizzo della memoria.
Man mano che un gioco si carica, il motore inizia automaticamente con texture di qualità inferiore e poi aumenta la qualità in base alla memoria disponibile del dispositivo, alla distanza dalla telecamera, alla quantità di spazio sullo schermo che la texture occupa e ad altri fattori. Anche così, dimensionare strategicamente le tue texture può migliorare l'utilizzo della memoria nel tuo gioco.
Mitigazione
Carica gli asset solo una volta - Riutilizza lo stesso ID di asset tra gli oggetti e assicurati che gli stessi asset, specialmente mesh e immagini, non vengano caricati separatamente più volte.
Trova e risolvi asset duplicati - Cerca parti mesh e texture identiche che sono state caricate più volte con ID diversi.
- Anche se non esiste un'API per rilevare automaticamente la somiglianza degli asset, puoi raccogliere tutti gli ID di asset immagine nel tuo posto (sia manualmente che con uno script), scaricarli e confrontarli utilizzando strumenti di confronto esterni.
- Per le parti mesh, la migliore strategia è prendere ID mesh unici e organizzarli per dimensione per identificare manualmente i duplicati.
- Invece di utilizzare texture separate per colori diversi, carica una singola texture e utilizza la proprietà SurfaceAppearance.Color per applicare varie tonalità.
Importa asset nella mappa separatamente - Invece di importare un'intera mappa in una sola volta, importa e ricostruisci gli asset nella mappa individualmente e ricostruiscili. L'Importatore non esegue alcuna de-duplicazione delle mesh, quindi se importi una grande mappa con molti piastrelle di pavimento separate, ciascuna di quelle piastrelle verrebbe importata come un asset separato (anche se sono duplicati). Questo può portare a problemi di prestazioni e memoria nel tempo, poiché ogni mesh viene trattata come individuale e occupa memoria e chiamate di disegno.
Limita i pixel delle immagini a non più della quantità necessaria. A meno che un'immagine non occupi una grande quantità di spazio fisico sullo schermo, di solito ha bisogno al massimo di 512x512 pixel. La maggior parte delle immagini minori dovrebbe essere più piccola di 256x256 pixel.
Utilizza fogli di ritaglio per garantire il massimo riutilizzo delle texture nelle mappe 3D. Per passaggi ed esempi su come creare fogli di ritaglio, consulta Crea fogli di ritaglio.
Potresti anche considerare di utilizzare sprite sheet per caricare molte immagini UI più piccole come un'unica immagine. Puoi quindi utilizzare ImageLabel.ImageRectOffset e ImageLabel.ImageRectSize per visualizzare porzioni del foglio.
Tempi di caricamento
Prima che un giocatore possa iniziare a giocare, il client deve ricevere tutto in ReplicatedFirst e ReplicatedStorage, i contenuti iniziali di Workspace, e qualsiasi asset che i tuoi script pre-caricano. Maggiore è il contenuto che il client deve scaricare e elaborare durante questo tempo, più a lungo i giocatori aspettano. Per una suddivisione di ciascun passaggio nel processo di accesso, consulta Identifica i problemi di prestazioni.
Problemi comuni
Streaming delle istanze disabilitato - Senza streaming delle istanze, l'intero Workspace deve replicarsi al client prima che il giocatore possa entrare, quindi i giochi con molte parti, modelli e asset hanno tempi di caricamento proporzionalmente più lunghi.
Contenuto eccessivo in ReplicatedStorage - Tutto in ReplicatedStorage si replica a ogni client prima che il giocatore possa vedere o interagire con qualsiasi cosa, indipendentemente dal fatto che il client ne abbia bisogno immediatamente. Modelli gratuiti e asset ereditati possono aggiungere silenziosamente un carico significativo.
Contenuto pesante in ReplicatedFirst - ReplicatedFirst si replica prima di qualsiasi altra cosa ed esiste principalmente per contenere uno schermo di caricamento personalizzato. Ogni asset o script aggiuntivo in esso ritarda l'inizio della fase principale di replicazione.
Uso eccessivo di PreloadAsync() - Molti giochi implementano schermi di caricamento personalizzati e utilizzano ContentProvider:PreloadAsync() per richiedere asset in modo che immagini, suoni e mesh vengano scaricati in background. Questo approccio garantisce che le parti importanti del tuo gioco siano completamente caricate senza pop-in, ma un errore comune è pre-caricare più asset di quanto sia realmente necessario.
Un esempio di cattiva pratica è caricare l'intero Workspace. Anche se questo potrebbe prevenire il pop-in delle texture, aumenta significativamente i tempi di caricamento.
Un'altra pratica simile è utilizzare ContentProvider.RequestQueueSize per garantire che tutti gli asset richiesti siano stati caricati. Tuttavia, questo presenta lo stesso problema di tempi di caricamento significativamente aumentati, oltre a essere un metodo inaffidabile a causa della sua natura fluttuante.
Script di avvio costosi - L'esecuzione di script pesanti in LocalScripts che vengono eseguiti durante il processo di accesso, come il caricamento sincrono di grandi strutture dati, ritarda la transizione dalla replicazione a uno stato giocabile.
Mitigazione
Abilita lo streaming delle istanze - Lo streaming invia solo la porzione del tuo mondo vicino al punto di spawn del giocatore all'accesso iniziale, quindi streamma il resto progressivamente man mano che il giocatore si muove nel tuo gioco. Per i giochi che non lo utilizzano già, lo streaming è uno dei modi più efficaci per ridurre i tempi di caricamento. Lo streaming è abilitato per impostazione predefinita per i nuovi luoghi. Per passaggi di configurazione, riferimento alle proprietà e guida alla migrazione, consulta Streaming delle istanze.
Mantieni ReplicatedStorage snello - Conserva solo ciò di cui i client hanno bisogno nel momento in cui entrano, come ModuleScripts condivisi di cui LocalScripts hanno bisogno. Sposta modelli grandi, audio e altri asset che non sono necessari immediatamente in ServerStorage, e spostali nel client solo quando un giocatore ne ha bisogno. Controlla regolarmente ReplicatedStorage per contenuti che non utilizzi più.
Mantieni ReplicatedFirst snello - Limita ReplicatedFirst a ciò di cui ha bisogno il tuo schermo di caricamento, come elementi UI di base piuttosto che texture grandi o modelli 3D, e evita calcoli pesanti negli script che vengono eseguiti da esso. Per una guida alla costruzione di uno schermo di caricamento personalizzato, incluso quando chiamare ReplicatedFirst:RemoveDefaultLoadingScreen(), consulta Schermi di caricamento.
Precarica solo asset necessari - Utilizza ContentProvider:PreloadAsync() solo in situazioni necessarie, che includono:
- Immagini nello schermo di caricamento.
- Immagini importanti nel menu del tuo gioco, come sfondi e icone dei pulsanti.
- Asset importanti nell'area di partenza o di spawn.
Se devi caricare un gran numero di asset, ti consigliamo di fornire un pulsante Salta caricamento.
Rimanda il lavoro di avvio non essenziale - Carica contenuti che un giocatore non ha ancora raggiunto, come pannelli UI che non ha ancora aperto, solo quando è necessario per la prima volta. Per il lavoro di inizializzazione che deve avvenire presto, distribuiscilo su più frame piuttosto che eseguirlo tutto in una volta. Per tecniche, consulta Computazione degli script.