提高效率

*此內容是使用 AI(Beta 測試版)翻譯,可能含有錯誤。若要以英文檢視此頁面,請按一下這裡

此頁面描述了常見的性能問題和降低它們的最佳方法。

腳本運算

在 Luau 代碼中的昂貴操作需要更長的處理時間,因此可能會影響幀率。除非並行執行,否則 Luau 代碼會同步運行並阻塞主線程,直到遇到產生線程的函數。

常見問題

減災

  • RunService 事件上輕量呼叫代碼,限制使用情況到需要高頻率呼叫的情況(例如,更新相機)。您可以在其他事件中執行大多數其他代碼,或在迴圈中較少執行。
  • 使用 task.wait() 來分解大或昂貴的任務,將工作分散到多個框架。
  • 識別並最佳化不必要昂貴的操作,並使用 多線程 來計算昂貴的任務,不需要存取數據模型。
  • 某些服務器側腳本可以從 原生代碼生成 中受益,這是一個簡單的標籤,可以將一個腳本轉換為機器代碼而不是字元碼。

微型偵測器範圍

范圍相關運算
運行服務。PreRender在預渲染事件上執行代碼
運行服務。PreSimulation在步驟事件上執行的代碼
RunService.PostSimulation在心跳事件上執行的代碼
RunService.心跳在心跳事件上執行的代碼

有關使用微型調試器檢查腳本的更多信息,請參閱 debug 圖書館,包括標記特定代碼和進一步提高特定性的功能,例如 debug.profilebegindebug.profileend 。許多由腳本呼叫的 Roblox API 方法也有自己相關的微型調試器標籤,可以提供有用的信號。

腳本記憶體使用率

當您寫出會消耗記憶的腳本時,記憶洩漏可能會發生,當垃圾收集器不再使用時無法正確釋放記憶。洩露在伺服器上特別普遍,因為它們可以連續多天在線,而客戶端會話則較短。

開發者控制台 中的下列記憶值可能表示需要進一步調查的問題:

  • LuaHeap - 高或增長的消耗建議發生記憶泄漏。
  • 實例數量 - 持續增長的實例數量表明您的代碼中的某些實例沒有被垃圾收集。
  • 放置脚本記憶體 - 提供通過分析記憶使用量的腳本來提供記憶。

常見問題

  • 保留連線連接 - 引擎永遠不會收集到連接到實例的事件和內部參考的任何值,連接到的回呼中的任何值都不會被刪除。因此,連接到連線的實例、連線的功能和參考值內的事件和代碼的主動連線,即使在事件發射後,也不在記憶碎片收集器的範圍內。

    雖然當屬於的實例被摧毀時,事件會斷開,但一個常見的錯誤是假設這適用於 Player 對象。使用者離開體驗後,引擎不會自動刪除他們的代表 Player 對象和角色模型,因此連接到角色模型下的 Player 對象和實例,例如 Player.CharacterAdded ,仍然會消耗記憶,如果您在腳本中未能夠將它們切斷,否則會導致應用程式性能降低。這可能會導致伺服器上的數百個使用者加入和離開體驗時,隨著時間推移發生非常大的記憶泄漏。

  • 桌子 - 將對象插入桌子但不會在不再需要時移除時,會導致不必要的記憶消耗,特別是當用戶數據加入時跟蹤用戶數據的桌子。例如,下面的代碼示例每次用戶加入時創建一個表,添加用戶信息:

    示例

    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)

物理運算

過度的物理模擬可能是服務器和客戶端增加每框計算時間的主要原因。

常見問題

  • 過度的物理時間步驟頻率 - 默認情況下,步行行為是在適應模式中,物理步驟在60Hz、120Hz或240Hz,取決於物理機制的複雜程度。

    還有一種固定模式,其準確度提高的物理學也可用,強制所有物理組件以 240 Hz(每秒四次)步行。這會導致每個框架的計算量顯著增加。

  • 模擬對象複雜度過多 - 模擬的 3D 裝配越多,每個框架的物理計算所需時間越長。經常會發生體驗會有對象被模擬,這些對象並不需要或會有超過需要的限制和關節的機制。

  • 過於精確的碰撞偵測 - 網格零件有一個 CollisionFidelity 偵測碰撞的屬性,可提供各種不同績效影響等級的模式。網格零件的精確碰撞偵測模式有最高的性能成本,需要花較長時間才能計算引擎。

