第五天:击中要害
出自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函数调用,这里我们让其显示包围盒,就是一个包围整个模型的立方体盒子,用来给场景管理器做碰撞检测使用。另外我们设置了QueryFlags,QueryFlags给场景中的实体附带了一个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上面讨论一下。
为了简单,我们尽量把所有知识点都只实现一边,所以说,既然我们有子弹了,就不会做敌人的子弹。但是这不是一个完整的游戏,需要你的修改才可以。
