OGRE+CEGUI游戏教程(2)----NPC对话演示

转载请注明出处:http://blog.csdn.net/pizzazhang

  源码和可执行程序链接http://code.google.com/p/pizzaprojects/downloads/list

  这篇文章将使用Ogre Wiki上Base框架和自己的GUI框架来做一个NPC对话的演示例子。

  主要实现的效果是: GUI窗口的响应、GUI窗口动画、Ogre轨迹动画实现镜头的转移。

  先看下效果图(因为是转换的GIF,比较模糊, 可惜这里不支持插入视频 = =!)

 

 

可以看到, 当鼠标点击NPC(兽头 = =!)后,镜头便会拉近,然后弹出一个对话窗口, 对话文本缓缓出现,退出游戏的时候(ESC),会有一个退出菜单缓缓降下。

下面就来实现这个demo。

首先我们需要BaseApplication.h和BaseApplication.cpp 以及 MyGUISystem.h、 MyGUISystem.cpp

在一个新文件,比如Demo.cpp中 我们来使用这2个框架。

先贴上代码,然后再解释关键部分。

Demo.cpp

//  [4/4/2011 Pizza]
#include "BaseApplication.h"
#include "MyGUISystem.h"
using namespace Ogre;
class Demo : public BaseApplication
{
	enum QueryMask
	{
		QUERY_MASK = 1<<0,			// 可查询
		HIDE_MASK = 1<<1			// 不可查询
	};
public:
	Demo()
		:mMouseLeftClicked(false), mStartTrack(false), mRaySceneQuery(0), mExitUI(false)
	{}
	virtual ~Demo()
	{
		if(mRaySceneQuery)
		{
			mSceneMgr->destroyQuery(mRaySceneQuery);
		}
	}
	bool frameRenderingQueued(const Ogre::FrameEvent& evt)
	{
		if(mShutDown)
			return false;
		if(mStartTrack)
		{
			mAnimState->addTime(evt.timeSinceLastFrame);
		}
		MyGUISystem::getSingletonPtr()->update(evt.timeSinceLastFrame);
		return BaseApplication::frameRenderingQueued(evt);
	}
	void createScene()
	{
		//创建射线查询
		mRaySceneQuery = mSceneMgr->createRayQuery(Ray());
		//设置GUI
		setupGUI();
		//灯光设置
		setupLight();
		//天空盒
		mSceneMgr->setSkyBox(true, "Examples/MorningSkyBox");
		//来个标志性的兽头演示
		Entity* head = mSceneMgr->createEntity("Head", "ogrehead.mesh");
		mCurrentNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
		mCurrentNode->attachObject(head);
		head->setQueryFlags(QUERY_MASK);
		mCamNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
		mCamNode->attachObject(mCamera);
		mCamNode->setPosition(0, 0, 230);
		mCamera->setQueryFlags(HIDE_MASK);
		createTrackAnimation();
	}
	void setupGUI()
	{
		MyGUISystem::getSingletonPtr()->init();
		MyGUISystem::getSingletonPtr()->loadLayout("DialogWindow");
		MyGUISystem::getSingletonPtr()->loadLayout("Root");
		//设置图片资源
		MyGUISystem::createImageset("bgImg", "bg.jpg");
		MyGUISystem::createImageset("npcpic", "npcpic.png");
		//设置图片属性,并添加给Static Image类型的窗口
		MyGUISystem::setProperty("Root/BgImg", "Image", "set:bgImg image:full_image");
		MyGUISystem::setProperty("DialogWindow/NPCPic", "Image", "set:npcpic image:full_image");
		//一开始隐藏ESCAPE的UI
		MyGUISystem::getSingletonPtr()->getWindow("Root")->hide();
		//对话框的设置
		CEGUI::MultiLineEditbox* dialogbox = static_cast<CEGUI::MultiLineEditbox*>(MyGUISystem::getSingletonPtr()
			->getWindow("DialogWindow/DialogBgImg/DilogText"));
		//只读,无法编辑
		dialogbox->setReadOnly(true);
		dialogbox->setWordWrapping(true);
		CEGUI::String str = (CEGUI::utf8*)Ogre::UTFString(L"你好,我的朋友!我是可爱的有着绿色环保肤色的Ogre先生,有什么可以帮忙的吗?").asUTF8_c_str();
		dialogbox->setText(str);
		//从XML载入动画
		CEGUI::AnimationManager::getSingleton().loadAnimationsFromXML("ExitUI_Anim.xml");
		CEGUI::AnimationManager::getSingleton().loadAnimationsFromXML("Dialog_Anim.xml");
		//GUI事件
		createGUIEvent();
	}
	void startGUIAnim(const std::string& animName, const std::string& targetWindow)
	{
		mGUIAnim = CEGUI::AnimationManager::getSingleton().getAnimation(animName);
		//实例化动画
		mGUIAnimInst = CEGUI::AnimationManager::getSingleton().instantiateAnimation(mGUIAnim);
		//动画的应用窗口
		mGUIAnimInst->setTargetWindow(MyGUISystem::getSingletonPtr()->getWindow(targetWindow));
		//动画开始
		mGUIAnimInst->start();
	}
	void createGUIEvent()
	{
		//注册事件
		MyGUISystem::subscribeEvent("Root/BgImg/QuitBtn", CEGUI::PushButton::EventClicked, 
			CEGUI::Event::Subscriber(&Demo::quit, this));
		MyGUISystem::subscribeEvent("DialogWindow/DialogBgImg/OkBtn", CEGUI::PushButton::EventClicked, 
			CEGUI::Event::Subscriber(&Demo::know, this));
	}
	//设置灯光
	void setupLight()
	{
		mSceneMgr->setAmbientLight(ColourValue(0.3, 0.3, 0.3));
		mSceneMgr->createLight()->setPosition(20, 80, 50);
	}
	
