.Net Core——用SignalR撸个游戏

  • A+
所属分类:.NET技术
摘要

之前开内部培训,说到实时web应用这一块讲到了SignalR,我说找时间用它做个游戏玩玩,后面时间紧张就一直没安排。这两天闲了又想起这个事,考虑后决定用2天时间写个斗D主,安排了前端同学写客户端,我写游戏逻辑和服务。

之前开内部培训,说到实时web应用这一块讲到了SignalR,我说找时间用它做个游戏玩玩,后面时间紧张就一直没安排。这两天闲了又想起这个事,考虑后决定用2天时间写个斗D主,安排了前端同学写客户端,我写游戏逻辑和服务。

这个项目难度并不高,但是游戏逻辑还是挺绕的,联调过程中也发现解决了很多小问题。来园子里整理一篇文章,记录一下。

基础的介绍就免了,毕竟官网跟着走两圈啥都懂了。没基础的可以戳这里,是我之前写的一篇SignalR基础介绍,带有一个极简聊天室。

tips:文章结尾有开源地址,游戏数据都是本地的,改下IP运行起来就可以玩了。

直接上干货,首先是数据模型:

    /// <summary>     /// 用户信息     /// </summary>     public class Customer     {         /// <summary>         /// 唯一ID         /// </summary>         public string? ID { get; set; }          /// <summary>         /// 昵称         /// </summary>         public string? NickName { get; set; }          /// <summary>         /// 卡片         /// </summary>         public List<string> Card { get; set; }     }       /// <summary>     /// 房间     /// </summary>     public class Room     {         /// <summary>         /// 房间名         /// </summary>         public string Name { get; set; }          /// <summary>         /// 房主id         /// </summary>         public string Masterid { get; set; }          /// <summary>         /// 当前出牌人         /// </summary>         public int Curr { get; set; }          /// <summary>         /// 当前卡片         /// </summary>         public List<string> CurrCard { get; set; } = new List<string>();          /// <summary>         /// 当前卡片打出人         /// </summary>         public string ExistingCardClient { get; set; }          /// <summary>         /// 房间成员列表         /// </summary>         public List<Customer> Customers { get; set; } = new List<Customer>();     }

tips:只是单纯为了斗D主设计的,商用版肯定不能这么搞,参考请慎用。

