第五天:击中要害

出自Ogre3D开放资源地带

跳转到: 导航, 搜索

目录

[编辑] 前言

如果你严格按照一天一课程的进度读到这里,那么恭喜我自己,这系列教程已经写完成了。但我恐怕你用半天时间就能把所有课程看完。哦,没错,这些教程太简单了,根本没什么挑战,让人昏昏欲睡。在写这系列教程之初,我是希望写一个最简单的游戏实现,但是目前看来简单过头了。不过还有办法补救,那就是恳请你的参与。现在我们拥有了一个简单的基础,你可以在其上面大胆扩展,完成自己的作品。让这个系列教程变得精彩和丰富。还是那句话,如果心情不错,拿来社区bbs.ogre3d.cn共享。

[编辑] 正文

怎么让子弹机中的人呢,需要碰撞检测。今天我们不会加入新的元素(文件),主要是对已有文件进行修改和增加代码。


[编辑] EnemyPlanes.h

首先我们需要对敌人做一些改变,让其能更容易的被“击中”。

//4这里我们使用了单件工具,这基本和Ogre里面相同(就是抄过来的)
class EnemyPlanes : public Singleton<EnemyPlanes>

这里仍然是单件设计模式,虽然说这里使用了Orz框架中的Singleton模版类实现,不过基本上就是拷贝Ogre3D中的实现,不过也不比觉得难为情,Ogre3D中的这个类也是来自某本游戏开发书籍中的代码。


//4我们在这里添加一个被击中的函数
void attackEnemy(uint32 id);

这里添加一个函数,提供给我们的战斗机调用,通知敌人们击中了谁。

[编辑] EnemyPlanes.cpp

//2构造函数
EnemyPlanes::EnemyPlanes(Ogre::SceneManager * sm):
_all(),
_planes(),
_time(0.0f)
{
	//2创建根节点
	_all = sm->getRootSceneNode()->createChildSceneNode();
	//2遍历节点并初始化
	for(uint32 i = 0; i< _planes.size(); ++i)
	{
		//在这里省略了一些代码
		//4在这一课我们临时把包围盒显示打开,便于观察
		_planes[i]->showBoundingBox(true);
		//4设置碰撞掩码,这里与子弹碰撞检查掩码相同,表明可以被子弹击中物体。
		ent->setQueryFlags(0x2);
		//4通过Any类型设置ID,
		//4注意的是,这个ID一定要在本动态库释放前删除,否则无法正确释放。
		ent->setUserAny(Ogre::Any(static_cast<uint32>(i)));
	}
}


这里我们对敌人个体进行了三个Ogre的操作,第一个对其节点进行了showBoundingBox函数调用,这里我们让其显示包围盒,就是一个包围整个模型的立方体盒子,用来给场景管理器做碰撞检测使用。另外我们设置了QueryFlagsQueryFlags给场景中的实体附带了一个mark(掩码),用于标记不同的碰撞组。碰撞查询时候,系统只会返回有相与(&)掩码的碰撞结果,既用查询器的标记和物体的QueryFlags进行&操作,如果不是零才尝试进行碰撞建查。

最后我们给实体上面挂接了一个Any类型,Any是一个可以保存任意类型的容器,类似Boost里面的Any库。总之就是我们给每一个实体设置一个Id号,当碰撞之后得到,需要注意的是,这个Any保存的类型一定要在本动态库内释放,否则会因为系统不知道如何释放而导致崩溃。


void EnemyPlanes::attackEnemy(uint32 id)
{
	//4在这里如果被击中我们首先设为不显示,然后把碰撞掩码设为0,
	//这样现在的物体既看不见,又打不倒。当然你也可以在这里开始播放爆炸动画。
	_planes[id]->setVisible(false);
	_planes[id]->getAttachedObject(0)->setQueryFlags(0);
}

当地人被击中时候,我们得到了一个敌人的id,我们这里只简单的把这个敌人隐藏,并让他的碰撞检测为0(已经被击毁的敌人不会在被击中)。

[编辑] Bullets.h

//4用于场景查询
Ogre::SphereSceneQuery * _query;
//4记录被击中敌人ID
uint32 _enemyID;

我们给子弹对象增加两个成员变量,Ogre::SphereSceneQuery类型是Ogre3D用于碰撞检测;然后我们吧被击中的敌人的ID储存在变量_enemyID中。

[编辑] Bullets.cpp

