[JavaWeb]谁是卧底游戏制作(网络游戏)

Hello,I'm Shendi,这次我制作了谁是卧底游戏(制作周期三天).

这里我写了一篇关于这个制作的教程,并附带了源码

下面是运行效果.


目录

主要技术

整理思路

开始界面实现

房间列表界面(快速开始,进入房间)

房间架构(对应 Room 类)

当我们点击快速开始游戏按钮的时候,请求了 JoinServlet 接口

房间界面实现

加入房间

用户类 Player

房间内聊天功能

切换聊天频道

发送聊天信息

系统发给自己的专属消息接收

房间用户以及状态显示

游戏开始

职业分配

投票机制

发送结果,展示胜利界面


主要技术

服务器: Tomcat,Servlet

前端: html,css,js

项目已放 Github(对应于WhoIsTheSpy项目):https://github.com/1711680493/Application

后面我会制作更多有意思的东西来分享,如果您感兴趣,点个关注呗~...

此文章对应实战专栏(一些实战案例都在此): https://blog.csdn.net/qq_41806966/category_9656338.html


整理思路

在开始之前,我们需要了解一下这个游戏的玩法,机制.

游戏机制: 房间功能(用户的加入退出,游戏状态等),聊天功能(不同频道处理),投票功能等

玩法(我参考谁是卧底定义的): 警杀模式

六个玩家一组,四个平民,一个警察一个杀手

杀手每晚可以投票杀一个人,警察每晚可以获取一个人的身份

白天所有玩家进行投票,票数最多的将会被处决

杀手杀掉所有平民或者警察则胜利

警察和平民则处决杀手胜利

了解到游戏的玩法后,我们就可以一步一步来实现了.(后面的实战例子我会绘制UML图来讲解)

下面是我这个项目所有的前端界面

开始界面实现

其中 index.html 是第一个界面 界面效果如下

界面包含一个文字和一个a标签...对应index.css文件

a标签点击后跳转到 main.html 界面

房间列表界面(快速开始,进入房间)

界面效果如下

 这个界面有一个div(用于显示现有的所有房间)

一个快速开始按钮(从现有的房间中选一个未满的进入,如果没有房间则新建一个,自己为1号)

以及顶部的 logo(没有啥作用,这篇博客主要讲后端实现...)

房间号的加载对应了roomList.js(请求服务器,拿到数据)和main.js(将数据放到div里显示)

刚开启服务器是没有任何房间的,当我们点击快速开始游戏的时候,会请求 Servlet 来进行房间的进入和创建

快速开始游戏的点击事件在 main.js 中绑定了.使用了 ajax 请求 join接口(对应 JoinServlet)

房间架构(对应 Room 类)

 Room类代表一个房间,是一个抽象类,用于处理用户的加入退出,游戏逻辑调用,拥有保存玩家状态的属性

有以下属性

 Room类的方法

 Room类有一个子类,Default类,实现了 start(),stop()方法

以及RoomManager(房间管理),用于创建,销毁房间.

当我们点击快速开始游戏按钮的时候,请求了 JoinServlet 接口

主要获取用户sessionId,并通过房间管理器(RoomManager)来加入房间

joinRoom方法有两个参数,一个是sessionId(String),和roomId(int)

 接下来我们点击快速开始游戏按钮(没有房间,创建了一个 id 为 1),在跳转的时候url上加上了id参数(main.js跳转room.js获取)

接下来就进入了这样一个界面 room.html页面.

房间界面实现

room.js 刚开始获取到url上的 id(当前房间号)

最上方的文字不再只是单纯的logo,可以用来退出房间(请求ExitRoomServlet->Room的exit方法)

带着房间id访问了ExitRoomServlet接口(不管用户在不在房间里,都会重定向到 main.html 中)

房间的进入,退出功能都有了,接下来就是玩家加入房间内(不是旁观者了)

加入房间

当点击加入房间按钮,就会带着房间 id 去请求JoinRoomServlet 接口(加入房间接口)

JoinRoomServlet 最终调用指定 Room 的 join 方法(也就是加入房间) 代码如下

join方法有sessionId参数,以及加了同步锁(避免人数超出限制),加入人数不能超过最大人数

