|
acejoy重新换机房花了一些时间,这一段时间无法登陆。现在终于搞好了。把春节前抽空写的一点东西补上来。
以前写过一些游戏的道具设计,沉淀了几年,一直没有时间回头整理,春节前几日,好多人都回家了,工作也不这么紧张了。于是抽时间写了一个道具功能。放在这里,供大家有兴趣玩赏。
这里是抛砖引玉,代码未必是最优的,但是提供大家一个思路。
本以为几日就能完成,毕竟还是小看它了,因为综合了自己以前的一些代码,发现自己当初的代码通用性以及扩展性都有欠缺。
道具系统有它自己的独立性,可以完全独立于其他系统。比如player,比如AI,比如寻路。(以后有时间我会一一写出相关文章,如果大家有兴趣的话)
想趁机写一个可以支持弹性扩展的。
于是拿了一张白纸,写下如下文字:
(1)实现道具字典的增删改查。
(2)实现道具的组合,分解。
(3)实现道具的任意属性扩展,也就是说,设计者可以随时添加自己想添加的道具属性,而不会影响已有道具的数据流。
(4)实现道具的交易
(5)实现道具的管理,生成监控。
(6)顺带实现一个可循环的邮件系统
(7)实现背包相关功能,存放,删除。以及道具相关执行。
(8)代码必须支持Linux和windows两种操作系统。
代码是在2014年春节前两周开始编写的,一直到春节前3天才真正写成。期间考虑了不少情况,导致一些之前的接口和结构不适应而返工。所以花费时间较长。
我会一点点的把这些设计点滴记录下来,给自己,也给想看它的朋友们,整个程序是可以运行的。我会在这个系列文章的最后,把完成可运行的代码程序贴进来供大家玩。
道具系统在整个游戏系统中是很重要的系统。无论MMO,还是页游手游,都需要它。
看上去很简单,但是当实际实现的时候,你会发现其中并不是那么好控制的,要想做到现实意义上的完美,那么需要大家踏下心来一点点的精雕细琢。
先看看,最终实现的界面吧。
有意思吧。
确实很有意思,呵呵(有点小成就感)
在开始了解它之前,我们必须知道,它需要注意什么。
因为前几年做过一些手游和MMO,所以,在这里,我第一考虑的是,道具的唯一性,这很重要,如果是单机游戏,完全可以不用考虑,但是网游中,如果你给别人留下复制的漏洞,你会死的很惨的。
首先,在我看来,先必须对道具总量进行控制。
总量怎么理解?
总量就是,如果把你的游戏想象成一个世界。我在这个世界构造的时候,我就会给这个游戏世界一个现实意义上的上限。(说白了就是道具对象池)
也就是说,无论这个世界上的道具是否被真正创造出来,我会给你一个上限。
比如,这个世界最多容纳1000件道具,无论这些道具是否被创造出来,我都会在程序启动的时候,把这1000件道具对象全部new出来。
你可能会问,你怎么可能知道未出现的道具是什么?你这样new出来是否有意义?
是的,我不知道,你也不知道,但是我能确定的是,这1000件道具的内存大小,这个很重要。
因为在整个道具系统运行过程中,我将不再new和delete任何东西,除了指针。
这样做,一是我可以完全控制我要创造的这个世界中的道具总数量,如果再多,那么道具创造的时候会就会返回失败。
二是我可以在BUG出现的时候,比如某些BUG大量道具生成的时候,有一个上限,让系统损失最小。
三是在游戏过程中,程序不会再有多余的new和delete操作。减少了内存碎片的可能性。
光控制道具总是往往在网游内是不够的,还有一项重要的工作要做。
那就是关键道具的追踪,有些稀有关键道具,要记录生成时间,销毁时间,甚至是转移时间,便于后期的运营追踪。
比如某个玩家被盗号了,物品要必须提供被追踪的途经。
如果对所有的物品都进行这样的追踪,在高并发的游戏服务器中,服务器是受不了的,你也不能为了一个血瓶的出现而煞费苦心。
但是所有的道具追踪,都现定于不可叠加的单一道具,因为,如果道具叠加了,这个道具的GUID就会消失。给追踪带来困难。
所以,在稀有道具中,对可叠加的物品,策划的时候就要专门考虑一下。
道具的限制,追踪是网游的基本功。这些事情一般的游戏服务器都是要干的。
好了,废话少说。先看看如果是我,我是怎么做的。
在动手前,我先罗列出了我需要对道具是用的一些基本材料,就像厨师一样,你要准备一些材料才能动手。- #ifndef _OBJECT_DEFINE_H
- #define _OBJECT_DEFINE_H
- //所有物品所需要的类型都在这里定义
- //add by freeeyes
- #include <stdio.h>
- #include "stdint.h"
- #include <WinSock2.h>
- typedef uint8_t uint8;
- typedef uint16_t uint16;
- typedef uint32_t uint32;
- typedef uint64_t uint64;
- #define MAX_ITEM_NAME 200 //对象名称最大长度
- #define MAX_ITEM_DESC 1024 //对象描述最大长度
- #define MAX_ITEM_ATTRIBUTE 51 //对象属性个数最大大小
- #ifdef WIN32
- #define MEMCOPY_SAFE(x, y, len) memcpy_s(x, len, y, len);
- #else
- #define MEMCOPY_SAFE(x, y, len) memcpy(x, y, len);
- #endif
- inline uint64_t htonll(uint64 v) {
- union { uint32 lv[2]; uint64 llv; } u;
- u.lv[0] = htonl(v >> 32);
- u.lv[1] = htonl(v & 0xFFFFFFFFULL);
- return u.llv;
- }
- inline uint64_t ntohll(uint64 v) {
- union { uint32 lv[2]; uint64 llv; } u;
- u.llv = v;
- return ((uint64_t)ntohl(u.lv[0]) << 32) | (uint64_t)ntohl(u.lv[1]);
- }
- #define HTONS(x, y) y = htons(x);
- #define HTONL(x, y) y = htonl(x);
- #define HTON64(x, y) y = htonll(x);
- #define NTOHS(x) x = ntohs(x);
- #define NTOHL(x) x = ntohl(x);
- #define NTOH64(x) x = ntohll(x);
- //定义一个函数,可以支持内存越界检查
- inline void sprintf_safe(char* szText, int nLen, const char* fmt ...)
- {
- if(szText == NULL)
- {
- return;
- }
- va_list ap;
- va_start(ap, fmt);
- #ifdef WIN32
- vsnprintf_s(szText, nLen, nLen, fmt, ap);
- #else
- vsnprintf(szText, nLen, fmt, ap);
- #endif
- va_end(ap);
- };
- enum ENUM_OBJECT_TYPE:uint16 //对象类型
- {
- OBJECT_UNKNOW = 0x00, //未知
- OBJECT_ITEM = 0x01, //道具
- OBJECT_MAIL = 0x02, //邮件
- };
- enum ENUM_OBJECT_STATE:uint8 //对象状态
- {
- OBJECT_UNKNOW_STATE = 0x00, //未知
- OBJECT_DISPLAY_STATE = 0x01, //可视
- OBJECT_INVALID_STATE = 0x02, //无效
- };
- enum ENUM_ITEM_ATTRIBUTE:uint8
- {
- ITEM_ATTRIBUTE_MIN_ATTACK = 0x00, //最小攻击
- ITEM_ATTRIBUTE_MAX_ATTACK = 0x01, //最大攻击
- ITEM_ATTRIBUTE_PHYSICS_DEFENCE = 0x02, //物理防御
- ITEM_ATTRIBUTE_MAGIC_DEFENCE = 0x03, //魔法防御
- ITEM_ATTRIBUTE_LIFE = 0x04, //生命
- ITEM_ATTRIBUTE_MAGIC = 0x05, //魔法
- ITEM_ATTRIBUTE_POWER = 0x06, //精力
- ITEM_ATTRIBUTE_CRIT = 0x07, //暴击
- ITEM_ATTRIBUTE_ATTACKSPEED = 0x08, //攻速
- ITEM_ATTRIBUTE_HIT = 0x09, //命中
- ITEM_ATTRIBUTE_DODGE = 0x0A, //躲闪
- ITEM_ATTRIBUTE_RECOVER_LIFE = 0x0B, //生命回复
- ITEM_ATTRIBUTE_RECOVER_MAGIC = 0x0C, //魔法回复
- ITEM_ATTRIBUTE_SKILL_ATTACK = 0x0D, //攻击技能
- ITEM_ATTRIBUTE_SKILL_P_DEFENCE = 0x0E, //物防技能
- ITEM_ATTRIBUTE_SKILL_M_DEFENCE = 0x0F, //魔防技能
- ITEM_ATTRIBUTE_SKILL_CRIT = 0x10, //暴击技能
- ITEM_ATTRIBUTE_SKILL_HIT = 0x11, //命中技能
- ITEM_ATTRIBUTE_SKILL_A_SPEED = 0x12, //攻速技能
- ITEM_ATTRIBUTE_SKILL_DODGE = 0x13, //躲闪技能
- ITEM_ATTRIBUTE_POSITION = 0x14, //位置
- ITEM_ATTRIBUTE_CLASS = 0x15, //物品类型
- ITEM_ATTRIBUTE_PHOTOID = 0x16, //图片ID
- ITEM_ATTRIBUTE_BASEID = 0x17, //物品基础ID
- ITEM_ATTRIBUTE_JEWEL_COUNT = 0x18, //孔槽个数
- ITEM_ATTRIBUTE_JEWEL_1 = 0x19, //孔槽1
- ITEM_ATTRIBUTE_JEWEL_2 = 0x1A, //孔槽2
- ITEM_ATTRIBUTE_JEWEL_3 = 0x1B, //孔槽3
- ITEM_ATTRIBUTE_JEWEL_4 = 0x1C, //孔槽4
- ITEM_ATTRIBUTE_JEWEL_5 = 0x1D, //孔槽5
- ITEM_ATTRIBUTE_VERSION = 0x1E, //道具版本号
- ITEM_ATTRIBUTE_USE_LEVEL = 0x1F, //使用等级
- ITEM_ATTRIBUTE_LEVEL = 0x20, //道具等级
- ITEM_ATTRIBUTE_SYNTHETIC_COUNT = 0x21, //合成所需道具数量(图纸专属)
- ITEM_ATTRIBUTE_SYN_BASEID_1 = 0x22, //合成道具1的BaseID(图纸专属)
- ITEM_ATTRIBUTE_SYN_BASEID_2 = 0x23, //合成道具2的BaseID(图纸专属)
- ITEM_ATTRIBUTE_SYN_BASEID_3 = 0x24, //合成道具3的BaseID(图纸专属)
- ITEM_ATTRIBUTE_SYN_BASEID_4 = 0x25, //合成道具4的BaseID(图纸专属)
- ITEM_ATTRIBUTE_SYN_BASEID_5 = 0x26, //合成道具5的BaseID(图纸专属)
- ITEM_ATTRIBUTE_SYN_BID_1_COUNT = 0x27, //合成道具1的BID数量(图纸专属)
- ITEM_ATTRIBUTE_SYN_BID_2_COUNT = 0x28, //合成道具2的BID数量(图纸专属)
- ITEM_ATTRIBUTE_SYN_BID_3_COUNT = 0x29, //合成道具3的BID数量(图纸专属)
- ITEM_ATTRIBUTE_SYN_BID_4_COUNT = 0x2A, //合成道具4的BID数量(图纸专属)
- ITEM_ATTRIBUTE_SYN_BID_5_COUNT = 0x2B, //合成道具5的BID数量(图纸专属)
- ITEM_ATTRIBUTE_SYN_NEWID_COUNT = 0x2C, //合成新道具BaseID的数量
- ITEM_ATTRIBUTE_SYN_NEWID_1 = 0x2D, //新BaseID1
- ITEM_ATTRIBUTE_SYN_NEWID_2 = 0x2E, //新BaseID2
- ITEM_ATTRIBUTE_SYN_NEWID_3 = 0x2F, //新BaseID3
- ITEM_ATTRIBUTE_SYN_NEWID_C_1 = 0x30, //新BaseID1数量
- ITEM_ATTRIBUTE_SYN_NEWID_C_2 = 0x31, //新BaseID2数量
- ITEM_ATTRIBUTE_SYN_NEWID_C_3 = 0x32, //新BaseID3数量
- };
- enum ENUM_ITEM_CLASS:uint8
- {
- ITEM_CLASS_SKILL_BOOK = 0x00, //技能书
- ITEM_CLASS_BOX = 0x01, //盒子(容器)
- ITEM_CLASS_EQUIPMENT = 0x02, //装备
- ITEM_CLASS_RESOURCE = 0x03, //材料
- ITEM_CLASS_JEWEL = 0x04, //宝石
- ITEM_CLASS_RUNE = 0x05, //符文
- ITEM_CLASS_STONE = 0x06, //石头
- ITEM_CLASS_TASK = 0x07, //任务道具
- ITEM_CLASS_DRAW = 0x08, //图纸
- };
- #endif
复制代码 以上是我对我需要的道具的一般性兴义。
可能有些朋友问,你不是支持跨平台么,怎么还会有#include <WinSock2.h>
这里我要解释一下,我用它并非是用socket部分,而是用里面的字符大小端转换。
为了兼容系统,我所有道具数值序列化以后,都会以网络字序存放,当解开的时候,都会以主机字序展示。
在多一句嘴,我为了保证数据的一致性,对enum做了一些限制。
enum ENUM_ITEM_ATTRIBUTE:uint8
这类代码必须在C++0x下才能编译。
如果你不喜欢,直接把:后面的数据类型去掉就行了。
这里我定义了,我可能要用到的道具类型,当然,你可以扩展和删除,只要符合想要的条件就行。
另外,ENUM_ITEM_ATTRIBUTE是数组,你所有道具需要的属性都可以在这里定义。
这里要注意,这里的定义必须是连续的,因为,我需要用这个做数组的索引。
当你要扩展和删除你的属性的时候,必须修改MAX_ITEM_ATTRIBUTE的属性数组总个数
#define MAX_ITEM_ATTRIBUTE 51 //对象属性个数最大大小
这个宏会被程序调用,当程序启动的时候,会生成于此相等的一个数组。
MAX_ITEM_ATTRIBUTE 的值必须和ENUM_ITEM_ATTRIBUTE里面的数值一一对应才可以。
另外,由于我个人需要对64位长整形的字序转换,我自己实现了一个函数htonll和ntohll
因为我的道具唯一ID是uint64位的。
好了,这样材料就差不多了。
下一讲,我将会讲怎么设计道具对象。
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?用户注册
×
|