Questa pagina descrive i comuni problemi di prestazioni e le migliori pratiche per mitigarli.
Calcolo degli script
Operazioni costose in codice Luau richiedono più tempo per essere elaborate e possono quindi influenzare il frame rate. A meno che non vengano eseguite 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 su strutture di tabelle - Operazioni complesse come serializzazione, deserializzazione e clonazione profonda richiedono un alto costo di prestazioni, specialmente su strutture di tabelle grandi. Questo è particolarmente vero se queste operazioni sono ricorsive o comportano l'iterazione su strutture dati molto grandi.
Eventi ad alta frequenza - Legare operazioni costose a eventi basati su frame di RunService senza limitare la frequenza significa che queste operazioni vengono ripetute ad ogni frame, il che spesso si traduce in un aumento non necessario del tempo di elaborazione. Questi eventi includono:
Mitigazione
- Richiama codice sugli eventi di RunService con parsimonia, limitando l'uso a casi in cui è essenziale l'invocazione ad alta frequenza (ad esempio, l'aggiornamento della camera). 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 i compiti computazionalmente costosi che non devono 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.
Scope del MicroProfiler
| Scope | Calcolo associato |
| 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, consultare 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 specificamente pervasivi sul server, perché possono rimanere 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 una perdita di memoria.
- InstanceCount - Numeri di istanze in costante crescita suggeriscono che i riferimenti ad alcune istanze nel tuo codice non vengono raccolti dal garbage collector.
- PlaceScriptMemory - Fornisce una ripartizione dell'uso della memoria script per script.
Problemi comuni
Lasciare le connessioni collegate - Il motore non raccoglie mai eventi collegati a un'istanza e qualsiasi valore referenziato all'interno del callback collegato. Pertanto, le connessioni attive degli eventi e il codice all'interno delle istanze collegate, funzioni collegate e valori referenziati, sono fuori portata per il garbage collector, 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 proprio 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 in tabelle ma non rimuoverli quando non sono più necessari causa un consumo di memoria non necessario, specialmente per tabelle che tracciano i dati degli utenti quando si uniscono. Ad esempio, il seguente esempio 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 dimensioni e consuma più memoria man mano che più utenti si uniscono alla sessione. Qualsiasi codice che itera su questa tabella diventa anche computazionalmente più costoso man mano che la tabella aumenta di dimensioni.
Mitigazione
Per pulire tutti i valori utilizzati per prevenire le 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 gli oggetti e i personaggi dei giocatori dopo che escono - Abilita Workspace.PlayerCharacterDestroyBehavior per distruggere automaticamente oggetti dei giocatori e 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)
Calcolo della 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 del passo è in modalità adattativa, in cui la fisica avviene a 60 Hz, 120 Hz o 240 Hz, a seconda della complessità del meccanismo fisico.
È disponibile anche una modalità fissa con un'accuratezza migliorata della fisica, che forza tutte le assemblee fisiche a eseguire il passo a 240 Hz (quattro volte per frame). Questo comporta un calcolo significativamente maggiore ad ogni frame.
Numero e complessità eccessivi di oggetti simulati - Più assemblee 3D vengono simulate, più a lungo ci vorrà per i calcoli fisici a ciascun frame. Spesso, i giochi avranno oggetti che vengono simulati che non devono esserlo o avranno meccanismi che hanno più vincoli e giunti di quanti ne necessitino.
Rilevamento delle collisioni troppo preciso - I pezzi mesh hanno una CollisionFidelity proprietà per rilevare collisioni che offre una varietà di modalità con diversi livelli di impatto sulle prestazioni. La modalità di rilevamento delle collisioni preciso per i pezzi mesh ha il costo di prestazione più costoso e richiede più tempo per il motore per calcolarlo.
Mitigazione
Ancorare parti che non richiedono simulazione - Ancorare tutte le parti che non devono essere gestite dalla fisica, ad esempio per NPC statici.
Usa passi fisici adattativi - Lo stepping adattivo regola dinamicamente il tasso di calcoli fisici per i meccanismi fisici, permettendo aggiornamenti fisici da effettuare meno frequentemente in alcuni casi.
Ridurre la complessità dei meccanismi
- Dove possibile, minimizza il numero di vincoli o giunti fisici in un'assemblea.
- Riduci la quantità di auto-collisione all'interno di un meccanismo, ad esempio applicando limiti o vincoli di nessuna collisione a arti di ragdoll per prevenire collisioni tra loro.
Riduci l'uso della precisione delle collisioni per le mesh
Per oggetti piccoli o non interattivi in cui gli utenti noterebbero raramente la differenza, usa la fedeltà delle scatole.
Per oggetti di piccole e medie dimensioni, usa fedeltà delle scatole o scocche, a seconda della forma.
Per oggetti grandi e molto complessi, costruisci collisioni personalizzate usando parti invisibili quando possibile.
Per oggetti che non richiedono collisioni, disabilita le collisioni e usa la fedeltà delle scatole o scocche, poiché la geometria delle collisioni è comunque memorizzata nella memoria.
Puoi rendere la geometria delle collisioni per scopi di debug nello Studio attivando Collision fidelity dal widget Options di visualizzazione nell'angolo in alto a destra del viewport 3D.
In alternativa, puoi applicare il filtro CollisionFidelity=PreciseConvexDecomposition all'Explorer che mostra un conteggio di tutti i pezzi mesh con la fedeltà precisa e ti consente di selezionarli facilmente.
Per un approfondimento su come scegliere un'opzione di fedeltà delle collisioni che bilanci i tuoi requisiti di precisione e prestazioni, consultare Imposta parametri fisici e di rendering.
Scope del MicroProfiler
| Scope | Calcolo associato |
| physicsStepped | Calcolo fisico complessivo |
| worldStep | Passi fisici discreti eseguiti ad ogni frame |
Utilizzo della memoria della 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 fedeltà inferiore.
Se vedi alti livelli di consumo di memoria sotto PhysicsParts, potrebbe essere necessario esplorare la possibilità di ridurre la fedeltà 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 minor sovraccarico di memoria, mentre Default e Precise sono generalmente più costosi.
- È generalmente sicuro impostare la fedeltà di collisione di qualsiasi parte piccola ancorata 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 giocatori e ai personaggi non giocanti (NPC). Anche se potente, un Humanoid comporta un costo di calcolo significativo.
Problemi comuni
- Lasciare attivi tutti i HumanoidStateTypes negli NPC - C'è un costo di prestazione per mantenere attive determinate HumanoidStateTypes. Disabilita quelle che non sono necessarie per i tuoi NPC. Ad esempio, a meno che il tuo NPC non debba arrampicarsi sulle scale, è sicuro disabilitare lo stato Climbing.
- Instanziamento, modifica e respawn di modelli con Humanoids o skinned MeshParts frequente
- Questo può essere impegnativo per il motore da elaborare, particolarmente se questi modelli utilizzano veste a strati. Questo può anche essere particolarmente problematico nei giochi in cui gli avatar respawnano frequentemente.
- Nel MicroProfiler, tag updateInvalidatedFastClusters lunghi (oltre 4 ms) sono spesso un segnale che l'instanziamento/modifica dell'avatar sta attivando invalidazioni eccessive.
- Usare Humanoids in casi in cui non sono richiesti - Gli NPC statici che non si muovono generalmente non hanno bisogno della classe Humanoid.
- Riprodurre 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ò comportare un carico eccessivo non necessario.
- Eseguire modifiche non necessarie a dimensioni e scale - Cambiamenti di dimensione/scala causano la ricostruzione del FastCluster. Cerca di ridurre ciò durante il gameplay se stai riscontrando problemi di prestazioni legati al FastCluster. Allo stesso modo, altre modifiche delle proprietà potrebbero anche causare la ricostruzione del FastCluster, quindi in generale cerca di ridurre questi cambiamenti 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 far funzionare le animazioni localmente. Questo riduce il carico sul server e la necessità di replica non necessaria. Consente anche ottimizzazioni ulteriori (come riprodurre animazioni solo per NPC che sono vicini al personaggio).
- Usa alternative a basso costo per i Humanoids - I modelli NPC non devono necessariamente contenere un oggetto umanoide.
- Per NPC statici, usa un semplice AnimationController, perché non devono muoversi, ma devono solo riprodurre animazioni.
- Per NPC in movimento, considera l'implementazione del tuo controller di movimento e l'utilizzo di un AnimationController per le animazioni, a seconda della complessità dei tuoi NPC.
- Disabilita stati umanoidi inutilizzati - Usa Humanoid:SetStateEnabled() per abilitare solo stati necessari per ciascun umanoide.
- Pool dei modelli NPC con respawn frequente - Invece di distruggere un NPC completamente, 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 di avatar hanno implicazioni di prestazioni significative. Sono disponibili alcune ottimizzazioni:
- Per animazioni procedurali personalizzate, non aggiornare le proprietà JointInstance.C0 e JointInstance.C1. Invece, aggiorna la proprietà Motor6D.Transform.
Scope del MicroProfiler
| Scope | Calcolo associato |
| stepHumanoid | Controllo e fisica humanoidi |
| stepAnimation | Animazione humanoidi e animatori |
| updateInvalidatedFastClusters | Associato all'instanziamento o modifica di un avatar |
Rendering
Una percentuale significativa del tempo che il client trascorre ogni frame è dedicata al rendering della scena nel frame attuale. 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 ci sono per frame, meno tempo computazionale viene speso a rendere un frame.
Puoi vedere quante chiamate di disegno si stanno attualmente verificando 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, più chiamate di disegno vengono fatte al GPU. Tuttavia, il motore Roblox utilizza un processo chiamato instancing per ridurre a un'unica chiamata di disegno le mesh identiche con le stesse caratteristiche di texture. Nello specifico, più mesh con lo stesso MeshContent vengono gestite in un'unica chiamata di disegno quando:
- SurfaceAppearances sono identiche se presenti, altrimenti quando TextureContents sono identici.
- I materiali sono identici quando sia SurfaceAppearance che MeshPart.TextureID non esistono.
Altri problemi comuni
Densità eccessiva degli oggetti - Se un gran numero di oggetti è concentrato con un'elevata densità, allora il rendering di quest'area della scena richiederà 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 chiamate di disegno aggiuntive. Presta 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 instancing mancate - Spesso, una scena includerà la stessa mesh duplicata molte volte, ma ogni copia della mesh ha ID di risorsa differenti. Questo impedisce l'instancing e può portare a chiamate di disegno non necessarie.
Una causa comune di questo problema è quando un'intera scena viene importata tutta insieme, anziché che singoli asset vengano importati in Roblox e successivamente duplicati dopo l'importazione per assemblare la scena.
Anche uno script semplice come questo può aiutarti ad identificare i pezzi 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 - Anche se non così importante come il numero di chiamate di disegno, il numero di triangoli in una scena influisce sul tempo necessario per renderizzare un frame. Scene con un numero molto elevato di mesh molto complesse sono un problema comune, così come scene con la proprietà MeshPart.RenderFidelity impostata su Precise su troppe mesh.
Proiezione delle ombre eccessiva - 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.
Sovrapposizione di trasparenze alte - Collocare oggetti con trasparenza parziale vicini l'uno all'altro costringe il motore a renderizzare più volte i pixel sovrapposti, il che può danneggiare le prestazioni. Per ulteriori informazioni su come identificare e risolvere questo problema, vedere Elimina trasparenze stratificate.
Movimento non necessario di MeshPart skinnati - I MeshParts skinnati che fanno parte di un Modello senza un Humanoid vengono raggruppati utilizzando FastClusters organizzati spazialmente. Quando questi MeshParts si muovono, devono essere continuamente aggiunti e rimossi da questi cluster spaziali, costringendo i cluster a essere ricostruiti e impattando sulle 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 unico e unificato FastCluster per l'intero Modello. Di conseguenza, gli aggiornamenti di posizione non richiedono più ricostruzioni del cluster, mitigando così il collo di bottiglia delle prestazioni. Questa tecnica dovrebbe essere riservata esclusivamente a MeshParts con movimento previsto, poiché potrebbe introdurre sovraccarico di memoria e annullare i benefici dell'ottimizzazione spaziale. Ti consigliamo di sempre profilare il tuo gioco dopo aver effettuato queste modifiche. Consulta Suggerimenti sulle prestazioni degli umani per ulteriori informazioni.
Troppi pezzi in un Model - Troppi pezzi in un Modello potrebbero comportare ricostruzioni più frequenti a causa del potenziale cambiamento di una proprietà di un pezzo che richiede una ricostruzione completa. Trova il giusto equilibrio di pezzi in un Modello quando si utilizza FastCluster.
Mitigazione
Instanziare mesh identiche e ridurre il numero di mesh uniche - Se ti assicuri che tutte le mesh identiche abbiano gli stessi ID di risorsa sottostanti, il motore può riconoscerle e renderizzarle in un'unica chiamata di disegno. Assicurati di caricare ciascuna mesh in una mappa una sola volta e poi duplicarle in Studio per il riutilizzo anziché importare grandi mappe in una volta, il che potrebbe far sì che mesh identiche abbiano ID di contenuto separati e vengano 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 reso finale. Per impostazione predefinita, il motore salta le chiamate di disegno per oggetti al di fuori del campo visivo della camera (frustum culling) e per porzioni, mesh e terreni occlusi dalla vista da altri oggetti (occlusione culling). In determinate situazioni, come ambienti interni, potresti essere in grado di implementare un sistema di stanze o portali e disabilitare manualmente gli 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 camera.
Riduzione del livello di dettaglio per gli avatar - Abilita il streaming delle istanze e imposta Workspace.EnableSLIMAvatars per rendere gli avatar della piattaforma come rappresentazioni SLIM leggere ottimizzate con supporto completo per l'animazione man mano che aumenta la distanza dalla camera.
Riduzione della fedeltà di rendering - Imposta MeshPart.RenderFidelity su Automatic o Performance. Questo consente alle mesh di passare a opzioni 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 fare o in fase di modifica o dinamicamente durante il runtime:
Usa la proprietà BasePart.CastShadow per disabilitare la proiezione delle ombre su parti piccole dove le ombre probabilmente non saranno visibili. Questa strategia è particolarmente efficace quando applicata a parti che sono lontane dalla camera dell'utente.
Disabilita ombre sugli oggetti in movimento quando possibile.
Disabilita Light.Shadows sulle istanze luminose dove l'oggetto non deve proiettare ombre.
Limita la portata e l'angolo delle istanze luminose.
Usa meno istanze luminose.
Prendi in considerazione la disabilitazione delle luci che si trovano al di fuori di una certa portata o su base stanza per stanza per ambienti interni.
Scope del MicroProfiler
| Scope | Calcolo associato |
| Prepare and Perform | Rendering complessivo |
| Perform/Scene/computeLightingPerform | Aggiornamenti della griglia luminosa e delle ombre |
| LightGridCPU | Aggiornamenti della griglia luminosa voxel |
| ShadowMapSystem | Shadow mapping |
| Perform/Scene/UpdateView | Preparazione per il rendering e aggiornamenti delle particelle |
| Perform/Scene/RenderView | Rendering e post processing |
Rete e replica
Rete e replica descrivono il processo tramite 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 grandi quantità di informazioni richiedono più tempo di calcolo.
Problemi comuni
Traffico remoto eccessivo - Inviare un gran numero di dati tramite gli oggetti RemoteEvent o RemoteFunction o invocarli molto frequentemente può portare a un gran numero di tempo CPU speso a elaborare pacchetti in ingresso ad ogni frame. Errori comuni includono:
- Replicare dati ogni frame che non necessitano di essere replicati.
- Replicare dati all'input dell'utente senza alcun meccanismo per limitarli.
- Dispatchare più dati del 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 istanza complessi - Quando si effettua una modifica al modello dati sul server, viene replicato ai client connessi. Ciò significa che la creazione e la distruzione di grandi gerarchie di istanze come mappe durante il runtime può essere molto intensiva dal punto di vista della rete.
Un colpevole comune qui è il complesso dato di animazione salvato dai plugin Animation Editor nei rig. Se questi non vengono rimossi prima di pubblicare il gioco e il modello animato viene clonato frequentemente, verrà replicata molta quantità di dati non necessaria.
TweenService lato server - Se TweenService viene utilizzato per tweenare un oggetto lato server, la proprietà tweenata viene replicata a ciascun client ad ogni frame. Non solo ciò provoca un movimento traballante mentre latenza dei client fluttua, ma causa anche un elevato traffico di rete non necessario.
Mitigazione
Puoi adottare le seguenti tattiche per ridurre la replicazione non necessaria:
- Evita di inviare grandi quantità di dati in una volta tramite eventi remoti. Invece, invia solo dati necessari a una frequenza inferiore. Ad esempio, per lo stato di un personaggio, replicalo quando cambia piuttosto che ad ogni frame.
- Frammenta alberi di istanza complessi come mappe e caricali in pezzi per distribuire il lavoro di replicazione su più frame.
- Pulire i metadati delle animazioni, in particolare la directory delle animazioni dei rig, dopo l'importazione.
- Limitare la replicazione di istanze non necessarie, soprattutto nei casi in cui il server non ha bisogno di avere conoscenza delle istanze create. Questo include:
- Effetti visivi come un'esplosione o un'esplosione di 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 di oggetti in prima persona.
- Tweenare oggetti sul client anziché sul server.
Scope del MicroProfiler
| Scope | Calcolo associato |
| ProcessPackets | Elaborazione di pacchetti di rete in arrivo, come invocazioni di eventi e modifiche alle proprietà |
| Allocate Bandwidth and Run Senders | Eventi in uscita pertinenti ai server |
Utilizzo della memoria degli asset
Il meccanismo con il maggiore impatto disponibile ai 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 considerevolmente ridotti e aumentare la capacità del client di prevenire i crash quando viene sottoposto a pressione di memoria.
Se stai riscontrando problemi di memoria e hai disabilitato lo streaming delle istanze, considera di aggiornare il tuo gioco per supportarlo, specialmente se il tuo mondo 3D è grande. Lo streaming delle istanze è basato sulla distanza nello spazio 3D, quindi mondi più grandi beneficiano naturalmente di più.
Se lo streaming delle istanze è abilitato, puoi aumentarne l'aggressività. Ad esempio, considera di:
- 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, vedere proprietà di streaming.
Altri problemi comuni
Duplicazione degli asset - Un errore comune è caricare lo stesso asset più volte risultando in ID di asset differenti. Questo può portare a caricare lo stesso contenuto 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 in una volta nel client piuttosto che solo ciò che ti serve per una porzione del gioco. Per strategie, vedere 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'uso della memoria. Ad esempio, una texture da 1024x1024 pixel consuma quattro volte la memoria grafica di una texture da 512x512 pixel.
Le immagini caricate su Roblox vengono ricodificate in un formato fisso, quindi non c'è alcun beneficio di memoria dal caricare immagini in un modello di colore associato a un numero inferiore di byte per pixel. Allo stesso modo, comprimere 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.
Quando un gioco viene caricato, il motore inizia automaticamente con texture a bassa qualità e poi aumenta la qualità in base alla memoria disponibile del dispositivo, alla distanza dalla camera, alla quantità di spazio su schermo che occupa la texture 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 ripara asset duplicati - Cerca parti mesh identiche e texture che vengano caricate più volte con ID differenti.
- Sebbene non esista una API per rilevare automaticamente la somiglianza degli asset, puoi raccogliere tutti gli ID di asset immagine nel tuo luogo (manuale o con uno script), scaricarli e confrontarli usando 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à ad essa.
Importa gli asset nella mappa separatamente - Invece di importare un'intera mappa tutta insieme, importa e ricostruisci gli asset nella mappa individualmente e ricostruiscili. L'Importer non fa alcuna deduplicazione delle mesh, quindi se dovessi importare una grande mappa con un sacco di piastrelle di pavimentazione separate, ognuna di quelle piastrelle verrebbe importata come un asset separato (anche se sono duplicati). Questo può portare a problemi di prestazioni e di memoria lungo il percorso, poiché ogni mesh viene trattata come un'istanza 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 un massimo riutilizzo delle texture nelle mappe 3D. Per passaggi ed esempi su come creare fogli di ritaglio, vedere Crea fogli di ritaglio.
Potresti anche considerare di utilizzare fogli di sprite per caricare molte immagini UI più piccole come un'immagine singola. 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 parti importanti del tuo gioco siano completamente caricate senza pop-in. Tuttavia, un errore comune è sovrautilizzare questo metodo per pre-caricare più asset di quanto siano effettivamente necessari.
Un esempio di cattiva pratica è caricare l'intero Workspace. Anche se questo potrebbe prevenire il pop-in delle texture, aumenta notevolmente i tempi di caricamento.
Una pratica simile consiste nell'utilizzare ContentProvider.RequestQueueSize per garantire che tutti gli asset richiesti siano stati completati nel caricamento. Tuttavia, ciò presenta lo stesso problema di un aumento significativo dei tempi di caricamento, pur essendo anche un metodo non affidabile 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 respawn.
Se devi caricare un gran numero di asset, ti consigliamo di fornire un pulsante Salta caricamento.