天天看點

Irrlicht學習備忘錄——4 Movement

4 Movement

官方代碼($sdk)\examples\04.Movement

Irrlicht學習備忘錄——4 Movement

這個例子講的是如何使場景節點動起來。

我在學習了官方例子,并仔細看了irr源碼後,個人覺着使irr場景節點動起來的方法,嚴格的說就是官方例子裡的兩種方法。第一種是直接操縱場景節點ISceneNode調整位置;第二種是通過ISceneNodeAnimator間接操作場景節點ISceneNode調整位置。但似乎官方教程隻是為了給大家清楚irr能幹什麼,并沒有對irr進行更深入的講解。從源碼裡可以看出,ISceneNodeAnimator并不僅僅是irr内部使用的接口,也是給使用者擴充的接口。通過自己擴充ISceneNodeAnimator,同樣也能夠操作ISceneNode位置,但這種方法說嚴格了,也是例子裡的方法,隻是它沒教如何做擴充。

直接操縱場景節點ISceneNode調整位置,這在前面的例子裡已經使用過,隻是對每個節點初始化時使用一次setPosition方法,将場景節點放到預定的位置,是以場景節點就是靜态的,并沒有動起來。每隔一段時間,就使用一次setPosition方法将場景節點放到不同的位置,場景節點就成動起來的了。按這樣說,使場景節點動起來很簡單嘛,隻需要在前面例子中的循環内對場景節點的位置進行重新設定就行了。想法雖然沒錯,但如果場景節點很多很多,而且每個場景節點的運動方法都不一樣,這方法還行得通嗎?我自認水準低,我可沒這水準管理好那麼多節點做不同的運動。irr提供的ISceneNodeAnimator就是一個用來自動控制場景節點運動的控制器。在場景節點上添加一個ISceneNodeAnimator後,這個場景節點的運動就受ISceneNodeAnimator控制了。用ISceneNodeAnimator來控制很多很多的場景節點運動,我覺着總比全在循環内對每一個場景節點進行位置重設要簡單的多吧。

場景節點的運動,也總不能全由程式控制吧,部分節點的運動還是需要輸入裝置,如滑鼠、鍵盤、搖桿等來控制才有意思嘛。這要怎麼做呢?irr本身就是遊戲引擎,遊戲能不支援輸入裝置嗎?既然遊戲需要輸入裝置,那遊戲引擎也就支援輸入裝置。irr裡提供了IEventReciver基類用來從消息循環裡接收使用者需要的輸入裝置的輸入消息。irr支援的輸入裝置有鍵盤、滑鼠和搖桿(遊戲操作杆),隻需要自己派生一個IEventReciver類,将自己需要的輸入裝置消息提取出來,就可以使用這些裝置了。要使用體感,就自己動腦筋去擴充吧,irr1.8裡還沒有體感的支援。

下面來具體看看irr移動場景節點的教程。

首先,為了擷取使用者滑鼠鍵盤輸入事件,以及GUI的各類事件(例如,XX按鈕被按下)并對其進行相應的處理,教程裡建立了一個EventReceiver事件接受器的實體對象,它繼承于IEventReciver類。繼承IEventReciver類是必須的,這個類是個抽象類,除非不打算使用它。

在頭檔案IEventReciver.h裡,能看的它的全貌。

classIEventReceiver

{

public:

virtual~IEventReceiver() {}

virtualbool OnEvent(const SEvent& event) = 0;

};

非常的簡單,隻需要重寫一個OnEvent方法就行了。SEvent就是irr消息循環裡的消息事件,在同一個頭檔案裡有它的結構。irr的消息事件種類不多,有EET_GUI_EVENT圖形界面事件、EET_MOUSE_INPUT_EVENT滑鼠輸入事件、EET_KEY_INPUT_EVENT鍵盤輸入事件、EET_JOYSTICK_INPUT_EVENT遊戲操作杆事件、EET_LOG_TEXT_EVENT标志文字事件、EET_USER_EVENT使用者事件。在例子中隻使用了鍵盤輸入事件。為了記錄鍵盤按鍵狀态,在派生的事件接受器MyEventReceiver類裡使用了一個數組來存儲按鍵狀态。例子裡擷取按鍵狀态并沒有直接從數組裡取,而是增加了個方法來操作,這種方式雖然效率不高,使用麻煩,但用起來這種封裝方式很安全,不容易因疏忽編寫出通路數組越界的代碼,值得學習。

