关闭

OGRE+CEGUI游戏教程(5)--物品/装备和技能系统

标签: 游戏layoutimagesystemexceptionuser
2317人阅读 评论(2) 收藏 举报
分类:

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

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

 

一般游戏只要是类RPG的,都会有一套装备/物品系统,还有技能系统。 玩火炬之光的同时,我在想它的装备/物品系统和技能系统是如何实现的。 由于火炬之光使用的是CEGUI, 所以我找到它的ui文件, 发现它的技能系统使用的是一个layout文件,但鼠标移到技能上的时候,有点像一个Tooltip渐入,然后显示(带图片和字体),鼠标移开后又消失。感觉上非常像一个Tooltip。

  如果是纯粹的一个layout进行显示/隐藏的话,那么每个物品/装备/技能栏都需要注册一个MouseEnterArea事件,而且这个layout必须得到这些槽绑定的物品/技能对象的数据。

  如果是Tooltip的话,必须自己重写Tooltip系统,让Tooltip的调用改用layout而不是固有的LookNFeel中的Tooltip样式。

  由于重写一个Tootip对于我来说还是有些难度的,所以我询问了CEGUI论坛的“大牛”们,得到了一种不错的解决方案:

http://www.cegui.org.uk/phpBB2/viewtopic.php?f=10&t=5549

 

下面是运用这种方法得到的效果图:


这里是一个简单的示范,基本的功能是:

鼠标Hover到一个槽中的时候,显示这个槽附带物品/技能的说明,并且可以使用中文。

点击装备的时候,装备可以附加到正确的装备槽中

 