	//	创建轨迹动画
	void createTrackAnimation()
	{
		//设置3秒的动画
		Animation* anim = mSceneMgr->createAnimation("CameraTrack", 3);
		//设置动画的Translation变化模式为样条线变化
		anim->setInterpolationMode(Animation::IM_SPLINE);
		// 创建节点轨迹动画
		NodeAnimationTrack* track = anim->createNodeTrack(0, mCamNode);
		// 关键帧信息
		track->createNodeKeyFrame(0)->setTranslate(Vector3(0, 0, 230));
		track->createNodeKeyFrame(1)->setTranslate(Vector3(-15, 0, 120));
		track->createNodeKeyFrame(2)->setTranslate(Vector3(-20, 0, 90));
		track->createNodeKeyFrame(3)->setTranslate(Vector3(-22, 0, 70));
		mAnimState = mSceneMgr->createAnimationState("CameraTrack");
	}
	/*
	*开始轨迹动画
	params
	@trackNode     动画跟踪节点
	@start		   是否开始动画
	@loop			动画是否循环
	 */
	void startTrackAnimation(SceneNode* trackNode, bool start, bool loop = false)
	{
		//设置摄像机的自动跟踪节点
		mCamera->setAutoTracking(start, trackNode);
		// 设置动画状态
		mAnimState->setTimePosition(0);
		mAnimState->setEnabled(start);
		mAnimState->setLoop(loop);
	}
	/*
	*	键盘鼠标事件
	*/
	bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
	{
		MyGUISystem::getSingletonPtr()->injectMouseButtonDown(MyGUISystem::convertButton(id));
		if(id == OIS::MB_Left)
		{
			if(!mStartTrack && !mExitUI)
			{
				mMouseLeftClicked = true;
				CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();
				Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.d_x/float(arg.state.width),
					mousePos.d_y/float(arg.state.height));
				mRaySceneQuery->setRay(mouseRay);
				//按深度排序,得到最近的物体
				mRaySceneQuery->setSortByDistance(true);
				mRaySceneQuery->setQueryMask(QUERY_MASK);
				RaySceneQueryResult& result = mRaySceneQuery->execute();
				RaySceneQueryResult::iterator itr = result.begin();
				if(itr != result.end() && itr->movable)
				{
					mCurrentNode = itr->movable->getParentSceneNode();
					mCurrentNode->showBoundingBox(true);
					//开始轨迹动画
					mStartTrack = true;
					startTrackAnimation(mCurrentNode, true);
					startGUIAnim("DialogAnim", "DialogWindow/DialogBgImg/DilogText");
					MyGUISystem::getSingletonPtr()->loadLayout("DialogWindow");
				}
				else
				{
					mCurrentNode->showBoundingBox(false);
					mCamera->setAutoTracking(false);
				}
			}
		}
		return true;
	}
	bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
	{
		MyGUISystem::getSingletonPtr()->injectMouseButtonUp(MyGUISystem::convertButton(id));
		if(id == OIS::MB_Left)
			mMouseLeftClicked = false;
		return true;
	}
	bool mouseMoved( const OIS::MouseEvent &arg )
	{
		MyGUISystem::getSingletonPtr()->injectMouseMove(arg.state.X.rel, arg.state.Y.rel);
		return true;
	}
	bool keyPressed( const OIS::KeyEvent &arg )
	{
		if(arg.key == OIS::KC_ESCAPE)
		{
			mExitUI = !mExitUI;
			if(mExitUI && !mStartTrack)
			{
				startGUIAnim("ExitUIAnim", "Root/BgImg");
				MyGUISystem::getSingletonPtr()->loadLayout("Root");
			}
			else
			{
				MyGUISystem::getSingletonPtr()->getWindow("Root")->hide();
			}
		}
		return true;
	}