返回值我这里提供了,没有处理(可以自行扩展...)

 现在我们的房间进入,退出,加入房间功能已经实现了

目前我们的程序看不到什么效果,就算点击加入房间...也只是用户的信息在Map里存储了.

所以,接下来我们就需要让我们房间里的信息在页面显示

(我制作的时候是先制作聊天功能,然后在制作用户的显示,因为要确定用户类里需要有什么功能)

在房间没有开始的时候.用户存储为 座位号=sessionId 的形式

在开始游戏后,用户存储为 sessionId=Player类的形式

用户类 Player

Player类有以下几个属性

 其中 objects 是用于存储此用户对应的一些特别的数据,比如警察晚上查询的玩家,杀手晚上选择执行的玩家

每一个玩家都对应一个 Player.

两个枚举

Player在Room中

房间内聊天功能

接下来我们把聊天功能先制作,在房间中有存所有聊天信息的map,以及对应的频道常量

 我们发送/接收就很简单了直接从map里取

在 room.js 中,有一个一直运行的代码(一直请求聊天信息,根据不同频道,然后放在div显示)

代码比较长,请求的接口为 InfoServlet,是专门处理消息的接口

//获取聊天框
var infoContent = document.getElementById("info_content");
//当前的频道
var infoType = "all";
//房间消息的线程 默认等于全部
{
	var infoUrl;
	if (window.XMLHttpRequest) infoUrl = new XMLHttpRequest();
	else infoUrl = new ActiveXObject("Microsoft.XMLHTTP");
	infoUrl.onreadystatechange = function () {
		if (infoUrl.readyState == 4) {
			//获取到消息
			let infos = infoUrl.responseText;
			if (infoUrl.status != 200) {
				//请求出错处理
				infoContent.innerHTML = infos;
				clearInterval(getInfo);
				return;
			}
			//替换内容
			let infoArray = eval(infos);
			infoContent.innerHTML = "";
			for (let i = 0;i < infoArray.length;i++) {
				let obj = infoArray[i];
				infoContent.innerHTML += obj.info + "<br />";
			}
		}
	}
	var getInfo = setInterval(function () {
		infoUrl.open("POST","/WhoIsTheSpy/info",true);
		infoUrl.setRequestHeader("Content-type","application/x-www-form-urlencoded");
		infoUrl.send("roomId=" + roomId + "&infoType=" + infoType);
	},800);
}

InfoServlet代码,一些基础的判断,获取指定房间的指定频道的消息.(数据传递使用了 json)

在 getInfo 方法中会判断频道类型是否为阵容,如果是阵容(游戏没开始)则没有任何消息.

对于阵容的消息,我定义的规则是,不同阵容用,阵容 + 职业类型 来区分

接下来我们可以接收到频道消息,则需要切换频道

切换聊天频道

 通过这几个按钮,点击的时候我们会把js里频道的值改变.也就是

对于系统频道是不允许发送消息的.

接收消息+切换频道弄好了,剩下的就是发送消息,点击发送按钮会带着输入框内的值去请求 SendInfoServlet.

发送聊天信息

SendInfoServlet 是用于发送聊天信息的接口,需要 房间id 聊天频道 和 消息内容 三个参数

sendInfo方法是 Room 类的方法,用于将消息添加进map.(因为有 js 一直获取 map里的值,所以就实现了发送消息的功能)

sendInfo代码比较长,有很多过滤的东西

  • 游戏开始,并且频道为公共则不能发送消息(return)
  • 如果频道是系统频道(这个只有代码里自己调用,而不是用户),则直接添加进map,不做一些判断,并且把消息也添加到公共消息中.
  • 参数 sessionId 为发送者,(房间内部调用则为 系统)
  • 如果房间内有此玩家,那么就以玩家座位号命名,否则直接用sessionId命名
