UE4局域网斗地主(三)

来源:互联网 发布:刷vip永久软件 编辑:程序博客网 时间:2024/04/30 18:56

工作繁忙,更新较慢。


   接着上一篇文章,游戏的创建和加入房间已经大概完成了,具体的扩展丰富,比如当前服务器列表之类的信息,UE4提供的sessionAPI都有相应的参数,就不一一解释了。下面直接切入正题,主要分三大块:发牌(包括抢地主),出牌(主要回合逻辑),牌型判断(算法)。其他的小细节在过程中一个个补充,在这里声明下,游戏大概的框架也是参照官方给的shootergame例子来的,因为是用了它的同步机制嘛。

   大概思路简单概括:用它的RPC函数把主要的逻辑算法判断等交给服务器来计算,客户端只要傻瓜式的表现效果就OK。是的,就这么简单一句话包括了很多东西,理解起来简单,执行起来有点困难。前两篇文章已经把大概的基础知识和内容过一遍了,所以我就很直接的写了。

   先完成发牌部分,这样的思路:

1.服务器发牌(并且随机一名玩家作为第一个抢地主的)-玩家客户端更新UI。

2.玩家提交抢地主数据-服务器存储计算结果。(循环)

3.服务器计算出抢地主结果-客户端更新UI。


大家发现,基本所有的逻辑计算都是服务器来完成,客户端都是做一些体力活:UI显示。因为是卡牌游戏,不存在什么移动,所有我就没有用playcontroller类,甚至player state也没用。有人就疑问了,那是怎么存储数据的?直接放pawn里同步不就完了,客户端通过Server RPC函数交给服务器计算,然后服务器上通过变量的同步来达到服务器的数据可以同步到客户端,然后在服务器上用multicast和client  RPC函数来实现客户端上的执行。这里服务器和客户端的通信压力会显得非常大,特别是使用multicast,本人才疏学浅,不懂服务器,所以可能存在一些隐患。

下面是步骤流程的运行截图:

1.三个玩家进入房间,蓝色表示玩家头像的UI(简陋了点),下面是服务器,玩家ID为0,右上角是玩家1,下表为1,左上角是玩家2,下标为2。之前的几个步骤大概介绍下:玩家按照加入房间按顺序在服务器上给予ID,然后满三个玩家后各自出现准备按钮的UI。


2.如下图,每当一名玩家准备后,调用RPC函数,在服务器上执行,在服务器上更新这个玩家准备好的逻辑,然后做判断是否满足游戏玩家数,并且调用multicast更新这名玩家在所有客户端上准备好的UI显示结果。



3.所有玩家准备好后,服务器上发牌,并随机一位玩家作为第一个抢地主的人。注意:这些都在服务器上执行。然后调用Multicast,参数为随机的这个玩家下标和三个玩家对应的牌的数据。我的抢地主流程介绍下:一共两轮,第一轮是1,2,3倍叫分,第二轮是抢地主和放弃抢地主。第三轮积分大的抢到地主,如果一样,则按照顺序越前越大的规则。比如第一个抢地主下标为1,抢到最后只剩0和2,且两个叫的分一样大,那么2为地主,因为2在1的下手。

部分代码如下:

//发牌

bool AMyNewPlayer::HandOutCardsToPlayers_Server()
{
 if (Role == ROLE_Authority&&m_server)
 {
  TArray<AActor*> _outActors;
  UGameplayStatics::GetAllActorsOfClass(GetWorld(), AMyNewPlayer::StaticClass(), _outActors);
  if (_outActors.Num() > 0&&m_cardsServer.Num() >= 54)
  {
   m_cardsTop = GetCardsByIndexRange(0, 2);
   for (int i=0;i<_outActors.Num();i++)
   {
    AMyNewPlayer* t_player = (AMyNewPlayer*)_outActors[i];
    if (t_player)
    {
     if (t_player->m_indexPlayer == 0)
      t_player->m_cardsClient = GetCardsByIndexRange(3, 19);
     else if (t_player->m_indexPlayer == 1)
      t_player->m_cardsClient =GetCardsByIndexRange(20, 36);
     else if(t_player->m_indexPlayer==2)
      t_player->m_cardsClient = GetCardsByIndexRange(37, 53);
    }   
   }
   return true;
  }
 }
 return false;
}

