1. 程式人生 > >基於Delta3D的walk運動模式實現

基於Delta3D的walk運動模式實現

這是一個課設。網上關於Delta3D的資料不算多,分享一下,算是做一點微小的工作。

本例程實現坦克在平地的加減速,並配音。大體來說下面兩個視訊講得很好了,不過他的方式實現的坦克轉向是瞬間轉的,對開發來說好處不少,不過不太符合實際,這裡簡單描述一下基於Walk運動模式的實現方法。完全沒基礎的盆友先看視訊,不然下面看不懂。

https://v.qq.com/x/page/s0357t9b3n1.html

https://v.qq.com/x/page/f036009i9h9.html


先說明一下,Walk模式帶來了平滑的轉彎,但我沒有做碰撞檢測,所以這個demo中坦克是在一個無限平面上面走。下面程式碼中的英文是我寫程式碼時的註釋,中文是本文新增的註釋。



==============================一下兩段為bug說明,看完程式碼再回頭看,不想看就不看==============================

另外有個待解決的bug。這個Walk模式採用了KeyPressed()和KeyReleased()函式來監聽按鍵,但實際上鍵盤輸入的連擊是存在打斷的情況的,比如先按住前進,鍵盤連擊不斷輸入前進訊號,這時我們可以通過KeyPressed()來得知這些輸入。但如果我們接著點選一下其它按鍵(如方向左鍵),前進的連擊就會被打斷,於是KeyPressed()函式就無法再被呼叫,也就無法再在函式裡播放音效,引擎聲隨後停止。

解決這個bug的方法是使用多執行緒,因為坦克啟動後的enabled屬性是恆為true的,使用另一個執行緒呼叫model->GetEnabled()方法,監聽其返回值,為true即坦克在運動,後播放音效即可。即不在KeyPressed()中播放音效,而是另起執行緒來播放。思路大致是這樣,我並沒有在下面程式碼中修正這個bug。

=======================================================================================================


下面開始正題。

首先配置好環境,場景模型的載入是一樣的。設計好App類的成員

private:
	RefPtr<Object> flatdirt;	//地形
	RefPtr<Object> brdm;		//坦克
	RefPtr<dtCore::WalkMotionModel> model;	//行走模式
	Sound* _sound;			//引擎聲音,這裡加/減/勻/速均使用同一個聲音
	float speed;			//坦克運動速度

下面在App類中重寫Config()函式,初始化,繫結walk模式到坦克。

virtual void Config(){

		Application::Config();
	
		//Add tank
		brdm = new Object("brdm");
		brdm->LoadFile("../module/brdm.ive");
		AddDrawable(brdm.get());

		speed = 6.0f;	//坦克初始速度

		//Add map
		flatdirt = new Object("flatdirt");
		flatdirt->LoadFile("../module/flatdirt.ive");
		AddDrawable(flatdirt.get());
	
		//Set tank position
		osg::Vec3 brdmPosition(0.0f,-25.0f,0.0f);
		osg::Vec3 brdmRotation(0.0f,0.0f,0.0f);
		dtCore::Transform trans;
		trans.SetTranslation(brdmPosition);
		trans.SetRotation(brdmRotation);
		brdm->SetTransform(trans);

		//Set camera
		dtCore::Transform camPos;
		osg::Vec3 canXYZ(0.0f,-100.0f,35.0f);
		osg::Vec3 lookAtXYZ(brdmPosition);
		osg::Vec3 upVec(0.0f,0.0f,1.0f);
		camPos.Set(canXYZ,lookAtXYZ,upVec);
		GetCamera()->SetTransform(camPos);

		//Basic walk mode
		model = new WalkMotionModel(GetKeyboard(),GetMouse());	//在這裡初始化運動模式,允許鍵/鼠控制,不需要的話對應引數改為0
		SetWalkMode(brdm,speed);	//把繫結和速度封裝到此方法中,方便在加減速中呼叫
		model->SetEnabled(false);	//先禁用model的運動,有前進/後退鍵輸入時方啟用,避免靜止時左右轉的情況
		cout<<"Congif finished, walk speed="<<model->GetMaximumWalkSpeed()<<endl;	//config結束,在控制檯提示
	} 


