引擎狀態監聽(觀察者)
底層變化時, 需要高層UI做出反應. 這個有一個原則, 就是下層邏輯不能依賴上層邏輯, 所以通過一個接口進行消息的派發. 如果是.net的話, 可以用delegate/event實現. C++的話, 可以自己用模板寫個delegate用, 效率比用接口高…
實體抽象(工廠方法)
擴展性是很強, 但是…類太多了-_-. 這裏有種基於DLL的插件模型可以學習. 工廠方法需要知道具體的工廠對象, 這裏要求初始化工廠的對象(OgitorRoot)要知道具體的工廠定義(C++的#include做得太失敗了, 導致編譯效率低下…). 但是做成DLL插件的話, 就可以利用DLL的導出函數反向調用OgitorRoot進行註冊. 不過, 對於內部使用, 進行二進制分離有意義嗎?
Undo/Redo(命令模式)
這機制實現的前提是, 所有CBaseEditor對象都可以用一系列的”屬性”來進行初始化/設置. 所以每一個Command都是針對屬性的修改. 對於Create/Destory來說, 可以看成對所有屬性的修改外加對象的創建/刪除. 屬性的變量類型並沒有進行抽象, 而是轉換成了字符串, 需要時再轉換回來. 如果是.net的話, 就方便許多, 直接可以獲得PropertyInfo[], 而且能跟PropertyGrid控件進行屬性的自動綁定. 不過這屬於語言的”反射”應用, 標準C++不具備這個特性(@_@).
鼠標編輯
就是移動, 旋轉, 縮放對象的操作響應還有可視化表示. 操作時的選中軸是所有編輯對象統一使用一套, 自定義的表示, 如下圖中的spot light, 是通過派生CVisualHelper來進行定製繪製的.
多選處理
這裏到是沒用什麼有用的模式, 不過對於OgitorsRoot::VolumeSelect()的算法到是很感興趣.
在viewport上的矩形選框, 可以對應3D空間一個5個平面組成的包圍體(遠面不包含), 以這個包圍體到場景樹去遍歷查詢所有實體, 就能得到選中的實體列表. 所有選中對象組合成一個CMultiSelEditor進行移動/旋轉/縮放.
屬性編輯
把SetXXX全部定義到一個函數指針數組, 根據類型(ID)進行索引, 避免了一堆if-else. 無論是UI到引擎, 還是引擎到UI的通知, 都是以這個ID來進行查找的. PropertyGrid的Item綁定的數據, 也是這個ID, 而不是實際的對象. 但是, 這樣也帶來另一個問題: 每擴充一個屬性就會去改動這個類型定義的頭文件, 那DLL中定義的特殊屬性怎麼辦?
序列化
這裏有能體現出屬性進行抽象的好處了, 所有對象直接寫進XML格式的工程文件. 用XML的好處就是, 增刪屬性不用改文件序列化的代碼, 缺點是解析速度慢, 佔用空間大.而二進制就比較鬱悶, 格式一改動就要進行代碼變更, 通常還要兼容幾個版本的文件. 地形方面由於是插件的原因, 文件是獨立的. 不過不管什麼場景, 地形獨立出來其實沒什麼不好的, 因爲地形的格式比較穩定, 不會經常變動, 獨立出來更方便做版本管理.