代码
1. tank.js
var ownBullets = new Array(),
otherBullets = new Array();
function Tank(args) {
this.x = args.x;
this.y = args.y;
this.w = 20;
this.h = 20;
this.id = args.id;
this.name = args.name;
this.speed = 2;
this.isLive = args.isLive;
this.direct = 'up';
}
function Tank1(args) {
this.tank = Tank;
this.tank(args);
this.color = args.color;
this.moveUp = function() {
this.direct = 'up';
if (this.y > 0) this.y -= this.speed;
};
this.moveDown = function() {
this.direct = 'down';
if (this.y + this.h < 500) this.y += this.speed;
}
this.moveLeft = function() {
this.direct = 'left';
if (this.x > 0) this.x -= this.speed;
}
this.moveRight = function() {
this.direct = 'right';
if (this.x + this.w < 1000) this.x += this.speed;
}
this.shot = function() {
var bullet = null;
switch(this.direct) {
case 'up':
bullet = new BulletOwn(this.x+8, this.y-3, this.direct);
break;
case 'down':
bullet = new BulletOwn(this.x+8, this.y+23, this.direct);
break;
case 'right':
bullet = new BulletOwn(this.x+20, this.y+8, this.direct);
break;
case 'left':
bullet = new BulletOwn(this.x-3, this.y+8, this.direct);
break;
}
ownBullets.push(bullet);
sendCmd("bullet", {x:bullet.x, y:bullet.y, direct:bullet.direct});
var timer = window.setInterval('ownBullets['+(ownBullets.length-1)+'].run()', 100);
ownBullets[ownBullets.length-1].timer = timer;
}
}
function Bullet(x, y, direct) {
this.x = x;
this.y = y;
this.w = 3;
this.h = 3;
this.speed = 10;
this.color = 'white';
this.timer = null;
this.isLive = true;
this.direct = direct;
}
function BulletOwn(x, y, direct) {
this.bullet = Bullet;
this.bullet(x, y, direct);
this.run = function() {
if (this.x <=0 || this.x >= 1000 || this.y <=0 || this.y >= 500) {
this.isLive = false;
}
switch(this.direct) {
case 'up':
this.y -= this.speed;
break;
case 'down':
this.y += this.speed;
break;
case 'right':
this.x += this.speed;
break;
case 'left':
this.x -= this.speed;
break;
}
for (var i in other) {
if (other[i].isLive && collide(this, other[i])) {
this.isLive = false;
other[i].isLive = false;
var p = document.createElement('p');
p.innerHTML = '你击杀了'+other[i].name;
notice.appendChild(p);
sendCmd("shot", {myId:myId, myName:myName, otherId:other[i].id, otherName:other[i].name});
break;
}
}
}
}
function BulletOther(x, y, direct) {
this.bullet = Bullet;
this.bullet(x, y, direct);
if (this.x <=0 || this.x >= 1000 || this.y <=0 || this.y >= 500) {
this.isLive = false;
}
this.run = function() {
switch(this.direct) {
case 'up':
this.y -= this.speed;
break;
case 'down':
this.y += this.speed;
break;
case 'right':
this.x += this.speed;
break;
case 'left':
this.x -= this.speed;
break;
}
var all = other;
all[myId] = own;
for (var i in all) {
if (all[i].isLive && collide(this, all[i])) {
this.isLive = false;
}
}
}
}
function collide(obj1, obj2) {
var mw = (obj1.w + obj2.w) / 2;
var mh = (obj1.h + obj2.h) / 2;
if (Math.abs(obj1.x + obj1.w/2 - obj2.x - obj2.w/2) < mw
&& Math.abs(obj1.y + obj1.h/2 - obj2.y - obj2.h/2) < mh) {
return true;
}
return false;
}
2. ws.js
var socket = null,
st = document.getElementById("service");
if (window.WebSocket) {
socket = new WebSocket("ws://localhost:8000");
} else {
alert("浏览器不支持websocket");
}
socket.onopen = function () {
st.innerHTML = "已连接";
}
socket.onclose = function () {
st.innerHTML = "连接已关闭";
}
socket.error = function () {
st.innerHTML = "连接异常";
}
socket.onmessage = function (event) {
var serverData = JSON.parse(event.data); //接受数据,有多种方式[数组,比较前后两次数据等]
worker(serverData); //在game.js中定义
}
function sendCmd(cmd, data) {
socket.send(JSON.stringify({cmd:cmd, data:data}));
}
3. game.js
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
notice = document.getElementById('notice');
var myInfo = {
x: Math.random()*1000, //坐标,并应后台生产
y: Math.random()*500,
id: myId,
name: myName,
color: myColor,
isLive: true
};
var own = new Tank1(myInfo);
setTimeout('sendCmd("create", myInfo)', 3000);
var other = {};
function worker(res) {
var resData = null;
if (res[0]) {
for (var i in res) {
resData = JSON.parse(res[i]).data;
if (resData.id !== myId && !resData[resData.id])
other[resData.id] = new Tank1(resData);
}
} else {
resData = res.data;
if (res.cmd === "move"){
var otherDataObj = other[resData.id];
otherDataObj.x = resData.x;
otherDataObj.y = resData.y;
otherDataObj.isLive = resData.isLive;
otherDataObj.direct = resData.direct;
} else if (res.cmd === "bullet"){
resData = res.data;
var bullet = new BulletOther(resData.x, resData.y, resData.direct);
otherBullets.push(bullet);
var timer = window.setInterval('otherBullets['+(otherBullets.length-1)+'].run()', 100);
otherBullets[otherBullets.length-1].timer = timer;
} else {
var p = document.createElement('p');
resData = res.data;
if (resData.otherId === myId) {
own.isLive = false;
p.innerHTML = resData.otherName+'击杀了你';
} else {
other[resData.otherId].isLive = false;
p.innerHTML = resData.myName+'击杀了'+resData.otherName;
}
notice.appendChild(p);
}
}
}
function drawTank(tank) {
if (tank.isLive) {
ctx.fillStyle = tank.color;
ctx.fillRect(tank.x, tank.y, tank.w, tank.h);
ctx.fillStyle = 'white';
switch(tank.direct) {
case 'up':
ctx.fillRect(tank.x+5, tank.y, 10, 10);
break;
case 'down':
ctx.fillRect(tank.x+5, tank.y+10, 10, 10);
break;
case 'left':
ctx.fillRect(tank.x, tank.y+5, 10, 10);
break;
case 'right':
ctx.fillRect(tank.x+10, tank.y+5, 10, 10);
break;
}
} else {
ctx.fillStyle = 'white';
ctx.fillRect(tank.x, tank.y, tank.w, tank.h);
}
}
function drawOwnBullet() {
for (var i in ownBullets) {
if (ownBullets[i].isLive) {
ctx.fillStyle = ownBullets[i].color;
ctx.fillRect(ownBullets[i].x, ownBullets[i].y, 3, 3);
} else {
window.clearInterval(ownBullets[i].timer);
delete ownBullets[i];
}
}
}
function drawOtherBullet() {
for (var i in otherBullets) {
if (otherBullets[i].isLive) {
ctx.fillStyle = otherBullets[i].color;
ctx.fillRect(otherBullets[i].x, otherBullets[i].y, 3, 3);
} else {
window.clearInterval(otherBullets[i].timer);
delete otherBullets[i];
}
}
}
function flush() {
ctx.clearRect(0,0,1000,500);
drawTank(own);
for (var key in other) {
drawTank(other[key]);
}
drawOwnBullet();
drawOtherBullet();
}
setInterval(flush, 50);
window.onkeydown = function(event) {
if (own.isLive) {
var code = event.keyCode;
switch(code) {
case 87:
own.moveUp();
break;
case 68:
own.moveRight();
break;
case 83:
own.moveDown();
break;
case 65:
own.moveLeft();
break;
case 74:
own.shot();
break;
}
if ([65, 68, 83, 87].indexOf(code) > -1) {
var obj = {
id: own.id,
x: own.x,
y: own.y,
isLive: own.isLive,
direct: own.direct
}
sendCmd("move", obj);
}
} else {
alert('你已阵亡');
}
}
原理
1. 坦克的移动射击动作,通过 ws.js 中 sendCmd 方法发送给后台,后台再广播给其他所有客户端。
2. 前端通过 game.js 中 worker 方法解析后端传回的数据,绘制其他坦克操作。