//函数内省略一些无关的代码
Bullet::Bullet(Ogre::SceneManager * sm)
{
	//4这里把子弹的碰撞检测掩码设置为0,即子弹实体不必检查被别人碰撞
	_ent->setQueryFlags(0);
	//4我们在这里创建一个球形场景查询器,
	//用于查询场景中和子弹碰撞的物体,第二个参数是场景查询掩码.
	//它将查询指定“and”的掩码的实体。
	_query =  _sm->createSphereQuery(Ogre::Sphere(), 0x2);
}

我们在构造函数中新加入两行,我们把子弹实体的QueryFlags设置为0,代表任何碰撞查询都不返回结果,即子弹不能被别人碰撞。然后我们又创建了一个球形场景查询器作为子弹碰撞检测使用,这里我们把第二个参数(即其碰撞检测参数)设置为2,代表子弹可以击中掩码包含2的所有实体。相关细节可以参考Ogre相关教程。

//4检查子弹是否击中敌人
bool Bullet::touch(void)
{

	//4设置一个圆心为子弹位置,半径极小的球体作为子弹碰撞体
	_query->setSphere(Ogre::Sphere(_node->getPosition(), 0.01f));
	//4执行碰撞,并返回结果
	Ogre::SceneQueryResult qres =  _query->execute();
	//4如果碰撞结果不是为空
	if(!qres.movables.empty())
	{ 
		//4我们把第一个碰撞物体所储存的ID取出
		const Ogre::Any& any = (*qres.movables.begin())->getUserAny();
		//4保存ID
		_enemyID = any.operator()<uint32>();
		//4碰撞成功,返回true
 		return true;
	}
	//4否则碰撞失败返回false
	return false;
} 

我们在touch函数中执行碰撞检查,通过Ogre提供的场景碰撞检查,检查子弹是否击中敌人。如果检测成功,我们从返回的结果中取出敌人的ID,保存备用。换句话说,这就是杀伤敌人身上的“狗牌”。如果击中敌人返回true否则返回false。


Bullet::~Bullet(void)
{
	//4销毁球形查询器
	_sm->destroyQuery(_query);
}

好了,别忘了在最后销毁查询器。


//函数里面省略了一些之前的代码
void Bullet::update(void)
{
	//4当子弹的y值大于400的时候,我们认为子弹已经飞出屏幕,在这里收回子弹
	if(_node->getPosition().y > 400)
	{
		_ent->setVisible(false);
		_active = false;
	}
}

当子弹飞出屏幕的时候,我们把子弹设置为非激活状态,也就是收回子弹到弹夹,毕竟我们的子弹也不多。

bool Bullet::flying(void)
{
	//4如果子弹仍然显示,我们认为仍然在飞行状态中
	return _ent->getVisible();
}

我们现在认为当子弹显示的时候就是在飞行中,否则要不就是没有激活,或者是爆炸状态。


void Bullet::burst(void)
{
	//4子弹被引爆,这里我们直接释放子弹,但是我们也可以在这里设置开始播放爆炸动画
	_ent->setVisible(false);
	_active = false;
}

引爆子弹,我们这里什么也没做过,不过你可以修改一下,播放一些粒子系统作为爆炸特效。

[编辑] BattlePlane.cpp

//4这里我们为了对应碰撞检查设置了掩码,默认情况下是掩码为全开,
//即什么都能碰撞到,我们这里设置是防止子弹打到自己
	ent->setQueryFlags(0x1);

在构造战斗机的时候,我没别忘了把战斗机的QueryFlags设置为1,这样就保证不会和自己发射的子弹碰撞了,不过敌人可以发射掩码为1的子弹,这样就可以被敌人击中。


void BattlePlane::update(void)
{
	//4寻找击中敌人的子弹
	while(Bullet* bullet = _bulletManager.touch())
	{
		//4如果子弹击中敌人,把敌人的ID通知敌人管理器
		EnemyPlanes::getSingleton().attackEnemy(bullet->getEnemyID());
	}
}

在每一次更新的时候,我们的战斗机应该检查一下是否有子弹击中敌人。如果有的话,我们就得到这个子弹,然后询问被击中敌人的ID,并告知敌人的管理器,让其销毁(也或者其他处理,比如损失生命值)。


[编辑] 课程结语

好了,可能这是最麻烦的一个课程了,虽然没有添加什么文件,但是增加了很多分散的代码。建议你如果有什么觉得奇怪的地方编译运行程序,跟踪一下好了。或者到bbs.ogre3d.cn上面讨论一下。

为了简单,我们尽量把所有知识点都只实现一边,所以说,既然我们有子弹了,就不会做敌人的子弹。但是这不是一个完整的游戏,需要你的修改才可以。