1. 程式人生 > >使用websocket實現“你畫我猜”

使用websocket實現“你畫我猜”

1,環境配置(nodejs)

檔案結構:

package.json:

{
  "name": "websocket",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "node-websocket-server": "^1.1.4",
    "ws": "^6.1.0"
  }
}

2,伺服器配置 (server.js)

//配置一些變量表達遊戲狀態
var LINE_SEGMENT = 0;
var CHAT_MESSAGE = 1;
var GAME_LOGIC = 2;

var WAITING_TO_START = 0;
var GAME_START = 1;
var GAME_OVER = 2;
var GAME_RESTART = 3;

var playerTurn = 0;
//配置遊戲中使用的關鍵詞
var wordsList = ["apple","pear","angry","happy","boat","desk"];
var currentGameState = WAITING_TO_START;
var gameOverTimeout;

//建立websocket連線
var WebSocketServer = require('ws').Server;

var wss = new WebSocketServer({ port: 8181 });

//定義廣播函式
wss.broadcast = function broadcast(message) {
  wss.clients.forEach(function each(client) {
        client.send(message);
  });
};

//建立連線時向客戶端傳送一條msg
wss.on('connection', function (ws) { 
	var msg = "Welcome to join the party! Total connection: " + wss.clients.size;    
    
    //定義傳送資料的型別,並廣播               
    var data = {};
    data.dataType = CHAT_MESSAGE;
    data.name = "Server";
    data.message = msg;
    wss.broadcast(JSON.stringify(data));

    //定義控制遊戲邏輯的資料型別,並廣播
    var gameLogicData = {};
    gameLogicData.dataType = GAME_LOGIC;
    gameLogicData.gameState = WAITING_TO_START;
    wss.broadcast(JSON.stringify(gameLogicData));

    //開始遊戲的條件
    if(currentGameState == WAITING_TO_START && wss.clients.size >=2){
    	startGame();
    }


    //廣播接收到的客戶端發來的訊息,並廣播
    ws.on('message', function (message) {
    	var obj = eval('(' + message + ')');
    	wss.broadcast(JSON.stringify(obj));

        //如果判斷收到的訊息是聊天訊息
    	if(obj.dataType == CHAT_MESSAGE){
            //如果接收到的訊息是正確答案,廣播並改變當前遊戲狀態
    		if(currentGameState == GAME_START && obj.message == currentAnswer){
    			var gameLogicData = {};
    			gameLogicData.dataType = GAME_LOGIC;
    			gameLogicData.gameState = GAME_OVER;
    			gameLogicData.winner = obj.name;
    			gameLogicData.answer = currentAnswer;
    			wss.broadcast(JSON.stringify(gameLogicData));

    			currentGameState = WAITING_TO_START;

    			clearTimeout(gameOverTimeout);
    		}
    	}else if(obj.dataType == GAME_LOGIC && obj.gameState == GAME_RESTART){
    		startGame();
    	}     
    });

});

//開始遊戲函式,初始化答案,控制開始的使用者是誰
function startGame(){
	playerTurn = (playerTurn +1) % wss.clients.size;

	var answerIndex = Math.floor(Math.random()* wordsList.length);
	currentAnswer = wordsList[answerIndex];

	var gameLogicData1 = {};
	gameLogicData1.dataType = GAME_LOGIC;
	gameLogicData1.gameState = GAME_START;
	gameLogicData1.isPlayerTurn = false;
	wss.broadcast(JSON.stringify(gameLogicData1));


	var index = 0;
	wss.clients.forEach(function each(client) {
        if(index == playerTurn){
        	var gameLogicData2 = {};
        	gameLogicData2.dataType = GAME_LOGIC;
			gameLogicData2.gameState = GAME_START;
			gameLogicData2.answer = currentAnswer;
			gameLogicData2.isPlayerTurn = true;
			wss.broadcast(JSON.stringify(gameLogicData2));
        }
        index ++;
  	});

  	gameOverTimeout = setTimeout(function(){
  		var gameLogicData = {};
		gameLogicData.dataType = GAME_LOGIC;
		gameLogicData.gameState = GAME_OVER;
		gameLogicData.winner = 'no-one';
		gameLogicData.answer = currentAnswer;
		wss.broadcast(JSON.stringify(gameLogicData)); 	

		currentGameState = WAITING_TO_START;	
  	},60*1000);

  	currentGameState = GAME_START;


}

console.log("Websocket server is running");

3,客戶端配置(client.html)

建立一個websocket連線到伺服器

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>WebSocket Echo Demo</title>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/js/bootstrap.min.js"></script>
   <link rel="stylesheet" type="text/css" href="index.css">
</head>

