Questa pagina descrive i problemi di prestazioni comuni e le migliori pratiche per mitigarli.
Computazione degli script
Le operazioni costose in 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 mette in pausa il thread.
Problemi comuni
Operazioni intensive sulle strutture delle tabelle - Operazioni complesse come serializzazione, deserializzazione e clonazione profonda comportano un alto costo sulle prestazioni, specialmente su grandi strutture di tabelle. 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 codice sugli eventi RunService con parsimonia, limitando l'uso ai casi in cui l'invocazione ad alta frequenza è essenziale (ad esempio, aggiornamento della telecamera). Puoi eseguire la maggior parte del codice in altri eventi o meno frequentemente in un ciclo.
- Suddividi attività grandi o costose utilizzando task.wait() per distribuire il lavoro su più frame.
- Identifica e ottimizza operazioni inutilmente costose e utilizza multithreading per attività computazionalmente costose che non devono accedere al modello dei dati.
- Alcuni script lato server possono trarre vantaggio dalla generazione di codice nativo, un semplice flag che compila uno script in codice macchina piuttosto che bytecode.
Scope del MicroProfiler
| Ambito | Computazione associata |
| RunService.PreRender | Codice eseguito durante l'evento PreRender |
| RunService.PreSimulation | Codice eseguito durante l'evento Stepped |
| RunService.PostSimulation | Codice eseguito durante l'evento Heartbeat |
| RunService.Heartbeat | Codice eseguito durante l'evento Heartbeat |
Per ulteriori informazioni sul debug degli script utilizzando il MicroProfiler, vedi la libreria debug, che include funzioni per il tagging di codice specifico e ulteriore aumento della specificità, come debug.profilebegin e debug.profileend. Molti metodi API di Roblox chiamati dagli script hanno anche i loro tag MicroProfiler associati che possono fornire segnali utili.
Utilizzo della memoria degli script
I problemi di memory leak possono verificarsi quando si scrivono script che consumano memoria che il garbage collector non può rilasciare correttamente quando non è più in uso. I leak sono particolarmente diffusi sul server, perché possono essere online continuamente 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 alto o in crescita suggerisce un memory leak.
- InstanceCount - Numeri di istanze in crescita costante suggeriscono che i riferimenti ad alcune istanze nel tuo codice non vengono gestiti dal garbage collector.
- PlaceScriptMemory - Fornisce un'analisi dell'utilizzo della memoria script per script.
Problemi comuni
Lasciare connessioni attive - Il motore non garbage colleziona mai eventi collegati a un'istanza e qualsiasi valore referenziato all'interno del callback connesso. Pertanto, le connessioni attive degli eventi e il codice all'interno delle istanze connesse, funzioni connesse e valori referenziati, sono fuori portata per il garbage collector della memoria, anche dopo che gli eventi sono stati attivati.
Sebbene gli eventi vengano disconnessi quando l'istanza a cui appartengono viene distrutta, un errore comune è assumere che ciò si applichi agli oggetti Player. Dopo che un utente lascia un gioco, il motore non distrugge automaticamente il loro oggetto rappresentativo di 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 leak di memoria molto significativi nel tempo sul server man mano che centinaia di utenti entrano ed escono dal gioco.
Tabelle - Inserire oggetti in tabelle ma non rimuoverli quando non sono più necessari causa un consumo di memoria non necessario, specialmente per tabelle che tracciano dati utente quando si uniscono. Ad esempio, il seguente esempio di codice crea una tabella aggiungendo informazioni sugli utenti ogni volta che un utente entra:
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 dimensioni e consuma più memoria man mano che più utenti partecipano alla sessione. Qualsiasi codice che itera su questa tabella diventa anche più costoso in termini computazionali quando la tabella cresce in dimensioni.
Mitigazione
Per pulire tutti i valori utilizzati per prevenire memory leak:
Disconnetti tutte le connessioni - Controlla il tuo codice e assicurati che ogni connessione venga pulita tramite uno dei seguenti metodi:
- Disconnettendo manualmente usando la funzione Disconnect().
- Distruggendo l'istanza a cui appartiene l'evento con la funzione Destroy().
- Distruggendo l'oggetto script da cui la connessione trae origine.
Rimuovere 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 è uscito. Se preferisci, puoi invece pulirli manualmente:
Esempio di pulizia di giocatore 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 delle cause principali di un aumento del tempo di calcolo per frame sia sul server che sul client.
Problemi comuni
Eccessiva frequenza del passo fisico - Per impostazione predefinita, il comportamento di passo è in modalità adattiva, dove la fisica viene effettuata 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 tutte le assemblaggi fisici a effettuare un passo a 240 Hz (quattro volte per frame). Questo comporta un notevole incremento di calcoli ad ogni frame.
Numero e complessità eccessivi di oggetti simulati - Più assemblaggi 3D vengono simulati, più a lungo occorre per i calcoli fisici per ogni frame. Spesso, i giochi avranno oggetti in fase di simulazione che non devono 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 il rilevamento delle 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 calcolare.
Mitigazione
Ancorare parti che non richiedono simulazione - Ancorare tutte le parti che non devono essere gestite dalla fisica, come quelle destinate a NPC statici.
Utilizzare il passo fisico adattivo - Il passo adattivo regola dinamicamente la frequenza dei calcoli fisici per i meccanismi fisici, consentendo di effettuare gli aggiornamenti fisici meno frequentemente in alcuni casi.
Ridurre la complessità dei meccanismi
- Dove possibile, minimizzare il numero di vincoli o giunti fisici in un assemblaggio.
- Ridurre la quantità di auto-collisione all'interno di un meccanismo, come applicando limiti o vincoli di non-collisione agli arti del corpo artificiale per impedire loro di collidere tra loro.
Ridurre l'uso di precisione elevata per le collisioni mesh
Per oggetti piccoli o non interattivi dove gli utenti noteranno raramente la differenza, utilizzare la precisione a scatola.
Per oggetti di dimensioni piccole-medie, utilizzare fidelità a scatola o a guscio, a seconda della forma.
Per oggetti grandi e molto complessi, costruire collisioni personalizzate utilizzando parti invisibili quando possibile.
Per oggetti che non richiedono collisioni, disabilitare le collisioni e utilizzare la fidelity a scatola o a guscio, poiché la geometria per le collisioni è comunque memorizzata in memoria.
Puoi rendere visibile la geometria delle collisioni per scopi di debug in Studio attivando Collision fidelity dal widget delle Opzioni di visualizzazione nell'angolo in alto a destra della vista 3D.
In alternativa, puoi applicare il filtro CollisionFidelity=PreciseConvexDecomposition a Explorer che mostra un conteggio di tutti i pezzi mesh con la precisione precisa e consente di selezionarli facilmente.
Per un walkthrough approfondito su come scegliere un'opzione di fedeltà per la collisione che bilancia le tue esigenze di precisione e prestazioni, vedere Impostare i parametri di fisica e rendering.
Scope del MicroProfiler
| Ambito | Computazione associata |
| physicsStepped | Computazione generale della fisica |
| worldStep | Passi fisici discreti effettuati per 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 del 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 bassa fedeltà.
Se osservi alti livelli di consumo di memoria sotto PhysicsParts, potresti dover esplorare la riduzione della fidelità delle collisioni degli oggetti nel tuo gioco.
Come mitigare
Per ridurre la memoria utilizzata per la fedeltà delle collisioni:
- Per le parti che non hanno bisogno 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 più basso sovraccarico di memoria, e Default e Precise sono generalmente più costosi.
- È generalmente sicuro impostare la fedeltà delle collisioni di qualsiasi parte piccola ancorata su Box.
- Per mesh molto complesse e grandi, potrebbe essere opportuno costruire la tua mesh di collisione da oggetti più piccoli con fedeltà di collisione a scatola.
Umani
Humanoid è una classe che fornisce una vasta gamma di funzionalità ai giocatori e ai personaggi non giocanti (NPC). Sebbene potente, un Humanoid comporta un costo di calcolo significativo.
Problemi comuni
- Lasciare tutte le HumanoidStateTypes abilitate sugli NPC - C'è un costo di prestazioni lasciando abilitate certe HumanoidStateTypes. Disabilita quelle che non sono necessarie 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 respawning di modelli con Humanoids o MeshParts frequentemente
- Questo può essere intensivo per il motore da elaborare, in particolare se questi modelli utilizzano abbigliamento a strati. Questo può essere particolarmente problematico nei giochi in cui gli avatar respawnano spesso.
- Nel MicroProfiler, le lunghe etichette updateInvalidatedFastClusters (superiori a 4 ms) sono spesso un segnale che l'istanza/modifica dell'avatar sta causando eccessive invalidazioni.
- Utilizzare Umani in casi in cui non sono necessari - Gli NPC statici che non si muovono generalmente non hanno bisogno della classe Humanoid.
- Riproduzione di animazioni su un numero elevato di NPC dal server - Le animazioni degli NPC che vengono eseguite sul server devono essere simulate sul server e replicate al client. Questo può comportare un sovraccarico non necessario.
- Effettuare modifiche di dimensioni e scala non necessarie - Le modifiche di dimensioni/scala causano la ricostruzione di FastCluster. Prova a ridurre questo durante il gioco se stai notando problemi di prestazioni relativi a FastCluster. Allo stesso modo, altre modifiche di proprietà potrebbero anche causare la ricostruzione di FastCluster, quindi in generale riduci al minimo queste modifiche il più possibile.
Mitigazione
- Riproduci animazioni degli NPC sul client - Nei giochi con un gran numero di NPC, considera di creare il Animator sul client e far girare le animazioni localmente. Questo riduce il carico sul server e la necessità di replicazione non necessaria. Rende possibili anche ottimizzazioni aggiuntive (come riprodurre animazioni solo per gli NPC che sono vicini al personaggio).
- Utilizza alternative ottimizzate agli Umani - I modelli NPC non hanno necessariamente bisogno di contenere un oggetto umanoide.
- Per NPC statici, utilizza un semplice AnimationController, poiché non devono muoversi ma devono 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.
- Disabilitare stati umanoidi non utilizzati - Utilizza Humanoid:SetStateEnabled() per abilitare solo gli stati necessari per ciascun umanoide.
- Pooling di modelli NPC con respawning frequente - Invece di distruggere un NPC completamente, invia l'NPC a una piscina di NPC inattivi. In questo modo, quando un nuovo NPC deve essere respawntato, puoi semplicemente riattivare uno degli NPC dalla piscina. Questo processo è chiamato pooling, il che minimizza il numero di volte in cui i personaggi devono essere istanziati.
- Spawn di NPC solo quando gli utenti sono vicini - Non spawnare NPC quando gli utenti non sono nel raggio d'azione e rimuovili quando gli utenti escono dal loro raggio.
- Evitare di apportare modifiche alla gerarchia dell'avatar dopo che è stata istanziata - Alcune modifiche a una gerarchia di avatar hanno implicazioni significative sulle prestazioni. Sono disponibili alcune ottimizzazioni:
- Per animazioni personalizzate procedurali, non aggiornare le proprietà JointInstance.C0 e JointInstance.C1. Invece, aggiorna la proprietà Motor6D.Transform.
- Se devi allegare oggetti BasePart all'avatar, fallo al di fuori della gerarchia del modello dell'avatar.
Scope del MicroProfiler
| Ambito | Computazione associata |
| stepHumanoid | Controllo e fisica umanoide |
| stepAnimation | Animazione umanoide e dell'animatore |
| updateInvalidatedFastClusters | Associato all'istanziamento o alla modifica di un avatar |
Rendering
Una parte significativa del tempo che il client trascorre in ogni frame è dedicato 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 rendere un frame.
Puoi vedere quante chiamate di disegno stanno attualmente avvenendo con l'elemento Render Stats ⟩ Timing in Studio. Puoi visualizzare Render Stats nel client premendo ShiftF2.
Più oggetti devono essere disegnati nella tua scena in un dato frame, maggiori saranno le chiamate di disegno fatte alla GPU. Tuttavia, il motore Roblox utilizza un processo chiamato istanza per accorpare mesh identiche con le stesse caratteristiche di texture in una singola chiamata di disegno. In particolare, più mesh con il MeshContent identico vengono gestite in una singola chiamata di disegno quando:
- SurfaceAppearances sono identici, se presenti, altrimenti quando TextureContents sono identici.
- I materiali sono identici quando sia SurfaceAppearance che MeshPart.TextureID non esistono.
Altri problemi comuni
Eccessiva densità di oggetti - Se un gran numero di oggetti è concentrato con una densità elevata, il rendering di quest'area della scena richiede più chiamate di disegno. Se stai vedendo che il tuo frame rate diminuisce quando guardi una parte specifica della mappa, questo può essere un buon segnale che la densità di oggetti in quest'area è troppo alta.
Oggetti come decalcomanie, trame e particelle non si aggregano bene e introducono chiamate di disegno aggiuntive. Fai particolare attenzione a questi tipi di oggetti in una scena. In particolare, modifiche alle proprietà di ParticleEmitters possono avere un impatto drammatico sulle prestazioni.
Opportunità di istanza perse - Spesso, una scena includerà la stessa mesh duplicata molte 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 volta, piuttosto che importare singoli asset in Roblox e poi duplicarli dopo l'importazione per assemblare la scena.
Anche uno script semplice 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 come segue. 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 duplicatiEccessiva complessità degli oggetti - Sebbene non sia importante come il numero di chiamate di disegno, il numero di triangoli in una scena influisce su quanto tempo un frame richiede per essere renderizzato. Scenari 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 piccole parti influenzate dalle ombre) possono avere problemi di prestazioni.
Overdraw di trasparenza elevata - Posizionare oggetti con trasparenza parziale vicini tra loro costringe il motore a rendere ripetutamente i pixel sovrapposti, il che può danneggiare le prestazioni. Per ulteriori informazioni su come identificare e risolvere questo problema, vedere Elimina le trasparenze a strati.
Movimento non necessario dei MeshPart con skin - I MeshPart con skin 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 a ricostruire i cluster 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, attenuando così il collo di bottiglia delle prestazioni. Questa tecnica dovrebbe essere riservata esclusivamente ai MeshPart con movimento atteso, poiché può introdurre 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. Vedi Suggerimenti sulle prestazioni degli Humanoid per ulteriori informazioni.
Troppi pezzi in un Model - Troppi pezzi in un modello potrebbero causare ricostruzioni più frequenti a causa della possibilità che la proprietà di un pezzo cambi, portando alla necessità di una ricostruzione completa. Trova il giusto equilibrio di pezzi in un modello quando utilizza FastCluster.
Mitigazione
Istanziare mesh identiche e ridurre il 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 una singola chiamata di disegno. Assicurati di caricare solo ogni mesh in una mappa una volta e di duplicarle in Studio per il riutilizzo piuttosto che importare grandi mappe nel loro complesso, il che potrebbe causare mesh identiche con ID di contenuto separati e essere riconosciute come asset unici dal motore. Pacchetti sono un meccanismo utile per il riutilizzo degli oggetti.
Culling - Il culling descrive il processo di eliminare chiamate di disegno per oggetti che non influenzano il frame finale renderizzato. Per impostazione predefinita, il motore salta le chiamate di disegno per oggetti al di fuori del campo visivo della telecamera (frustum culling) e parti, mesh e terreno occlusi dalla vista da altri oggetti (occlusion culling). In determinate situazioni, come ambienti interni, potresti essere in grado di implementare un sistema di stanze o portali e effettuare manualmente il culling degli oggetti per ridurre ulteriormente le chiamate di disegno o il carico computazionale complessivo.
Ridurre il livello di dettaglio per i modelli - Abilita streaming degli istanti e imposta la proprietà LevelOfDetail dei tuoi modelli mondiali su SLIM per rendere mesh SLIM leggere ottimizzate man mano che aumenta la distanza dalla telecamera.
Ridurre il livello di dettaglio per gli avatar - Abilita il streaming degli istanti e imposta la proprietà Workspace.EnableSLIMAvatars per rendere i modelli dei personaggi delle piattaforme con qualsiasi numero di accessori o strati di abbigliamento come rappresentazioni di avatar leggere ottimizzate con pieno supporto per le animazioni man mano che aumenta la distanza dalla telecamera.
Ridurre la fedeltà del rendering - Imposta MeshPart.RenderFidelity su Automatic o Performance. Questo consente alle mesh di tornare a opzioni meno complesse, che possono ridurre il numero di poligoni da disegnare.
Disabilitare la proiezione delle ombre su pezzi 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 del tutto le ombre a livelli di qualità sotto il 4. Tuttavia, puoi disabilitare selettivamente le proprietà di proiezione delle ombre su oggetti luminosi e pezzi 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 o durante il tempo di modifica o dinamicamente durante l'esecuzione:
Utilizza la proprietà BasePart.CastShadow per disabilitare la proiezione delle ombre su piccoli pezzi in cui le ombre sono improbabili da essere visibili. Questa strategia è particolarmente efficace se applicata a pezzi lontani dalla telecamera dell'utente.
Disabilita le ombre sugli oggetti in movimento quando possibile.
Disabilita Light.Shadows su istanze luminose in cui l'oggetto non ha bisogno di proiettare ombre.
Limita la gittata 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.
Scope del MicroProfiler
| Ambito | Computazione associata |
| Prepare and Perform | Rendering generale |
| Perform/Scene/computeLightingPerform | Aggiornamenti della griglia luminosa e delle ombre |
| LightGridCPU | Aggiornamenti della griglia di voxel luminosa |
| ShadowMapSystem | Mappe delle ombre |
| Perform/Scene/UpdateView | Preparazione per il rendering e aggiornamenti delle particelle |
| Perform/Scene/RenderView | Rendering e post elaborazione |
Networking e replica
Networking e replica 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 in 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 utilizzo di tempo CPU durante l'elaborazione dei pacchetti in ingresso ogni frame. Errori comuni includono:
- Replicare dati ogni frame che non devono essere replicati.
- Replicare dati sull'input dell'utente senza un meccanismo per limitare la frequenza.
- Inviare più dati di quanto necessario. Ad esempio, inviare l'intero inventario del giocatore quando acquistano un oggetto piuttosto che solo i dettagli dell'oggetto acquistato.
Creazione o rimozione di alberi di istanza complessi - Quando viene effettuata una modifica al modello di 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 per la rete.
Un colpevole comune qui è la complessa animazione dei dati salvati dai plugin Animation Editor nelle configurazioni. Se non vengono rimossi prima che il gioco venga pubblicato e il modello animato viene clonato regolarmente, una grande quantità di dati sarà replicata inutilmente.
TweenService lato server - Se TweenService viene utilizzato per tweenare un oggetto sul lato server, la proprietà tweenata viene replicata a ciascun client ogni frame. Non solo ciò comporta un tween scattoso man mano che la latenza dei client fluttua, ma provoca anche un sacco di traffico di rete non necessario.
Mitigazione
Puoi adottare le seguenti tattiche per ridurre la replica non necessaria:
- Evita di inviare grandi quantità di dati tutte in una volta attraverso eventi remoti. Invece, invia solo dati necessari a una frequenza ridotta. Ad esempio, per lo stato di un personaggio, replicalo quando cambia anziché ogni frame.
- Scomponi alberi di istanza 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 delle configurazioni, dopo l'importazione.
- Limita la replica delle istanze non necessaria, specialmente nei casi in cui il server non ha bisogno di conoscere le istanze create. Questo include:
- Effetti visivi come un'esplosione o un attacco magico. Il server deve solo conoscere la posizione per determinare il risultato, mentre i client possono creare visivi localmente.
- Modelli di oggetti in prima persona.
- Tween oggetti sul client piuttosto che sul server.
Scope del MicroProfiler
| Ambito | Computazione associata |
| ProcessPackets | Elaborazione per i pacchetti di rete in ingresso, come le invocazioni di eventi e le modifiche di proprietà |
| Allocate Bandwidth and Run Senders | Eventi in uscita pertinenti sui server |
Utilizzo della memoria degli asset
Il meccanismo con il maggiore impatto a disposizione dei creatori per migliorare l'utilizzo della memoria del client è abilitare lo streaming delle istanze.
Streaming delle istanze
Lo streaming delle istanze carica selettivamente le parti del modello di dati che non sono necessarie, 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 questo. Ad esempio, considera di:
- Ridurre l'uso di Enum.ModelStreamingMode.Persistent dove possibile. Potrebbe essere necessario 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, vedi le proprietà di streaming.
Altri problemi comuni
Duplicazione degli asset - Un errore comune è caricare più volte lo stesso asset risultando in diversi ID di asset. Questo può portare allo stesso contenuto che viene caricato in memoria più volte.
Volume eccessivo di asset - Anche quando gli asset non sono identici, ci sono casi in cui le opportunità di riutilizzare lo stesso asset e risparmiare memoria vengono perse.
File audio - I file audio possono contribuire in modo sorprendente all'uso della memoria, particolarmente se carichi tutti in una volta piuttosto che solo caricare ciò di cui hai bisogno per una parte del gioco. Per strategie, vedere Tempi di caricamento.
Texture ad alta risoluzione - Il consumo di memoria grafica per una texture non è correlato alle dimensioni della texture su disco; il numero di pixel nella texture determina l'uso della memoria. Ad esempio, una texture 1024x1024 pixel consuma quattro volte la memoria grafica di una texture 512x512.
Le immagini caricate su Roblox vengono transcodificate in un formato fisso, quindi non c'è vantaggio di memoria per 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 alpha dalle immagini che non ne hanno bisogno può diminuire la dimensione dell'immagine su disco, ma non migliora l'uso della memoria.
Man mano che un gioco si carica, il motore inizia automaticamente con texture di qualità inferiore e poi aumenta la qualità sulla base della memoria disponibile del dispositivo, della distanza dalla telecamera, della quantità di spazio dello schermo che la texture occupa e altri fattori. Anche così, dimensionare strategicamente le tue texture può migliorare l'uso 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, in particolare mesh e immagini, non vengano caricati separatamente più volte.
Cerca e correggi asset duplicati - Cerca parti mesh e texture identiche che sono state caricate più volte con ID diversi.
- Anche se non c'è un'API per rilevare automaticamente la somiglianza degli asset, puoi raccogliere tutti gli ID degli asset immagine nel tuo posto (manuale o con uno script), scaricarli e confrontarli utilizzando strumenti di confronto esterni.
- Per le parti mesh, la strategia migliore è 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 sfumature.
Importa asset nelle mappe separatamente - Invece di importare un'intera mappa in una volta, importa e ricostruisci gli asset nella mappa individualmente e ricostruiscili. L'Importatore non esegue alcuna deduplicazione delle mesh, quindi se importi una grande mappa con molti piastrelli del pavimento separati, ciascuno di quei piastrelli verrà importato come un asset separato (anche se sono duplicati). Questo può portare a problemi di prestazioni e memoria in seguito, poiché ciascuna mesh viene trattata come un'unità 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 rifinitura per garantire il massimo riutilizzo delle texture nelle mappe 3D. Per passaggi ed esempi su come creare fogli di rifinitura, vedere Crea fogli di rifinitura.
Potresti anche considerare di utilizzare fogli sprite 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
Molti giochi implementano schermate di caricamento personalizzate e utilizzano il metodo ContentProvider:PreloadAsync() per richiedere asset in modo che immagini, suoni e mesh vengano scaricati in background.
Il vantaggio di questo approccio è che ti consente di assicurarti che le parti importanti del tuo gioco siano completamente caricate senza pop-in. Tuttavia, un errore comune è sovrautilizzare questo metodo per pre-caricare più asset di quanti ne siano realmente richiesti.
Un esempio di cattiva pratica è caricare l'intero Workspace. Sebbene ciò possa prevenire il pop-in delle texture, aumenta significativamente i tempi di caricamento.
Un'altra pratica simile è l'utilizzo di ContentProvider.RequestQueueSize per garantire che tutti gli asset richiesti siano stati caricati. Tuttavia, ciò presenta lo stesso problema di un incremento significativo dei tempi di caricamento, risultando anche in un metodo inaffidabile a causa della sua natura fluttuante.
Invece, utilizza ContentProvider:PreloadAsync() solo in situazioni necessarie, che includono:
- Immagini nella schermata di caricamento.
- Immagini importanti nel menu del tuo gioco, come sfondi e icone dei pulsanti.
- Asset importanti nell'area di partenza o di spawning.
Se devi caricare un gran numero di asset, ti consigliamo di fornire un pulsante Salta caricamento.