/**
	 * 发送消息到指定频道.
	 * @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
	 * @param sessionId 是谁发送的消息.
	 * @param infoType 频道类型
	 * @param info 消息内容
	 */
	public void sendInfo(String sessionId,String infoType,String info) {
		//游戏开始 类型公共 并且是晚上则不执行操作
		if (state && infoType.equals(INFO_ALL) && gameState == GameState.夜晚) {
			return;
		}
		//系统的则直接发送
		if (INFO_SYSTEM.equals(infoType)) {
			if (infos.containsKey(infoType)) {
				List<String> list = infos.get(infoType);
				list.add(sessionId + ":" + info);
			} else {
				List<String> list = new ArrayList<>();
				list.add(sessionId + ":" + info);
				infos.put(infoType,list);
			}
			if (infos.containsKey(INFO_ALL)) {
				List<String> list = infos.get(INFO_ALL);
				list.add(sessionId + ":" + info);
			} else {
				List<String> list = new ArrayList<>();
				list.add(sessionId + ":" + info);
				infos.put(INFO_ALL,list);
			}
			return;
		}
		//获取sessionId在房间里的位置 如果没有,则以sessionId命名
		if (players.containsValue(sessionId)) {
			Set<Integer> set = players.keySet();
			for (int k : set) {
				if (sessionId.equals(players.get(k))) {
					sessionId = String.valueOf(k);
					break;
				}
			}
		}
		if (infos.containsKey(infoType)) {
			List<String> list = infos.get(infoType);
			list.add(sessionId + ":" + info);
		} else {
			List<String> list = new ArrayList<>();
			list.add(sessionId + ":" + info);
			infos.put(infoType,list);
		}
	}

除了所有人的消息之外,有时候系统需要给用户私发一些消息,所以弄一个私人消息接收框

系统发给自己的专属消息接收

也就是这个,是一个Div元素,一次只显示一条数据(最后一条),如果点击,则会变大,并显示所有的数据

设置这个div为只读(不可选),点击的时候将css样式切换一下达到这种效果.

我们获取私信的系统消息与获取信息的方式一致,

这里请求的是RoomEventServlet接口,有两种类型 通过 isLast判断获取一条数据或者所有数据

当游戏开始的时候才会有数据返回.

我们前端获取游戏结果也是通过这个获取

现在消息系统弄好了,接下来就是显示房间的用户

房间用户以及状态显示

在 room.js 里有一个一直执行获取当前房间其余玩家信息的ajax

通过获取 RoomInfoServlet 接口中拿到数据并进行显示

js代码

//获取房间用户状态的线程
var userInfo = "<table border='1'>" +
"<tr>" +
	"<th>编号</th>" +
	"<th>当前玩家</th>" +
	"<th>身份</th>" +
	"<th>操作</th>" +
	"<th>操作数量</th>" +
	"<th>游戏状态</th>" +
"</tr>";
var players = document.querySelector(".players");
var userUrl;
if (window.XMLHttpRequest) userUrl = new XMLHttpRequest();
else userUrl = new ActiveXObject("Microsoft.XMLHTTP");
userUrl.onreadystatechange = function () {
	if (userUrl.readyState == 4) {
		//获取到消息
		let infos = userUrl.responseText;
		if (userUrl.status != 200) {
			//请求出错处理
			clearInterval(getUser);
			return;
		}
		//替换内容
		let infoArray = eval(infos);
		let text = userInfo;
		for (let i = 0;i < infoArray.length;i++) {
			let obj = infoArray[i];
			text += "<tr><td>";
			text += obj.id + "</td>";
			text += "<td>" + obj.user + "</td>";
			//标识
			let identity = obj.identity;
			if (identity == undefined) {
				text += "<td>无</td>";
			} else {
				text += "<td>" + identity + "</td>";
			}
			//操作
			let operation = obj.operation;
			if (operation == undefined) {
				text += "<td>无</td>";
			} else {
				text += "<td>" + operation + "</td>";
			}
			//操作数量
			let operationNum = obj.operationNum;
			if (operation == undefined) {
				text += "<td>无</td>";
			} else {
				text += "<td>" + operationNum + "</td>";
			}
			//游戏状态
			let gameState = obj.gameState;
			if (gameState == undefined) {
				text += "<td>无</td>";
			} else {
				text += "<td>" + gameState + "</td>";
			}
			text += "</tr>";
		}
		text += "</table>";
		players.innerHTML = text;
	}
}
//获取房间里的用户
var getUser = setInterval(function () {
	userUrl.open("POST","/WhoIsTheSpy/roomInfo",true);
	userUrl.setRequestHeader("Content-type","application/x-www-form-urlencoded");
	userUrl.send("roomId=" + roomId);
},800);