減災

  • 不需要模擬的錨定部件 - 錨定所有不需要由物理學驅動的部件,例如靜態NPC。

  • 使用適應物理步驟 - 適應步驟會動態調整物理機制的計算速率,在某些情況下讓物理更新更少頻繁地進行。

  • 減少機制複雜度 * 在可能的情況下,盡量減少裝配中的物理限制或節點數量。

    • 通過對機制內的自我碰撞數量進行減少,例如應用限制或無碰撞限制來限制布偶肢體之間的碰撞,以防止它們相互碰撞。
  • 減少對網格精確碰撞穩定性的使用 * 對於用戶很少會注意到差異的小或非互動對象,請使用盒子穩定性。

    • 對於小型到中型的物件,可以使用盒子或殼體忠實度,取決於形狀。

    • 對於大型且非常複雜的物件,可以在可能的情況下使用隱形零件來構建自訂衝突。

    • 對於不需要碰撞的對象,禁用碰撞並使用箱子或船體穩定性,因為碰撞幾何仍然存儲在記憶中。

    • 您可以在 Studio 中以調整碰撞精確度來渲染碰撞幾何圖形,以進行故障排除。在 3D 視窗右上角的 視覺選項 控件中切換碰撞精確度來實現此操作。

      或者,您可以將 CollisionFidelity = Precise 過濾器應用於 導航器 ,該導航器會顯示所有精確匹配的網格零件的數量,並允許您輕鬆選擇它們。

    • 如需深入了解如何選擇一個平衡精度和性能要求的碰撞忠實度選項的方法,請參閱設置物理和渲染參數

微型偵測器範圍

范圍相關運算
物理步驟綜合物理計算
世界步伐每個框架執行的分散物理步驟數

物理內存使用率

物理運動和碰撞偵測會消耗記憶。網格零件有一個 CollisionFidelity 屬性,決定用於評估網格碰撞邊界的方法。

常見問題

預設和精確的碰撞偵測模式會消耗比其他兩種模式(精度較低的碰撞形狀)多得多的記憶。

如果您在 物理零件 下看到高記憶使用量,您可能需要探索減少體驗中對象的 碰撞精度

如何緩解

要減少用於碰撞穩定性的記憶使用量:

  • 對於不需要碰撞的零件,通過設置 BasePart.CanCollideBasePart.CanTouchBasePart.CanQuery 將其碰撞關閉到 false
  • 使用 CollisionFidelity 設置減少碰撞的忠實度。Box 具有最低的記憶擴展,而 DefaultPrecise 一般來說較貴。
    • 一般來說,設定任何小型錨定零件的碰撞精度為 Box 是很安全的。
    • 對於非常複雜的大型網格,您可能想使用盒子碰撞精確度的較小物體來建立自己的碰撞網格。

人型怪物

Humanoid 是一個提供給玩家和非玩家角色(NPC)廣泛功能的類別。雖然強大,但 Humanoid 帶來了相當大的計算成本。

常見問題

  • 將所有人形狀態類型在 NPC 上啟用 - 啟用某些 HumanoidStateTypes 將有一定的性能成本。停用任何不需要您的 NPC 使用的服務。例如,除非您的 NPC 要攀爬梯子,否則禁用 Climbing 狀態是安全的。
  • 經常使用機器人模型來啟動、修改和重生模型 * 這可能需要引擎處理,尤其是如果這些模型使用 分層服裝 。這也可能在經常重生的虛擬人偶體驗中特別令人擔憂。
    • 微型調試器 中,長 更新不支持快速集群 標籤(超過 4 毫秒)經常是表示虛擬人偶即時化/修改引發過多無效化的信號。
  • 在不需要的情況下使用人形 - 不會移動的靜態 NPC 通常沒有需要 Humanoid 類。
  • 從伺服器播放大量 NPC 動畫 - 在伺服器上運行的 NPC 動畫需要在伺服器上模擬並複製到客戶端。這可能是不必要的額外開支。