private:
	bool quit(const CEGUI::EventArgs& e)
	{
		mShutDown = true;
		return true;
	}
	bool know(const CEGUI::EventArgs& e)
	{
		MyGUISystem::getSingletonPtr()->getWindow("DialogWindow")->hide();
		mStartTrack = false;
		mExitUI = false;
		return true;
	}
private:
	bool						 mMouseLeftClicked;	//鼠标是否左击
	RaySceneQuery*				 mRaySceneQuery;	//射线查询
	SceneNode*					 mCurrentNode;		//当前点击节点
	AnimationState*				 mAnimState;		//Ogre实体动画状态
	CEGUI::Animation*			 mGUIAnim;			//GUI动画
	CEGUI::AnimationInstance*	 mGUIAnimInst;		//GUI动画实例
	bool						 mStartTrack;		//是否开始轨迹动画
	SceneNode*					 mCamNode;			//摄像机节点
	bool						 mExitUI;			//是否是退出UI
};
INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, INT)
{
	Demo demo;
	try
	{
		demo.go();
	}
	catch(Exception& e)
	{
		MessageBox(NULL, e.getFullDescription().c_str(), "Ogre Exception", MB_OK);
	}
}


 

首先,这个轨迹动画的实现, 步骤为:

mSceneMgr->createAnimation   创建动画

setInterpolationMode(Animation::IM_SPLINE)设置动画移动变换的插值模式: IM_SPLINE是样条线插值,就是使用一条不规则的样条线定义一些列关键帧。

anim->createNodeTrack(0, mCamNode) 创建节点轨迹,第二个参数指定轨迹运动的节点,这里需要让镜头移动,所以绑定镜头节点。

createNodeKeyFrame(0)->setTranslate 接下来就是设置关键帧信息了。 每个关键帧都使用的是移动的变换。

最后创建动画状态,createAnimationState, 我们使用动画的时候,都是引用这个动画状态来实现动画。

动画的使用很简单:// 设置动画状态

mAnimState->setTimePosition(0);

mAnimState->setEnabled(start);

mAnimState->setLoop(loop);

第一句设置当前的时间轴为0, 就是动画从头开始。

接下来我们需要实现点击NPC后才开始轨迹动画。 所以这里使用了一个射线查询来得到点击的物体。 注意需要使用一个遮罩把摄像机设置为不查询, 否则很可能第一个查询的总是摄像机而不是我们想要的物体。

if(itr != result.end() && itr->movable)

