這個頁面說明了常見的性能問題和最佳實踐方法來解決它們。
腳本計算
在 Lua 代碼中的高價運作需要更長的處理時評分,因此可能會導致延遲。除非它被並行執行,否則 Lua 代碼會同步執行並且直到遇到輸入子線程,直到它們能夠輸出子執行緒。
常見問題
對表結構的激烈操作 - 複雜操作,例如 serialization、 deserialization 和深度複製會導致高額外成本,尤其是在大型表結構上。這是特別的,如果這些操作是重複的或涉及重大數據結構。
高頻度事件 - 將高價值的操作綁定到 RunService 的框架基礎事件,而不是限制這些操作每個框架重複的頻率,這通常會導致不必要的增加計算時間。這些事件包括:
減少
- 在 RunService 事件上,僅在有高頻率召用需要的情況下邀請代碼。你可以在其他事件或更少的頻率執行大多數其他代碼。
- 使用 task.wait() 來分解大型或昂貴的任務,以將工作擴展到多個框架。
- 識別並最佳化不必要的高價操作,並且使用 多線程 以對於不需要存取資料模型的計算價值較高的任務。
- 某些服務器端的指令碼可以從原始碼生成中受益,這是一種簡單的旗幟,可以將指令碼變成機器代碼而不是原始碼。
微型螺絲分析器瞄準器
範圍 | 聯合計算 |
RunService.PreRender | 在預處理事件上執行代碼 |
RunService.PreSimulation | 階段事件上的代碼執行 |
RunService.PostSimulation | 心跳事件上的代碼執行 |
RunService.心跳 | 心跳事件上的代碼執行 |
有關使用 MicroProfiler 來調試指定的代碼的更多資訊,請參閱 debug 資料庫,其包括標記特定代碼並進一步提升特定性的功能,例如 debug.profilebegin 和 debug.profileend。許多 Roblox API 方法的名稱也有自己的微型處理器標籤,
指令碼記憶體使用
記憶體漏水可能發生當你寫下使用垃圾收集器無法正確釋放的記憶體,這會導致垃圾收集器無法正常釋放。漏水會在伺服器上發生,因為它們可以持續在線上幾天,而不是客戶端的會話。
開發者控制器 中的以下記憶體值可以指示需要進一步調查的問題:
- Lua堆 - 高或增長的使用率表示記憶體漏水。
- InstanceCount - 一致地增加實例數量表示有些實例在您的代碼中沒有收集垃圾。
- PlaceScriptMemory - 提供記錄記憶體使用率的指標。
常見問題
離開已連接的連接 - 引擎永遠不會收集連接到實例和任何內部參考的事件。因此,連接到連接的事件和代碼內的任何值都超出內存垃圾收集器的範圍,即使發生了事件。
雖然當實例與屬性對應的時候會斷開連接,但在實例被摧毀時,一般的錯誤是假設此應用程式對應的 Player 對象。在使用者離開體�
桌子 - 將對象插入桌子,但不會移除它們,如果他們不再需要時,會導致不必要的內存使用,尤其是跟蹤用戶數據時,當用戶重新加入時。 例如,下列代碼示例創建了一個桌子,添加用戶信息每次用戶加入:
範例local playerInfo = {}Players.PlayerAdded:Connect(function(player)playerInfo[player] = {} -- 一些資訊end)如果您不再需要這些項目,表將繼續擴大,並且會在更多用戶加入會作業時導致更多記憶體使用。任何與此表相關的代碼也會變得更貴重,因為表會隨著大小增長。
減少
要清除所有用於防止記憶體漏水的值:
切斷所有連接 - 穿過您的代碼基地確認每個連接是否清潔過程中的一個絕對路徑:
- 使用 Disconnect() 功能手動連接。
- 使用 Destroy() 函數摧毀事件所屬的實例。
- 摧毀連接追蹤回來的指令碼對象。
離開後移除玩家對象和角色 - 實現程式碼,確保使用者離開後不會有連線保持,像在下列範例中:
範例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)
物理學計算
過度的物理模擬可能是導致服務器和客戶端每個框架的增算時間的主要原因。
常見問題
超出物理時間步驟頻率 - 預設值是,步驟行為是在 適應模式 中,其中物理步驟在 60 Hz、120 Hz 或 240 Hz,這取決於物理機制的複雜程度。
還有一個固定模式,其準確度改善了物理學,這使所有物理學組合步驟為 240 Hz (每個幀率四次)。這導致每個幀率的計算量增加。
複製模擬對象的複製數量過多 - 複製的 3D 數量越多,則會需要更長的物理計算時間。經常發生體驗會有不需要或會有機制的體驗。
過度精確的衝突偵測 - 網格零件有 CollisionFidelity 屬性,用於偵測偶爾發生的衝突,並且提供各種模式以及不同的性能影響。精確的衝突偵測模式對網格零件的成本最高,並且需要更長的時間來計算。
減少
錨定不需要模擬的零件 錨定所有不需要被物理駕駛的零件,例如靜態 NPC。
使用適應性物理步驟 - 適應性物理步驟會動態調整物理計算的速度,以便能在某些情況下讓物理更新變得更少。
減少機器複雜性 * 在能力範圍內,盡量減少裝配中的物理限制或關係。
- 在機制中減少自衝突的數量,例如通過應用限制或非衝突約束來限制撕裂人偶關節以防止它們之間的衝突。
減少網格的精確碰撞準確度 * 對於小型或非互動物品,使用箱子�idelity,以便用戶難以察覺到區別。
對於小型至中型物件,請使用箱子或船體穿真度,取決於形狀。
對於大型、非常複雜的物件,建議您使用可見零件,當可能。
對於不需要衝突的對象,請停用衝突和使用箱子或船體穩定性,因為衝突 геометrie仍在記憶體中儲存。
要了解如何選擇一個能夠平衡您的精準度和性能需求的衝突穿越選項,請參閱 設定物理學和渲染參數。
微型螺絲分析器瞄準器
範圍 | 聯合計算 |
物理步驟 | 整體物理計算 |
世界步驟 | 每個框架採用的粒子物理步驟 |
物理學記憶體使用
物理學運動和衝突偵測會消耗記憶體。網格零件有一個 CollisionFidelity 屬性,它決定使用什麼方法來評估網格的衝突限制。
常見問題
預設和精準的碰撞偵測模式消耗的記憶體比兩個低靈敏度的碰撞偵測模式少得多。
如果您在 PhysicsParts 下看到高消耗記憶體的水平,您可能需要探索減少對象在您的體驗中的 碰撞穩定性。
如何處理
要減少衝突準確度所使用的記憶體:
- 使用 CollisionFidelity 設定來減少碰撞的靈活度。 Box 有最低的內存擁有量,而 Default 和 1>Enum.CollisionFidel
- 一般來說,將任何小型錨定零件的衝突靈敏度設為 Box 。
- 對於非常複雜的大型網格,您可能需要從較小的對象中建立自己的衝突網格,以獲得更高的穩定性。
人形怪物
Humanoid 是一個提供玩家和非玩家角色(NPC)的豐富功能的類別。雖然 Humanoid 很強大,但是來自 Class.Humanoid 的代幣也會花費重大的計算成本。
常見問題
- 將所有人形狀類型啟用在 NPC 上 - 離開某些 HumanoidStateTypes 的時候會有性能成本。停用任何不需要的 Climbing 狀態。 例如,除非您的 NPC 正在攀爬梯子,否則就沒有必要啟用0>
- 使用人形模型來啟動、修改和重生模型 часто * 這可能會對引擎造成嚴重壓力,特別是如果這些模型使用 分層服裝 。這也可能會在體驗中經常重生的虛擬人偶中尤發突出。
- 在 MicroProfiler 中,長度為 UpdateInvalidatedFastClusters 標籤 (超過 4 毫秒) 通常是指示頭像即時化/修改正在導致過度無效化的信號。
- 在不需要人形的情況下使用人形在遊戲中使用人形的 NPC - 靜態 NPC,通常不需要 Humanoid 類。
- 在服務器上播放大量 NPC 動畫 - 服務器上執行的 NPC 動畫需要在服務器上模擬並重複到客戶端。這可能是不必要的額外延遲。
減少
- 在客戶端上播放 NPC 動畫 - 在有大量 NPC 的體驗中,請考慮在客戶端上創建 Animator 並且在本地執行動畫。這減少服務器的負載,並且提供更少的複製需求。它也可以啟用額外的最佳化(例如只對靠近角色的 NPC 播放動�
- 使用友善人形的替代方案來使用人形。 - NPC 模型不必須包含人形對物件。
- 對於靜態 NPC,使用一個簡單的 AnimationController ,因為他們不需要移動,但只需要播放動畫。
- 對於移動 NPC,請考慮實現自己的移動控制器並使用 AnimationController 為動畫,視乎 NPC 的複雜程度。
- 停用未使用的人形狀態 - 使用 Humanoid:SetStateEnabled() 來僅為每個人形狀態啟用必要狀態。
- 池 NPC 模型有頻繁重生 - 而不是將 NPC 完全摧毀,將 NPC 發送到一個空閒 NPC 池。這樣,當新 NPC 需要重生時,您可以僅僅重新啟用池中的一個 NPC。此過程稱為池畔,這會最大限度地降低角色需要即時重生的次數。
- 僅當玩家在附近時生成 NPC - 不要在玩家不在範圍內時生成 NPC,並且在玩家離開範圍時清除 NPC。
- 避免在啟動後對虛擬人偶階級進行變更 - 對虛擬人偶階級進行的某些變更會導致性能上的重大影響。一些最佳化選項可用:
- 如果您需要為虛擬人偶添加任何 BasePart 物件,請在虛擬人偶的階層 Model 外進行添加。
微型螺絲分析器瞄準器
範圍 | 聯合計算 |
步驟人形 | 人形控制和物理學 |
步驟動畫 | 人形和動畫師動畫 |
更新無效的快速群組 | 與啟動或修改虛擬人偶有關 |
渲染
客戶端在每個額欄框中的時間的大部分是在渲染目前框中的場景。服務器不進行任何渲染,因此此區域專屬於客戶端。
畫制呼叫
從引擎到GPU渲染物時,這是一個從引擎到GPU的一系列指令。 從圖紙到圖片的時間,一般來說,越少有圖紙,越少的計算時間。 通常,越少有圖紙,越少的圖片,越少的計算時間。
您可以看到 Studio 中目前發生的畫眉呼叫數量,並且在 渲染統計資料 > 計時 項目中查看時間。您可以在客戶端按下 Shift 1>F21> 來查看 4>渲染統計資料4>。
您在指定的框架中需要繪製的物件越多,繪製呼叫就越多。但是,Roblox 引擎會使用名為 instancing 的過程來協調不同的材質特性集成到一個繪製呼叫中。 特別是,當多個材質特性集成到一個繪製呼叫中時,當 MeshId 為基礎。
- SurfaceAppearances 和 TextureIDs 相同。SurfaceAppearance 不存在時,0> Class.MeshPart.TextureID|TextureIDs0> 相同。
- 材料相同,當 SurfaceAppearance 和 MeshPart.TextureID 不存在。
其他常見問題
過度物體密度 - 如果物體密度集中在高密度,則需要更多的圖像樣本才能渲染此區域的場景。如果您正在查看特定區域的地圖時發現您的幀率下降,這可能是一個好的信號,表示物體密度在這個區域太高。
像貼紙、紋理和粒子一樣的對象不能很好取得批量,並且介紹額外的畫眉呼叫。 在場景中特別小心這些對象類型。 特別是屬性變更到 ParticleEmitters 可能會導致履約。
錯過建立機會 - 常常,場景會包含一個重複的網格,但每個網格的複製擁有不同的網格或材質資產 ID。這會導致錯誤的建立,並且可能會導致不必要的圖像呼叫。
這個問題的常見原因是,當整個場景一次匯入時,而不是個別資產匯入到 Roblox 並在此後重複以組合場景。
過度的對象複雜性 - 雖然不是數量的畫喱呼叫數量,但場景中的三角形數量會影響圖像的渲染時間。有很大數量的複雜網格的場景是一個常見的問題,因為有很多網格的場景設有 MeshPart.RenderFidelity 屬性值
過度暗影投射 - 處理暗影是一個費時的過程,而包含大量和密度光對投射陰影(或暗影影響的小部件)的地圖可能會發生性能問題。
高透明度畫層撤回機制 - 放置零件靠近對方會強制引擎重複相同的畫素,這可能會導致履約降低。為了識別並修復此問題,請參閱 刪除層次畫透明度。
減少
- 建立相同的網格並減少網格的數量,以便引擎可以在單個網格上輸入相同的資產 ID - 如果您確保所有相同的網格都擁有相同的基礎資產 ID ,引擎就可以在單個調用中認識並渲染它們。務必只在地圖上
- 減少渲染粒度 - 將渲染粒度設定為 自動 或 性能 。這允許網格回歸到更簡單的替代方案,從而減少需要畫畫的畫眉數量。
- 在適當的零件和光物上停用暗影投射機制 - 在光物和零件上選擇地禁用暗影投射機制可以降低複雜的暗影在場景中的影響。這可以在執行階段時或動態時執行。一些範例是:
使用 BasePart.CastShadow 屬性來禁用陰影投射在小零件上,陰影通常不會在用戶鏡頭很遠處的地方可見。這可能是一種特別有效的方法,僅適用於遠離用戶攝影機頭的零件。
移動時啟用陰影。
在 Light.Shadows 上停用,以便在光源實例中不需要拋影。
限制光照實例的範圍和角度。
使用少於一個光照實例。
微型螺絲分析器瞄準器
範圍 | 聯合計算 |
準備並執行 | 整體渲染 |
執行/場景/計算燈光演出 | 光網和暗影更新 |
光網路電腦 | 復素光網更新 |
暗影地圖系統 | 暗影映射 |
執行/場景/更新視圖 | 準備渲染和粒子更新 |
演出/場景/渲染視圖 | 渲染和貼圖處理 |
網路與複製
網路和複製描述資料在服務器和連接的客戶端之間的傳輸過程。 資訊在客戶端和服務器之間每個框架中發送,但大量資訊需要更多的計算時間。
常見問題
發送大量資料通過 Class.RemoteEvent 或 Class.RemoteFunction 對象或呼叫它們非常頻繁來導致大量的 CPU 時間被花在處理每個框架中的來自端的交通上。一些常見的錯誤包括:
每個不需要複製的框架重複資料。
沒有任何機制可以限制它的輸入上的數據。
發送超過所需的資料。例如,發送玩家的整個道具欄,當他們購買一個道具而不是只是購買道具的詳細資料。
創建或移除複雜的案例樹 - 當服務伺服器上的資料模型發生變更時,它會被重複到連接的客戶端。這意味著在執行時創建和摧毀大型案例階層,例如在地圖上的地圖,可能會導致網路堵塞。
這裡的常見惡意製造者是動畫編輯器 插件在陰影中儲存的複雜動畫資料。如果這些資料未在遊戲發佈之前被移除,並且動畫模型正常複製,大量資料將無法必要地重複。
服務器端 TweenService - 如果 TweenService 用於斷片對象服務器側,則斷片的屬性會在每個客戶端每個框架中重複。這不僅會導致斷片在客戶端的延遲變化,而且還會導致不必要的網路交通。
減少
您可以使用以下方法來減少不必要的複製:
- 避免一次傳送大量資料通過遠程事件 。 相反,只要發送必要的資料在更低的頻率。 例如,對於一個角色的狀態,在更改幾個幀而不是每個幀。
- 像地圖一樣將複雜的實例樹砌成塊,並將它們分片來在多個框架上分配工作複製這些工作。 * 清除動畫資料庫的動畫導覽屬性,尤其是對陰船的動畫導覽屬性。 ,在匯入後。
- 限制不必要的實例複製 ,例如在服務器不需要知道複製的實例是否為知識。這包括:
- 視覺效果,例如爆炸或魔法法術爆炸。服務器只需知道位置,而客戶端可以在本地創建視覺。
- 第一人稱物品視圖模型。
- 在客戶端上擁有更多的對象,而不是服務伺服器上。
微型螺絲分析器瞄準器
範圍 | 聯合計算 |
處理包 | 處理網路上來的網路包裹,例如事件邀請和屬性變更 |
分配帶寬並執行發射器 | 與服務器相關的出陣事件 |
資產記憶體使用
可對創作者提供的最高影響機制來改善客戶端記憶體使用率是啟用實例串流。
實例流媒
實例串流會選擇地載入資料模型中不需要的零件,這可能會導致較低的載入時間和提升客戶端在記憶體壓力下來處理崩溃時的能力。
如果您遇到記憶體問題並且停用了實例串流,請考慮更新您的體驗來支持它,特別是如果您的 3D 世界是大的。實例串流基於 3D 空間的距離,因此大型世界通常會從它中獲得更多好處。
如果啟用了實例串流,您可以提升它的攻擊性。例如:
- 減少使用持久的 StreamingIntegrity 。
- 縮小播放範圍。
了解有關流媒體選項和其優惠,請參閱 流媒體屬性。
其他常見問題
- 資產複製 - 一個常見的錯誤是上傳相同的資產多次導致不同的資產 ID。這可能會導致相同的內容被載入到記憶體幾次。
- 過度資產容量 - 即使資產不相同,但在重用相同資產和節省記憶體時也會發生機會錯過重用相同資產並儲存記憶體。
- 音訊檔案 - 音訊檔案可能是記憶體使用的驚人貢獻者,特別是如果您將它們全部載入到客戶端一次而不是只是載入您需要的一部分體驗。對於戰略,請參閱載入時間。
- 高解析度的材質 - 材質的圖形記憶體消耗與材質的大小無關,而是與紋理的像素數量有關。
- 舉例來說,1024x1024 像素的材質會消耗 512x512 的紋理的四倍的圖形記憶體。
- Roblox 上傳的圖像以固定格式傳輸,因此沒有上傳圖像在一個縮寫格式中帶來的記憶體好處。 同樣,在上傳圖像之前壓縮圖像或移除 alpha 通道可以減少圖像大小在硬碟上,但是沒有改善或只有最小限度地提高記��
- 您可以在 開發者控制 中擴展 圖形 類別來識別特定的圖形內存消耗。
減少
- 只有一次上傳資產 - 重用相同的資產 ID 在對象上,並確保網格和圖像等資產不會在多個對象上傳。
- 尋找並修復重複的資產 - 尋找類似的網格零件和材質,以及上傳於不同 ID 的重複資產。
- 雖然沒有 API 可以自動偵測資產類似性,但您可以在您的位置收集所有圖像資產 ID (手動或使用指令碼),下載它們並使用外部比較工具來比較它們。
- 對於網格零件,最佳策略是取得獨特的網格 ID 並按大小排序來手動識別重複。
- 而不是使用單獨的材質為不同的顏色,上傳單一的材質並使用 SurfaceAppearance.Color 屬性來應用各種顏色。
- 在地圖上分別導入資產和重建資產 - 而不是要在一次導入整個地圖,會在地圖上分別導入和重建資產。 3D 匯入器不會對網格進行任何複製,因此如果您導入大型地圖並且有很多個獨立的地板
- 將圖像的像素限制在最多可能的程度內 ,直到不需要。除非圖像佔用屏幕上的大量空間,否則通常需要最多 512x512 像素。最小的圖像通常應小於 256x256 像素。
- 使用 Trim Sheets 以確保 3D 地圖的最大重用材料。對於步驟和示例,如何創建 Trim Sheets,請參閱 創建 Trim Sheets 。
載入次數
許多體驗使用自訂載入屏幕,並使用 ContentProvider:PreloadAsync() 方法來請求資源,以便圖像、聲音和網格在後台下載。
這種方法的優點是,它讓您可以確認您的體驗重要部分已經載入完畢,而不需要 pop-in。 但是,一個常見的錯誤是使用此方法來預加載更多資產,而不是實際需要的。
一個壞習的例子是載入 整個Workspace 。雖然這可能會防止紋理 pop-in,但它會大大提升載入時間。
相反,只有 ContentProvider:PreloadAsync() 在必要的情況下使用,這包括:
- 載入屏幕上的圖像。
- 在您的體驗選單中,匯入按鈕背景和圖示等重要圖像。
- 在開始或重生區域中重要資產。
如果您必須載入大量資產,我們建議您提供一個 跳過載入 按鈕。