您的位置:首页 > 产品设计 > UI/UE

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

2011-10-30 02:56 387 查看
 转载请注明出处: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论坛链接有详细的说明, 先看下我根据他的思想写的代码:

#include "BaseApplication.h"
#include "MyGUISystem.h"
#include "Item.h"
#include "Skill.h"
#include <vector>
class Demo : public BaseApplication
{
public:
bool frameRenderingQueued(const Ogre::FrameEvent& evt)
{
if(mShutDown)
return false;
//这里需要update否则Tooltip不会显示
MyGUISystem::getSingletonPtr()->update(evt.timeSinceLastFrame);
return BaseApplication::frameRenderingQueued(evt);
}
void createScene()
{
createItems();
createSkills();
setupGUI();
}
void setupGUI()
{
MyGUISystem::getSingletonPtr()->init();
//创建图片资源
CEGUI::ImagesetManager::getSingleton().create("items2.imageset");
CEGUI::ImagesetManager::getSingleton().create("skill.imageset");
MyGUISystem::getSingletonPtr()->loadLayout("GameUI");
createGUIEvents();
CEGUI::Window* itemWidget = MyGUISystem::getSingletonPtr()->getWindow("InvBottomSlot1");
//这里传递一个User字串映射来装备到特定槽中
itemWidget->setUserString("type", "Weapon");
//格式化信息
formatInvTooltip(itemWidget, mItems[0]);
CEGUI::Window* skillWidget = MyGUISystem::getSingletonPtr()->getWindow("SkillSlot1");
formatSkillTooltip(skillWidget, mSkills[0]);
}
void createGUIEvents()
{
MyGUISystem::subscribeEvent("InvBottomSlot1", CEGUI::Window::EventMouseClick,
CEGUI::Event::Subscriber(&Demo::onEquip, this));
}
bool onEquip(const CEGUI::EventArgs& args)
{
const CEGUI::WindowEventArgs& WindowArgs = static_cast<const CEGUI::WindowEventArgs&>(args);
if(WindowArgs.window->getUserString("type") == "Weapon")
{
//得到User字串传递过来的信息来进行操作
CEGUI::Window* invBottom = MyGUISystem::getSingletonPtr()->getWindow("InventoryBottom");
//从当前槽中移除
invBottom->removeChildWindow(WindowArgs.window);
CEGUI::Window* invTop = MyGUISystem::getSingletonPtr()->getWindow("InventoryTop");
//加入到装备栏的槽中
invTop->getChild(0)->addChildWindow(WindowArgs.window);
//重新设定位置和大小(相对)
WindowArgs.window->setPosition(CEGUI::UVector2(CEGUI::UDim(0, 0), CEGUI::UDim(0, 0)));
WindowArgs.window->setSize(CEGUI::UVector2(CEGUI::UDim(1, 0), CEGUI::UDim(1, 0)));
}
return true;
}
//创建我们需要的物品,保存在一个向量中
void createItems()
{
Item* item = new Item("Item21", "Weapon");
item->setDesc("A Nice Weapon!");
item->setIconName("set:items2 image:item21");
item->setCost("3000");
item->setTechLevel("14");
mItems.push_back(item);
}
//创建技能,保存在一个向量中
void createSkills()
{
Skill* skill = new Skill((CEGUI::utf8*)Ogre::UTFString(L"火球术").asUTF8_c_str());
skill->setDesc((CEGUI::utf8*)Ogre::UTFString(L"发射一颗巨大的火球冲向敌人").asUTF8_c_str());
skill->setLevel(4);
skill->setIconName("set:skill image:skill1");
mSkills.push_back(skill);
}
//格式化信息提示:使用字串流加入图片、字体等来格式化
void formatInvTooltip(CEGUI::Window* window, Item* item)
{
std::stringstream ssTooltip;
ssTooltip
<<"[font='SimHei-14'][colour='FF0000FF']"<<item->getName()<<std::endl
<<"[top-padding='5'][bottom-padding='10'][image-width='60'][image-height='90'][colour='FFFFFFFF'][image='"
<<item->getIconName()<<"']"<<std::endl
<<"------------------------"<<std::endl
<<"[top-padding='0'][bottom-padding='0'][font='SimHei-14'][colour='FF00FFFF']" << item->getDesc() << std::endl
<<"------------------------"<<std::endl
<<"Tech Level: " << item->getTechLevel() << std::endl
<<"------------------------"<<std::endl
<<"Cost: "<<item->getCost()<<std::endl;
//简单地设置TooltipText就可以获得图片和字体的效果
window->setTooltipText(ssTooltip.str());
}
//格式化技能提示,同物品信息提示的格式化操作
void formatSkillTooltip(CEGUI::Window* window, Skill* skill)
{
std::stringstream ssTooltip;
ssTooltip
<<"[font='SimHei-14'][colour='FFFFFF00']"<<skill->getName()<<std::endl
<<"[top-padding='5'][bottom-padding='10'][image-width='80'][image-height='80'][colour='FFFFFFFF'][image='"
<<skill->getIconName()<<"']"<<std::endl
<<"------------------------"<<std::endl
<<"[top-padding='0'][bottom-padding='0'][font='SimHei-14'][colour='FF00FF00']" << skill->getDesc() << std::endl
<<"------------------------"<<std::endl
<<(CEGUI::utf8*)Ogre::UTFString(L"技能等级: ").asUTF8_c_str()<< skill->getLevel() << std::endl
<<"------------------------"<<std::endl
<<(CEGUI::utf8*)Ogre::UTFString(L"技能消耗: ").asUTF8_c_str()<<skill->getCost()<<std::endl
<<"------------------------"<<std::endl
<<(CEGUI::utf8*)Ogre::UTFString(L"技能伤害:").asUTF8_c_str()<<skill->getDamage()<<std::endl;
//简单地设置TooltipText就可以获得图片和字体的效果
window->setTooltipText((CEGUI::utf8*)ssTooltip.str().c_str());
}
private:
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;
}
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:
std::vector<Item*> mItems;
std::vector<Skill*> mSkills;
};
INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, INT)
{
try
{
Demo demo;
demo.go();
}
catch(Ogre::Exception& e)
{
MessageBox(0, e.getFullDescription().c_str(), "Exception", MB_OK);
}

}


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

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

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

 

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

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