減災

  • 在客戶端播放 NPC 動畫 - 在擁有大量 NPC 的體驗中,考慮在客戶端創建 Animator 並運行動畫,以便在本地執行。這樣會減少服務器的負載和不必要的複製。它也讓額外的最佳化變得可能(例如只為靠近角色的 NPC 播放動畫)。
  • 使用性能友好的替代方案來取代人形物體 - NPC 模型不一定需要包含人形物體。
    • 對於靜態 NPC,請使用簡單的 AnimationController ,因為它們不需要移動,只需要播放動畫。
    • 對於移動 NPC,請考慮實裝自己的移動控制器並使用 AnimationController 來執行動畫,取決於您的 NPC 的複雜程度。
  • 停用未使用的人型狀態 - 使用 Humanoid:SetStateEnabled() 僅啟用每個人型狀態所需的狀態。
  • 具有頻繁重生的游泳池 NPC 模型 - 而不是完全摧毀 NPC,將 NPC 傳送到一個不活躍的 NPC 池。這樣,當新的 NPC 需要重生時,您可以從池中重新啟用其中一個 NPC 。這個過程稱為泳池化,可以減少角色需要被實例化的次數。
  • 只有當使用者在附近時才生成 NPC - 不要在使用者不在範圍內時生成 NPC,並在使用者離開範圍時刪除它們
  • 避免在它被實例化後對虛擬人偶層級進行變更 - 對虛擬人偶層級進行某些修改會導致性能問題。有些最佳化可用:
    • 對於自訂程序動畫,不要更新 JointInstance.C0JointInstance.C1 屬性。相反,更新 Motor6D.Transform 屬性。
    • 如果您需要將任何 BasePart 物件附加到虛擬人偶上,請在虛擬人偶的階層之外進行 Model

微型偵測器範圍

范圍相關運算
步驟人形化人型控制和物理學
步驟動畫人形與動畫師動畫
更新無效快速集群與創建或修改虛擬人偶相關

渲染中

客戶每次花費的時間的重要部分是在當前框架中渲染場景。伺服器不進行任何渲染,因此此部分僅供客戶端使用。

呼叫绘图

一個繪圖呼叫是由引擎向 GPU 渲染某些內容的一組指令。呼叫有明顯的延遲。一般來說,每個框架的呼叫次數越少,渲染一個框架所需的計算時間越少。

您可以在 Studio 中查看多少绘制调用正在发生,以及与 渲染统计 > 计时 项目有多少关联。您可以透過按下 Render Stats 在客戶端查看 ShiftF2

在給定的框架中需要在場景中繪製的物件越多,就會向 GPU 發出越多的繪製呼叫。然而,Roblox 引擎使用稱為 實例化 的過程將相同的紋理特性的相同網格瓦解為單一呼叫。特別是,具有相同 MeshId 的多個網格在單次繪製呼叫中處理時:

其他常見問題

  • 過度物件密度 - 如果大量物件集中在高密度,那麼渲染此場景區域的繪圖需要更多的呼叫。如果您查看地圖的某部分時發現幀率下降,這可能是地圖中某個區域物件密度過高的好跡象。

    像裝飾、紋理和粒子等對象不會很好地批量,並且會導入額外的繪圖呼叫。在場景中給予這些對象類型額外關注。特別是,屬性變更為 ParticleEmitters 可能會對性能產生巨大影響。

  • 錯過了實例化機會 - 經常會發生場景包含同一網格複製多次的情況,但每個網格複製的網格或紋理資產ID各不相同這會防止實例化並可能導致不必要的呼叫。

    這個問題的常見原因是當一次匯入整個場景,而不是個別資產被匯入 Roblox 然後在匯入後複製以組裝場景時。

  • 過度複雜的物件複雜度 - 雖然不像是呼叫數量那麼重要,但場景中的三角形數量會影響框架渲染所需的時間。擁有非常大數量的非常複雜的網格的場景是一個常見問題,場景擁有 MeshPart.RenderFidelity 屬性設置到 Enum.RenderFidelity.Precise 上的網格過多也是一個問題

  • 過度投射陰影 - 處理陰影是一個昂貴的過程,包含大量投射陰影的地圖(或大量受陰影影響的小部件)可能會出現性能問題。

  • 高透明度擊過 - 將部分透明度的對象放置在一起,強迫引擎多次渲染重疊像素,這可能會傷害性能。要了解有關識別和修復此問題的更多信息,請參閱刪除分層透明度

