前言
分享人介紹——CDPR的資深開發人員,項目中的UI開發負責人
1 已有框架的不足與新框架的預期
為什麼定製化UI框架
對UI框架的期望
- 引擎內創建
- 更少的迭代時間
- 使用引擎內的子系統
- 更高的密度和複雜度
- 更多動態元素
- 支持多語言
- 在3D空間顯示與交互的可行性
- 可包含嵌入的視頻
- 使用自定義材質和特效的可行性
研究現有解決方案中的缺乏項
- 不支持Scaleform中間件
- 不是單一完整解決方案(需要一堆中間件組合)
- 部分中間件或方案——不夠高效、不夠可伸縮、集成到Red引擎中比較困難
2 原型階段與初版開發
決定定製開發UI框架的時間點
UI框架開發——步驟1/3
- 簡單的widget類型(widget就不翻了 這個是UI系統控件的常用詞語)
- 2D輸入傳播(有些引擎裡用dispatch這個詞,類似的意思)
- 排版佈局構建
- 集成到Red引擎的系統中——渲染、輸入系統、文件系統
MVP開發時間節點
原型框架示意
UI框架開發——步驟2/3
- 包含完整功能的UI系統
- 簡單的3D功能實現
- 無編輯器(所有佈局通過C++硬編碼)
- 支持嵌入視頻
首個遊戲內UI和內部DEMO的時間點
DEMO的UI功能示意
UI框架開發——步驟3/3
- 合適的實現——引擎、UI、遊玩層的連接
- 合適的管線——編輯器和資源導入工具
- 合適的遊戲內UI——不是通過硬編碼
- 和更多...
3 性能指標與整體優化方向分析
UI的性能指標
UI的內存指標
UI時間指標
- 指標:在高同步性的線程上3-5毫秒執行;需要做到多線程執行
- 現狀:單線程10-15毫秒
引入指標並開始優化的時間點
為什麼UI系統性能開銷那麼高
- 解答1:UI系統沒有經過代碼優化
- 解答2:太多的UI實例(內容)了
- 解決方案——在內存、刷新和繪製方面優化UI系統。(在運行時)只保留某一特定時刻真正需要的UI實例。
複雜度的來源
高密度的來源
一些運行時的狀態統計
UI術語定義——第一部分 層級
4 UI實例分類——Group UI Instances
層概念(1/3):定義
- 4個主要元件:事件委託、刷新處理器、控制器、動畫處理器
- 獨立的資源管理系統
- 定製化的獨立的繪製邏輯
層概念(1/3):類型
- 全屏(水印、系統體型、加載、遊戲提醒、菜單、視頻、HUD、相片模式、編輯器)
- 遊戲世界中(世界物體、廣告、街邊告示牌)
- 雜項(畫外項、Debug)
層概念(1/3):一些設想
- 異步的層刷新:異步遊戲控制刷新、異步執行生成請求(同步化的關聯處理)、異步的動畫刷新(同步化的參數應用處理)
- 異步的層繪製:同步化的最終合併處理
- 每層有獨立的多線程調度鏈
第一批UI層解耦完畢的時間點
多線程UI框架方面的Take Away
- 提取所有獨立的計算項,並使它們異步進行
- 使用獨立的渲染目標來異步繪製UI
- 緩存一切必要的東西
- 一幀中儘早開始UI的執行過程
5 減少UI刷新——Reduce UI Updates
激活與未激活的模式
- 兩類邏輯執行模式:激活——UI傾向於是可見的、所有邏輯都被執行;未激活(Passive 被動的)——UI不可見、只執行關鍵邏輯。
- 單獨層可以有不同的定製化。
- 非常有彈性的機制。
UI術語定義——第二部分 控制器
- 可以被幀調度執行(默認是關閉的。這裡tick是邏輯上執行一幀代碼的概念,我翻譯成幀調度)
- 可以訪問所有遊戲系統
- 被控制處理器管理
- 只能被添加到UI實例上
- 被一箇中央系統控制
- 例如:車輛控制器、電梯控制器、小地圖控制器、紙娃娃控制器
- 基於事件的(沒有幀調度執行的功能)
- 僅包含UI邏輯
- 只能訪問下屬的widget和UI層級
- 可以被添加到任意widget上
- 被用來功能性地擴展widget系統
- 例如:按鈕邏輯控制器、審查邏輯控制器、滑塊邏輯控制器
控制器的處理器
- 包含一個特定層的所有控制器
- 中心化的幀調度執行
- 可以為每個控制器決定不同的調度模式
- 傳遞一個遊戲內系統的context(上下文,指相關數據和狀態等)給遊戲控制器
- 多線程執行
- 決定所有遊戲控制器的生命週期
生成處理器
- 生成UI實例默認是一個異步的過程
- 每一幀有一個激活生成處理器的數量上限
- 可以延遲或取消生成過程
- 管理不同資源的加載過程
- 可以通過對象池來重用同樣層級結構的UI
- 隊列化關聯新實例(這裡指雖然生成加載是異步的,但是邏輯需要保證時序正確)
獨立的事件調度邏輯Take Away
- 基礎的事件邏輯是相對輕量級的
- 嚴格控制基礎邏輯的幀調度
- 如果可能就關閉幀調度
- 避免大規模的數據拉取
6 減少UI動畫刷新——Reduce UI Animation Updates
動畫方面的挑戰
- 全部(UI控件)可動畫化的
- 包含視頻
- 幾乎所有文字都是需要本地化的
- 基於玩家的選擇動畫可能有分支表現
- 任何部分可能在任何時候被播放
UI術語定義——第三部分 動畫
動畫處理器
- 遞增的動畫時間
- 異步的插值計算
- 異步的參數提交處理流程(保持其基於依賴關係的順序)
- 發出所有(幀上定義的)事件
- 遞增的動畫時間
- 發出有意義的事件
優化UI動畫的TakeAway
- 內存中僅保持同一動畫的一份模板數據
- 對每個實例使用輕量化的參數定義數據(metadata)
- 只計算並運行對玩家可見的動畫效果
- 對不可見的動畫只更新時間
7 UI實例剔除——UI Instance Culling
引入廣告物體層概念的時間點
廣告物體設計
- 減少紋理內存:使用一張紋理圖集、多種不同的廣告佈局、重用渲染對象的內存(生成並繪製可見的物體)
- 動畫廣告的可能性
- 內容審查過濾器
- 運行時的隨機化
- 定製化的光照支持
圖集和佈局示意
每個廣告的可動版本
右邊是和諧版 懂的都懂
有多少這種廣告牌? 圖中框出的都是
實際運行畫面示意
玩家可視範圍優化
- 距離檢測
- 視錐體剔除:旋轉、運動預測,慣性機制
- 遮擋剔除:定製化的軟件實現
- 屏幕覆蓋:“weapon plane”的問題。
- 靜態紋理替換
- “車內”情況:延後流式加載,跳過刷新或繪製
可視範圍優化的TakeAway
- 相對於普通3D幾何體,使用優化後的管線
- 如果UI實例不可見,則設置為未激活狀態
- 基於UI實例佔的屏幕比例調整渲染質量
8 延遲刷新與繪製——Deffered Update & Draw
越來越多的UI實例
- 5個全局的TV頻道,每個包含3個繪製pass
- 車內過多UI實例
- 巨量的圖標
引入畫外層的時間
畫外層
- 混合在遊戲世界中和全屏幕的(渲染)方案中
- 延遲處理
- 不阻塞(線程)
- 成對生效(UI資源與渲染對象)
- 依賴於幀的狀態
- 實例列舉:道具欄圖標、全局TV的疊加層、複雜特效的動態遮罩
畫外處理流程示例
畫外渲染結果示例
畫外層的一些TakeAway
- 儘量緩存和重用
- 僅在一幀有空閒時間時使用它(作為畫外層處理)
- 使用不同的獨立渲染對象做即發即棄式的管理(fire and forget是一個既有詞組)
9 3D空間中UI的HLOD——HLOD for UI in 3D-World
街邊路牌被提取到單獨層的時間
街邊路牌層的設想
- 類似廣告牌
- 需要本地化,但不是隨機的
- 在運行時進行集成
- 在夜之城有成百上千這樣的路牌
- 渲染目標快速的片元著色
渲染對象管理
- 渲染目標片元著色的方案
- 作為圖集渲染
- 對渲染對象使用包圍層(wrapper 一般是一種加載結構或對象管理結構)
- 複雜的匹配機制:支持各種邊緣情況、對圖標有特定渲染規則
- 獨立的渲染對象池(3DUI和特效)
HLOD渲染對象的示例
渲染對象管理的TakeAway
- 在世界空間中UI常常是縮小後的
- UI佔屏幕範圍是一個很好的衡量(需要)質量的指標
- 在渲染對象的範圍內繪製到較小的區域,而不是縮小渲染對象本身
10 作者的總結
全部任務都搞定了(麼)
其它未提到但是也很耗時的工作(可以粗略看看 就不翻譯了)
項目上線時間
整體性的TakeAway
- 截止時間和指標預算是你的朋友(高情商環節)
- 代碼解耦和並行執行是非常重要的解決方案
- 你需要有一個驚人的團隊來做驚人的事(高情商環節)
UI組的構成
- 最初包含3個開發人員
- 最終包含了3個UI組(藝術、設計、編程)
- UI編程人員的峰值:11個