Ogre 1.7 SDKTRAY 初探

      Ogre 1.7 這個版本比以前有了很多的變化.其中之一就是在Ogre官方不再包含CEGUI的組件.但是儘管沒有使用CEGUI,在官方的demo中仍然可以看見有gui的功能,那麼Ogre又是怎麼實現的呢?

      根據官網的說明,新的gui系統使用的是一個叫"tray"的系統.這個系統的主要代碼在SdkTray.h這個文件之中.sdktray使用的Ogre overlay來實現gui,並且Ogre 1.7比以前版本似乎也多了一些gui相關的組件.這大概預示,Ogre即將開發自己的gui組件,而cegui也許將被拋棄.

      不管未來如何,研究下sdktray還是很有意義的一件事情.使用overlay來實現gui,最大的特點在於可以靈活利用Ogre強大的material系統,實現很多不可思議的效果.比起使用cegui來,減少了學習週期.但是比較遺憾的是,目前Ogre 1.7的sdktray還不能作爲一個完整的gui組件,

有些功能還很不完善.

      現在來研究以下sdktray.h文件的源代碼.

      首先是2個enum;ButtonState就如同其名字一樣,指的是鼠標按下 懸浮以及釋放的情況下的gui對應3種狀態.不過一般的說,這3種狀態所有gui應該都具備.接下來是一個sdkTrayListener的基類,從接口來看,應該是實現gui對應的功能.包括點擊 選擇 移動等等.

      Widget是所有窗口組件的基類.一個Widget包含一個OverlayElement,和一個sdktraylistener,分別實現外觀和對應的事件響應.這個基類還包括幾個static的工具函數.nukeOverlayElement()的作用是刪除掉OverlayElement(包括子element),isCursorOver ()用來判斷鼠標是否懸浮在gui的上方.需要注意的是這個函數判定包括了邊框的判斷.並且僅僅實現了矩形的判斷.這個大概是因爲目前所有的OverlayElment都是矩形的緣故.cursorOffset()獲取鼠標離OverlayElement的中心有多少像素遠.getCaptionWidth()用來獲取一個textAreaOverlayElement中的caption的長度.caption應該指的是textAreaOverlayElment保存的文本.fitCaptionToArea()這個函數用來對具體的caption實現簡單的格式化.,包括空格的長度以及根據gui長度實現裁剪.但是這個函數沒有實現string太長時候的自動換行處理.接下來的4個virtual函數是回調函數, 用來實現鼠標對應事件響應處理.有意思的是這裏沒有keyboard對應的callback 這大概意味着目前的sdktray還沒有實現響應鍵盤的功能.

      接下的是一個繼承自Widget的Button類.如同Ogre許多的部件,Ogre註釋中說明,要求使用sdkmanager來創建具體的button.在button的構造函數中可以看到widget的OverlayElement 是使用OverlayManager::createOverlayElementFromTemplate()函數創建的.這個函數的參數是硬編碼.這意味着如果我們要使用自己定義的外觀,需要更改button的源代碼.也許copy一個多出2個參數的構造函數是個不錯的選擇.button使用了一個有限狀態機來實現了基類的4個callback.注意_cursorRelease()中調用了listener的buttonHit()函數,說明button響應點擊的邏輯判斷在鼠標釋放的時候,並且沒有對鼠標按下進行邏輯事件的處理.這也是sdktray不夠完善的地方.

      TextBox繼承自Widget,註釋說明是"Scrollable Text box",這個類實現了string超出gui寬度時候的換行處理.並且實現了一個下拉條.