減災

  • 重複複製相同的網格並減少獨特網格數量 - 如果您確保所有相同網格都擁有相同的基礎資產ID,引擎可以在單次呼叫中認識並渲染它們。請確保只上傳每個網格在地圖上一次,然後在工作室複製它們,而不是匯入大型地圖的整體,這可能會導致相同的網格擁有不同的內容ID並被引擎認為是獨特資產。包裝是一個有用的對象重複機制。
  • 篩選 - 篩選描述對不屬於最終渲染框架的對象的呼叫進行篩選過程。預設情況下,引擎會跳過攝影機領域外的物件的呼叫(碎片篩選),但不會跳過其他物件遮蔽視線的物件的呼叫(遮蔽篩選)。如果你的場景有大量的繪圖呼叫,考慮在運行時動態實現自己的額外篩選,例如應用以下常見策略:
    • 隱藏遠離相機或設置的 MeshPartBasePart
    • 對於室內環境,實裝一個房間或傳送門系統,隱藏目前任何使用者都未使用的物件。
  • 降低渲染精度 - 將渲染精度設為 自動效能 。這會允許網格回落到較少複雜的替代方案,可以減少需要繪製的多邊形數量。
  • 停用適當部件和光物體上的陰影投射功能 - 在選擇關閉陰影投射功能的光物體和部件時,場景中的陰影複雜度可以減少。這可以在編輯時間或在運行時動態執行。一些例子如下:
    • 使用 BasePart.CastShadow 屬性來禁用在小部件上投射陰影,這些部件的陰影很可能不會被看到。這可能特別適用於只對離使用者攝影機很遠的零件進行應用。

    • 如果可能,關閉移動物體上的陰影。

    • 在光環境中停用 Light.Shadows ,當對象不需要投射陰影時。

    • 限制光源範圍和角度的光實例。

    • 使用更少的光源實例。

微型偵測器範圍

范圍相關運算
準備和執行整體渲染
執行/場景/計算光源執行燈光網格和陰影更新
光子網路CPU音箱燈網格更新
暗影地圖系統陰影地圖
執行/場景/更新視圖為渲染和粒子更新做準備
執行/場景/渲染視圖渲染和後處理

網絡和複製

網路和複製描述數據在服務器和連線的客戶端之間傳送的過程。資訊每一個框架都會在客戶端和伺服器之間傳送,但需要更多運算時間來傳送更多資訊。

常見問題

  • 過度的遠端流量 - 通過 RemoteEventRemoteFunction 對象發送大量數據或過度頻繁地呼叫它們可能會導致每個框架花費大量的CPU時間來處理收到的包。常見錯誤包括:

    • 重複不需要重複的每一個框架的資料。
    • 沒有任何機制來限制用戶輸入的數據複製。
    • 派發比需要的數據多。例如,當玩家購買一件物品時,發送整個物品庫存,而不是只有購買的物品細節
  • 創建或移除複雜的實例樹 - 當在伺服器上對資料模型進行變更時,變更將被複製到連接的客戶端。這意味著創建和摧毀在運行時創建和摧毀像地圖一樣的大規模實例層次可能會非常消耗網絡資源。

    常見的罪魁是動畫編輯器插件在模型中保存的複雜動畫數據如果在遊戲發布和動畫模型定期複製之前這些內容未被移除,大量不必要的資料將被複製。

  • 服務器端暫停服務 - 如果 用於暫停對象服務端,暫停的屬性將在每一個框架中複製到每個客戶端。這不僅會導致青少年因客戶端延遲波動而感到煩躁,還會導致大量不必要的網絡流量。

減災

您可以採用以下策略來減少不必要的複製:

  • 避免一次通過遠端事件發送大量資料 。相反,只在較低頻率下傳送必要數據。例如,對於角色的狀態,在每一個框架更改之前複製它,而不是每一次更改。
  • 塊化複雜的實例樹 像地圖並將它們分塊載入以分配這些在多個框架上複製的工作。
  • 清理動畫元數據 ,特別是在导入後清理動畫目錄的骨架。
  • 限制不必要的實例複製 ,特別是在伺服器不需要知道創建的實例的情形下。這包括:
    • 視覺效果,例如爆炸或魔法攻擊波。伺服器只需要知道位置來確定結果,而客戶端可以在本地創建視覺效果
    • 第一人稱項目檢視模型。
    • 在客戶端而不是伺服器上檢查子對象。

微型偵測器範圍

范圍相關運算
過程包包括處理來自網絡的進入網包,例如事件呼叫和屬性變更
配置帶寬並執行發送者與伺服器相關的出事件

資產記憶體使用率

給創作者提升客戶端記憶使用效率的最高影響機制是啟用實例傳輸

實例傳送

