简介
两个玩家连接服务器,服务器如何分别处理每个玩家的消息并广播出去。
以我改写的联机坦克大战为例。
客户端:只发送控制方向的键盘信息,接收服务器发送的所有玩家的坦克坐标,将坦克画出来
服务器:区分两个玩家,接收两个玩家的键盘信号并分别处理改变坦克的坐标,最后广播出去。
服务端
通过定义一个Clients数组来存放每个连接上来的ws;
利用数组的索引来确定客户端,处理其信息。
客户端
客户端连接上服务器
代码
paly.css
h1{
font-family: Verdana, Tahoma, sans-serif;
font-size: 40px;
color: #0714b9;
position: absolute;left:45%;top:-3%;
white-space:nowrap;
}
/*body{
background-color: rgb(239, 239, 240);
background-size: auto 600px;
background-image: url("image/12.jpeg");
background-repeat: no-repeat;
background-attachment: fixed;
}*/
body {
width: 500px;
height: 300px;
margin: 150px auto;
background-color: #FF9500;
padding: 0 20px 20px 20px;
}
control.js
//定义敌人和我们自己的坦克的颜色
var heroColor = new Array("#f00","#ff5");
var heroColor2 = new Array("#0f0","#ff5");
//封装一个公用的坦克父类
function Tank(x,y,direct){
this.x = x;
this.y = y;
this.direct = direct;
}
//英雄坦克类
function Hero(x,y,direct,color){
//将坦克类的构造方法赋给hero
this.hero = Tank;
//调用,拥有坦克类的所有的属性和方法
this.hero(x,y,direct);
this.hero.speed = 10
this.color = color;
this.direct = direct;
this.isLive = true;
this.type=0;
this.shotEnemy = function(){
switch(this.direct){
case 0:
heroBullet = new Bullet(this.x+9,this.y,this.direct,this.hero,this.type);
break;
case 1:
heroBullet = new Bullet(this.x+30,this.y+9,this.direct,this.hero,this.type);
break;
case 2:
heroBullet = new Bullet(this.x+9,this.y+30,this.direct,this.hero,this.type);
break;
case 3:
heroBullet = new Bullet(this.x,this.y+9,this.direct,this.hero,this.type);
break;
}
//heroBullet.timer=window.setInterval(heroBullet.run(),50);
lines.push(heroBullet);
lines[lines.length-1].timer = window.setInterval("lines["+(lines.length-1)+"].run()",50);
}
}
//绘制坦克
function drawTank(hero){
switch(hero.direct){
case 0:
case 2:
ctx.fillStyle = hero.color[0];
ctx.fillRect(hero.x,hero.y,5,30);
ctx.fillRect(hero.x+15,hero.y,5,30);
ctx.fillRect(hero.x+6,hero.y+5,8,20);
ctx.fillStyle = hero.color[1];
ctx.beginPath();
ctx.arc(hero.x+10,hero.y+15,3,0,Math.PI*2,true);
ctx.closePath();
ctx.fill();
//画出炮筒(直线)
ctx.strokeStyle = hero.color[1];
ctx.lineWidth = 2;
ctx.moveTo(hero.x+10,hero.y+15);
if(hero.direct==0){
ctx.lineTo(hero.x+10,hero.y);
}else if(hero.direct==2){
ctx.lineTo(hero.x+10,hero.y+30);
}
ctx.stroke();
break;
case 1:
case 3:
ctx.fillStyle = hero.color[0];
ctx.fillRect(hero.x,hero.y,30,5);
ctx.fillRect(hero.x,hero.y+15,30,5);
ctx.fillRect(hero.x+5,hero.y+6,20,8);
//需要注意,画圆的时候需要重新开启路径
ctx.fillStyle = hero.color[1];
ctx.beginPath();
ctx.arc(hero.x+15,hero.y+10,3,0,Math.PI*2,true);
ctx.closePath();
ctx.fill();
//画出炮筒(直线)
ctx.strokeStyle = hero.color[1];
ctx.lineWidth = 2;
ctx.moveTo(hero.x+15,hero.y+10);
if(hero.direct ==1){
ctx.lineTo(hero.x+30,hero.y+10);
}else if(hero.direct ==3){
ctx.lineTo(hero.x,hero.y+10);
}
ctx.stroke();
break;
}
}
server.js
const WebSocket = require('ws');
let Clients = new Array();
let num = 0;
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws,req) {
console.log('有用户连接上来了'+req.socket.remoteAddress);
Clients[num] = ws;
num++;
if(num>=2){
action();
}
ws.on('message', function incoming(message) {
let strings = JSON.parse(message);
console.log(message.toString());
});
ws.on("close", (code, reason)=>{
Clients.pop(num-1);
num--;
console.log('连接关闭。。。')
});
ws.on("error", (error)=>{
console.log('连接异常。。。。')
});
});
var H_1=[380,260,0];//x轴,y轴,方向
var H_2=[180,260,0];
//定义广播函数
function broadcast(msg){
wss.clients.forEach((connect)=>{
connect.send(JSON.stringify(msg)) ;
})
}
var broad=function(Array1,Array2){
broadcast({
HERO1:Array1,
HERO2:Array2,
})
}
//定义改变方向函数
var speed = 3;
function ChangeDirect(number,Array){
if(number==38 || number==87){//上
if(Array[1]>0)
{
Array[1]-=speed;
}
Array[2]=0;
}
if(number==39 || number==68){//右
if(Array[0]+30<500)
{
Array[0]+=speed;
}
Array[2]=1;
}
if(number==40 || number==83){//下
if(Array[1]+30<300)
{
Array[1]+=speed;
}
Array[2]=2;
}
if(number==37 || number==65){//左
if(Array[0]>0)
{
Array[0]-=speed;
}
Array[2]=3;
}
/* if(number==32 || number==13){//发射
Array[3]=-1;
} */
}
//服务器控制坦克移动
function action(){
Clients[0].on('message', function incoming(str) {
//console.log("0已经调用!!!!!!"+"H1的值:"+str)
let string1 = str.toString();
let string0 = JSON.parse(string1);
//对于玩家一接收移动信号改变位置
H_1[3]=parseInt(string0['key']);
ChangeDirect(H_1[3],H_1);
console.log("一号客户端收到信息:"+string1);
});
Clients[1].on('message', function incoming(str1) {
let string0 = str1.toString();
let strings = JSON.parse(string0);
//对于玩家一接收移动信号改变位置
H_2[3]=parseInt(strings['key']);
console.log("二号客户端收到信息:"+string0);
ChangeDirect(H_2[3],H_2);
});
setInterval(broad,50,H_1,H_2);
}
test1.html
<!DOCTYPE html>
<html lang="ch">
<head>
<meta charset="utf-8">
<title>html5坦克大战</title>
<script src='./js/control.js'></script>
<link rel="stylesheet" type="text/css" href="./css/play.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1 >html5坦克大战</h1>
<div style="margin: 30px;">
<canvas id='tankMap' width='500px' height='300px' style='background-color:#3e0549;'>
你的浏览器不支持canvas标签 ||||| You are seeing enemyTanks[i].message because your web browser does not support the canvas tag.
</canvas>
</div>
<div id='hero1_hit' style="position: absolute;left:35.5%;top:100px"></div>
<div id='hero1_death' style="position: absolute;left:35.5%;top:125px"></div>
<div id='hero2_hit' style="position: absolute;left:58%;top:100px"></div>
<div id='hero2_death' style="position: absolute;left:58%;top:125px"></div>
<div id='add1'></div>
<div id='add2'></div>
<div style="margin: 30px;font-size: 12px">
<p>控制方法:按下键盘w s a d或者↑ ↓ ← →分别是坦克的上下左右行驶<br/>按下j或者Enter键为射击</p>
</div>
</body>
<script>
var canvas = document.getElementById('tankMap');
var ctx = canvas.getContext('2d');
var hero1 = new Hero();
var hero2 = new Hero();
//创建发送对象
var information = {
key:null,
}
//var heroBullet=null;
//建立socket
//var socket = new WebSocket('ws://10.129.63.238:8080')
var socket = new WebSocket('ws://127.0.0.1:8080')
// 监听键盘事件
document.onkeydown = keyDown;
//在Document对象中注册keyDown事件处理函数
function keyDown(event){ // 方向键控制元素移动函数
var event = event || window.event; // 标准化事件对象
information['key']=event.keyCode;
socket.send(JSON.stringify(information));
information['key']=null;
}
socket.addEventListener ('message',(e)=>{
//console.log(e);
//console.log(e.data);
let ms = JSON.parse(e.data);
//console.log(ms['BAD']['tanks']);
//let ms = e.data
t1=ms["HERO1"];
t2=ms["HERO2"];
console.log("t1信息:"+t1);
console.log("t2信息:"+t2);
var hero1 = new Hero(t1[0],t1[1],t1[2],heroColor);
var hero2 = new Hero(t2[0],t2[1],t2[2],heroColor2);
drawTank(hero1);
drawTank(hero2);
var flashMap=function(){
ctx.clearRect(0,0,500,300);
if(hero1.isLive){
drawTank(hero1);
document.getElementById('add1').innerText = " hero的坐标:("+hero1.x+","+hero1.y+")";
}
if(hero2.isLive){
drawTank(hero2);
document.getElementById('add2').innerText = " hero2的坐标:("+hero2.x+","+hero2.y+")";
}
}
flashMap();
})
</script>
</html>
代码链接
代码文件的阿里云链接https://www.aliyundrive.com/s/aw2RRQHeoRH
提示
配置好的nodejs环境下,新建文件夹将上述代码按照“代码”目录上的图片存放。
跳过步骤1,继续执行即可
运行结果
坦克大战代码部分参照https://www.cnblogs.com/SemiconductorKING/p/6602600.html