工作繁忙,更新较慢。

接着上一篇文章,游戏的创建和加入房间已经大概完成了,具体的扩展丰富,比如当前服务器列表之类的信息,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机制的基础上,理解起来并不困难。代码和蓝图太多了,只展示了主要的流程,私有实现就不贴了,半年新手程序员,望大家赐教。

UE4局域网斗地主(三)相关推荐

  1. 初识UE4 VR开发三

    初识UE4 VR开发三 UE4入门 虚幻编辑器介绍(From:百度百科) 配置需求 软件需求 UE4入门 很多天不写了,因为我发现我电脑配置不太够,不太支持HTC VIVE的头显,所以想办法上了RTX ...

  2. UE4 HTC VIVE - 番外篇 - 局域网联机三

    环境设置和检测 1.打开引擎下的在线调试 引擎在线模式开启 2.打开项目的在线调试 项目在线模式开启 3.检查机器网段 我们需要对每台要加入局域网的机器都进行网段检测,这是能客户端能找到主机广播的前提 ...

  3. ue4 android联机,UE4 局域网联机(LAN)

    实现目标: 两台同属于一个局域网下的PC进行通讯. 1 同属于一个局域网下: 有时候会发现客户端搜索不到主机的现象一般是2台机器没在同一网段,光ping通不能算做可以进行联机的条件. 检测和修改方法: ...

  4. UE4学习笔记#三、蓝图混合空间

    UE4学习笔记(谌嘉诚大佬的PUBG教程) 三.蓝图混合空间 1. 设置初始角色(关卡开始时pawn获得的角色) 2. 角色移动蓝图 3. 镜头方向蓝图 4. 利用TimeLine平滑切换行走跑步 5 ...

  5. UE4 局域网联机案例

    效果: 局域网联机案例 步骤: 1.新建一个第三人称模板 2.创建三个控件蓝图,分别命名为主菜单.加入服务器.查找服务器 3.双击打开 主菜单 控件蓝图,添加三个按钮,分别是创建游戏.搜索服务器和退出 ...

  6. 斗地主三步走——洗牌、发牌和看牌

    相信大家都玩过牌游斗地主吧,今天呢,阿Q就带大家写一个简单的斗地主的洗牌.发牌和看牌的小Demo. public static void main(String[] args) {//1,买一副扑克, ...

  7. java斗地主socket_纯JAVA写的socket局域网斗地主游戏

    前两天没项目做 就想着写点什么. 就写了这个小游戏没事跟同事玩玩. 5块钱一把 不带开挂的. 先来几张效果图 废话不多 开篇纪念. 部分源码 连接服务器类 public class Connect { ...

  8. 无线路由器接入局域网的三种方式

    接触过宽带路由器的用户,大抵都了解宽带路由器上的端口有WAN口和LAN口之分.宽带路由器在工作过程中有这样一个特点:从LAN到WAN方向上的数据流默认不受限制通过路由器,从WAN到LAN方向上默认不能 ...

  9. 多人在线斗地主游戏开发——自定义TCP网络通信协议包格式

    什么叫做通信协议?为什么制定通信协议? 怎么制定通信协议? 不知道大家有没有迷茫过这个问题,反正我是有的,,, 想我在刚接触网络编程的时候,是linux下用socket懵懵懂懂地按照pdf书籍上的代码 ...

最新文章

  1. Qt中两种定时器用法
  2. NBT:PICRUSt2预测宏基因组功能
  3. 最新!中国内地大学ESI排名出炉:362所高校上榜,南方科技大学、深圳大学、暨南大学表现出色!...
  4. matlab中textread
  5. mysql-索引-笔记
  6. mysql为查询结果字段赋默认值
  7. 用python打开视频_python读取视频流提取视频帧的两种方法
  8. javascript学习系列(6):数组中的pop等方法
  9. git下载及安装向导如何配置
  10. 关于MongoDB内存占用不断上升,导致OOM问题
  11. 微课|中学生可以这样学Python(例5.1):生成不重复的随机数
  12. html5 圆形加载进度条,纯css3超酷圆形Loading加载进度条特效
  13. Verilog 语法小结
  14. lcms质谱仪_液相色谱-质谱联用(lcms)的原理及应用
  15. Tims中国上市背后:以新流派打法,“开源”咖啡市场
  16. 分享四款实用流程图模板
  17. VMware16的安装及VMware配置Linux虚拟机(详解版)
  18. 视频教程-Oracle数据库开发技巧与经典案例讲解一-Oracle
  19. Display Port 1.4 link Training 过程
  20. Visual Studio 2019 打包生成.exe安装文件,附带.net框架与自定义安装插件

热门文章

  1. JS中双层for循环执行顺序
  2. 【阿旭机器学习实战】【24】信用卡用户流失预测实战
  3. 2022前端面试题汇总
  4. NCBI SRA数据库使用详解----学习笔记
  5. UE4使用射线检测来显示UI并与物体交互
  6. android camera textureview,textureview_learn
  7. 专业知识vs行业知识
  8. 新版微信订阅号和服务号的区别
  9. mysql计算年龄大于30并删除_还在苦恼MySQL如何根据日期精确计算年龄?看这一篇,就够了!...
  10. STM32 LWIP SNTP实现毫秒级的时间校准