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

本着高效简洁的目的, 根据 <深入学习 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。
举一反三,设计其他功能系统时, 比如好友系统, 工会系统, 也可以参照上述的背包系统设计思路,轻松进行开发
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值