//选地主

FProcessDisplayData_selectLandlord AMyNewPlayer::SelectLandlord()
{
 FProcessDisplayData_selectLandlord t_res;
 if (Role == ROLE_Authority)
 {  
  AMyCardGameGameMode* t_gameMode=(AMyCardGameGameMode*)(GetWorld()->GetAuthGameMode());
  if (t_gameMode)
  {
   return t_gameMode->SelectLandlord();
  }
 }
 return t_res;
}
FProcessDisplayData_selectLandlord AMyCardGameGameMode::SelectLandlord()
{
 FProcessDisplayData_selectLandlord t_res;
 //first index in first round
 if (m_currentRoundingPlayerIndx == -1 && m_roundSelectLandlord == 0)
 {
  m_currentRoundingPlayerIndx = GetRandomTheFirstPlayerSelectLandlord();
  t_firstRandomIndexOfRounding = m_currentRoundingPlayerIndx;
  t_res.Init(m_roundSelectLandlord, m_currentRoundingPlayerIndx);
  m_roundingPlayers = SortRoundingPlayersByFirstIndex(m_playingPlayers, m_currentRoundingPlayerIndx);
  t_firstIndexOfRounding = m_currentRoundingPlayerIndx;
  t_roundNumCurrent=1;
  t_roundNumTotal = m_roundingPlayers.Num();
  return t_res;
 }
 //change round
 if (t_changeRound)
 {
  t_changeRound = false;
  FliterToGetRemainRoundPlayersWhoSelectLandlord();
  //last round over
  if (m_roundSelectLandlord >= 2)
  {
   t_res.Init(m_roundSelectLandlord, m_currentRoundingPlayerIndx, true, m_currentRoundingPlayerIndx);
   m_landlordIndex = m_currentRoundingPlayerIndx;
   return t_res;
  }    
 }
 //only one player has max integral
 if (m_roundingPlayers.Num() == 1 && m_roundSelectLandlord != 0)
 {
  t_res.Init(m_roundSelectLandlord, m_currentRoundingPlayerIndx, true, m_currentRoundingPlayerIndx);
  m_landlordIndex = m_currentRoundingPlayerIndx;
  return t_res;
 }
 
 //next player this round  
 t_res.Init(m_roundSelectLandlord, m_currentRoundingPlayerIndx);
 return t_res;
}

//记录叫分
bool AMyNewPlayer::SetPlayerIntegralByDataFromPlayerSelect(int _index,int _integralTimes,bool _giveup)
{
 if (Role < ROLE_Authority)
 {
  ServerSetPlayerIntegralByDataFromPlayerSelect(_index,_integralTimes,_giveup);
 }
 AMyCardGameGameMode* t_gamemode = (AMyCardGameGameMode*)(GetWorld()->GetAuthGameMode());
 if (t_gamemode)
 {
  return t_gamemode->SetPlayerIntegralByDataFromPlayerSelect(_index,_integralTimes,_giveup);
 }
 return false;
}
bool AMyCardGameGameMode::SetPlayerIntegralByDataFromPlayerSelect(int _index, int _integralTimes, bool _giveup/* =false */)
{
 //update integral
 if (_giveup)
 {
  RemoveIndexGrapping(_index);
 }
 else
 {
  if (_integralTimes > 0)
  {
   if (m_roundingPlayers.Contains(_index))
   {
    if (m_integralsOfPlayers.Contains(_index))
    {
     int t_num1 = m_integralsOfPlayers.FindChecked(_index);
     m_integralsOfPlayers.Remove(_index);
     m_integralsOfPlayers.Add(_index, t_num1*_integralTimes); 
     
     if (t_num1*_integralTimes>m_integral)
      m_integral = t_num1*_integralTimes;
    }
    else
    {
     m_integralsOfPlayers.Add(_index, _integralTimes);  
     if (_integralTimes > m_integral)
      m_integral = _integralTimes;
    }
   }
  }  
 }
 //round control 
 int t_remainNum = m_roundingPlayers.Num();
 if (t_remainNum >1)
 {
  int t_nextIndex = GetNextIndexOfPlayerWhenRounding();
  if (t_nextIndex >= 0) //is valid index?
  {
   //change round
   if (t_roundNumTotal == t_roundNumCurrent)
   {
    m_roundSelectLandlord++;
    t_changeRound = true;
    t_roundNumCurrent = 0;
    t_roundNumTotal = m_roundingPlayers.Num();
    t_nextIndex = m_roundingPlayers[0];
    t_firstIndexOfRounding = t_nextIndex;
   }
   m_currentRoundingPlayerIndx = t_nextIndex;
   t_roundNumCurrent++;
  }
 }
 else
 {
  //all players has not grap
  if (t_remainNum == 0)
  {
   m_roundingPlayers.Add(t_firstRandomIndexOfRounding);
   m_roundSelectLandlord++;
  }  
  m_currentRoundingPlayerIndx = m_roundingPlayers[0];
 }
 
 return true;
}