有了数据模型,自然少不了CRUD:

    /// <summary>     /// 用户操作     /// </summary>     public static class CustomerAction     {         /// <summary>         /// 用户列表         /// </summary>         private static List<Customer> cusList = new List<Customer>();          /// <summary>         /// 不存在则新增,存在则修改昵称         /// </summary>         /// <param name="customer"></param>         public static void Create(Customer customer)         {             Customer curr = null;              if (cusList.Count > 0)                 curr = cusList.Where(x => x.ID == customer.ID).FirstOrDefault();              if (curr is null)                 cusList.Add(customer);             else             {                 curr.NickName = customer.NickName;                  Up4ID(curr);             }         }          /// <summary>         /// 用户列表         /// </summary>         /// <returns></returns>         public static List<Customer> GetList()         {             return cusList;         }          /// <summary>         /// 获取单个         /// </summary>         /// <param name="id"></param>         /// <returns></returns>         public static Customer GetOne(string id)         {             return cusList.Where(x => x.ID == id).FirstOrDefault();         }          /// <summary>         /// 删除用户         /// </summary>         /// <param name="id"></param>         public static void Delete(string id)         {             cusList.RemoveAll(x => x.ID == id);         }          /// <summary>         /// 增加卡片         /// </summary>         /// <param name="id"></param>         /// <param name="cards"></param>         public static void InCard(string id, List<string> cards)         {             Customer customer = cusList.Where(x => x.ID == id).FirstOrDefault();              if (customer.Card is null)                 customer.Card = cards;             else                 customer.Card.AddRange(cards);              Up4ID(customer);         }          /// <summary>         /// 扣除卡片         /// </summary>         /// <param name="id"></param>         /// <param name="cards"></param>         /// <param name="group"></param>         /// <returns></returns>         public static bool OutCard(string id, List<string> cards, Room group)         {             Customer client = cusList.Where(x => x.ID == id).FirstOrDefault();              if (client is null)                 return false;              //卡片不匹配直接失败             if (client.Card.Where(x => cards.Contains(x)).ToList().Count != cards.Count)                 return false;              //不符合出牌规则直接失败             if (!new Game.WithCard().Rule(group.CurrCard, cards, group.ExistingCardClient is null || group.ExistingCardClient == id))                 return false;              foreach (var item in cards)             {                 client.Card.Remove(item);             }              group.CurrCard = cards;              group.ExistingCardClient = id;              Up4ID(client);              RoomAction.Up4Name(group);              return true;         }          /// <summary>         /// 更新(根据ID)         /// </summary>         /// <param name="customer"></param>         /// <returns></returns>         public static bool Up4ID(Customer customer)         {             if (cusList.Count == 0)                 return false;              cusList.RemoveAll(x => x.ID == customer.ID);              cusList.Add(customer);              return true;         }     }       /// <summary>     /// 房间操作     /// </summary>     public static class RoomAction     {         /// <summary>         /// 房间列表         /// </summary>         private static List<Room> roomList = new List<Room>();          /// <summary>         /// 新增房间         /// 如果房间已存在则不新增         /// </summary>         /// <param name="group"></param>         public static void Create(Room group)         {             if (!roomList.Where(x => x.Name == group.Name).Any())                 roomList.Add(group);         }          /// <summary>         /// 获取列表         /// </summary>         /// <returns></returns>         public static List<Room> GetList()         {             return roomList;         }          /// <summary>         /// 获取单个         /// </summary>         /// <param name="masterid">房主id</param>         /// <param name="roomName">房间名称</param>         /// <returns></returns>         public static Room GetOne(string masterid = null, string roomName = null)         {             if (roomList.Count == 0)                 return null;              if (masterid != null)                 return roomList.Where(x => x.Masterid == masterid).FirstOrDefault();              if (roomName != null)                 return roomList.Where(x => x.Name == roomName).FirstOrDefault();              return null;         }          /// <summary>         /// 加入房间         /// </summary>         /// <param name="client"></param>         /// <param name="roomName"></param>         public static bool Join(Customer client, string roomName)         {             if (roomList.Count == 0)                 return false;              var room = roomList.Where(x => x.Name == roomName).FirstOrDefault();              if (room is null)                 return false;              if (room.Customers.Count == 3)                 return false;              room.Customers.Add(client);              Up4Name(room);              return true;         }          /// <summary>         /// 删除房间         /// </summary>         /// <param name="masterid">房主id</param>         public static bool Delete(string masterid)         {             if (roomList.Count == 0)                 return false;              var room = roomList.Where(x => x.Masterid == masterid).FirstOrDefault();              if (room == null)                 return false;              roomList.Remove(room);              return true;         }          /// <summary>         /// 更新(根据房名)         /// </summary>         /// <param name="room"></param>         /// <returns></returns>         public static bool Up4Name(Room room)         {             if (roomList.Count == 0)                 return false;              roomList.RemoveAll(x => x.Name == room.Name);              roomList.Add(room);              return true;         }          /// <summary>         /// 更新当前出牌人         /// </summary>         /// <param name="roomName"></param>         /// <param name="index">传入则强制修改,不传按规则走</param>         public static Customer ChangeCurr(string roomName, int index = -1)         {             var room = roomList.Where(x => x.Name == roomName).FirstOrDefault();              if (index != -1)                 room.Curr = index;             else                 room.Curr = (room.Curr + 1) % 3;              Up4Name(room);              return room.Customers[room.Curr];         }     }

因为所有数据都是通过静态属性保存的,所以大部分都是linq操作(原谅我linq水平有限)。