<body >
    <div id="game">
        <h1>你畫我猜</h1>
        <canvas id="drawing-pad" width="450" height="320"></canvas>

        <div class="vertical-center">
        	<ul id="chat-history"></ul>

            <div class="container">

                <form role="form" id="chat_form" onsubmit="sendMessage(); return false;">
                    <div class="form-group">
                        輸入:<input class="form-control" type="text" name="message" id="message" value="" />
                        <button type="button" id="send" class="btn btn-primary" onclick="sendMessage();">
                        Send
                        </button>
                        <button type="button" id="restart" class="btn btn-primary">
                            restart
                        </button>
                    </div>

                </form>
            </div>
        </div>
    </div>

    <script type="text/javascript" src="index.js"></script>


    <script>
        //啟動一個websocket,並連線到伺服器
        var ws = new WebSocket("ws://localhost:8181");
        ws.onopen = function (e) {
            console.log('Connection to server opened');
        }

        ws.onmessage = function(evt)
        {
            var data = JSON.parse(evt.data);
            if(data.dataType == websocketGame.CHAT_MESSAGE){
                $("#chat-history").append("<li>" + data.name + " said: " + data.message + "</li>");
            }else if(data.dataType == websocketGame.LINE_SEGMENT){
                drawLine(ctx,data.startX,data.startY,data.endX,data.endY,1);
            }else if(data.dataType == websocketGame.GAME_LOGIC){
                if(data.gameState == websocketGame.GAME_OVER){
                    websocketGame.isTurnToDraw = false;
                    $("#chat-history").append("<li>" + data.winner + " wins! The answer is '" + data.answer + "'.</li>");
                    $("#restart").show();
                }else if(data.gameState == websocketGame.GAME_START){
                    canvas.width = canvas.width;
                    $("#restart").hide();
                    $("#chat-history").html("");
                    if(data.isPlayerTurn){
                        isTurnToDraw = true;
                        $("#chat-history").append("<li>Your turn to draw. pLease draw ' " + data.answer + "'.</li>");
                    }else{
                        $("#chat-history").append("<li>Game started. Get ready. You have ne minute to guess.</li>");
                    }
                }
            }
            
        };

        function sendMessage() {
            var msg = $('#message').val();
            var data = {};
            data.dataType = websocketGame.CHAT_MESSAGE;
            data.message = msg;   
            data.name = "client1";  
            ws.send(JSON.stringify(data));
            $('#message').val("");
        }
    </script>


</body>
</html>

 4,客戶端JS檔案(Index.js)

控制畫板的繪圖功能,同步多個使用者之間的繪圖

var websocketGame = {
    isDrawing:false,
    startX:0,
    startY:0,
	
    LINE_SEGMENT : 0,
    CHAT_MESSAGE : 1,
    GAME_LOGIC:2,
    
    WAITING_TO_START:0,
    GAME_START:1,
    GAME_OVER:2,
    GAME_RESTART:3,
    isTurnToDraw:false
}


var canvas = document.getElementById("drawing-pad");
var ctx = canvas.getContext('2d');

function drawLine(ctx,x1,y1,x2,y2,thickness){
	ctx.beginPath();
	ctx.moveTo(x1,y1);
	ctx.lineTo(x2,y2);
	ctx.lineWidth = thickness;
	ctx.strokeStyle = "#444";
	ctx.stroke();
}


$(document).ready(function(){

	$('#drawing-pad').mousedown(function(e){
		var canvasPosition = $(this).offset();
		var mouseX = (e.pageX - canvasPosition.left) || 0;
		var mouseY = (e.pageY - canvasPosition.top) || 0;

		websocketGame.startX = mouseX;
		websocketGame.startY = mouseY;
		websocketGame.isDrawing = true;
	});

	$('#drawing-pad').mousemove(function(e){
		if(websocketGame.isDrawing){
			var canvasPosition = $(this).offset();
			var mouseX = (e.pageX - canvasPosition.left) || 0;
			var mouseY = (e.pageY - canvasPosition.top) || 0;
		}

		if(!(mouseX == websocketGame.startX && mouseY == websocketGame.startY)){
			drawLine(ctx,websocketGame.startX,websocketGame.startY,mouseX,mouseY,1);

			var data = {};
			data.dataType = websocketGame.LINE_SEGMENT;
			
			data.startX = websocketGame.startX;
			data.startY = websocketGame.startY;
			data.endX = mouseX;
			data.endY = mouseY;
			ws.send(JSON.stringify(data));

			
			websocketGame.startX = mouseX;
			websocketGame.startY = mouseY;
		}
	});

	$('#drawing-pad').mouseup(function(e){
		websocketGame.isDrawing = false;
	});

})

5,CSS檔案配置

body{
    background:#ccd6e1;
    font-family:arial,serif;
}

#game{
    width:450px;
    margin:0 auto;
}

#game h1{
    text-align: center;
    margin-bottom:10px;
    font-size:20px;
}

#drawing-pad{
    position:relative;
    border:solid 1px black;
    background: white;
}

.vertical-center{
    position:relative;
}

ul li{
    list-style:none;
}

#chat-history{
    height:100px;
    overflow:auto;
    border:solid 1px #ccc;
    font-size:14px;
}