上面用到了SetWalkMode()方法,下面定義:

void SetWalkMode(RefPtr<Object> obj ,float _speed){
	model->SetTarget(obj);	//繫結物件(坦克物件)
	model->SetMaximumWalkSpeed(_speed);	//設定坦克前進速度
}

至此,坦克已經可以在場景中作勻速運動(當然,還要先執行model->SetEnabled(true);)。

下面實現加減速和音訊的播放。調速邏輯是:坦克必須先啟動,後方可加減速。啟動後坦克以怠速6.0f運動,按住左Ctrl加速,後按住左Shift可減速(有最高/最低速度限制)。加減速鍵一旦釋放則馬上停止加減速,保持當前速度運動。一旦前進/後退被釋放,坦克馬上靜止,速度屬性回到怠速,引擎聲音停止。再次啟動時重新以怠速勻速前進。


於是需要設定函式監聽按鍵的按下、擡起,並獲取指定按鍵狀態,delta3D已經封裝好了函式,我們需要稍作重寫。首先是按鍵按下的監聽:

	virtual bool KeyPressed(const Keyboard* keyboard, int kc){

		//有方向鍵按下
		if(keyboard->GetKeyState(osgGA::GUIEventAdapter::KEY_Up)||keyboard->GetKeyState(osgGA::GUIEventAdapter::KEY_Down)||keyboard->GetKeyState(osgGA::GUIEventAdapter::KEY_Left)||keyboard->GetKeyState(osgGA::GUIEventAdapter::KEY_Right)){
			if(!_sound->IsPlaying() && model->IsEnabled()){		//音效未播放且車在運動,則播放音效
				_sound->Play();
			}
		}

		//Must start moving first
		if(keyboard->GetKeyState(osgGA::GUIEventAdapter::KEY_Up)||keyboard->GetKeyState(osgGA::GUIEventAdapter::KEY_Down)){
			
			model->SetEnabled(true);	//前進或後退已被按下,允許坦克啟動
			
			
			//左Ctrl加速,最大速度30.0f
if(keyboard->GetKeyState(osgGA::GUIEventAdapter::KEY_Control_L)){//if(kc==osgGA::GUIEventAdapter::KEY_Control_L){if(speed<30.0f){speed += 0.5f;SetWalkMode(brdm,speed); }}//左Shift減速,最小速度6.0fif(keyboard->GetKeyState(osgGA::GUIEventAdapter::KEY_Shift_L)){//if(kc==osgGA::GUIEventAdapter::KEY_Shift_L){if(speed>6.0f){speed -= 0.5f;SetWalkMode(brdm,speed);}}return true;}//Any other button is pressedelse {return false;}
 
 
接下來監聽按鍵擡起事件:

	virtual bool KeyReleased(const Keyboard* keyboard,int kr){
		if(kr==osgGA::GUIEventAdapter::KEY_Up||kr==osgGA::GUIEventAdapter::KEY_Down){
			model->SetEnabled(false);
			if(_sound->IsPlaying()){
				_sound->Stop();		//停止音效
			}
			speed=6.0f;
			SetWalkMode(brdm,speed);	//回到怠速
		}
		return true;
	}


當然,我們的音效還沒有初始化,也沒有設計好釋放邏輯。在App的構造和解構函式中進行這些任務:

	App(){
		//Sound init
		AudioManager::Instantiate();
		AudioManager::GetInstance().LoadFile("../Image.wav");

		_sound=AudioManager::GetInstance().NewSound();
		_sound->LoadFile("../Image.wav");
	}

	~App(){
		AudioManager::GetInstance().FreeSound(_sound);
		AudioManager::Destroy();
	}

最好在main()函式中啟動場景即可
int main()
{
	App app;
	app.Config();
	app.Run();
	return 0;
	
}

完整程式碼見我的github: pigcage的github