RoomInfoServlet

用于获取当前房间内用户的信息(有很多判断)

  • 房间不存在则返回-500
  • 游戏未开始(准备中)则除了用户的座位号 用户名 其余的信息都为无
  • 游戏开始则判断请求的用户是什么职业
    • 警察: 获取已经查询了的其余职业的身份,白天,晚上可以投票
    • 杀手: 白天,晚上可以投票.
    • 平民: 白天可以投票.
@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		//获取当前session
		String sessionId = req.getSession().getId();
		//获取房间id
		String roomId = req.getParameter("roomId");
		//获取房间信息
		Room room = RoomManager.getRooms().get(Integer.parseInt(roomId));
		if (room == null) {
			//返回房间号 -500为无房间
			resp.getOutputStream().write(String.valueOf(room).getBytes("UTF-8"));
			return;
		}
		//获取房间状态 游戏未开启输出的 操作什么都为无
		JsonArray array = new JsonArray();
		if (!room.state) {
			room.getPlayers().forEach((k,v) -> {
				JsonObject player = new JsonObject();
				player.addProperty("id", k);
				if (v.equals(sessionId)) player.addProperty("user", "我");
				else player.addProperty("user", v);
				array.add(player);
			});
		} else {
			//获取自己的信息
			Player my = room.startPlayers.get(sessionId);
			room.startPlayers.forEach((k,v) -> {
				JsonObject player = new JsonObject();
				//id
				player.addProperty("id", v.getId());
				//名称标识
				if (k.equals(sessionId)) {
					player.addProperty("user", "我");
					//获取自己的职业
					player.addProperty("identity",v.getType().toString());
				} else {
					player.addProperty("user", k);
					//其余人的职业一律未知 (警察可以获取到查询了的职业),已死亡的角色职业将暴露
					if (my != null && my.getType() == PlayerType.警察) {
						@SuppressWarnings("unchecked")
						List<String> identitys = (List<String>) my.getObjects().get("identitys");
						if (identitys != null && identitys.contains(k)) {
							player.addProperty("identity",v.getType().toString());
						} else {
							player.addProperty("identity","未知");
						}
					} else if (v.getState() == PlayerState.死亡) {
						player.addProperty("identity",v.getType().toString());
					} else {
						player.addProperty("identity","未知");
					}
				}
				//当前状态
				player.addProperty("gameState", v.getState().toString());
				//操作,和操作数量 用户死亡 或者为自己 则无操作选项和数量
				if (v.getState() == PlayerState.死亡 || k.equals(sessionId)) {
					player.addProperty("operation", "无");
					if (k.equals(sessionId)) {
						player.addProperty("operationNum",getOperationNum(k, room,PlayerType.平民));
					} else {
						player.addProperty("operationNum", "无");
					}
				} else {
					//旁观不能投票
					if (my == null) {
						player.addProperty("operation", "无");
						player.addProperty("operationNum",getOperationNum(k, room,PlayerType.平民));
					} else {
						//白天都有投票操作 并且只有投票操作
						if (room.gameState == GameState.白天) {
							//获取自己选中的是什么
							if (k.equals(my.getObjects().get("selection"))) {
								player.addProperty("operation", "<label><input type='radio' name='selection' checked='checked' οnclick='selection(\"" + k + "\")' />投票</label>");
							} else {
								player.addProperty("operation", "<label><input type='radio' name='selection' οnclick='selection(\"" + k + "\")' />投票</label>");
							}
							//获取此用户的投票数量
							player.addProperty("operationNum",getOperationNum(k, room,PlayerType.平民));
						} else {
							//晚上 自己的身份是警察则是查操作 杀手则是杀操作 平民则无
							switch (my.getType()) {
							case 平民:
								player.addProperty("operation", "无");
								player.addProperty("operationNum", "无");
								break;
							case 警察:
								//获取自己选中的是什么
								if (k.equals(my.getObjects().get("getIdentity"))) {
									player.addProperty("operation", "<label><input type='radio' name='getIdentity' checked='checked' οnclick='getIdentity(\"" + k + "\")' />查询</label>");
								} else {
									player.addProperty("operation", "<label><input type='radio' name='getIdentity' οnclick='getIdentity(\"" + k + "\")' />查询</label>");
								}
								//获取此用户的投票数量
								player.addProperty("operationNum",getOperationNum(k, room,PlayerType.警察));
								break;
							case 杀手:
								//获取自己选中的是什么
								if (k.equals(my.getObjects().get("kill"))) {
									player.addProperty("operation", "<label><input type='radio' name='kill' checked='checked' οnclick='kill(\"" + k + "\")' />杀掉</label>");
								} else {
									player.addProperty("operation", "<label><input type='radio' name='kill' οnclick='kill(\"" + k + "\")' />杀掉</label>");
								}
								//获取此用户的投票数量
								player.addProperty("operationNum",getOperationNum(k, room,PlayerType.杀手));
								break;
							}
						}
					}
				}
				array.add(player);
			});
		}
		resp.getOutputStream().write(array.toString().getBytes("UTF-8"));
	}
	
	/**
	 * 获取指定用户的操作数
	 * @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
	 * @param sessionId 用户标识,获取此用户的操作数.
	 * @param room 房间
	 * @param type 代表获取的类型 平民则获取投票数,警察则获取用户的警投票数,杀手则获取杀投票数.
	 * @return 操作数量
	 */
	private int getOperationNum(String sessionId,Room room,PlayerType type) {
		Set<String> set = room.startPlayers.keySet();
		int num = 0;
		for (String session : set) {
			switch (type) {
			case 平民:
				if (sessionId.equals(room.startPlayers.get(session).getObjects().get("selection"))) num++;
				break;
			case 警察:
				if (sessionId.equals(room.startPlayers.get(session).getObjects().get("getIdentity"))) num++;
				break;
			case 杀手:
				if (sessionId.equals(room.startPlayers.get(session).getObjects().get("kill"))) num++;
				break;
			}
		}
		return num;
	}

