房卡麻将分析系列 "牌局回放" 之 数据设计详解及实例

 更新时间:2017年03月08日 09:06:55   投稿:lqh  
这篇文章主要介绍了房卡麻将分析系列 "牌局回放" 之 数据设计详解及实例的相关资料,需要的朋友可以参考下

房卡麻将分析系列 "牌局回放" 之 数据设计                                                                   

             最近几个月,”房卡“棋牌游戏成为了资本追逐的热点,基于微信的广大用户和社交属性,”房卡”棋牌发展迅速。红孩儿团队因为之前几年有过相关项目的经验积累,鉴于未来广阔的地方棋牌市场和”开发间“机制的发展前景,也开始转向基于”开房间“棋牌游戏的项目开发中。为了更好的与开发者进行交流学习,特开设”房卡麻将游戏分析系列“。

                                                                            红孩儿团队研发的"大赢家"红中麻将

           本套麻将分析基于网络上流传的“网狐”房卡麻将源码做为基础,按照功能模块分为"架设指南",”服务器框架","后台系统","胡牌算法","客户端界面",“防作弊功能”等等细节做一些分析和指导,帮助广大的棋牌游戏开发者迅速掌握“房卡”麻将的研发原理和技巧设计。也希望有兴趣的朋友多多关注。

       第一次开公众号,挑个简单的下手,先来讲一讲房卡麻将中一个重要功能:“牌局回放”,我们都知道,棋牌类游戏注重公平真实不作弊,如果玩家感觉到游戏的过程有作弊,我相信他一定会对这款游戏失去兴趣。但作弊与否,玩家并不容易进行判断。这时候提供一个“牌局回放”功能给玩家进行分析就尤为重要。

       “网狐”等一些长期耕耘在棋牌领域的企业,在这方面都有完整的经验和框架,通过参考,我发现它是通过下面一套流程来完成”牌局回放“功能的。

        首先,在游戏服务器的房间类CTableFrameSink里需要有一个GameRecord结构,这个结构对 玩家信息,手牌以及每一步的动作都可以进行相应的记录:

struct GameRecordPlayer 
{ 
  DWORD dwUserID; 
  std::string kHead; 
  std::string kNickName; 
  std::vector<BYTE> cbCardData; 
  void StreamValue(datastream& kData, bool bSend) 
  { 
    Stream_VALUE(dwUserID); 
    Stream_VALUE(kHead); 
    Stream_VALUE(kNickName); 
    Stream_VECTOR(cbCardData); 
  } 
}; 
 
struct GameRecordOperateResult 
{ 
  enum Type 
  { 
    TYPE_NULL, 
    TYPE_OperateResult, 
    TYPE_SendCard, 
    TYPE_OutCard, 
    TYPE_ChiHu, 
  }; 
 
  GameRecordOperateResult() 
  { 
    cbActionType = 0; 
    wOperateUser = 0; 
    wProvideUser = 0; 
    cbOperateCode = 0; 
    cbOperateCard = 0; 
  } 
 
  BYTE    cbActionType; 
  WORD    wOperateUser;            //操作用户 
  WORD    wProvideUser;            //供应用户 
  BYTE    cbOperateCode;           //操作代码 
  BYTE    cbOperateCard;           //操作扑克 
 
  void StreamValue(datastream& kData, bool bSend) 
  { 
    Stream_VALUE(cbActionType); 
    Stream_VALUE(wOperateUser); 
    Stream_VALUE(wProvideUser); 
    Stream_VALUE(cbOperateCode); 
    Stream_VALUE(cbOperateCard); 
  } 
}; 
 
struct GameRecord 
{ 
  std::vector<GameRecordPlayer>   kPlayers; 
  std::vector<GameRecordOperateResult> kAction; 
   
  void StreamValue(datastream& kData, bool bSend) 
  { 
    StructVecotrMember(GameRecordPlayer, kPlayers); 
    StructVecotrMember(GameRecordOperateResult, kAction); 
  } 
 
  void CleanUp() 
  { 
    kPlayers.clear(); 
    kAction.clear(); 
  } 
}; 

          在datastream.h中,有一套set,get数据流的宏,能够将数据放入到数据流中或从中拿出。

#define Stream_VALUE(Name) \ 
  if(bSend)      \ 
{              \ 
  kData.pushValue(Name);\ 
}\ 
else\ 
{\ 
  kData.popValue(Name);\ 
}\ 

          好了,有了这样一个结构,在游戏开始的时候,我们就可以开始记录本局了。

//游戏开始 
void CTableFrameSink::GameStart() 
{ 
    ... 
    //填充四个玩家的基础信息 
  for (int i = 0; i < 4; i++) 
  { 
    GameRecordPlayer  tNewRecordPlayer; 
         
    tagUserInfo *  tpUserInfo = m_pITableFrame->GetTableUserItem(i)->GetUserInfo(); 
    tNewRecordPlayer.dwUserID = tpUserInfo->dwUserID; 
    tNewRecordPlayer.kNickName = tpUserInfo->szNickName; 
     
        //取得手牌信息 
    BYTE cbCardData[MAX_COUNT]; 
    m_GameLogic.SwitchAllToCardData(m_cbCardIndex[i], cbCardData); 
 
    for (int j = 0; j < MAX_COUNT ; j++) 
    { 
      tNewRecordPlayer.cbCardData.push_back(cbCardData[j]); 
    } 
        //存储到当前记录结构中的玩家信息容器。 
    m_sGameRecord.kPlayers.push_back(tNewRecordPlayer); 
  } 
} 

        然后我们开始记录操作,分别在玩家出牌,以及玩家应答吃,碰,杠,胡等操作时加入记录。