{

mCurrentNode = itr->movable->getParentSceneNode();

mCurrentNode->showBoundingBox(true);

//开始轨迹动画

mStartTrack = true;

startTrackAnimation(mCurrentNode, true);

startGUIAnim("DialogAnim", "DialogWindow/DialogBgImg/DilogText");

MyGUISystem::getSingletonPtr()->loadLayout("DialogWindow");

}

以上代码基本就是查询到物体后, 显示包围框,然后开始镜头的运动并弹出对话窗口。

接下来是GUI的动画系统。在CEGUI中使用动画其实也很简单, 在可以定位到的资源路径下新建你的动画脚本, 比如我定义一个对话框的动画效果, 我就新建一个Dialog_Anim.xml

然后写上:

<Animations>
  <AnimationDefinition name="DialogAnim" duration="2" replayMode="once">
    <Affector property="Alpha" interpolator="float">
      <KeyFrame position="0" value="0.5" />
      <KeyFrame position="2" value="1.0" />
    </Affector>
    <Affector property="UnifiedAreaRect" interpolator="URect">
	    <KeyFrame position="0" value="{{0.1,0},{1,0},{0.9,0},{1.4,0}}" />
	    <KeyFrame position="2" value="{{0.1,0},{0.25,0},{0.9,0},{0.65,0}}" />
    </Affector>
  </AnimationDefinition>
</Animations>


 

基本就是一个效果器和关键帧的设置。效果器里的property可以是CEGUI窗口的任意属性。 可以查阅CEGUI的API获取。

以上动画的效果是2秒内某个窗口逐渐出现并且从下面看不见的地方逐渐上升到合适的位置。 该动画播放一次(once)。

在代码中,这样使用动画:

//从XML载入动画

CEGUI::AnimationManager::getSingleton().loadAnimationsFromXML("ExitUI_Anim.xml");

CEGUI::AnimationManager::getSingleton().loadAnimationsFromXML("Dialog_Anim.xml");

然后在需要调用的地方开始动画:

mGUIAnim = CEGUI::AnimationManager::getSingleton().getAnimation(animName);

//实例化动画

mGUIAnimInst = CEGUI::AnimationManager::getSingleton().instantiateAnimation(mGUIAnim);

//动画的应用窗口

mGUIAnimInst->setTargetWindow(MyGUISystem::getSingletonPtr()->getWindow(targetWindow));

//动画开始

mGUIAnimInst->start();

这篇文章的基本内容就是这样。希望对你有所帮助。也希望有想法的同学多提意见

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CEGUI(Crazy Eddie’s GUI http://www.cegui.org.uk)是一个自由免费的GUI库,基于LGPL协议,使用C++实现,完全面向对象设计。CEGUI开发者的目的是希望能够让游戏开发人员从繁琐的GUI实现细节中抽身出来,以便有更多的开发时间可以放在游戏性上。 CEGUI的渲染需要3D图形API的支持,如OpenGL或Direct3D。另外,使用更高级的图形库也是可以的,比如OGRE、Irrlicht和RenderWare等,关键需求可以简化为二点: 纹理(Texture)的支持直接写屏(RHW的顶点格式、正交投影、或者使用shader实现) 本书截止日时,CEGUI的最新版本是0.6.0(本书的讨论也是基于此版本),本书光盘提供了SDK和全部源码的下载。 除此之外,CEGUI还同步提供了官方界面编辑器LayoutEditor和ImageSet编辑器,以方便UI和图像集的制作。作为界面编辑器,它需要系统级界面以提供编辑器操作,0.3.0版是基于MFC实现的;而在0.4.0版本以后,改为基于wxWidgets(跨平台的本地UI框架,这里的UI指Window操作系统底层,如:Windows、Unix和Mac,详见http://www.wxwidgets.org)实现。 目前将CEGUI作为游戏界面库开发的游戏已经有好多种,国内的天龙八部,巨人等游戏就是很好的例子。 CEGUI的功能是非常强大的,而且使用也非常的灵活,可以和脚本配合。可以通过配置文件自定义窗口外观。通过布局文件实现窗口布局等等特性,使得游戏的界面开发更加方便。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值