接下来是游戏逻辑:

    /// <summary>     /// 卡片相关     /// </summary>     public class WithCard     {         /// <summary>         /// 黑桃-S、红桃-H、梅花-C、方块-D         /// BG大王,SG小王,14-A,15-2         /// </summary>         readonly List<string> Cards = new List<string>()         {             "S-14","S-15","S-3","S-4","S-5","S-6","S-7","S-8","S-9","S-10","S-11","S-12","S-13",             "H-14","H-15","H-3","H-4","H-5","H-6","H-7","H-8","H-9","H-10","H-11","H-12","H-13",             "C-14","C-15","C-3","C-4","C-5","C-6","C-7","C-8","C-9","C-10","C-11","C-12","C-13",             "D-14","D-15","D-3","D-4","D-5","D-6","D-7","D-8","D-9","D-10","D-11","D-12","D-13",             "BG-99","SG-88"         };          /// <summary>         /// 发牌         /// </summary>         public List<List<string>> DrawCard()         {             List<string> a = new List<string>();             List<string> b = new List<string>();             List<string> c = new List<string>();              Random ran = new Random();              //剩3张底牌             for (int i = 0; i < 51; i++)             {                 //随机抽取一张牌                 string item = Cards[ran.Next(Cards.Count)];                  switch (i % 3)                 {                     case 0:                         a.Add(item);                         break;                     case 1:                         b.Add(item);                         break;                     case 2:                         c.Add(item);                         break;                 }                  Cards.Remove(item);             }              return new List<List<string>>()             {                 a,b,c,Cards             };         }          /// <summary>         /// 规则         /// </summary>         /// <param name="existingCard"></param>         /// <param name="newCard"></param>         /// <param name="isSelf"></param>         /// <returns></returns>         public bool Rule(List<string> existingCard, List<string> newCard, bool isSelf)         {             //现有牌号             List<int> existingCardNo = existingCard.Select(x => Convert.ToInt32(x.Split('-')[1])).ToList().OrderBy(x => x).ToList();              //新出牌号             List<int> newCardNo = newCard.Select(x => Convert.ToInt32(x.Split('-')[1])).ToList().OrderBy(x => x).ToList();              //上一手是王炸,禁止其他人出牌             if (existingCardNo.All(x => x > 50) && existingCardNo.Count == 2)             {                 if (isSelf)                     return true;                 else                     return false;             }              //王炸最大             if (newCardNo.All(x => x > 50) && newCard.Count == 2)                 return true;              //单张             if (newCardNo.Count == 1)             {                 if (existingCardNo.Count == 0)                     return true;                  if ((existingCardNo.Count == 1 && newCardNo[0] > existingCardNo[0]) || isSelf)                     return true;             }              //对子/三只             if (newCardNo.Count == 2 || newCardNo.Count == 3)             {                 if (existingCardNo.Count == 0 && newCardNo.All(x => x == newCardNo[0]))                     return true;                  if (newCardNo.All(x => x == newCardNo[0]) && (isSelf || newCardNo.Count == existingCardNo.Count && newCardNo[0] > existingCardNo[0]))                     return true;             }              if (newCard.Count == 4)             {                 //                 if (newCardNo.All(x => x == newCardNo[0]))                 {                     if (existingCardNo.Count == 0 || isSelf)                         return true;                      if (existingCardNo.All(x => x == existingCardNo[0]) && existingCardNo.Count == 4)                     {                         if (newCardNo[0] > existingCardNo[0])                             return true;                     }                      return true;                 }                  //三带一                 {                     List<int> flagA = newCardNo.Distinct().ToList();                      //超过2种牌直接失败                     if (flagA.Count > 2)                         return false;                      //没有上一手牌,或者上一手是自己出的牌                     if (existingCardNo.Count == 0 || isSelf)                         return true;                      int newCardFlag = 0;                      if (newCardNo.Where(x => x == flagA[0]).ToList().Count() > 1)                     {                         newCardFlag = flagA[0];                     }                     else                         newCardFlag = flagA[1];                      List<int> flagB = existingCardNo.Distinct().ToList();                      //上一手牌不是三带一                     if (flagB.Count > 2)                         return false;                      int existingCardFlag = 0;                      if (existingCardNo.Where(x => x == flagB[0]).ToList().Count() > 1)                     {                         existingCardFlag = flagB[0];                     }                     else                         existingCardFlag = flagB[1];                      if (newCardFlag > existingCardFlag)                         return true;                 }             }              if (newCard.Count >= 5)             {                 bool flag = true;                  for (int i = 0; i < newCardNo.Count - 1; i++)                 {                     if (newCardNo[i] + 1 != newCardNo[i + 1])                     {                         flag = false;                         break;                     }                 }                  //顺子                 if (flag)                 {                     if (existingCardNo.Count == 0 || (newCardNo[0] > existingCardNo[0] && newCardNo.Count == existingCardNo.Count) || isSelf)                         return true;                 }             }              return false;         }     }

单张规则和普通斗D主一样(除了王以外2最大,其次是A),多张规则目前支持:王炸、对子、三只、顺子、三带一。目前只做到这里,各位同学可以拿回去自行扩展。

上一些运行图。房主建房并加入:

.Net Core——用SignalR撸个游戏

新玩家加入:

.Net Core——用SignalR撸个游戏

房间人满以后房主开始游戏,随机分配地主:

.Net Core——用SignalR撸个游戏

出牌特效:

.Net Core——用SignalR撸个游戏

游戏结算:

.Net Core——用SignalR撸个游戏

最后附上开源地址(客户端在web分支):https://gitee.com/muchengqingxin/card-game

tips:前端同学在没有UI配合的情况下做到现在这样,必须给个赞。最后提醒大家,不要拿去商用。