#pragma once
#include <string>
class Item
{
public:
Item(const std::string& name, const std::string& type)
{
mName = name;
mType = type;
}
virtual ~Item()
{
}
const std::string& getName()
{
return mName;
}
const std::string& getType()
{
return mType;
}
void setIconName(const std::string& iconName)
{
mIconName = iconName;
}
const std::string& getIconName()
{
return mIconName;
}
void setDesc(const std::string& desc)
{
mDesc = desc;
}
const std::string& getDesc()
{
return mDesc;
}
void setTechLevel(const std::string& level)
{
mTechLevel = level;
}
const std::string& getTechLevel()
{
return mTechLevel;
}
void setCost(const std::string& cost)
{
mCost = cost;
}
const std::string& getCost()
{
return mCost;
}
private:
std::string mName;			//物品名称
std::string mIconName;		//图片名称
std::string mTechLevel;		//技术等级
std::string mDesc;			//物品描述
std::string mCost;			//物品消费
std::string mType;			//物品类型
};

技能的话差不多, 不过基本上只需要设置技能等级,然后技能的消耗和伤害等根据公式计算:
Skill(const CEGUI::String& name)
{
mName = name;
mLevel = 1;
mCost = 100;
mDamage = 1000;
}
float getCost()
{
return mCost * mLevel/mMaxLevel;
}

float getDamage()
{
return mDamage * mLevel * mLevel/mMaxLevel;
}


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

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

 

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

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

然后注册一个点击事件,对应的响应方法:
bool onEquip(const CEGUI::EventArgs& args)
{
const CEGUI::WindowEventArgs& WindowArgs = static_cast<const CEGUI::WindowEventArgs&>(args);
if(WindowArgs.window->getUserString("type") == "Weapon")
{
//得到User字串传递过来的信息来进行操作
CEGUI::Window* invBottom = MyGUISystem::getSingletonPtr()->getWindow("InventoryBottom");
//从当前槽中移除
invBottom->removeChildWindow(WindowArgs.window);
CEGUI::Window* invTop = MyGUISystem::getSingletonPtr()->getWindow("InventoryTop");
//加入到装备栏的槽中
invTop->getChild(0)->addChildWindow(WindowArgs.window);
//重新设定位置和大小(相对)
WindowArgs.window->setPosition(CEGUI::UVector2(CEGUI::UDim(0, 0), CEGUI::UDim(0, 0)));
WindowArgs.window->setSize(CEGUI::UVector2(CEGUI::UDim(1, 0), CEGUI::UDim(1, 0)));
}
return true;
}

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

 

对于一个复杂的装备/物品和技能系统,远不止这些。我只是一个示范,而且是在CEGUI+OGRE这个环境中。所以希望对研究Ogre和CEGUI的同学有帮助,同时希望有更好方案的同学提出意见,共同进步。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息