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

 转载请注明出处: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的同学有帮助,同时希望有更好方案的同学提出意见,共同进步。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值