那么如何实现呢?对于显示带图片和颜色字体的Tooltip的话,上面那个CEGUI论坛链接有详细的说明, 先看下我根据他的思想写的代码:

  1. #include "BaseApplication.h"  
  2. #include "MyGUISystem.h"  
  3. #include "Item.h"  
  4. #include "Skill.h"  
  5. #include <vector>  
  6. class Demo : public BaseApplication  
  7. {  
  8. public:  
  9.     bool frameRenderingQueued(const Ogre::FrameEvent& evt)  
  10.     {  
  11.         if(mShutDown)  
  12.             return false;  
  13.         //这里需要update否则Tooltip不会显示  
  14.         MyGUISystem::getSingletonPtr()->update(evt.timeSinceLastFrame);  
  15.         return BaseApplication::frameRenderingQueued(evt);  
  16.     }  
  17.     void createScene()  
  18.     {  
  19.         createItems();  
  20.         createSkills();  
  21.         setupGUI();  
  22.     }  
  23.     void setupGUI()  
  24.     {  
  25.         MyGUISystem::getSingletonPtr()->init();  
  26.         //创建图片资源  
  27.         CEGUI::ImagesetManager::getSingleton().create("items2.imageset");  
  28.         CEGUI::ImagesetManager::getSingleton().create("skill.imageset");  
  29.         MyGUISystem::getSingletonPtr()->loadLayout("GameUI");  
  30.         createGUIEvents();  
  31.         CEGUI::Window* itemWidget = MyGUISystem::getSingletonPtr()->getWindow("InvBottomSlot1");  
  32.         //这里传递一个User字串映射来装备到特定槽中  
  33.         itemWidget->setUserString("type""Weapon");  
  34.         //格式化信息  
  35.         formatInvTooltip(itemWidget, mItems[0]);  
  36.         CEGUI::Window* skillWidget = MyGUISystem::getSingletonPtr()->getWindow("SkillSlot1");  
  37.         formatSkillTooltip(skillWidget, mSkills[0]);  
  38.     }  
  39.     void createGUIEvents()  
  40.     {  
  41.         MyGUISystem::subscribeEvent("InvBottomSlot1", CEGUI::Window::EventMouseClick,   
  42.             CEGUI::Event::Subscriber(&Demo::onEquip, this));  
  43.     }  
  44.     bool onEquip(const CEGUI::EventArgs& args)  
  45.     {  
  46.         const CEGUI::WindowEventArgs& WindowArgs = static_cast<const CEGUI::WindowEventArgs&>(args);  
  47.         if(WindowArgs.window->getUserString("type") == "Weapon")  
  48.         {  
  49.             //得到User字串传递过来的信息来进行操作  
  50.             CEGUI::Window* invBottom = MyGUISystem::getSingletonPtr()->getWindow("InventoryBottom");  
  51.             //从当前槽中移除  
  52.             invBottom->removeChildWindow(WindowArgs.window);  
  53.             CEGUI::Window* invTop = MyGUISystem::getSingletonPtr()->getWindow("InventoryTop");  
  54.             //加入到装备栏的槽中  
  55.             invTop->getChild(0)->addChildWindow(WindowArgs.window);  
  56.             //重新设定位置和大小(相对)  
  57.             WindowArgs.window->setPosition(CEGUI::UVector2(CEGUI::UDim(0, 0), CEGUI::UDim(0, 0)));  
  58.             WindowArgs.window->setSize(CEGUI::UVector2(CEGUI::UDim(1, 0), CEGUI::UDim(1, 0)));  
  59.         }  
  60.         return true;  
  61.     }  
  62.     //创建我们需要的物品,保存在一个向量中  
  63.     void createItems()  
  64.     {  
  65.         Item* item = new Item("Item21""Weapon");  
  66.         item->setDesc("A Nice Weapon!");  
  67.         item->setIconName("set:items2 image:item21");  
  68.         item->setCost("3000");  
  69.         item->setTechLevel("14");  
  70.         mItems.push_back(item);  
  71.     }  
  72.     //创建技能,保存在一个向量中  
  73.     void createSkills()  
  74.     {  
  75.         Skill* skill = new Skill((CEGUI::utf8*)Ogre::UTFString(L"火球术").asUTF8_c_str());  
  76.         skill->setDesc((CEGUI::utf8*)Ogre::UTFString(L"发射一颗巨大的火球冲向敌人").asUTF8_c_str());  
  77.         skill->setLevel(4);  
  78.         skill->setIconName("set:skill image:skill1");  
  79.         mSkills.push_back(skill);  
  80.     }  
  81.     //格式化信息提示:使用字串流加入图片、字体等来格式化  
  82.     void formatInvTooltip(CEGUI::Window* window, Item* item)  
  83.     {  
  84.         std::stringstream ssTooltip;  
  85.         ssTooltip  
  86.             <<"[font='SimHei-14'][colour='FF0000FF']"<<item->getName()<<std::endl  
  87.             <<"[top-padding='5'][bottom-padding='10'][image-width='60'][image-height='90'][colour='FFFFFFFF'][image='"  
  88.             <<item->getIconName()<<"']"<<std::endl  
  89.             <<"------------------------"<<std::endl  
  90.             <<"[top-padding='0'][bottom-padding='0'][font='SimHei-14'][colour='FF00FFFF']" << item->getDesc() << std::endl  
  91.             <<"------------------------"<<std::endl  
  92.             <<"Tech Level: " << item->getTechLevel() << std::endl  
  93.             <<"------------------------"<<std::endl  
  94.             <<"Cost: "<<item->getCost()<<std::endl;  
  95.         //简单地设置TooltipText就可以获得图片和字体的效果  
  96.         window->setTooltipText(ssTooltip.str());  
  97.     }  
  98.     //格式化技能提示,同物品信息提示的格式化操作  
  99.     void formatSkillTooltip(CEGUI::Window* window, Skill* skill)  
  100.     {  
  101.         std::stringstream ssTooltip;  
  102.         ssTooltip  
  103.             <<"[font='SimHei-14'][colour='FFFFFF00']"<<skill->getName()<<std::endl  
  104.             <<"[top-padding='5'][bottom-padding='10'][image-width='80'][image-height='80'][colour='FFFFFFFF'][image='"  
  105.             <<skill->getIconName()<<"']"<<std::endl  
  106.             <<"------------------------"<<std::endl  
  107.             <<"[top-padding='0'][bottom-padding='0'][font='SimHei-14'][colour='FF00FF00']" << skill->getDesc() << std::endl  
  108.             <<"------------------------"<<std::endl  
  109.             <<(CEGUI::utf8*)Ogre::UTFString(L"技能等级: ").asUTF8_c_str()<< skill->getLevel() << std::endl  
  110.             <<"------------------------"<<std::endl  
  111.             <<(CEGUI::utf8*)Ogre::UTFString(L"技能消耗: ").asUTF8_c_str()<<skill->getCost()<<std::endl  
  112.             <<"------------------------"<<std::endl  
  113.             <<(CEGUI::utf8*)Ogre::UTFString(L"技能伤害:").asUTF8_c_str()<<skill->getDamage()<<std::endl;  
  114.         //简单地设置TooltipText就可以获得图片和字体的效果  
  115.         window->setTooltipText((CEGUI::utf8*)ssTooltip.str().c_str());  
  116.     }  
  117. private:  
  118.     bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )  
  119.     {  
  120.         MyGUISystem::getSingletonPtr()->injectMouseButtonDown(MyGUISystem::convertButton(id));  
  121.         return true;  
  122.     }  
  123.     bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )  
  124.     {  
  125.         MyGUISystem::getSingletonPtr()->injectMouseButtonUp(MyGUISystem::convertButton(id));  
  126.         return true;  
  127.     }  
  128.     bool mouseMoved( const OIS::MouseEvent &arg )  
  129.     {  
  130.         MyGUISystem::getSingletonPtr()->injectMouseMove(arg.state.X.rel, arg.state.Y.rel);  
  131.         return true;  
  132.     }  
  133.     bool keyPressed( const OIS::KeyEvent &arg )  
  134.     {  
  135.         CEGUI::System::getSingleton().injectKeyDown(arg.key);  
  136.         CEGUI::System::getSingleton().injectChar(arg.text);  
  137.         if(arg.key == OIS::KC_ESCAPE)  
  138.             mShutDown = true;  
  139.         return true;  
  140.     }  
  141.     bool keyReleased( const OIS::KeyEvent &arg )  
  142.     {  
  143.         CEGUI::System::getSingleton().injectKeyUp(arg.key);  
  144.         return true;  
  145.     }  
  146. private:  
  147.     std::vector<Item*> mItems;  
  148.     std::vector<Skill*> mSkills;  
  149. };  
  150. INT WINAPI WinMain(HINSTANCEHINSTANCELPSTRINT)  
  151. {  
  152.     try  
  153.     {  
  154.         Demo demo;  
  155.         demo.go();  
  156.     }  
  157.     catch(Ogre::Exception& e)  
  158.     {  
  159.         MessageBox(0, e.getFullDescription().c_str(), "Exception", MB_OK);  
  160.     }  
  161.       
  162. }  