爲了實現這些功能,這個類包含了一個顯示標題的borderPanel,包含標題的textArea,包含caption的textarea,2個實現下拉條效果的Borderpanel和panel.TextBox的構造函數主要就是讀取各個子OverlayElement並設置對應的的屬性.同button一樣,許多屬性的值都是硬編碼.最後構造函數調用了一個refitContents的子函數.這個函數的功能是根據新設置的字體屬性調用setText()重新排列文本.要複用的話,setText()是很好用的一個對外接口,這個函數設置需要顯示的文本,並且將文本自動換行.setTextAlignment()實現文本對齊,appentText()實現字體追加.如果要自己實現打字輸入,這個函數估計會有很大用途.但是這個函數每次都調用setext(),每次都對所有string重新計算位置,用來實現輸入的話應該不是很有效率.setScrollPercentage()根據下拉的百分比來設置顯示的字體.函數調用了filterLines(),這個子函數的功能是決定下拉的時候,哪些行的字體應該被顯示._cursorPressed()響應了對下拉條點擊的處理.需要注意的是 函數使用基類cursorOffset()來進行範圍判定,當鼠標在設定的範圍內的時候,下拉條纔開始響應對應的事件.從demo來看,下拉條是一個圓形,我估計這個圓形的半徑是9像素,所以函數中判斷使用的數值是81(9的平方).

       SelectMenu實現了下拉菜單的功能.getItems(),setItems(),clearItems(),是對所有下拉的組操作,addItem(),removeItem(),selectitem(),getSelectedItem()實現子操作.其中selectItem調用了listener的itemSelected()函數.

       label大概是最簡單的一個組件.需要注意的這個label在鼠標pressed的時候調用了listener的labelHit()函數.

      separator沒有響應任何邏輯事件和鼠標事件.也許是用來顯示一些把什麼東西分開的東西.

       slider實現基本的滑塊功能.這個用來實現在一定範圍內值的選取.setRange() 確定值的上下限,getvalue() setValue()獲取設置value.注意value的類型是Real.從setValue()函數實現來看,設置的是滑塊的left,也就是說slider類 只能實現橫向的滑塊移動.

       paramsPanel的功能是顯示一個基於string類型的<key,value>.從實現上看,沒有響應邏輯,也沒有響應基本的鼠標事件,他的唯一功能就是顯示key的value.程序中可以使用setvalue()來改變key對應的值.

       checkBox 選擇框.類似於單選框.一個checkBox框對應一個條目.有多個條目要實現多個checkBox.toggle()是一個對外接口,這個函數調用了listener的checkBoxToggled()函數.

       DecorWidget 註釋說明是用來實現用戶自己的widget.這個DecorWidget什麼都不做...如果要實現自己的widget,自己寫一個繼承自widget的類大概比較簡明.

       ProgressBar 進度條.包含3個部分,標題,正在讀取的內容,以及顯示進度用的顯示條.顯示條使用了2個overlayelement,其中一個根據百分比改變寬度,從而實現進度條效果.進度條沒有響應任何事件.

       sdkTrayManager是widget的管理器.不過看上去爲了實現示例的輪盤效果這個manager多了許多的額外功能.如果不是使用示例的那個輪盤的話,很多功能似乎可以去掉.

       構造函數中sdktrayManager實現了4個顯示層.backdrop trays priority 以及 cursor 層.zorder依次從低到高.這裏可以看出sdktrymanager是如何顯示鼠標的.在一個單獨的overlay層中使用一個overlayElement跟蹤鼠標的位置,並實現對應顯示.priority層的用途是顯示一個dialogShade的OverlayElement,這個看上去是個對話框.但是dialogShade不是一個widget.不明白爲什麼不單獨把這個功能分離出來,要整合到manager中去.接下來的代碼設置了tray層.應該對應的是sample demo中對應的那個輪盤gui.出於複用的目的,不討論tray相關的內容.

       幾個靜態的工具函數:Ogre::Ray screneToscrene()和Ogre::Vector2 screneToScrene(),實現2D座標和基於相機視口的3D射線之間的互換.refreshCursor()這個函數實現鼠標的更新,並且更新是基於ois unbuffered模式.我個人認爲與鼠標有關的都應該獨立出來作爲一個類.getCursorRay()是screentoScreen()的再封裝.接下來是各種widget對應的create函數.需要注意的是create函數將一個自己的mListener成員賦予了widget.這意味着響應事件的邏輯可以集中到manager的listener中,而不需要針對每個widget都寫一個listener.

        showFrameStates() 這個函數的功能替代了以前debugOverlay的功能.showLoadingBar()顯示一個資源讀取進度的進度條.showOKDialog() 顯示一個有OK button的對話框.showYesNodialog()顯示一個有yes no的對話框.這些功能也許都應該獨立出來作爲一個工具類.

        frameRedneringQueued()響應了每幀循環.主要功能是清除不需要的widget以及更新framestates.

        injectMouseDown() injectMouseUp() injectMouseMove()處理鼠標事件.overlay zorder 在前的overlayElement先獲得處理.

        總的來說,各種widget複用程度還是很高的,但是如果需要改變他們的顯示的話,需要更改構造函數.sdkmanager的功能比較雜亂,如果需要實現自己的gui系統,應該將一些功能分拆出來.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章