基于mongodb, 设计游戏中的个人背包系统

原创 2015年07月06日 20:28:43
本着高效简洁的目的, 根据 <深入学习 MONGODB > 这本书做指导, 数据库设计遵循下面两条规则

规则 1:  预先分配磁盘空间并填充空白数据

规则 2:  文档要自给自足。数据的计算是由 C++ 客户端程序( 这里的客户端程序不是指游戏客户端, 是指数据库服务器)来完成,若查找的信息需要经过计算且无法从文档中获得, 就会付出高昂的性能代价,优化文档使得这些计算信息能从文档中直接获得


//////////////////////////////////////////////////////          分割线         ///////////////////////////////////////////////////////////////


MONGODB 的文档 ( 相当于 MYSQL 的记录 )能非常直观的表达游戏中的逻辑数据结构,并且跟客户端(这里的客户端,是指游戏数据库服务器 )的代码能很好的建立逻辑映射。可以用一个mongodb文档来表示一个游戏角色的个人背包信息。

在 C++ 的服务端代码中, 用下面这个结构体来表达一个背包格子的信息

// 可以表示游戏中的装备(非叠加道具, 比如武器, 盔甲等), 也可以表示叠加道具(比如血瓶,回程卷等)
typedef struct tagItem
{    
    int nItemID; // 道具唯一编号; 比如某把武器, 或者血瓶, 都有唯一编号的
    int nNum; // 数量, 作为可叠加道具, 这个字段才有意义; 作为装备, 这个字段没意义
}ST_ITEM;

而背包信息, 就是一组有限数量的 ST_ITEM 对象集合( 假设背包有 64 个格子 ):

typedef struct tagBagDocument
{
    int nCharID; // 角色唯一 ID
    int nUsed; // 已使用的格子数, used <= 64
    ST_ITEM stBagItemList[ 64 ]; // 如果 stBagItemList[ i ].nItemID 等于 0, 就认为 stBagItemList[ i ] 是空格子
}ST_BAG_DOCUMENT;


在 mongodb 中, 假设数据库 test 中的集合 bag 是存放所有角色的背包信息, 而 bag 中的一个文档, 就是一个角色的背包信息。可以用结构体 ST_BAG_DOCUMENT 来描述文档信息


示范代码:

mongo::DBClientConnection oDB; // 连接 mongodb 的代码省略, 默认 oDB 已经连接到 mongodb 了

// oDB.connect(...)


//创建背包

void CreateBagDoc(const int nCharID)
{
    mongo::BSONObjBuilder oBOJ;
    oBOJ.append( "characterid", nCharID );    
    oBOJ.append( "used", 0 ); // 已使用的格子数, 参见页首 规则 2    
    
    
    // 背包有 64 个格子, 参见页首 规则 1

    mongo::BSONArrayBuilder arr_o;

    for( int i=0; i<64; i++ )     
    {
        mongo::BSONObjBuilder o;    

        o.append( "itemid", 0 );

        o.append( "num", 0 );


        arr_o.append( o.obj() );        
    }

    oBOJ.appendArray("array", arr_o.arr());    
    oDB.insert( "test.bag", oBOJ.obj() );    

    oDB.ensureIndex( "test.bag", BSON("characterid"<<1), /*unique*/true);
}


//读取背包信息

bool LoadBagDoc(const int nCharID, ST_BAG_DOCUMENT& stBagDoc)
{
    memset( ( void* )&stBagDoc, 0, sizeof( stBagDoc ) );
    std::auto_ptr< mongo::DBClientCursor > cursor = m_oDB.query( "test.bag", QUERY( "characterid" << nCharID ) );
    if( cursor->more() )
    {  
        mongo::BSONObj p = cursor->next();
                
        stBagDoc.nCharID = nCharID;
        stBagDoc.nUsed = p.getIntField( "used" );

        mongo::BSONObj myarray = p["array"].Obj();
        std::vector< mongo::BSONElement > v;
        myarray.elems( v );

        int i = 0;
        std::vector< mongo::BSONElement >::iterator vecIter = v.begin();
        for ( ; vecIter!=v.end(); ++vecIter )
        {
            stBagDoc.stBagItemList[ i ].nItemID = (*vecIter)[ "itemid" ].Int();
            stBagDoc.stBagItemList[ i ].nNum = (*vecIter)[ "num" ].Int();            

            i++;

            if ( i >= 64 )
            {
                break;
            }
        }

        return true;
    }

    return false;
}

// 删除道具, nPos 是背包格子的下标值, 从 0 开始计数

void DeleteItem(const int nCharID, const int nPos)
{
    char szItemID[ 30 ];
    sprintf( szItemID, "array.%d.itemid", nPos );

    char szNum[ 30 ];
    sprintf( szNum, "array.%d.num", nPos );
    
    mongo::BSONObj obj = BSON("$set"<< BSON( szItemID << 0 << szNum << 0 "$inc" << BSON( "used" << -1 ) ) ); // 已使用的格子数 "used" 要减 1
        
    m_oDB.update( "test.bag", QUERY( "characterid" << nCharID ), obj );
}