有了用户的显示后,就是核心部分玩法了.

游戏开始

当房间最后一个用户加入后就会执行开始游戏操作.

switchState() 是 Room的切换房间状态方法,这里开线程的原因是游戏逻辑要在后台执行,而不是使用最后一个用户的线程,

state默认是false,所以上述代码会执行 start(); 方法(子类实现的 start)

职业分配

在DefaultRoom中的Start运行进行职业分配.(分配一个警察和一个杀手,其余的都是平民)

 switchGameState方法

调用的是 GameBlockState 中的 switchState方法 传递了一个 room.

GameWhiteState执行完就会调用GameBlockState的方法,GameBlockState执行完就会调用GameWhiteState的方法.

直至游戏结束条件成立...

投票机制

游戏开始时,刚开始时夜晚,警察和杀手进行行动,

点击单选框其实会调用 js 请求接口

这里的三个接口一个是警察投票,杀手投票,白天投票的接口.

主要功能就是获取投票的用户,给他的objects(存数据的HashMap)加投票数据(获取房间玩家信息的时候就可以获取到,并且游戏后台可以获取到)

  • 当投票成功后,执行对应的操作,先判断结果(比如杀手可以杀死一个玩家,杀死后如果没有平民/警察则胜利).
    • 如果没有胜利,则切换状态(夜晚变成白天).
  • 白天所有玩家都可以投票,进行处决一名玩家,投票结果会取票数最多的玩家进行处决
    • (上述警察和杀手也是,只不过目前只有一个警察和一个杀手)
    • 处决完后判断游戏结果是否胜利/失败
    • 如果警察/平民/杀手都没有全灭亡 则切换状态(白天变夜晚)

晚上代码

public class GameBlockState implements RoomGameState {
	private static GameWhiteState whiteState = new GameWhiteState();
	
