Irrlicht 游戏引擎开发 cooliris 界面(三)
来源:互联网 发布:下载 windows 10 编辑:程序博客网 时间:2024/05/29 09:20
(三)着重于介绍如何让场景动起来,以及如何获取和处理消息。
源码:example_3.zip
1. Irrlicht 的运动机制
所谓运动,实际上计算机在不停得绘制场景,每绘制一次称之为一帧。 当各帧中物体的位置或外观有所变化,那么它就动起来了。 在irrlicht中,绘制一帧是在run循环中完成的:
- while (device->run())
- {
- if (device->isWindowActive())
- {
- driver->beginScene(true, true, video::SColor(0, 0, 0, 0));
- scene_mgr->drawAll(); // 绘制一帧
- driver->endScene();
- }
- }
- device->drop();
while (device->run()) { if (device->isWindowActive()) { driver->beginScene(true, true, video::SColor(0, 0, 0, 0)); scene_mgr->drawAll(); // 绘制一帧 driver->endScene(); } } device->drop();
我们所要做的,就是在 drawAll() 函数中,更新物体的位置,那么场景就动起来了。
所有的一切,都与 scene::ISecenNodeAnimator 这个接口相关。凡是实现这个接口的类实例,都可以通过 addAnimator() 函数加入到 ISceneNode 的 animators 列表中。
SceneManager 的drawAll() 函数在绘制(render)场景前,会调用 OnAnimate() 函数。这个函数是递归的,以保证加入场景中的每个 SceneNode 都被调用。 在OnAnimate() 函数中, SceneNode 的每一个 ISecenNodeAnimator 的 animateNode() 函数都会被调用,以更新 SceneNode 的位置、大小或纹理。
整理其调用顺序如下:
1. SceneManager --> drawAll() 绘制一帧
2. ISceneNode --> OnAnimate() SceneNode运动
3. ISceneNodeAnimator --> animateNode(ISceneNode) 实现运动的具体函数
4. SceneManager --> render() 绘制
可见,只要实现 ISecenNodeAnimator 接口,并加入到 SceneNode 中,就能够让其动起来。
2. Irrlicht 的消息传递
Irrlicht消息的传递是从device->run()开始的,在windows中首先调用 WndProc() 收集消息,打包成其内部的SEvent结构,再由 postEventFromUser() 将消息依次传递给 UserReceiver, GUI 和 3D Scene。
Irrlicht 中所有处理消息的类都必须实现 IEventReciever 接口。UserReceiver 就是在CreateDevice函数中指定的一个 IEventReceiver。 也就是说,消息处理的优先级为 UserReceiver > GUI > 3D Scene。
在我们的程序中,并没有指定UserReceiver,暂时也没有GUI,所以消息直接交给了 SceneManager。
SceneManager 的消息,也由 ISceneManager 的 postEventFromUser() 传递。 这个函数的实现如下:
- bool CSceneManager::postEventFromUser(const SEvent& event)
- {
- bool ret = false;
- ICameraSceneNode* cam = getActiveCamera();
- if (cam)
- ret = cam->OnEvent(event);
- _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
- return ret;
- }
bool CSceneManager::postEventFromUser(const SEvent& event){bool ret = false;ICameraSceneNode* cam = getActiveCamera();if (cam)ret = cam->OnEvent(event);_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;return ret;}
也就是说,只有当前Active(有效)的 ICameraSceneNode 才会接收到消息。ICameraSceneNode 会检测ISceneNodeAnimator 的isEventReceiverEnabled(), 如果为真则调用其 OnEvent 函数。
整理消息传递的机制如下:
1. IrrlichtDevice --> run() 搜集消息并打包
2. IrrlichtDevice --> postEventFromUser() 传递消息到userReceiver GUI 和 Scene
3. ISceneManager --> postEventFromUser() 传递消息到CameraNode
4. ICameraSceneNode --> onEvent() 调用 Animator 的onEvent
5. ISceneNodeAnimator --> onEvent() // if enabled 响应消息。
可见,实现 ISceneNodeAnimator 虽然可以 SceneNode 动起来,但只有在 CameraSceneNode 上,才能够接收和处理消息。
3. 实现CameraAnimator
如果我们实现一个 CameraAnimator,在其中处理消息,更新 CameraNode 和 CubeNode 的位置,就可以实现交互和动画。该类关键的几个函数如下:
- class CameraAnimator : public ISceneNodeAnimator
- {
- public:
- //! Constructor
- CameraAnimator(...
- //! Destructor
- virtual ~CameraAnimator(){};
- //! 更新 SceneNode 位置
- virtual void animateNode(ISceneNode* node, u32 timeMs);
- //! 处理消息
- virtual bool OnEvent(const SEvent& event);
- //! This animator will receive events when attached to the active camera
- virtual bool isEventReceiverEnabled() const
- {
- return true; // 必须为true才能接收消息
- }
- .... // other member functions.
- private:
- ... // fields
- };
class CameraAnimator : public ISceneNodeAnimator{public://! ConstructorCameraAnimator(...//! Destructorvirtual ~CameraAnimator(){};//! 更新 SceneNode 位置 virtual void animateNode(ISceneNode* node, u32 timeMs);//! 处理消息 virtual bool OnEvent(const SEvent& event);//! This animator will receive events when attached to the active cameravirtual bool isEventReceiverEnabled() const{return true; // 必须为true才能接收消息} .... // other member functions.private: ... // fields};
需要实现的关键有三点:
(1) 拖拽时, 场景跟着移动
(2) 单击某个图片时,场景移动到该位置,图片突出显示
(3) 滚轮实现缩放
在编程时有以下几个关键点:
(1) 三维场景中Node的选取,还好 irrlicht 替我们实现了这点,只需如下代码,就可以将鼠标点击处的node选取出来:
- core::position2d<s32> mouse_position(event.MouseInput.X, event.MouseInput.Y);
- ISceneNode* node = scene_manager_->getSceneCollisionManager()
- ->getSceneNodeFromScreenCoordinatesBB(mouse_position, -1);
core::position2d<s32> mouse_position(event.MouseInput.X, event.MouseInput.Y);ISceneNode* node = scene_manager_->getSceneCollisionManager() ->getSceneNodeFromScreenCoordinatesBB(mouse_position, -1);
(2) 运动的实现
包括三个运动: CameraNode 的位置, CameraNode 的拍摄点,选取的图片的位置
CameraNode的位置决定了场景的移动;
CameraNode 的拍摄点决定了拍摄的角度,可以实现场景的倾斜;
被选取的图片的位置可以使其比其它图片更接近Camera,实现突出的效果。
(二)中描述的图片位置是排布在 z = 0 这个平面上的,Camera 的位置在 z = -700 平面上,初始拍摄点为原点。
camera拍摄点(target)在 X 轴移动的处理如下:
- if (is_drag_ && current_node_ == NULL) // 拖拽
- {
- core::position2di mouse_position = device_->getCursorControl()->getPosition();
- s32 xdiff = drag_start_point_.X - mouse_position.X;
- drag_start_point_ = mouse_position;
- x_target += 0.01*xdiff / core::max_(dt, 0.0000001f);
- }
- if (current_node_!=NULL) // 选取了某幅图片
- {
- x_target = current_node_->getPosition().X;
- }
- sign = (x_target < x_current) ? -1 : 1;
- x_speed = sign*sqrt(abs(x_target - x_current)) * 50; // 运动速度
- target_position.X += x_speed * dt; // 移动位置
if (is_drag_ && current_node_ == NULL) // 拖拽{core::position2di mouse_position = device_->getCursorControl()->getPosition();s32 xdiff = drag_start_point_.X - mouse_position.X;drag_start_point_ = mouse_position;x_target += 0.01*xdiff / core::max_(dt, 0.0000001f);}if (current_node_!=NULL) // 选取了某幅图片{x_target = current_node_->getPosition().X;}sign = (x_target < x_current) ? -1 : 1;x_speed = sign*sqrt(abs(x_target - x_current)) * 50; // 运动速度target_position.X += x_speed * dt; // 移动位置
在 Y 轴和 Z 轴方向的移动,包括图片的移动, 均用类似的方式处理。参见(animateNode)函数。
(3) 鼠标滚轮实现缩放
只需要改变 Camera 在Z轴的位置,作简单的限幅即可:
- z_target += 100*event.MouseInput.Wheel;
- if (z_target > -200)
- {
- z_target = -200;
- }
- if (z_target < -3000)
- {
- z_target = -3000;
- }
z_target += 100*event.MouseInput.Wheel; if (z_target > -200){z_target = -200;}if (z_target < -3000){z_target = -3000;}
4.最后
最后我们还需要将实现的 animator 附加到 Camera 上:
- scene::ICameraSceneNode* camera = scene_mgr->addCameraSceneNode(0,
- core::vector3df(0,0,-700),
- core::vector3df(0,0,0), 0);
- scene::CameraAnimator* animator = new scene::CameraAnimator(wall_mgr->GetWallItemList(),
- device, scene_mgr,
- device->getTimer()->getTime());
- camera->addAnimator(animator);
- animator->drop();
scene::ICameraSceneNode* camera = scene_mgr->addCameraSceneNode(0, core::vector3df(0,0,-700), core::vector3df(0,0,0), 0);scene::CameraAnimator* animator = new scene::CameraAnimator(wall_mgr->GetWallItemList(), device, scene_mgr, device->getTimer()->getTime());camera->addAnimator(animator);animator->drop();
编译运行后,您应该能够用鼠标实现类似 cooliris 的交互。更进一步的完善,还需要支持键盘等……
p.s.
3d 界面就介绍到这里了,也算告一段落。
还有两个部分:
(1) GUI: 介绍GUI编写,并加入Truetype中文字体支持;
(2) AnimatedGUI:介绍目录浏览控件的编写,实现一个皮肤系统。
由于临近毕业,导师催着要论文,过段时间再继续吧。
鉴于本人的学习经历,劝各位千万不要盲目读研,不要盲目崇拜名校,别被糟蹋了。
我是学机械方向的,盲目接受了当年所谓的推研资格。sigh~~学术气氛糟糕啊……
From: http://arec.iteye.com/blog/338135
- Irrlicht 游戏引擎开发 cooliris 界面(三)
- Irrlicht游戏引擎 初探
- 用C++实现跨平台游戏开发之Irrlicht引擎
- 用C++实现跨平台游戏开发之Irrlicht引擎
- 用C++实现跨平台游戏开发之Irrlicht引擎
- 用C++实现跨平台游戏开发之Irrlicht引擎
- 3D游戏引擎Irrlicht
- Irrlicht游戏引擎(V0.1)源码学习系列之一
- Irrlicht 0.1引擎源码分析与研究(三)
- Irrlicht游戏引擎初步分析与研究
- 开源3D游戏引擎Irrlicht简介
- 利用Android游戏引擎Angle开发界面
- Irrlicht引擎手册示例(三):CustomSceneNode!
- android开发(三):android界面与游戏开发
- Python游戏引擎开发(三):显示图片
- Irrlicht引擎1.3发布(3D图形游戏引擎)
- Irrlicht引擎
- 游戏引擎剖析(三)
- 不要因为英语和数学而害怕学习编程.
- Android中的状态机 机制
- 一个c#即时监控小程序
- [读书笔记]游戏引擎需要关注什么?
- Tomcat优化(心得经验)
- Irrlicht 游戏引擎开发 cooliris 界面(三)
- struts2的<s:select> 如何获取选中的值传入数据库
- Tomact配置虚拟主机
- 乐Phone开发环境搭建及开发经验分享
- MFC封装WinMain的原理
- 在tomcat中配置虚拟主机
- 【c#基础3】HttpHandler
- proxool各个属性的详细说明
- 如何用过滤器过滤HTTP协议和非HTTP协议编码