//用户出牌 
bool CTableFrameSink::OnUserOutCard(WORD wChairID, BYTE cbCardData) 
{ 
     ... 
  //记录动作数据 
  GameRecordOperateResult  tNewRecordOperateResult; 
  tNewRecordOperateResult.cbActionType =    GameRecordOperateResult::TYPE_OutCard; 
  tNewRecordOperateResult.cbOperateCard = cbCardData; 
  tNewRecordOperateResult.cbOperateCode = WIK_NULL; 
  tNewRecordOperateResult.wOperateUser = wChairID; 
  tNewRecordOperateResult.wProvideUser = wChairID; 
  m_sGameRecord.kAction.push_back(tNewRecordOperateResult); 
     ... 
} 


//用户操作 
bool CTableFrameSink::OnUserOperateCard(WORD wChairID, BYTE cbOperateCode, BYTE cbOperateCard) 
{ 
     ... 
//记录动作数据 
    GameRecordOperateResult  tNewRecordOperateResult; 
      tNewRecordOperateResult.cbActionType = XZDDGameRecordOperateResult::TYPE_OperateResult; 
    tNewRecordOperateResult.cbOperateCard = cbOperateCard; 
    tNewRecordOperateResult.cbOperateCode = cbOperateCode; 
    tNewRecordOperateResult.wOperateUser = wChairID; 
    tNewRecordOperateResult.wProvideUser = m_wProvideUser; 
    m_sGameRecord.kAction.push_back(tNewRecordOperateResult); 
     ... 
} 

          就这样,基本的操作记录也完成了。最后当牌局结束时,我们需要将记录提交到数据库中。

//游戏结束 
bool CTableFrameSink::OnEventGameConclude(WORD wChairID, IServerUserItem * pIServerUserItem, BYTE cbReason) 
{ 
  switch (cbReason) 
  { 
  case GER_NORMAL:    //常规结束 
    { 
         ... 
            //将记录转化为数据流。 
      datastream kDataStream; 
      m_sGameRecord.StreamValue(kDataStream, true);  
            //除去写分等处理,这里最后一个参数即是数据流。 
            m_pITableFrame->WriteTableScore(ScoreInfoArray, CountArray(ScoreInfoArray), kDataStream); 
 
         ... 
        } 
    } 
}  

           在私人场服务器中,会通过WriteTableScore这个函数调用PrivateTableInfo的writeSocre,它将将数的流记录下来。

          并最终在牌局结束时DismissRoom(pTableInfo);发给了数据库。

             数据库最终会通过一个存储过程的执行完成将数据流入库的工作。具体的代码就不再展示了,大家可以参考

CDataBaseEngineSink::OnRequestPrivateGameRecord()。            

           这样一套完整的回放数据流程就结束了。      

           好,今天的分析就到这里,红孩儿欢迎大家下次继续听课哦~

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

相关文章

  • Android实现精确到天时分秒的抢购倒计时

    Android实现精确到天时分秒的抢购倒计时

    这篇文章主要为大家详细介绍了Android实现精确到天时分秒的抢购倒计时,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-02-02
  • 一款超酷的Android自定义加载控件

    一款超酷的Android自定义加载控件

    这篇文章主要为大家详细介绍了一款超酷的Android自定义加载控件,具有一定的实用性和参考价值,感兴趣的朋友可以参考一下
    2016-05-05
  • Android实现在TextView文字过长时省略部分或滚动显示的方法

    Android实现在TextView文字过长时省略部分或滚动显示的方法

    这篇文章主要介绍了Android实现在TextView文字过长时省略部分或滚动显示的方法,结合实例形式分析了Android中TextView控件文字显示及滚动效果相关操作技巧,需要的朋友可以参考下
    2016-10-10
  • Android重力传感器实现滚动的弹球

    Android重力传感器实现滚动的弹球

    所谓传感器能够探测如光、热、温度、重力、方向 等等的功能,本文给大家介绍Android传感器应用之重力传感器实现滚动的弹球,对android重力传感器相关知识感兴趣的朋友一起看看吧
    2015-12-12
  • 详解OpenGL Shader彩虹条纹效果的实现

    详解OpenGL Shader彩虹条纹效果的实现

    这篇文章主要为大家介绍了如何通过OpenGL Shader实现彩虹条纹效果,最后的效果和图片处理软件colorow中的彩虹效果滤镜相似,需要的可以参考一下
    2022-02-02
  • Android实现下拉菜单Spinner效果

    Android实现下拉菜单Spinner效果

    这篇文章主要介绍了Android实现下拉菜单Spinner效果,学习Spinner组件的使用方法,非常好用的一款组件,相当于从下拉列表中选择项目,感兴趣的小伙伴们可以参考一下
    2016-04-04
  • Android shape与selector标签使用详解

    Android shape与selector标签使用详解

    Android中提供一种xml的方式,让我们可以自由地定义背景,比较常用的就是shape标签和selector标签,这篇文章主要介绍了Android shape与selector标签使用,需要的朋友可以参考下
    2022-05-05
  • Android实现文字和图片混排(文字环绕图片)效果

    Android实现文字和图片混排(文字环绕图片)效果

    这篇文章主要介绍了Android实现文字和图片混排的方法,实例分析了文字环绕图片效果的具体功能显示及页面布局实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-10-10
  • Android中调用另一个Activity并返回结果(选择头像功能为例)

    Android中调用另一个Activity并返回结果(选择头像功能为例)

    这篇文章主要介绍了Android中调用另一个Activity并返回结果,本文以模拟选择头像功能为例通过实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2020-01-01
  • Android ActivityManager使用案例详解

    Android ActivityManager使用案例详解

    这篇文章主要介绍了Android ActivityManager使用案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08

最新评论