	public GameBlockState() {
		GameWhiteState.setBlockState(this);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void switchState(Room room) {
		//休息一秒
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//提示晚上来临
		room.sendInfo("系统", Room.INFO_SYSTEM, "夜晚来临.");
		//睡眠一秒 提示进行操作
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		room.startPlayers.forEach((k,v) -> {
			if (PlayerType.警察 == v.getType()) {
				room.sendInfoByUser(k, "请选择你要搜查的目标");
			} else if (PlayerType.杀手 == v.getType()) {
				room.sendInfoByUser(k, "请选择你要击杀的目标");
			}
		});
		//倒计时20秒 执行结果
		room.sendInfo("系统", Room.INFO_SYSTEM, "距离白天还有20秒.");
		try {
			Thread.sleep(20000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		room.gameState = GameState.白天;
		try {
			Thread.sleep(1000);;
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		//执行夜晚的操作
		{
			//获取警察 杀手选择的目标 并执行操作
			HashMap<String,Integer> officeSelection = new HashMap<>();
			HashMap<String,Integer> killSelection = new HashMap<>();
			room.startPlayers.forEach((k,v) -> {
				if (v.getType() == PlayerType.警察) {
					if (v.getObjects().containsKey("getIdentity")) {
						String sessionId = (String) v.getObjects().get("getIdentity");
						if (officeSelection.containsKey(sessionId)) {
							officeSelection.put(sessionId,officeSelection.get(sessionId) + 1);
						} else {
							officeSelection.put(sessionId,1);
						}
						v.getObjects().remove("getIdentity");
					}
				} else if (v.getType() == PlayerType.杀手) {
					if (v.getObjects().containsKey("kill")) {
						String sessionId = (String) v.getObjects().get("kill");
						if (killSelection.containsKey(sessionId)) {
							killSelection.put(sessionId,killSelection.get(sessionId) + 1);
						} else {
							killSelection.put(sessionId,1);
						}
						v.getObjects().remove("kill");
					}
				}
			});
			//通知警察被查询的身份
			{
				String officeSelect = null;
				int officeMax = -1;
				//获取投票最多的用户
				{
					Set<String> keys = officeSelection.keySet();
					for (String key : keys) {
						int value = officeSelection.get(key);
						if (value > officeMax) {
							officeSelect = key;
							officeMax = value;
						}
					}
				}
				Player officeSelectPlayer = room.startPlayers.get(officeSelect);
				if (officeSelect != null) {
					//所有警察都获取此用户身份
					room.sendInfo("系统", Room.INFO_TOGETHER + PlayerType.警察,officeSelectPlayer.getId() + " 已经被查,身份是:" + officeSelectPlayer.getType());
					Set<String> keys = room.startPlayers.keySet();
					for (String key : keys) {
						Player player = room.startPlayers.get(key);
						if (player.getType() == PlayerType.警察) {
							room.sendInfoByUser(key, officeSelectPlayer.getId() + " 已经被查,身份是:" + officeSelectPlayer.getType());
							if (player.getObjects().containsKey("identitys")) {
								((List<String>)(player.getObjects().get("identitys"))).add(officeSelect);
							} else {
								List<String> list = new ArrayList<>();
								list.add(officeSelect);
								player.getObjects().put("identitys",list);
							}
						}
					}
				}
			}
			//执行杀手操作
			{
				String killSelect = null;
				int killMax = -1;
				//获取投票最多的用户
				{
					Set<String> keys = killSelection.keySet();
					for (String key : keys) {
						int value = killSelection.get(key);
						if (value > killMax) {
							killSelect = key;
							killMax = value;
						}
					}
				}
				//击杀玩家
				if (killSelect != null) {
					Player killPlayer = room.startPlayers.get(killSelect);
					killPlayer.setState(PlayerState.死亡);
					room.sendInfo("系统",Room.INFO_SYSTEM,killPlayer.getId() + " 被杀害,身份是“" + killPlayer.getType() + "”");
					//判断游戏结果 如果场上没有警察 平民则胜利
					boolean officeExists = false;
					boolean peopleExists = false;
					Collection<Player> values = room.startPlayers.values();
					for (Player player : values) {
						if (player.getType() == PlayerType.警察 && player.getState() == PlayerState.存活) {
							officeExists = true;
						} else if (player.getType() == PlayerType.平民 && player.getState() == PlayerState.存活) {
							peopleExists = true;
						}
					}
					if (!officeExists || !peopleExists) {
						//提示游戏结束 一秒后跳出结果界面并停止游戏
						room.startPlayers.forEach((k,v) -> {
							if (v.getType() == PlayerType.杀手) {
								room.sendInfoByUser(k, "|stop|杀手胜利,警察或平民全灭.");
							} else {
								room.sendInfoByUser(k, "|stop|失败,警察或平民全灭.");
							}
						});
						try {
							Thread.sleep(1000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						room.switchState();
						return;
					}
				}
			}
		}
		room.roomGameState = whiteState;
		room.switchGameState();
	}

}

 白天代码

public class GameWhiteState implements RoomGameState {
	private static GameBlockState blockState;
	
	public static void setBlockState(GameBlockState blockState) {
		GameWhiteState.blockState = blockState;
	}
	
	@Override
	public void switchState(Room room) {
		//休息一秒
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//提示早上来临
		room.sendInfo("系统", Room.INFO_SYSTEM, "早上了,请开始投票.");
		//睡眠一秒 提示进行操作
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		room.startPlayers.forEach((k,v) -> {
			room.sendInfoByUser(k, "请投票.");
		});
		//倒计时50秒 执行结果
		room.sendInfo("系统", Room.INFO_SYSTEM, "距离夜晚还有50秒.");
		try {
			Thread.sleep(50000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		room.gameState = GameState.夜晚;
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		//执行白天的操作
		{
			//获取所有的投票
			HashMap<String,Integer> selection = new HashMap<>();
			room.startPlayers.forEach((k,v) -> {
				if (v.getObjects().containsKey("selection")) {
					String sessionId = (String) v.getObjects().get("selection");
					if (selection.containsKey(sessionId)) {
						selection.put(sessionId,selection.get(sessionId) + 1);
					} else {
						selection.put(sessionId,1);
					}
					v.getObjects().remove("selection");
				}
			});
			String select = null;
			int selectMax = -1;
			//获取投票最多的用户
			{
				Set<String> keys = selection.keySet();
				for (String key : keys) {
					int value = selection.get(key);
					if (value > selectMax) {
						select = key;
						selectMax = value;
					}
				}
			}
			//处决用户
			if (select != null) {
				Player player = room.startPlayers.get(select);
				//将用户状态改为死亡
				player.setState(PlayerState.死亡);
				//通知其他用户
				room.sendInfo("系统", Room.INFO_SYSTEM, "玩家 " + player.getId() + " 被处决,身份是 “" + player.getType() + "”");
				//休息两秒
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				//获取职业状态(是否全灭)
				boolean officeExists = false;
				boolean killExists = false;
				boolean peopleExists = false;
				Collection<Player> values = room.startPlayers.values();
				for (Player p : values) {
					if (p.getType() == PlayerType.警察 && p.getState() == PlayerState.存活) {
						officeExists = true;
					} else if (p.getType() == PlayerType.杀手 && p.getState() == PlayerState.存活) {
						killExists = true;
					} else if (p.getType() == PlayerType.平民 && p.getState() == PlayerState.存活) {
						peopleExists = true;
					}
				}
				//判断结果
				if (!officeExists || !peopleExists) {
					//提示游戏结束 一秒后跳出结果界面并停止游戏
					room.startPlayers.forEach((k,v) -> {
						if (v.getType() == PlayerType.杀手) {
							room.sendInfoByUser(k, "|stop|杀手胜利,警察或平民全灭.");
						} else {
							room.sendInfoByUser(k, "|stop|失败,警察或平民全灭.");
						}
					});
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					room.switchState();
					return;
				} else if (!killExists) {
					//提示游戏结束 一秒后跳出结果界面并停止游戏
					room.startPlayers.forEach((k,v) -> {
						if (v.getType() == PlayerType.杀手) {
							room.sendInfoByUser(k, "|stop|杀手失败,杀手全灭.");
						} else {
							room.sendInfoByUser(k, "|stop|胜利,杀手全灭.");
						}
					});
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					room.switchState();
					return;
				}
			}
		}
		room.roomGameState = blockState;
		room.switchGameState();
	}

}

发送结果,展示胜利界面

根据之前说到的私人消息,如果我们发送的消息包含结束 -- 在js中判断手为结束的数据,是则弹出结果面板

游戏状态切换(状态变为等待) 几秒后,如果房间内还是满人则等待20秒自动开始游戏.

差不多就到这里了,可以自行扩展.

源码在顶部获取.

关注了解更多.

猜你喜欢

转载自blog.csdn.net/qq_41806966/article/details/105905987
今日推荐