// 把拾取到的装备放入背包

// nPos 是背包格子的下标值, 从 0 开始计数

// nUsedCell 是更新后已使用的格子数, stItem 是道具信息

void CDBEventFunc::UpdateBagCell(const int nCharID, const int nPos, const int nUsedCell, const ST_ITEM& stItem)
{
    char szItemID[ 30 ];
    sprintf( szItemID, "array.%d.itemid", nPos );

    char szNum[ 30 ];
    sprintf( szNum, "array.%d.num", nPos );    

    mongo::BSONObj obj = BSON("$set"<< BSON( "used" << nUsedCell << szItemID << stItem.nItemID ) );

    m_oDB.update( "test.bag", QUERY( "characterid" << nCharID ), obj );
}

// 更新( 增加或者扣除 )可叠加的道具数量

// nPos 是背包格子的下标值, 从 0 开始计数
// nUsedCell 是更新后已使用的格子数, stItem 是道具信息

void CDBEventFunc::UpdateItemNum(const int nCharID, const int nPos, const int nUsedCell, const ST_ITEM& stItem)
{
    int nItemID = stItem.nItemID;        
    int nNum = stItem.nNum;    
    
    char szNum[ 30 ];
    sprintf( szNum, "array.%d.num", nPos );

    char szItemid[ 30 ];
    sprintf( szItemid, "array.%d.itemid", nPos );

    // 如果堆叠道具的数量为 0, 那么这个格子应该设置为空格子
    int nTmpID = ( 0 == nNum ) ?  0 : nItemID;

    // 条件修改
    mongo::BSONObj obj = BSON("$set"<< BSON( "used" << nUsedCell << szNum << nNum << szItemid << nTmpID ) );
    
    m_oDB.update( "test.bag", QUERY( "characterid" << nCharID ), obj );    
}



上述几个函数, 基本上能满足背包系统的一切操作。从开发效率上来看, 在游戏中合理使用 MONGODB, 远胜于 MYSQL。
举一反三,设计其他功能系统时, 比如好友系统, 工会系统, 也可以参照上述的背包系统设计思路,轻松进行开发

相关文章推荐

《游戏脚本的设计与开发》-(RPG部分)3.5 游戏背包和任务系统

背包系统在游戏中是必不可少的,在游戏中,所有获得的物品都会储存在背包里面。背包的种类,我一般将它分成两大类,一种是类似于《吞食天地》的“个人背包”,在游戏中每个人物都有一个背包,每个人的背包都互不影响...

UGUI--背包系统之二--------Inventory

背包系统2

物品存储与背包系统

8 九月, 2012 // by 麦希     游戏中,物品存储和背包系统是最基础的模块,因为两者关联密切,这里放在一起论述。     物品和背包都是一个广义的概念,物品通常包括道...

游戏服务器背包设计与开发

http://www.youxijishu.com/h-nd-151-2_323.html 在游戏开发中,游戏背包是一个非常重要的功能,游戏服务器背包设计是的非常重要的,它要防止一些...

数据结构 与游戏背包的设计

数据结构分为:结构体、共用体、枚举型。 结构体:在定义的时候必须要写关键字struct  然后是结构体名,这个可写可不写 最后是结构体 例struct fun { 类型标示符  成员名; ...

游戏服务器背包设计与实现

在游戏开发中,背包是一个非常重要的功能。几乎每个复杂点的游戏都会有背包的功能。不管是手游戏还是网页游戏,不管是SLG游戏,还是ARPG游戏,背包是必不可少的。背包的功能根据策划的要求,有的简单,有的复...

当游戏爱上MongoDB会怎么样???

前端时间魔兽这个电影我相信大家都看过了哈,作为一个码农,有时候我也会去思考魔兽世界这个游戏背后他的一些设计和实现,比如他用什么数据库。当然真正用什么数据库这个我是不确定的,我们今天的主题是当游戏爱上M...

疯狂了!当游戏爱上MongoDB会怎么样???

导读 前端时间魔兽这个电影我相信大家都看过了哈,作为一个码农,有时候我也会去思考魔兽世界这个游戏背后他的一些设计和实现,比如他用什么数据库。当然真正用什么数据库这个我是不确定的,我们今天的主题是当游...

mysql时间存储用什么类型

关于夏令时,UTC,GMT这几个概念建议先简单了解下,下面不做解释 先丢结论以表诚意: 如果程序不需要考虑时区,夏令时或者将来数据库的机子迁移到别的地方时区变化,用datetime类型比较方便; 用...

mysql---多表关联

nysql多表关联及左右连接、全相乘简单介绍
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:基于mongodb, 设计游戏中的个人背包系统
举报原因:
原因补充:

(最多只允许输入30个字)