classMyEventReceiver : public IEventReceiver

{

public:

virtualbool OnEvent(const SEvent& event)

{

擷取鍵盤輸入消息,将産生該消息的按鍵狀态進行修改

if(event.EventType == irr::EET_KEY_INPUT_EVENT)

KeyIsDown[event.KeyInput.Key]= event.KeyInput.PressedDown;

returnfalse;

}

例子中增加的方法,擷取按鍵是否按下。

virtualbool IsKeyDown(EKEY_CODE keyCode) const

{

returnKeyIsDown[keyCode];

}

對按鍵狀态進行初始化

MyEventReceiver()

{

for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)

KeyIsDown[i]= false;

}

private:

儲存按鍵狀态的數組

boolKeyIsDown[KEY_KEY_CODES_COUNT];

};

派生的MyEventReceiver類寫好後,怎麼用呢?這裡的确跟前面的例子有不同了。前面的例子中,建立irr裝置時,createDevice最後一個參數是0,這裡需要修改成MyEventReceiver對象的指針。

MyEventReceiverreceiver;

IrrlichtDevice*device = createDevice(driverType,core::dimension2d<s32>(640,480), 16, false, false, false, &receiver);

看起來在建立irr裝置時就得設定IEventReceiver事件接收器,似乎設計IEventReceiver派生類就不能太簡單了。設計的簡單,以後功能不夠用時又得改,而且使用者界面事件也在裡面,操作界面太多的話,這個派生類也會設計的很臃腫,這類不容易設計啊。實際上不是這樣的,irr裝置提供了一個setEventReceiver方法,用來重新設定派生的事件接收器,還提供了一個getEventReceiver方法來擷取目前使用的事件接收器,隻需要設計一個滿足當時需要的事件接收器就行了,場景或界面變化後,重新設定一個相符的事件接收器就可以了。

例子後面的代碼,基本跟前面的例子差不多,建立了一個球、一個立方體。還有一個動畫模型,三種場景節點。對這三個場景節點,球采用在循環裡直接設定場景節點位置的方法來控制運動;立方體和動畫模型使用ISceneNodeAnimator來自動控制場景節點的運動。

在建立立方體的代碼後,有ISceneNodeAnimator*anim = smgr->createFlyCircleAnimator(core::vector3df(0,0,30),20.0f),建立了一個繞圈轉的運動控制器,使用立方體場景節點的addAnimator(anim)将改運動控制器添加到了立方體場景節點。這樣立方體場景節點就會在運動控制器的控制下,圍繞着(0,0,30)位置,以半徑20繞圈。

在建立動畫模型節點的代碼後,有ISceneNodeAnimator*anim=smgr->createFlyStraightAnimator(core::vector3df(100,0,60),core::vector3df(-100,0,60),3500,true),建立了一個直線運動的運動控制器,同樣使用addAnimator(anim)将改運動控制器添加到場景節點裡,這樣這個動畫模型節點就會不斷的用3500毫秒從位置A(100,0,60)移動到位置B(-100,0,60)。

在循環部分,在繪圖代碼前,通過使用device->getTimer()->getTime()擷取irr裝置目前時間,計算出目前幀和上一幀的時間間隔,再通過事件接收器擷取W、S、A、D四個鍵的狀态和球體移動的速度,算出目前幀球體場景節點移動到什麼位置,最後使用setPosition重新設定球體場景節點的位置。這樣每顯示一幀圖像,球體的位置都會根據按鍵情況進行調整,球體也就成了手動控制移動的場景節點。

irr官方教程教的就這麼多,再深入的擴充ISceneNodeAnimator的方法沒講,我同樣準備把擴充單獨寫。

繼續閱讀