转载请注明出处:http://blog.csdn.net/pizzazhang
源码和可执行程序链接http://code.google.com/p/pizzaprojects/downloads/list
这次主要做了一个角色创建的小Demo。 用了火炬之光里面的图片资源(谁让这个游戏是用Ogre开发的呢,大爱)。
- 使用了 CEGUI的ImageButton
- 使用了文本窗口的一些特殊属性
- 使用了输入框输入英文(暂时不支持输入法)。
效果图:选择角色, 第一个头像图片是鼠标移动上去的效果。
第二个头像图片是鼠标点击后的效果
点击头像后,会有人物出现。人物会原地播放动画。 当在输入框中输入名字的时候, 会出现开始按钮。 如果删除掉名字, 开始按钮消失。
开始游戏后,载入另一个UI
这是选择另一个人物后载入的UI
整体动态效果图:
下面是实现过程。
先把代码贴上:
Main.cpp
- #include "MyGUISystem.h"
- #include "BaseApplication.h"
- using namespace Ogre;
- class Demo : public BaseApplication
- {
- enum CharaType
- {
- CHARA_A,
- ChARA_B,
- CHARA_C
- };
- public:
- Demo()
- :mCharaAnimState1(0), mCharaAnimState2(0)
- { }
- virtual ~Demo()
- { }
- bool frameRenderingQueued(const Ogre::FrameEvent& evt)
- {
- if(mShutDown)
- return false;
- //这里需要update否则Tooltip不会显示
- MyGUISystem::getSingletonPtr()->update(evt.timeSinceLastFrame);
- //这里需要addTime才能使动画动起来
- if(mCharaAnimState1)
- mCharaAnimState1->addTime(evt.timeSinceLastFrame);
- if(mCharaAnimState2)
- mCharaAnimState2->addTime(evt.timeSinceLastFrame);
- return BaseApplication::frameRenderingQueued(evt);
- }
- void createScene()
- {
- mCharaNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
- mCharaEntA = mSceneMgr->createEntity("sinbad", "sinbad.mesh");
- mCharaEntB = mSceneMgr->createEntity("jaiqua", "jaiqua.mesh");
- mCharaEntC = mSceneMgr->createEntity("ninja", "ninja.mesh");
- createParticle();
- setupGUI();
- }
- void setupGUI()
- {
- MyGUISystem::getSingletonPtr()->init();
- //角色图片按钮的Imageset创建
- CEGUI::ImagesetManager::getSingleton().create("chara_image.imageset");
- //载入角色创建窗口
- MyGUISystem::getSingletonPtr()->loadLayout("CharaCreateWindow");
- MyGUISystem::getSingletonPtr()->getWindow("StartBtn")->hide();
- //创建图片资源
- MyGUISystem::createImageset("CharaIntroA", "chara_introa.tga");
- MyGUISystem::createImageset("CharaIntroB", "chara_introb.tga");
- MyGUISystem::createImageset("CharaIntroC", "chara_introc.tga");
- //从XML载入动画
- CEGUI::AnimationManager::getSingleton().loadAnimationsFromXML("Intro_Anim.xml");
- createGUIEvent();
- }
- void createGUIEvent()
- {
- MyGUISystem::subscribeEvent("CharaImgBtnA", CEGUI::PushButton::EventClicked,
- CEGUI::Event::Subscriber(&Demo::charaSelctedHandler, this));
- MyGUISystem::subscribeEvent("CharaImgBtnB", CEGUI::PushButton::EventClicked,
- CEGUI::Event::Subscriber(&Demo::charaSelctedHandler, this));
- MyGUISystem::subscribeEvent("CharaImgBtnC", CEGUI::PushButton::EventClicked,
- CEGUI::Event::Subscriber(&Demo::charaSelctedHandler, this));
- //当输入框中的文本变化时触发的事件
- MyGUISystem::subscribeEvent("NameInputbox", CEGUI::Editbox::EventTextChanged,
- CEGUI::Event::Subscriber(&Demo::inputAcceptedHandler, this));
- MyGUISystem::subscribeEvent("StartBtn", CEGUI::PushButton::EventClicked,
- CEGUI::Event::Subscriber(&Demo::startHandler, this));
- }
- void createLight()
- {
- mSceneMgr->setAmbientLight(ColourValue(0.3, 0.3, 0.3));
- mSceneMgr->createLight()->setPosition(20, 80, 50);
- }
- void createParticle()
- {
- //创建一个粒子系统
- Ogre::ParticleSystem* particle = mSceneMgr->createParticleSystem("chara_particle", "chara_particle");
- Ogre::SceneNode* particleNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
- particleNode->attachObject(particle);
- particleNode->setPosition(0, -25, 0);
- }
- void createChara(CharaType charaType)
- {
- //因为只用一个场景节点绑定个对象,所以这里要重置所有位置、方位和缩放参数
- mCharaNode->detachAllObjects();
- mCharaNode->resetOrientation();
- mCharaNode->setPosition(0, 0, 0);
- mCharaNode->setScale(1, 1, 1);
- //根据角色类型创建角色,并设定参数使得正好在屏幕中间出现。大小适中。
- switch(charaType)
- {
- case CHARA_A:
- mCharaType = CHARA_A;
- mCharaNode->attachObject(mCharaEntA);
- mCharaNode->setScale(3, 3, 3);
- //sinbad有上下2部分的Idle动画,所以这里分开使用
- mCharaAnimState1 = mCharaEntA->getAnimationState("IdleBase");
- mCharaAnimState2 = mCharaEntA->getAnimationState("IdleTop");
- mCharaAnimState2->setLoop(true);
- mCharaAnimState2->setEnabled(true);
- break;
- case ChARA_B:
- mCharaType = ChARA_B;
- mCharaNode->attachObject(mCharaEntB);
- mCharaNode->setScale(2, 2, 2);
- mCharaNode->setPosition(0, -15, 0);
- mCharaNode->yaw(Ogre::Degree(180));
- mCharaAnimState1 = mCharaEntB->getAnimationState("Turn");
- mCharaAnimState2 = 0;
- break;
- case CHARA_C:
- mCharaType = CHARA_C;
- mCharaNode->attachObject(mCharaEntC);
- mCharaNode->setScale(0.2, 0.2, 0.2);
- mCharaNode->setPosition(0, -15, 0);
- mCharaNode->yaw(Ogre::Degree(180));
- mCharaAnimState1 = mCharaEntC->getAnimationState("Idle3");
- mCharaAnimState2 = 0;
- break;
- }
- if(mCharaAnimState1)
- {
- mCharaAnimState1->setLoop(true);
- mCharaAnimState1->setEnabled(true);
- }
- }
- private:
- bool charaSelctedHandler(const CEGUI::EventArgs& e)
- {
- //根据参数e得到获得事件的窗口
- const CEGUI::WindowEventArgs& windowArgs = static_cast<const CEGUI::WindowEventArgs&>(e);
- CEGUI::String charaType = windowArgs.window->getName();
- CEGUI::String charaDetail;
- if(charaType == "CharaImgBtnA")
- {
- charaDetail = (CEGUI::utf8*)Ogre::UTFString(L"哦,朋友,我是厉害的Sinbad战士。我帅不?").asUTF8_c_str();
- createChara(CHARA_A);
- }
- if(charaType == "CharaImgBtnB")
- {
- charaDetail = (CEGUI::utf8*)Ogre::UTFString(L"别看我是女人,我是很厉害的神箭手哦:)").asUTF8_c_str();
- createChara(ChARA_B);
- }
- if(charaType == "CharaImgBtnC")
- {
- charaDetail = (CEGUI::utf8*)Ogre::UTFString(L"别看我是忍者,其实我是大法师,只是/n伪装成忍者= =!").asUTF8_c_str();
- createChara(CHARA_C);
- }
- MyGUISystem::getSingletonPtr()->getWindow("CharaDetailText")->setText(charaDetail);
- return true;
- }
- bool startHandler(const CEGUI::EventArgs& e)
- {
- //清理工作
- mSceneMgr->clearScene();
- //这里需要清除指针,否则退出会有错误
- mCharaAnimState1 = mCharaAnimState2 = 0;
- //场景开始,载入介绍GUI
- MyGUISystem::getSingletonPtr()->loadLayout("IntroWindow");
- switch(mCharaType)
- {
- case CHARA_A:
- MyGUISystem::setProperty("IntroTopImg", "Image", "set:CharaIntroA image:full_image");
- break;
- case ChARA_B:
- MyGUISystem::setProperty("IntroTopImg", "Image", "set:CharaIntroB image:full_image");
- break;
- case CHARA_C:
- MyGUISystem::setProperty("IntroTopImg", "Image", "set:CharaIntroC image:full_image");
- break;
- }
- //介绍文本
- CEGUI::String str = (CEGUI::utf8*)Ogre::UTFString(L"很久很久以前......").asUTF8_c_str();
- MyGUISystem::getSingletonPtr()->getWindow("IntroText")->setText(str);
- //得到动画
- CEGUI::Animation* anim = CEGUI::AnimationManager::getSingleton().getAnimation("IntroAnim");
- //实例化动画
- CEGUI::AnimationInstance* inst = CEGUI::AnimationManager::getSingleton().instantiateAnimation(anim);
- //动画的应用窗口
- inst->setTargetWindow(MyGUISystem::getSingletonPtr()->getWindow("IntroText"));
- //动画开始
- inst->start();
- return true;
- }
- bool inputAcceptedHandler(const CEGUI::EventArgs& e)
- {
- mCharaName = MyGUISystem::getSingletonPtr()->getWindow("NameInputbox")->getText().c_str();
- if(mCharaName != "")
- {
- //如果输入框有文本的话,显示开始按钮
- MyGUISystem::getSingletonPtr()->getWindow("StartBtn")->show();
- }else
- {
- //否则隐藏按钮
- MyGUISystem::getSingletonPtr()->getWindow("StartBtn")->hide();
- }
- return true;
- }
- bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
- {
- MyGUISystem::getSingletonPtr()->injectMouseButtonDown(MyGUISystem::convertButton(id));
- return true;
- }
- bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
- {
- MyGUISystem::getSingletonPtr()->injectMouseButtonUp(MyGUISystem::convertButton(id));
- return true;
- }
- bool mouseMoved( const OIS::MouseEvent &arg )
- {
- MyGUISystem::getSingletonPtr()->injectMouseMove(arg.state.X.rel, arg.state.Y.rel);
- return true;
- }
- //这里需要注入keychar和键盘事件才能在Editbox中输入字符(不接受输入法)
- bool keyPressed( const OIS::KeyEvent &arg )
- {
- CEGUI::System::getSingleton().injectKeyDown(arg.key);
- CEGUI::System::getSingleton().injectChar(arg.text);
- if(arg.key == OIS::KC_ESCAPE)
- mShutDown = true;
- return true;
- }
- bool keyReleased( const OIS::KeyEvent &arg )
- {
- CEGUI::System::getSingleton().injectKeyUp(arg.key);
- return true;
- }
- private:
- Ogre::String mCharaName; //角色名字
- Ogre::Entity* mCharaEntA; //角色A
- Ogre::Entity* mCharaEntB; //角色B
- Ogre::Entity* mCharaEntC; //角色C
- Ogre::SceneNode* mCharaNode; //角色节点
- Ogre::AnimationState* mCharaAnimState1; //角色动画状态
- Ogre::AnimationState* mCharaAnimState2; //备用角色动画状态
- CharaType mCharaType; //保存选择的角色类型
- };
- INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, INT)
- {
- Demo demo;
- try
- {
- demo.go();
- }
- catch(Ogre::Exception& e)
- {
- MessageBox(0, e.getFullDescription().c_str(), "Exception", MB_OK);
- }
- }
代码没有什么特别的解释。 这个Demo的实现主要是CEGUI的ImageButton的使用。
首先, 使用官方的Imageset编辑器对一张图片进行编辑,实际操作也就是把一张图片进行切片。 每个部分都在一个文件中定义一个UV坐标。这个Demo里使用的Imageset文件如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <Imageset Name="CharaImageset" Imagefile="./logo.tga" >
- <Image Name="Logo" XPos="208" YPos="0" Width="608" Height="146" />
- <Image Name="HoverA" XPos="0" YPos="553" Width="128" Height="128" />
- <Image Name="HoverB" XPos="129" YPos="553" Width="128" Height="128" />
- <Image Name="HoverC" XPos="256" YPos="552" Width="128" Height="128" />
- <Image Name="NormalA" XPos="0" YPos="424" Width="128" Height="128" />
- <Image Name="NormalB" XPos="129" YPos="424" Width="128" Height="128" />
- <Image Name="NormalC" XPos="257" YPos="425" Width="128" Height="128" />
- <Image Name="PushedA" XPos="1" YPos="682" Width="128" Height="128" />
- <Image Name="PushedB" XPos="129" YPos="682" Width="128" Height="128" />
- <Image Name="PushedC" XPos="256" YPos="681" Width="128" Height="128" />
- <Image Name="HoverStart" XPos="255" YPos="255" Width="125" Height="50" />
- <Image Name="NormalStart" XPos="252" YPos="255" Width="125" Height="50" />
- <Image Name="PushedStart" XPos="252" YPos="255" Width="125" Height="50" />
- <Image Name="Intro_Bottom" XPos="2" YPos="334" Width="1006" Height="90" />
- </Imageset>
接下来就是在Layout文件中使用这个Imageset的各个图片资源了。
火炬之光里的创建角色的角色按钮使用的是RadioButton, 由于ImageButton在游戏里用的最多,所以我打算用ImageButton实现角色选择的按钮。
下面展示Layout文件中某一个ImageButton的使用:
- <Window Type="TaharezLook/ImageButton" Name="CharaImgBtnA" >
- <Property Name="Font" Value="SimHei-14" />
- <Property Name="Tooltip" Value="杩欐槸瑙掕壊A" />
- <Property Name="HoverImage" Value="set:CharaImageset image:HoverA" />
- <Property Name="NormalImage" Value="set:CharaImageset image:NormalA" />
- <Property Name="PushedImage" Value="set:CharaImageset image:PushedA" />
- <Property Name="UnifiedAreaRect" Value="{{0.14953,0},{0.0922273,0},{0.252194,0},{0.289911,0}}" />
- <Window Type="TaharezLook/StaticText" Name="CharaTextA" >
- <Property Name="Font" Value="SimHei-14" />
- <Property Name="Text" Value="瑙掕壊A" />
- <Property Name="HorzExtent" Value="58" />
- <Property Name="VertExtent" Value="16.25" />
- <Property Name="TextColours" Value="tl:FF0000FF tr:FF0000FF bl:FF0000FF br:FF0000FF" />
- <Property Name="FrameEnabled" Value="False" />
- <Property Name="HorzFormatting" Value="HorzCentred" />
- <Property Name="UnifiedAreaRect" Value="{{0.00228956,0},{-0.0175811,0},{1.00038,0},{0.97344,0}}" />
- <Property Name="BackgroundEnabled" Value="False" />
- <Property Name="MousePassThroughEnabled" Value="True" />
- </Window>
- </Window>
先定义一个ImageButton, 然后设置它的属性:
HoverImage: 鼠标移到上面的时候显示的图片
NormalImage: 平常时候显示的图片
PushedImage: 鼠标点击后显示的图片
Tooltip是鼠标停留在这个控件上一段时间后出现的提示文本。
以这个ImageButton为父窗口,建立一个StaticText, 这个文本窗口我们是用来显示按钮的文字的。由于ImageButton不能直接定义文本,要么通过图片直接绘制文本,要么在它上面建立一个文本窗口。
文本窗口既然在ImageButton上面,那么就需要对它设置一些特殊属性,否则我们点击ImageButton就得不到响应。
首先去除文本窗口的 Background和Frame, 就是背景和边框。
然后要使底下的ImageButton响应操作,需要设置上面的窗口MousePassThroughEnabled属性为True。 这个属性会忽略设置窗口的鼠标操作而传递到下面的控件。
在Main.cpp代码中, 使用EventTextChanged来响应文本的输入变化。 如果文本框里的文本变化了(即玩家输入了名字)那么开始按钮就会出现。 对Window调用show()和hide()就可以控制窗口的显示与否。
当选择了一个角色后,场景中间会建立一个人物实体。 可以只用一个场景节点来实现角色的切换的效果。只有当一个实体依附(attach)在一个节点时,场景中才会绘制出这个实体。所以我们建立3个人物的实体, 需要显示某个的时候Attach就行。
最后把这个例子中所需要的资源(layout、 imageset、animationXML文件以及材质、纹理图片等)放入Ogre可以定位的路径下。 比如就放在exe的目录中, 然后在resource配置中在一个资源组中添加 FileSystem=./
希望本文对你有所帮助。