[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秒自動開始遊戲.

差不多就到這裏了,可以自行擴展.

源碼在頂部獲取.

關注瞭解更多.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章