實例傳輸會選擇性地載入資料模型中不需要的部分,這可能會導致載入時間大幅縮短,並提高客戶端在記憶壓力下防止發生故障的能力。

如果您遇到記憶體問題且已停用實例傳輸,請考慮升級您的體驗來支持它,特別是如果您的 3D 世界很大。實例傳輸基於 3D 空間的距離,因此更大的世界自然會受益更多。

如果實例傳輸啟用,您可以增加它的攻擊性。例如,考慮:

  • 減少使用持久的 傳輸完整性
  • 減少 傳輸範圍

要了解更多關於傳輸選項和其優點的信息,請參閱傳輸屬性

其他常見問題

  • 資產複製 - 一個常見的錯誤是多次上傳相同的資產,導致不同的資產ID。這可能導致同一內容多次載入記憶。
  • 過度的資產量 - 即使資產不相同,也有情況會錯過重複使用相同資產和節省記憶的機會。
  • 音頻文件 - 音頻文件可能是記憶使用率的惊人貢獻者,特別是如果你一次載入所有音頻,而不是只載入你需要一部分體驗的東西。有關策略,請參閱載入時間
  • 高解析度紋理 - 圖形記憶對紋理的尺寸沒有影響,而是與紋理上的像素數量相關。
    • 例如,1024x1024 像素紋理消耗 512x512 紋理的圖形記憶量的四倍。
    • 上傳到 Roblox 的圖像會被轉換為固定格式,因此沒有上傳圖像到顏色模型中所帶來的記憶優惠,因為每個像素的資料量較少。相同地,在上傳或從不需要它的圖像中移除Alpha通道之前壓縮圖像可能會減少圖像在磁碟上的大小,但是或不會改善或只會稍微改善記憶使用率。雖然引擎會自動將某些裝置上的紋理解析度降低,但降低的程度取決於裝置特性,過多的紋理解析度仍然可能導致問題。
    • 您可以在 開發者控制台 中擴展 圖形紋理 類別來識別給定紋理的圖形記憶使用量。

減災

  • 只上傳資產一次 - 在對象間重複使用相同的資產ID,確保相同的資產,特別是網格和圖像,不會獨立多次上傳。
  • 尋找並修復重複資產 - 尋找具有相同網格部分和紋理的資產,並上傳多次使用不同ID的資產。
    • 雖然沒有 API 自動偵測資產相似性,但您可以在位置中收集所有圖像資產 ID(手動或使用腳本)、下載它們,並使用外部比較工具進行比較
    • 對於網格零件,最好的策略是使用獨特的網格 ID 並依照尺寸將它們組織,手動識別重複項。
    • 取代使用不同顏色的獨立紋理,上傳單一紋理,並使用 SurfaceAppearance.Color 屬性將各種染色應用到它上。
  • 獨立地在地圖上匯入資產 - 而不是一次匯入整個地圖,請獨立地在地圖上匯入和重建資產,並重建它們。3D 导入器不會對網格進行重複,因此如果您要匯入大型地圖並包含大量獨立地板網格,每個網格都會被匯入為獨立資產(即使它們是重複的)。這可能會導致線程中的性能和記憶問題,因為每個網格都被視為獨立並消耗記憶和呼叫。
  • 限制圖像像素 不超過必要數量。除非圖像佔用了大量物理空間在畫面上,通常最多只需要 512x512 像素。大多數小型圖像應小於 256x256 像素。
  • 使用修邊表格 以確保在 3D 地圖上最大限度地重複紋理。有關如何創建修邊單的步驟和例子,請參閱 創建修邊單

加載時間

許多體驗實現了自定義載入屏幕,並使用 ContentProvider:PreloadAsync() 方法請求資產,以便圖像、聲音和網格在背景下下載。

這種方法的優點是,它可以讓你確保體驗的重要部分無需彈出即可完全載入。然而,一個常見的錯誤是過度使用此方法來預加載超過實際需要的更多資產。

一個壞習慣的例子是載入整個 **Workspace 。雖然這可能會阻止紋理彈出,但它會顯著增加載入時間

相反,只在必要的情況下使用 ContentProvider:PreloadAsync(),這包括:

  • 載入畫面的圖像。
  • 體驗菜單中的重要圖像,例如按鈕背景和圖示。
  • 啟動或生成區域中的重要資產。

如果必須載入大量資產,我們建議您提供 跳過載入 按鈕。