|
下面来看看具体的实现代码吧。- #ifndef _OBJECT_H
- #define _OBJECT_H
- //所有物品的基类
- //add by freeeyes
- #include "ObjectDefine.h"
- //此宏用于memcopy边界检查
- #define CHECK_SCOPE(a,b) if(a < b){ return false; }
- //所有对象的总类
- class CBaseObject
- {
- public:
- CBaseObject()
- {
- m_u8GUID = 0;
- m_emObjectType = OBJECT_UNKNOW;
- m_emObjectState = OBJECT_UNKNOW_STATE;
- m_u2Count = 1;
- m_u2MaxCount = 1;
- };
- CBaseObject(ENUM_OBJECT_TYPE emObjectType, ENUM_OBJECT_STATE emObjectState)
- {
- m_emObjectType = emObjectType;
- m_emObjectState = emObjectState;
- }
- virtual ~CBaseObject() {};
- /*
- * @Description: 设置对象的全局ID
- */
- void SetGUID(uint64 u8GUID) { m_u8GUID = u8GUID; };
- /*
- * @Description: 获得对象的全局ID
- */
- uint64 GetGUID() { return m_u8GUID; };
- /*
- * @Description: 设置对象类型
- */
- void SetObjectType(ENUM_OBJECT_TYPE emObjectType)
- {
- m_emObjectType = emObjectType;
- }
- /*
- * @Description: 得到对象类型
- */
- ENUM_OBJECT_TYPE GetObjectType()
- {
- return m_emObjectType;
- }
- /*
- * @Description: 设置对象状态
- */
- void SetObjectState(ENUM_OBJECT_STATE emObjectState)
- {
- m_emObjectState = emObjectState;
- }
- /*
- * @Description: 得到对象状态
- */
- ENUM_OBJECT_STATE GetObjectState()
- {
- return m_emObjectState;
- }
- /*
- * @Description: 设置对象叠加个数
- */
- void SetCount(uint16 u2Count)
- {
- m_u2Count = u2Count;
- }
- /*
- * @Description: 得到对象叠加个数
- */
- uint16 GetCount()
- {
- return m_u2Count;
- }
- /*
- * @Description: 设置最大个数
- */
- void SetMaxCount(uint16 u2MaxCount)
- {
- m_u2MaxCount = u2MaxCount;
- }
- /*
- * @Description: 获得最大个数
- */
- uint16 GetMaxCount()
- {
- return m_u2MaxCount;
- }
- /*
- * @Description: 将对象写入数据流
- */
- virtual bool WriteToBuffer(char* pBuffer, int& nSize)
- {
- int nPos = 0;
- CHECK_SCOPE(nSize, (int)(nPos + sizeof(uint64)));
- uint64 u8NetGUID = 0;
- HTON64(m_u8GUID, u8NetGUID);
- MEMCOPY_SAFE((char* )&pBuffer[nPos], (char* )&u8NetGUID, sizeof(uint64));
- nPos += sizeof(uint64);
- CHECK_SCOPE(nSize, (int)(nPos + sizeof(uint16)));
- uint16 u2NetObjectType = 0;
- HTONS(m_emObjectType, u2NetObjectType);
- MEMCOPY_SAFE((char* )&pBuffer[nPos], (char* )&u2NetObjectType, sizeof(uint16));
- nPos += sizeof(uint16);
- CHECK_SCOPE(nSize, (int)(nPos + sizeof(uint8)));
- MEMCOPY_SAFE((char* )&pBuffer[nPos], (char* )&m_emObjectState, sizeof(uint8));
- nPos += sizeof(uint8);
- CHECK_SCOPE(nSize, (int)(nPos + sizeof(uint16)));
- uint16 u2NetCount = 0;
- HTONS(m_u2Count, u2NetCount);
- MEMCOPY_SAFE((char* )&pBuffer[nPos], (char* )&u2NetCount, sizeof(uint16));
- nPos += sizeof(uint16);
- CHECK_SCOPE(nSize, (int)(nPos + sizeof(uint16)));
- uint16 u2NetMaxCount = 0;
- HTONS(m_u2MaxCount, u2NetMaxCount);
- MEMCOPY_SAFE((char* )&pBuffer[nPos], (char* )&u2NetMaxCount, sizeof(uint16));
- nPos += sizeof(uint16);
- nSize = nPos;
- return true;
- }
- /*
- * @Description: 从数据流还原对象
- */
- virtual bool ReadFromBuffer(char* pBuffer, int& nSize)
- {
- int nPos = 0;
- CHECK_SCOPE(nSize, (int)(nPos + sizeof(uint64)));
- MEMCOPY_SAFE((char* )&m_u8GUID, &pBuffer[nPos], sizeof(uint64));
- NTOH64(m_u8GUID);
- nPos += sizeof(uint64);
- CHECK_SCOPE(nSize, (int)(nPos + sizeof(uint16)));
- uint16 u2objectType = 0;
- MEMCOPY_SAFE((char* )&u2objectType, &pBuffer[nPos], sizeof(uint16));
- NTOHS(u2objectType);
- m_emObjectType = (ENUM_OBJECT_TYPE)u2objectType;
- nPos += sizeof(uint16);
- CHECK_SCOPE(nSize, (int)(nPos + sizeof(uint8)));
- MEMCOPY_SAFE((char* )&m_emObjectState, &pBuffer[nPos], sizeof(uint8));
- nPos += sizeof(uint8);
- CHECK_SCOPE(nSize, (int)(nPos + sizeof(uint16)));
- MEMCOPY_SAFE((char* )&m_u2Count, &pBuffer[nPos], sizeof(uint16));
- NTOHS(m_u2Count);
- nPos += sizeof(uint16);
- CHECK_SCOPE(nSize, (int)(nPos + sizeof(uint16)));
- MEMCOPY_SAFE((char* )&m_u2MaxCount, &pBuffer[nPos], sizeof(uint16));
- NTOHS(m_u2MaxCount);
- nPos += sizeof(uint16);
- nSize = nPos;
- return true;
- }
- /*
- * @Description: 重载等于运算符
- */
- CBaseObject& operator = (CBaseObject& ar)
- {
- this->SetGUID(ar.GetGUID());
- this->SetObjectType(ar.GetObjectType());
- this->SetObjectState(ar.GetObjectState());
- this->SetCount(ar.GetCount());
- return *this;
- }
- private:
- uint64 m_u8GUID; //唯一编号
- ENUM_OBJECT_TYPE m_emObjectType; //对象类型
- ENUM_OBJECT_STATE m_emObjectState; //对象状态
- uint16 m_u2Count; //当前个数
- uint16 m_u2MaxCount; //最大个数
- };
- #endif
复制代码 首先我构造了一个obj对象类,所有的物品都从此派生。
当然,你也可以选择从此派生出一些别的有意思的东东。
这个类我只提供4个参数,唯一编号,对象类型,对象状态,当前个数和最大个数。
唯一编号是当这个物品被创造到这个世界的时候必胜生成的唯一ID,这个算法有很多,64位长度,为了保持物品的唯一性并考虑合区的问题,建议在这个算法里面可以添加一个初始值。不同的服务器初始值不同,这样就可以方便的实现和服而不用在费力去合并那些重复的ID。
我在这个算法里面只提供了一个基本的计算方法。如果你愿意,你可以组织你的算法。- #ifndef _ITEM_H
- #define _ITEM_H
- //道具对象,继承自CBaseObject
- //add by freeeyes
- #include <stdio.h>
- #include <string>
- #include "Object.h"
- #define ITEM_BUFFER_SIZE 10 * 1024
- class CItem : public CBaseObject
- {
- public:
- CItem();
- CItem(uint32 u4Version);
- virtual ~CItem();
- //以下物品设计相关方法
- public:
- /*
- * @Description: 添加相关物品属性
- */
- void AddItemAttribute(ENUM_ITEM_ATTRIBUTE emAttrName, uint16 u2Value);
- /*
- * @Description: 显示所有该物品属性
- */
- void DisPlay(char* pAttr, uint16& u2Size);
- /*
- * @Description: 创建物品相关唯一ID,物品名称,物品描述
- */
- void Create(const char* pItemName, int nItemNameSize, const char* pItemDesc, int nItemDesc);
- /*
- * @Description: 得到物品属性数组
- */
- uint32* GetItemAttributeList();
- /*
- * @Description: 设置物品属性数组
- */
- void SetItemAttributeList(uint32* ayAttribute);
- //物品使用相关方法
- public:
- /*
- * @Description: 得到指定的物品属性
- */
- uint32 GetItemAttribute(ENUM_ITEM_ATTRIBUTE emAttrName);
- /*
- * @Description: 得到物品名称
- */
- const char* GetItemName();
- /*
- * @Description: 得到物品描述
- */
- const char* GetItemDesc();
- /*
- * @Description: 清除所有属性
- */
- void ClearAttribute();
- //物品存取相关方法
- public:
- /*
- * @Description: 从数据流还原Item对象
- */
- bool ReadFromBuffer(char* pBuffer, int& nSize);
- /*
- * @Description: 序列化入数据流
- */
- bool WriteToBuffer(char* pBuffer, int& nSize);
- /*
- * @Description: 重载等于运算符
- */
- CItem& operator = (CItem& ar)
- {
- //这里由于创建新道具的时候涉及拷贝操作,所以这里不负责拷贝GUID
- //this->SetGUID(ar.GetGUID());
- this->SetObjectType(ar.GetObjectType());
- this->SetObjectState(ar.GetObjectState());
- this->SetCount(ar.GetCount());
- this->SetMaxCount(ar.GetMaxCount());
- this->Create(ar.GetItemName(), (int)strlen(ar.GetItemName()), ar.GetItemDesc(), (int)strlen(ar.GetItemDesc()));
- //复制属性
- for(uint16 i = 0; i < MAX_ITEM_ATTRIBUTE; i++)
- {
- uint32 u4Value = ar.GetItemAttribute((ENUM_ITEM_ATTRIBUTE)i);
- if(u4Value > 0)
- {
- this->AddItemAttribute((ENUM_ITEM_ATTRIBUTE)i, u4Value);
- }
- }
-
- return *this;
- }
- private:
- uint32 m_ayAttribute[MAX_ITEM_ATTRIBUTE];
- char m_szItemName[MAX_ITEM_NAME];
- char m_szItemDesc[MAX_ITEM_DESC];
- };
- #endif
复制代码 这里是道具的具体结构了,继承了CBaseObject
这里首先我会根据上一章所说的宏,也就是MAX_ITEM_ATTRIBUTE创建一个数组,这个数组就是标识各个属性的。
这样做,有两种好处,因为在实际运行过程中,所有数据都已数组形式提供,快而且高效。不用遍历,根据数组下表定位具体属性的位置,方便查询。
在这里,实现了对指定对象的串行化操作。由于考虑到了不同的主机字序,所以写入文件的一律是网络字序,到本地打开文件流的时候是主机字序。
这里提供了一系列的方法,用于道具的功能。
考虑到了一些道具复制操作,于是在这里重载了=,如果你需要对这个类有添加,在这里也需要提供相应的代码。
道具的基础属性并没有什么问题,这些代码基本都是固定的。
那么,如果我的道具需要合并,分解,使用等等其他操作,应该怎么办呢?
这里首先提出一个概念,那就是,道具的合成,分解,使用,销毁或者其它功能,都离不开数据源。也就是说,我必须要有一个道具的容器。
这里,我们把这些道具的集合称为一种容器。
背包,仓库,银行。除了货币。只要出现的道具,都必须存在在一种容器中,哪怕是掉落了,也会在一个容器中。
如果有存在于没有所在容器指针的道具,说明程序出现了BUG,需要找出来。
这样做,可以最大程序的避免程序设计时以及使用时候的道具追踪。
好了,让我们看看道具容器(背包)是怎么实现的。- #ifndef _BAG_H
- #define _BAG_H
- //背包容器
- //add by freeeyes
- #include "Item.h"
- #include "Contraner.h"
- #include "ItemManager.h"
- #define MAX_BAG_COUNT 100
- #define BAG_FILE_NAME "Bag.pmf"
- class CBag : public CContainer<CItem>
- {
- public:
- CBag(uint16 u2Count = MAX_BAG_COUNT);
- virtual ~CBag();
- /*
- * @Description: 从数据流还原容器对象,需要从子类去实现
- */
- bool ReadFromBuffer(const char* pFileName);
- /*
- * @Description: 从数据流还原容器对象,需要从子类去实现
- */
- bool WriteToBuffer(const char* pFileName);
- /*
- * @Description: 设置对象管理器
- */
- void SetItemManager(CItemManager* pItemManager);
- /*
- * @Description: 背包清空事件
- */
- void Close();
- /*
- * @Description: 添加一个背包道具
- */
- uint16 AddObject(CItem* pItem);
- /*
- * @Description: 添加一个背包道具
- */
- bool AddObject(uint16 u2Index, CItem* pItem);
- /*
- * @Description: 删除指定类型的道具以及数量
- */
- bool DelObject(uint32 u4ItemBaseID, uint16 u2Count);
- /*
- * @Description: 删除指定单元格的物品
- */
- bool DelObject(uint16 u2Index);
- /*
- * @Description: 得到指定道具类型的总数
- */
- uint16 GetBagCurrCount(uint32 u4ItemBaseID);
- /*
- * @Description: 得到指定位置的道具
- */
- CItem* GetContainObject(uint16 u2Index);
- /*
- * @Description: 得到指定类型的道具,如果有多个返回第一个
- */
- CItem* GetContainObject(uint32 u4ItemBaseID);
- /*
- * @Description: 从背包里移出一个物品
- */
- CItem* MoveOutItem(uint32 u2Index);
- /*
- * @Description:分解一个叠加的道具格子
- */
- bool SplitItem(uint32 u2Index, uint16 u2ItemCount);
- public:
- CItemManager* m_pItemManager;
- };
- #endif
复制代码 看这些函数的注释,你应该会大概理解其中的意思。
一般背包的基本功能都会实现。
实现了道具的容器,就要实现道具的各种使用花样了。
这里我单独独立出来了一个类,用于道具的使用,比如合成和分解。- #ifndef _ITEMSYNTHETIC_H
- #define _ITEMSYNTHETIC_H
- #include "Bag.h"
- //合成专用类
- //赫拉迪克方块
- //add by freeeyes
- //最大合成道具总数不能超过30个
- #define MAX_NEWITEM_SYNTHETIC_COUNT 30
- enum ENUM_SYNTHETIC_ERRROR:uint8
- {
- SYNTHETIC_ERRROR_NONE = 0, //无措
- SYNTHETIC_ERRROR_NOSYN, //无法合成,公式错误
- SYNTHETIC_ERRROR_NOENOUGTH, //不够
- SYNTHETIC_ERRROR_NOEXIST, //不存在
- SYNTHETIC_ERRROR_DELETE, //扣减道具失败
- SYNTHETIC_ERRROR_NOITEM, //不是合成物品
- SYNTHETIC_ERRROR_NULLITEM, //道具池已没有空余
- SYNTHETIC_ERRROR_ADDBAG, //添加到背包错误
- SYNTHETIC_ERRROR_BAGFULL, //背包已满
- SYNTHETIC_ERRROR_GUID, //GUID已存在
- };
- class CItemSynTheTic
- {
- public:
- CItemSynTheTic();
- ~CItemSynTheTic();
- ENUM_SYNTHETIC_ERRROR SynTheTic(uint32 u4BaseID, CBag* pBag, CItemDictionary* pItemDictionary, CItemManager* pItemManager);
- private:
- ENUM_SYNTHETIC_ERRROR Check_NewItem(CItem* pDrawItem, CItemDictionary* pItemDictionary);
- ENUM_SYNTHETIC_ERRROR Check_Item_Enougth(CItem* pDrawItem, CBag* pBag);
- ENUM_SYNTHETIC_ERRROR Check_Bag_Enougth(CItem* pDrawItem, CBag* pBag);
- ENUM_SYNTHETIC_ERRROR Check_Item_Pool_Enougth(CItem* pDrawItem, CItemManager* pItemManager);
- ENUM_SYNTHETIC_ERRROR Reduce_Bag_Item(CItem* pDrawItem, CBag* pBag, CItemManager* pItemManager);
- ENUM_SYNTHETIC_ERRROR Create_New_Bag_Item(CBag* pBag, CItemManager* pItemManager, CItemDictionary* pItemDictionary);
- void Get_New_Item_Count(CItem* pDrawItem);
- private:
- uint32 m_u4ItemNewBaseID[MAX_NEWITEM_SYNTHETIC_COUNT];
- uint32 m_u4ItemClassCount;
- };
- #endif
复制代码 合成而言,方法就是SynTheTic,分解同理,用一个函数。
仔细想想,合成和分解无外乎,组合成一个或者多个新道具,分解岂不是一样?
这里需要几个参数。
BaseID:道具的基本类型,这个属性在道具设计的时候,就会被指定。
pBag: 背包的指针,我会根据BaseID去背包寻找是否存在这个物品,因为很可能存在的这个物品,是叠加的,或者是多个,我只需要找到其中一个去做这件事就行了。
CItemDictionary: 道具字典,我会根据这个字典去创建指定的物品。一个或者多个。
CItemManager: 这个就是整个世界的道具管理器,记录所有生成的道具,在这个类里面都有对应关系。
这里,其他的大家都容易理解,我要重点说一下CItemManager
CItemManager的类结构如下- #ifndef _ITEMMANAGER_H
- #define _ITEMMANAGER_H
- //道具管理器
- //add by freeeyes
- #include "ItemDictionary.h"
- #include "ItemMonitor.h"
- #include <time.h>
- enum ENUM_ITEM_MANAGER_ERROR:uint8
- {
- ITEM_MANAGER_NO_ERROR = 0x00, //无措
- ITEM_MANAGER_DICTIONARY_NULL = 0x01, //字典为空
- ITEM_MANAGER_COUNT_FULL = 0x02, //已达到最大上限
- ITEM_MANAGER_BASEID_NULL = 0x03, //BaseID不存在
- ITEM_MANAGER_USED_NULL = 0x04, //该物品不存在
- ITEM_MANAGER_MAP_EXIST = 0x05, //关系已存在
- };
- //这个类需要单件来处理
- class CItemManager
- {
- public:
- CItemManager();
- ~CItemManager();
- /*
- * @Description: 创建一个GUID
- */
- uint64 CreateGUID();
- /*
- * @Description: 初始化道具管理器
- */
- bool Init(CItemDictionary* pItemDic, uint32 u4ItemMaxCount);
- /*
- * @Description: 清除道具管理器
- */
- void Close();
- /*
- * @Description: 从道具管理器中获得一个空闲的CItem并返回
- */
- CItem* CreateItem();
- /*
- * @Description: 从道具管理器中回收一个道具(这个道具还未生效)
- */
- ENUM_ITEM_MANAGER_ERROR DeleteItemUnRegedit(CItem*& pItem);
- /*
- * @Description: 从道具管理器中回收一个道具
- */
- ENUM_ITEM_MANAGER_ERROR DeleteItem(CItem*& pItem);
- /*
- * @Description: 从道具管理器中回收一个道具
- */
- ENUM_ITEM_MANAGER_ERROR DeleteItem(uint64 u6GUID);
- /*
- * @Description: 建立物品和GUID之间的对应关系
- */
- ENUM_ITEM_MANAGER_ERROR RegeditItem(uint64 u8GUID, CItem* pItem);
- /*
- * @Description: 得到空余的道具数量
- */
- uint32 GetFreeItemCount();
- /*
- * @Description: 保存监控日志
- */
- void SaveMonitor();
- private:
- typedef map<uint64, CItem*> mapItemUsed;
- typedef vector<CItem*> vecItemPool;
- mapItemUsed m_mapItemUsed; //记载和GUID的映射关系
- vecItemPool m_vecItemUnUsed; //空闲的道具
- CItemDictionary* m_pItemDic; //物品字典,用于翻阅
- uint32 m_u4MaxCount; //最大生成物品个数(包含目前已经有的)
-
- //生成GUID相关参数
- uint32 m_u4TimeSeed; //时间种子
- uint32 m_u4TimeSeedCount; //当前种子个数
- //道具监控者
- CItemMonitor m_objItemMonitor; //监控道具的类
- };
- #endif
复制代码 我如何从这个类里面创建一个道具呢?
//创建一个道具
CItem* pItem = NULL;
pItem = m_objItemManager.CreateItem();
首先,我去道具池里面获取一个空余的道具对象。
如果没有了,那么就会得到一个NULL,说明全世界的道具已经都被生成出来了,已经没有空余的道具可以被生成了。
CItem* pItemDoc = m_ItemDictionary.GetItem(u4ItemBaseID);
(*pItem) = (*pItemDoc);
从字典里面获取一个要生成的指定BaseID的道具信息,并赋值给这个空道具对象,让他具象化。
m_objItemManager.RegeditItem(pItem->GetGUID(), pItem)
告诉CItemManager,我已经具象化完成了,并赋予了它一个GUID(唯一ID),建立道具和GUID的唯一关联关系。
如果这时候这个GUID已经存在,说明代码存在问题。生成的这个道具是不合法的。
于是回收之
//如果当前道具的GUID已被使用,则将物品销毁归还道具池
m_objItemManager.DeleteItemUnRegedit(pItem);
反之,放入容器。
完整整个操作,呵呵。
挺简单吧,其实,道具说复杂不复杂,但是要想做好可控的道具流,还是要费一些心思的。
下一章,我会讲讲邮件是怎么做的。下一章也是最后一章,在那时候我会贴出所有的代码。
|
|