使用一个stringstream来对Tooltip的文本进行加工,需要图片的地方就加入图片的tag, 需要颜色的地方加入colour的tag。

如果需要在Tooltip中加入中文,那么你需要一个CEGUI::String字串对象,然后在设置Tooltip文本的时候,把这个字串对象转换成CEGUI::utf8文本。 当然转换中文后显示会变慢 = =

对于图片的显示,需要更改LookNFeel中的Tooltip的Colour Range,在ImagerySection Name="label“的地方,把颜色值调成白色

 

如何把对应槽和物品/装备等联系起来呢?我想到的一种方法是使用UserString来进行通信。

首先我们需要一个Item和Skill类来封装我们的物品和技能的属性:

  1. #pragma once  
  2. #include <string>  
  3. class Item  
  4. {  
  5. public:  
  6.     Item(const std::string& name, const std::string& type)  
  7.     {  
  8.         mName = name;  
  9.         mType = type;  
  10.     }  
  11.     virtual ~Item()  
  12.     {  
  13.     }  
  14.     const std::string& getName()  
  15.     {  
  16.         return mName;  
  17.     }  
  18.     const std::string& getType()  
  19.     {  
  20.         return mType;  
  21.     }  
  22.     void setIconName(const std::string& iconName)  
  23.     {  
  24.         mIconName = iconName;  
  25.     }  
  26.     const std::string& getIconName()  
  27.     {  
  28.         return mIconName;  
  29.     }  
  30.     void setDesc(const std::string& desc)  
  31.     {  
  32.         mDesc = desc;  
  33.     }  
  34.     const std::string& getDesc()  
  35.     {  
  36.         return mDesc;  
  37.     }  
  38.     void setTechLevel(const std::string& level)  
  39.     {  
  40.         mTechLevel = level;  
  41.     }  
  42.     const std::string& getTechLevel()  
  43.     {  
  44.         return mTechLevel;  
  45.     }  
  46.     void setCost(const std::string& cost)  
  47.     {  
  48.         mCost = cost;  
  49.     }  
  50.     const std::string& getCost()  
  51.     {  
  52.         return mCost;  
  53.     }  
  54. private:  
  55.     std::string mName;          //物品名称  
  56.     std::string mIconName;      //图片名称  
  57.     std::string mTechLevel;     //技术等级  
  58.     std::string mDesc;          //物品描述  
  59.     std::string mCost;          //物品消费  
  60.     std::string mType;          //物品类型  
  61. };  

