基于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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值