.Net Core——用SignalR撸个游戏
之前开内部培训,说到实时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),多张规则目前支持:王炸、对子、三只、顺子、三带一。目前只做到这里,各位同学可以拿回去自行扩展。
上一些运行图。房主建房并加入:
新玩家加入:
房间人满以后房主开始游戏,随机分配地主:
出牌特效:
游戏结算:
最后附上开源地址(客户端在web分支):https://gitee.com/muchengqingxin/card-game
tips:前端同学在没有UI配合的情况下做到现在这样,必须给个赞。最后提醒大家,不要拿去商用。
.Net Core——用SignalR撸个游戏的更多相关文章
- asp.net core 使用 signalR(一)
asp.net core 使用 signalR(一) Intro SignalR 是什么? ASP.NET Core SignalR 是一个开源代码库,它简化了向应用添加实时 Web 功能的过程. 实 ...
- asp.net core 使用 signalR(二)
asp.net core 使用 signalR(二) Intro 上次介绍了 asp.net core 中使用 signalR 服务端的开发,这次总结一下web前端如何接入和使用 signalR,本文 ...
- Asp.Net Core使用SignalR进行服务间调用
网上查询过很多关于ASP.NET core使用SignalR的简单例子,但是大部分都是简易聊天功能,今天心血来潮就搞了个使用SignalR进行服务间调用的简单DEMO. 至于SignalR是什么我就不 ...
- Asp.net Core中SignalR Core预览版的一些新特性前瞻,附源码(消息订阅与发送二进制数据)
目录 SignalR系列目录(注意,是ASP.NET的目录.不是Core的) 前言 一晃一个月又过去了,上个月有个比较大的项目要验收上线.所以忙的脚不沾地.现在终于可以忙里偷闲,写一篇关于Signal ...
- asp.net core 五 SignalR 负载均衡
SignalR : Web中的实时功能实现,所谓实时功能,就是所连接的客户端变的可用时,服务端能实时的推送内容到客户端,而不是被动的等待客户端的请求.Asp.net SignalR 源码 ...
- ASP.NET Core 使用 SignalR 遇到的 CORS 问题
问题 将 SignalR 集成到 ASP.NET Core MVC 程序的时候,按照官方 DEMO 配置完成,但使用 DEMO 页面建立连接一直提示如下信息. Access to XMLHttpReq ...
- ABP .net Core MQTT+signalr通讯
abp版本: 4.3.0.0 .net core 版本 2.2 1.Mqtt 1.1 添加程序集:M2MqttDotnetCore(差点以为没有.net core 的) 2.2 实现代码:抄了个单例模 ...
- 最新 .NET Core 中 WebSocket的使用 在Asp.Net MVC 中 WebSocket的使用 .NET Core 中 SignalR的使用
ASP.NET MVC 中使用WebSocket 笔记 1.采用控制器的方法 这个只写建立链接的方法的核心方法 1.1 踩坑 网上都是直接 传个异步方法 直接接受链接 自己尝试了好多次链接是打开的,到 ...
- 华为HMS Core图形引擎服务携手三七游戏打造移动端实时DDGI技术
在2021年HDC大会的主题演讲中提到,华为HMS Core图形引擎服务(Scene Kit)正协同三七游戏一起打造实时DDGI(动态漫反射全局光照:Dynamic Diffuse Global Il ...
随机推荐
- 『学了就忘』Linux基础 — 3、CentOS镜像下载
下载CentOS镜像可以从官网下载:https://www.centos.org/download/. 也可以从国内的镜像网站下载. 阿里云:https://mirrors.aliyun.com/ce ...
- Noip模拟77 2021.10.15
T1 最大或 $T1$因为没有开$1ll$右移给炸掉了,调了一年不知道为啥,最后实在不懂了 换成$pow$就过掉了,但是考场上这题耽误了太多时间,后面的题也就没办法好好打了.... 以后一定要注意右移 ...
- 轻松掌握stm32直流电机驱动与测速
说实话就现在的市场应用中stm32已经占到了绝对住到的地位,51已经成为过去式,32的功能更加强大,虽然相应的难度有所增加,但是依然阻止不了大家学习32的脚步,不说大话了这些大家都懂要不然也不会学习s ...
- Less-5闯关失败
进行第五关的通关还是用之前的方式进行测试以及判断是什么类型的注入.通过判断我们不难发现是字符型注入.但是出了问题,我们会发现按照原来的步骤进行注入都会返回"You are in " ...
- 『学了就忘』Linux基础 — 9、虚拟机中快照的使用
目录 1.快照的含义 2.快照的使用 步骤一:创建拍摄快照 步骤二:填写快照信息并创建 步骤三:查看快照 步骤四:操作快照 3.管理虚拟机小技巧 4.关于快照说明 快照和克隆是VMware中两个非常实 ...
- 助你上手Vue3全家桶之VueX4教程
目录 1,前言 2,State 2.1,直接使用 2.2,结合computed 3,Getter 3.1,直接使用 3.2,结合computed 4,Mutation 4.1,直接使用 4.2,结合c ...
- Bzoj P2054 疯狂的馒头 | 并查集
题目链接 思路:因为每次染色都会将某些馒头的颜色彻底更改,所以每个馒头的最终的颜色其实是由最后一次染色决定的,那么我们只考虑最后一次染色即可.对此,我们可以从后往前倒着染色,当目前的染色区间中存在白色 ...
- cm3 逆向分析
目录 cm3 逆向分析 前言 逆向分析 cm3 逆向分析 前言 这道题没加壳,也没加密算法,主要看代码逻辑. 逆向分析 代码很短,一共这么几句. 看提示知道只让我们输入w.s.a.d这几个字符,并且用 ...
- Linux&C ——信号以及信号处理
linux信号的简单介绍 信号的捕捉和处理 信号处理函数的返回 信号的发送 信号的屏蔽 一:linux信号的简单介绍. 信号提供给我们一种异步处理事件的方法,由于进程之间彼此的地址空间是独立的,所以进 ...
- 第39篇-Java通过JNI调用C/C++函数
在某些情况下,Java语言需要通过调用C/C++函数来实现某些功能,因为Java有时候对这些功能显的无能为力,如想使用X86_64 的 SIMD 指令提升一下业务方法中关键代码的性能,又或者想要获取某 ...