技能的话差不多, 不过基本上只需要设置技能等级,然后技能的消耗和伤害等根据公式计算:

  1. Skill(const CEGUI::String& name)  
  2. {  
  3.     mName = name;  
  4.     mLevel = 1;  
  5.     mCost = 100;  
  6.     mDamage = 1000;  
  7. }  

  1. float getCost()  
  2. {  
  3.     return mCost * mLevel/mMaxLevel;  
  4. }  

  1. float getDamage()  
  2. {  
  3.     return mDamage * mLevel * mLevel/mMaxLevel;  
  4. }  

接下来就是在逻辑地方调用了它们了。 在上面的代码中,我使用了一个vector来保存需要使用的物品和技能,当然你需要更复杂的管理系统,就像对象工厂那样的系统。

在Tooltip的显示中,使用传递进去的Item或者Skill来动态的加载物品或装备的信息。

 

当装备一件装备时,为了能让它到特定的槽中, 我们可以传递一个UserString来通信:

  1. //这里传递一个User字串映射来装备到特定槽中  
  2. itemWidget->setUserString("type""Weapon");  

然后注册一个点击事件,对应的响应方法:

  1. bool onEquip(const CEGUI::EventArgs& args)  
  2. {  
  3.     const CEGUI::WindowEventArgs& WindowArgs = static_cast<const CEGUI::WindowEventArgs&>(args);  
  4.     if(WindowArgs.window->getUserString("type") == "Weapon")  
  5.     {  
  6.         //得到User字串传递过来的信息来进行操作  
  7.         CEGUI::Window* invBottom = MyGUISystem::getSingletonPtr()->getWindow("InventoryBottom");  
  8.         //从当前槽中移除  
  9.         invBottom->removeChildWindow(WindowArgs.window);  
  10.         CEGUI::Window* invTop = MyGUISystem::getSingletonPtr()->getWindow("InventoryTop");  
  11.         //加入到装备栏的槽中  
  12.         invTop->getChild(0)->addChildWindow(WindowArgs.window);  
  13.         //重新设定位置和大小(相对)  
  14.         WindowArgs.window->setPosition(CEGUI::UVector2(CEGUI::UDim(0, 0), CEGUI::UDim(0, 0)));  
  15.         WindowArgs.window->setSize(CEGUI::UVector2(CEGUI::UDim(1, 0), CEGUI::UDim(1, 0)));  
  16.     }  
  17.     return true;  
  18. }  

先从当前槽中移除物品绑定的窗口对象,然后根据传递过来的UserString来判断应该放在哪个槽中,对后把它加入这个槽窗口中并重置位置和大小。

 

对于一个复杂的装备/物品和技能系统,远不止这些。我只是一个示范,而且是在CEGUI+OGRE这个环境中。所以希望对研究Ogre和CEGUI的同学有帮助,同时希望有更好方案的同学提出意见,共同进步。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:32192次
    • 积分:434
    • 等级:
    • 排名:千里之外
    • 原创:9篇
    • 转载:6篇
    • 译文:0篇
    • 评论:15条
    文章分类
    最新评论