第二天:战斗机起飞
出自Ogre3D开放资源地带
目录 |
前言
非常感谢您看完第一课之后还能坚持下来,这是我对我努力的最大奖励。昨天晚上我看到论坛里面有人对我们Orz框架给予正面的评价(论坛里面第一个相关的帖子),让我兴奋的一晚上没睡好。好像自己是一个希望被表扬的小孩子一样。不过虽然鼓励可以让我很快乐,但是我们同样会重视任何建议和批评。Orz虽然是一个通用的框架,但他最核心最重要的任务是:帮助希望窥探这个领域的爱好者可以顺利的进入。
好的,让我们进入正题。
正文
好了,如果你下载的资源没什么问题,然后编译器又能正常运作,现在我们已经拥有了一个"宇宙"了。我们还缺少什么呢,一个游戏的主角——一个可以被我们操纵的家伙。
今天我们进入./Tutorial/Tutorial_1目录,打开Tutorial_vc8.sln解决方案。哦,什么,怎么和昨天的工程差不多少。也不完全一样,这里多了两个文件。好吧,开始我们今天的找茬之旅。
BattlePlane.h
//1战斗机对象,实现键盘消息监听接口
class BattlePlane : public KeyListener
{
public:
BattlePlane(Ogre::SceneManager * sm);
~BattlePlane(void);
//1当键盘按键按下
void onKeyPressed(const KeyEvent & evt);
//1当键盘按键释放
void onKeyReleased(const KeyEvent & evt);
//1更新
void update(void);
private:
Ogre::SceneNode * _node;
bool _leftDown;
bool _rightDown;
};
好家伙,一架战斗机就这么简单啊。我们来分析一下,首先看这行:
class BattlePlane : public KeyListener
这个战斗机类型继承于KeyListener(键盘监听者),哦没错这里有一个监听者设计模式。它的功能是给予这个类的实例有监听键盘消息的能力,不过等一下我们还需要注册。键盘的消息会通过:
//1当键盘按键按下 void onKeyPressed(const KeyEvent & evt); //1当键盘按键释放 void onKeyReleased(const KeyEvent & evt);
这两个接口回调给我们使用。你可能发现今天的注释上面都会有一个数字1,这是为了在之后的代码中更容易找到我们教程课时的索引。
另外几个函数很简单,分别是构造函数,析构函数,以及更新用的函数。
我们来看看其具体的实现:
BattlePlane.cpp
首先来看看构造函数,这里进行了战斗机的初始化工作。
//1战斗机构造函数
BattlePlane::BattlePlane(Ogre::SceneManager * sm):
_node(NULL),
_leftDown(false),
_rightDown(false)
{
using namespace Ogre;
//1监听键盘消息
IInputManager::getSingleton().addKeyListener(this);
//1通过飞机模型创建一个实体
Ogre::Entity *ent = sm->createEntity( "razor", "razor.mesh" );
//1创建一个节点
_node = sm->getRootSceneNode()->createChildSceneNode();
//1设置位置
_node->setPosition(0, -320, 0);
//1在创建一个节点用于调整飞机模型方向
Ogre::SceneNode * sn = _node->createChildSceneNode();
//1roll PI
sn->roll(Ogre::Radian(Ogre::Math::PI));
//1设置朝向
sn->setDirection(0.0f, 1.0f ,0.0f );
//1最后在节点上面挂接实体
sn->attachObject( ent );
}
哦,又是一些基本的Ogre3D的函数调用,创造实体,挂接节点,设置位置,旋转,设置朝向,最后挂接实体。如果你看过Ogre3D的教程或者文档你可能很快就能熟悉。如果没有看过哪么给你两条建议:1去我们的网站看看ogre3d.cn;2去我们的论坛说说bbs.ogre3d.cn。
哦慢着,这里有一行代码需要我们注意。
IInputManager::getSingleton().addKeyListener(this);
我们把飞机作为一个键盘监听者注册到输入管理器中,这行代码中包含了单件以及监听者两种设计模式的应用。
BattlePlane::~BattlePlane(void)
{
//1注销键盘监听
IInputManager::getSingleton().removeKeyListener(this);
}
别忘了在析构函数中卸载监听器。
当用户使用键盘的时候,会通过下面接口传递给我们响应消息:
void BattlePlane::onKeyPressed(const KeyEvent & evt)
{
//1处理键盘按下消息
if(evt.getKey() == KC_LEFT)
_leftDown = true;
else if(evt.getKey() == KC_RIGHT)
_rightDown = true;
}
void BattlePlane::onKeyReleased(const KeyEvent & evt)
{
//1处理键盘释放消息
if(evt.getKey() == KC_LEFT)
_leftDown = false;
else if(evt.getKey() == KC_RIGHT)
_rightDown = false;
}
这里我们只是简单的把键盘中的[←][→]两个按键的状态记录下来。
void BattlePlane::update(void)
{
//1在每一frame中更新飞机位置
if(_leftDown && _rightDown)
return;
if(_leftDown && _node->getPosition().x > -530.0f)
_node->translate(-10,0,0);
if(_rightDown&& _node->getPosition().x < 530.0f)
_node->translate(10,0,0);
}
最后,我们在更新函数中根据按键信息更新战斗机节点的位置:
- 当两个箭头按键都按下的时候,飞机不动。
- 当按下左箭头,并且x边界没有超出的时候,向左移动10各单位。
- 当按下右箭头,并且x边界没有超出的时候,向右移动10各单位。
这里我们注意到,并没有通过时间值来计算。这是因为,我们现在所写的逻辑段的更新是固定时间间隔的(可以认为是每0.015秒调用一次)。时间的同步工作框架已经帮我们做了。
其他文件修改
我们知道,Director(导演)是负责整个游戏逻辑调度的工具,所以我们要把刚刚完成的战斗机交给导演来处理。对相应文件做以下修改:
TutorialDirector.h
//1通过智能指针管理战斗机对象 boost::scoped_ptr<BattlePlane> _plane;
这里加入一行,还是通过boost的智能指针来管理战斗机类型的实体。
bool doFrame(void);//1在每一个frame调用
我们也加入了一个新的成员方法实现,在我们打开每frame更新后,框架会在每一个固定时间间隔的逻辑frame调用这个函数。之后我们会看到如何打开这个功能。
TutorialDirector.cpp
//这个函数会在初始化调用
bool TutorialDirector::doEnable(void)
{
//1在这里我们省略之前的代码
//1构造飞机类对象
_plane.reset(new BattlePlane(sm));
//1打开每frame更新
enableUpdate();
return true;
}
在导演的初始化中,我们增加了两行代码,第一行我们创建了战斗机类的实例,并交给智能指针管理。 第二行我们调用了enableUpdate()成员方法,调用之后我们才能开启每Frame更新doFrame的调用。
bool TutorialDirector::doFrame(void)
{
//每fream更新战斗机状态(这里的frame是定帧的,没帧间隔0.015s)
_plane->update();
return true;
}
在每一frame中,我们都会更新战斗机的状态,返回值表明是否成功(返回false表示失败。)
最后我们在销毁的时候析构战斗机的实例:
//销毁之前调用
void TutorialDirector::doDisable(void)
{
//1销毁飞机
_plane.reset(static_cast<BattlePlane*>(NULL));
}
好了,这就是所有的区。
然后编译运行。嗯... 我们的战斗机在宇宙中飞行了(虽然只能平飞)。没有什么值得炫耀的,但是总算可以操作了。对了,忘了告诉你,在目前的框架中Esc按键可以直接退出程序。
课程结语
好了,第二天的课程也完成了。
今天我们接触了两个设计模式监听者和单件,这是在程序实现中比较常用的两个模式,如果对这方面感兴趣的朋友,可以单独看看设计模式方面的书籍。
就像我们所看到的,目前Orz框架提供了输入系统的简单封装(当前底层使用了OIS库),我们这里只是用了键盘。Orz框架第一设计目标是灵活,第二是易用,同时兼顾效率。
那,透漏一下第三课的内容吧:敌人来了!