部分蓝图:

4.下图为第二轮抢地主。


5.抢地主结束后,服务器记录数据,并且给予地主三张牌,然后同样的办法,在所有客户端上刷新UI数据。之后地主开始出牌。


//给地主牌

FPlayerOperationData AMyNewPlayer::GiveLandlordCardsToLandlord()
{
 FPlayerOperationData t_res;
 if (Role == ROLE_Authority)
 {
  if (m_cardsTop.Num() > 0)
  {
   AMyCardGameGameMode* t_gamemode = (AMyCardGameGameMode*)GetWorld()->GetAuthGameMode();
   if (t_gamemode)
   {
    int t_landlordNum = t_gamemode->m_landlordIndex;
    if (t_landlordNum >= 0)
    {
     AMyNewPlayer* t_landlord=GetPlayerByIndexOnServer(t_landlordNum);
     if (t_landlord)
     {
      for (int i=0;i<m_cardsTop.Num();i++)
      {
       t_landlord->m_cardsClient.Add(m_cardsTop[i]);
      } 
      t_landlord->m_cardsClient=SortCards(t_landlord->m_cardsClient);
      t_res.Init(FPlayerOperationType::ReadyToOutCards, GetCardsUIDataByCards(t_landlord->m_cardsClient), t_landlordNum);
     }
    }
   }
  }  
 }
 return t_res;
}


过程有点快,但是在掌握了RPC机制的基础上,理解起来并不困难。代码和蓝图太多了,只展示了主要的流程,私有实现就不贴了,半年新手程序员,望大家赐教。委屈


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 吃了变质的饭怎么办 颈椎病犯了头晕恶心怎么办 感冒引起的耳朵疼怎么办 感冒了左耳朵疼怎么办 受风引起的面瘫怎么办 两边的脸不一样大怎么办 脖子扭到怎么办快速好 卡马西平片过量怎么办 天冷眼睛神经跳怎么办 每天失眠怎么办要疯了 老是失眠是怎么办才好 汗毛又多又长怎么办 脸上出油毛孔粗大黑头怎么办 毛长在皮肤里怎么办 腰韧带拉伤怎么办恢复快 脚扭伤伤了韧带怎么办 膝关节韧带拉伤怎么办恢复快 脚踝韧带拉伤怎么办恢复快 脚扭伤一年没好怎么办 脚扭伤半年还疼怎么办 脚崴过有后遗症怎么办 脚扭伤脚面肿了怎么办 腰突然扭了好痛怎么办 腰扭伤了怎么办最有效 腰扭伤了不能动怎么办 前交叉韧带增粗怎么办 膝盖前交叉韧带损伤怎么办 狗的腿肌肉拉伤怎么办 胳膊上的筋拉伤怎么办 肩周炎胳膊抬不起来怎么办 脖子上的筋拉伤怎么办 脚踝骨扭伤肿了怎么办 脚扭伤肿起来了怎么办 月经量特别少该怎么办 月经血沾床单上怎么办 月经弄到棉被上怎么办 血弄床单上干了怎么办 不小心吃了指甲怎么办 月经没有干净同房了怎么办 撞红了怎么办要吃药吗 自己长得太丑怎么办