手写坦克大战联网版(持续更新)

用到脚本语言

            javascript es5 es6 es7

            node

                |   models

                         |       socket.io

            mysql

先了解下目录结构

前端

       css:游戏的一些样式

       js:动态脚本 触碰系统等】

       index.html 网页脚本

       images 图片        

后台

       node_modules  模块

       main.js  主启动

搭建服务

 服务器部署

我使用的是nginx   node部署到服务器 需要 反向代理接口

server服务

listen   监听端口号80

location/api  监听到/api的参数就将值代理给3000端口号

准备工作

brick砖块

explose爆炸图

my坦克图

bullet子弹图

分享图片

下载素材当前无效过后我会搭建素材页面的

地图计算

所有素材 模型 大小 32 * 32

横向14 * 32  

竖向18 * 32

<canvas id = 'ctx' width = '448' height = '576'></canvas>

首先我们先获取canvas

let c = document.getElementById("ctx");
var ctx=c.getContext("2d");

接下来创建绘制图片系统

drawCreate = async (url,x,y,w,h,id) =>{  //绘制图片
    let image = new Image();
    let app = await new Promise((resolve)=>{
        image.src = url;
        image.onload = function(){
            resolve(image);
        }
    }).then((image)=>{
        if(id == 0){
            _1ps = {
                w:w,
                h:h,
                x:x,
                y:y
            }
        }
        ctx.drawImage(image,x,y,w,h);
    });
    return image;
}

改绘制图片方法有6个参数  url为图片路径  x y 为坐标 w h 为图片 大小 id 0,1,2   0为1p玩家   1为2p玩家  2为地图绘制也就是素材

移动系统

//添加移动事件
let move = async (url,obj,type,value) =>{    //玩家移动方法
    ctx.clearRect(obj.x,obj.y,obj.w,obj.h);
    if(type == 1){
        if(obj.up == undefined && value < 0){    //判断是否是首次加载图片
            let item = await drawCreate(url,obj.x,obj.y + value,obj.w,obj.h,0);
            return item;
        }

        if(obj.down == undefined && value > 0){
            if(type == 1){
                let item = await drawCreate(url,obj.x,obj.y + value,obj.w,obj.h,0);
                return item;
            }
        }
    }else{
        if(obj.right == undefined && value > 0){
            let item = await drawCreate(url,obj.x + value,obj.y,obj.w,obj.h,0);
            return item;
        }
    
        if(obj.left == undefined && value < 0){
            let item = await drawCreate(url,obj.x + value,obj.y,obj.w,obj.h,0);
            return item;
        }
    }
    if(type == 1){
        ctx.drawImage(url,obj.x,obj.y += value ,obj.w,obj.h);
        return url;
    }else{
        ctx.drawImage(url,obj.x += value,obj.y ,obj.w,obj.h);
        return url;
    }
}
document.body.onkeydown = async (event) =>{
    let item = "";
    switch(event.keyCode){
        case 65:    //a后
            if(mapScope(_1ps.x - 4,0,32,28))return;
            item = await move(_1ps.left || _1p.left,_1ps,0,-4);
            _1ps.left = item;
            break;
        case 68:    //d前
            if(mapScope(_1ps.x + 4,0,32,28)))return;
            item = await move(_1ps.right || _1p.right,_1ps,0,4);
            _1ps.right = item;
            break;
        case 83:    //s下
            if(mapScope(_1ps.y + 4,1,32,28)))return;
            item = await move(_1ps.down || _1p.down,_1ps,1,4);
            _1ps.down = item;
            break;
        case 87:    //w上
            if(mapScope(_1ps.y - 4,1,32,28)))return;
            item = await move(_1ps.up || _1p.up,_1ps,1,-4);
            _1ps.up = item;
            break;
    }
}

范围限制

let mapScope = (value,type,w,h) => {    //检测是否超出地图限制    type = 0 左右地图限制   type = 1 上下地图限制
    if(type == 0){
        if(value < 0 || value > 448 - w)return true;
    }else{
        if(value > 576 - h || value < 0)return true;
    }
}

编辑一张地图

因为先前设置的地图高度是32*18 宽度是32*14  我们要创建一个 14*18的二维数组

 

像上面构造玩家一样构造没个建筑物 有些不合适 隐藏 需要一个地图构造器

let level = [   //关卡
    [
        [3,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,2,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,2,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,3,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,4,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,5,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,6,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0]
    ]
]

地图构造器

现在地图控制器设置一个规则 方便创建出来的对象样式 然后对该规则做一套适配

1p   0

2p   1

普通砖   2

刚砖  3

草坪  4

水池  5

冰路  6

修改上面我们改好的loadGame

let loadGame = async () =>{ //加载游戏
    let item = await drawCreate(_1p.up,32,545,32,32,0);
    mapCreate();
}
let mapCreate = async () => {//地图构造器  
    // 1p:1 普通砖:2 刚转:3 草坪:4 水池:5 冰路:6
    let s = 0;
    for(let i = 0;level[levels][i]!=undefined;i++){
        for(let j = 0;level[levels][i][j]!=undefined;j++){
            if(level[levels][i][j] > 0){
                if(level[levels][i][j] >= 1){
                    let item = await drawCreate(matter[level[levels][i][j] - 2],j*32,i*32,32,32);
                    obj.push({  //将所有建筑物的坐标保存到obj
                        w:32,
                        h:32,
                        x:j*32,
                        y:i*32,
                        ev:level[levels][i][j],
                        index:s++,
                        el:item,
                        id:createId()
                    })
                }
            }
        }
    }
}
let createId = (length = 32)=>{    //创建id
    let str = "";
    let arr = "qwertyuiopasdfghjklmnbcvxzQWERTYUIOPLKJHGFDSAZXCBNM1234567890"
    for(let i = 0;i<=length;i++){
        str += arr[Math.round(Math.random() * 61)];
    }
    return str;
}

地图构造器原理其实很简单 就是遍历关卡的数组中以及对应的位置

当前现在虽然地图制作好了 但是由于没有触碰检测 触碰砖块时怪物就会消失  因此我们还要写个触碰检测

每次移动和子弹发射以及一系列的操作都需要通过触碰检测

触碰检测

let detection = (x,y,w,h,type) => {  //检测触碰
    for(let i = 0;obj[i]!=undefined;i++){
        if(type == 0){
            if((y < obj[i].y + 40 && y > obj[i].y) && (x >= obj[i].x - 28 && x <= obj[i].x + 28))return true;
        }else if(type == 1){
            if((y < obj[i].y + 28 && y > obj[i].y - 40) && (x >= obj[i].x - 28 && x <= obj[i].x + 28))return true;
        }else if(type == 2){
            if((y < obj[i].y + 32 && y > obj[i].y - 28) && (x >= obj[i].x - 28 && x <= obj[i].x + 28))return true;
        }else if(type == 3){
            if((y < obj[i].y + 32 && y > obj[i].y - 28) && (x >= obj[i].x - 28 && x <= obj[i].x + 28))return true;
        }
    }
}

将该检测放到移动时

document.body.onkeydown = async (event) => {
    let item = "";
    switch(event.keyCode){
        case 65:    //a后
            if(mapScope(_1ps.x - 4,0,32,28)))return;
            if(detection(_1ps.x - 4,_1ps.y,32,32,2))return;
            item = await move(_1ps.left || _1p.left,_1ps,0,-4);
            _1ps.left = item;
            break;
        case 68:    //d前
            if(mapScope(_1ps.x + 4,0,32,28)))return;
            if(detection(_1ps.x + 4,_1ps.y,32,32,3))return;
            item = await move(_1ps.right || _1p.right,_1ps,0,4);
            _1ps.right = item;
            break;
        case 83:    //s下
            if(mapScope(_1ps.y + 4,1,32,28)))return;
            if(detection(_1ps.x,_1ps.y - 4,32,32,1))return;
            item = await move(_1ps.down || _1p.down,_1ps,1,4);
            _1ps.down = item;
            break;
        case 87:    //w上
            if(mapScope(_1ps.y - 4,1,32,28)))return;
            if(detection(_1ps.x,_1ps.y + 4,32,32,0))return;
            item = await move(_1ps.up || _1p.up,_1ps,1,-4);
            _1ps.up = item;
            break;
        case 74:    //j子弹
            createBullet();
            break;
    }
}

接下来我们构造子弹

构造子弹

let createBullet = async (x,y,direction) => { //创建子弹
    if(disabled == true)return;
    if(direction == 0){ //上
        _bullet = await drawCreate(bullet,x + 11,y - 8,8,8);
        bulletMove(0,x+11,y - 8,8,8);
    }
    if(direction == 1){ //下
        _bullet = await drawCreate(bullet,x + 11,y + 24,8,8);
        bulletMove(1,x+11,y+24,8,8)
    }
    if(direction == 2){ //右
        _bullet = await drawCreate(bullet,x + 32,y + 4,8,8);
        bulletMove(2,x+32,y+4,8,8);
    } 
    if(direction == 3){ //左
        _bullet = await drawCreate(bullet,x - 8,y+4,8,8);
        bulletMove(3,x - 8,y+4,8,8);
    }
    disabled = true;
    setTimeout(()=>{    //子弹设置0.5秒一发
        disabled = false;
    },500)
}

当然 现在这些子弹还是不会动的

稍后我们修改让其动起来

会动的子弹

function bulletMove(d,x,y,w,h){  //子弹移动 方向 坐标 大小
    let value = d == 0 || d == 3?-4:4;
    if(disabled == true)return;
    let move = setInterval(()=>{
        if(d == 0 || d == 1){
            ctx.clearRect(x,y,w,h);
            if(mapScope(y + value,1)){
                clearInterval(move);
                return false;
            }
            ctx.drawImage(_bullet,x,y += value,w,h);
        }else{
            ctx.clearRect(x,y,w,h);
            if(mapScope(x + value,0)){
                clearInterval(move);
                return false;
            }
            ctx.drawImage(_bullet,x += value,y,w,h);
        }
    },20);
}

子弹触碰

let bulletDetection = (d,x,y,w,h) =>{    //子弹触碰检测
    for(let i = 0;obj[i]!=undefined;i++){
        if(d == 0){
            if((y - 36 <= obj[i].y && y + 8 >= obj[i].y) && (x >= obj[i].x - 8 && x <= obj[i].x + 28))return true;
        }else if(d == 1){
            if((y >= obj[i].y - 8 && y <= obj[i].y + 8) && (x >= obj[i].x - 8 && x <= obj[i].x + 28))return true;
        }else if(d == 2){
            if((x >= obj[i].x - 8 && x <= obj[i].x + 32) && (y >= obj[i].y - 8 && y <= obj[i].y + 32))return true;
        }else if(d == 3){
            if((x >= obj[i].x - 8 && x <= obj[i].x + 32) && (y >= obj[i].y - 8 && y <= obj[i].y + 32))return true;
        }
    }
}

 

let bulletDetection = (d,x,y,w,h) =>{    //子弹触碰检测
    for(let i = 0;obj[i]!=undefined;i++){
        if(d == 0){
            if((y - 36 <= obj[i].y && y + 8 >= obj[i].y) && (x >= obj[i].x - 8 && x <= obj[i].x + 28)){
                if(touchBulletElement(obj[i]))return true;
            }
        }else if(d == 1){
            if((y >= obj[i].y - 8 && y <= obj[i].y + 8) && (x >= obj[i].x - 8 && x <= obj[i].x + 28)){
                let bullet = {
                    x:x,
                    y:y,
                }
                if(touchBulletElement(obj[i],bullet))return true;
            }
        }else if(d == 2){
            if((x >= obj[i].x - 8 && x <= obj[i].x + 32) && (y >= obj[i].y - 8 && y <= obj[i].y + 32)){
                if(touchBulletElement(obj[i],bullet))return true;
            }
        }else if(d == 3){
            if((x >= obj[i].x - 8 && x <= obj[i].x + 32) && (y >= obj[i].y - 8 && y <= obj[i].y + 32)){
                if(touchBulletElement(obj[i],bullet))return true;
            }
        }
    }
}

判断当前打到的道具是什么

let touchBulletElement = (obj,bullets,d) =>{   //判断子弹触碰到的是什么元素
    switch(obj.ev){
        case 2:
            attackDirection(obj,bullets,d)
            break;
        case 3: //刚转
            attackDirection(obj,bullets,d)
            break;
        case 6: //草地
            ctx.clearRect(obj.x,obj.y,obj.w,obj.h);
            ctx.drawImage(obj.el,obj.x,obj.y,obj.w,obj.h);
            return false;
        case 5: //水池
            ctx.clearRect(obj.x,obj.y,obj.w,obj.h);
            ctx.drawImage(obj.el,obj.x,obj.y,obj.w,obj.h);
            return false;
    }
    return true;
}

修改这些后需要将创建子弹套上触碰

接下来该判断子弹打中了什么元素了

在此我们来分析一下

  每块砖都要一分为四  也就是说当前x ~ 16  y ~ 16为第一块砖  x + 16 ~ x + 32 ~ y ~ y + 16 第二块砖

                                                                   x ~ 16 y + 16 ~ y + 32第三块砖  x + 16 y + 16 ~ y + 32 最后一块砖

在bulletDetection嵌套检测攻击子弹函数

打中后清空砖块

let attackDirection = (obj,bullets,d) =>{ //攻击砖块
    ctx.clearRect(obj.x,obj.y,32,32);
    removeObj(obj.id)
}
let removeObj = (id) =>{  //删除对象
    for(let i = 0;obj[i]!=undefined;i++){
        if(obj[i].id == id)obj.splice(i,1);
    }
}

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
========================================================== 局域网坦克大战V1.0使用说明 权所有 (c)2011, 卓跃计算机职业培训学校。 作者 :余松鹰 ---------------------------------------------------------- 1.请确保游戏客户端(TankGame_Client.jar)的同级目录下存在img图片资源文件夹和sounds声音文件夹。 2.请确保游戏服务器端(TankGame_Server.jar)的同级目录下存在map文件夹且文件夹内至少存在一个以上的 XX.MAP文件,并且还存在游戏服务器配置文件ServerData.xml文件和用户信息文件Data.xml。 3.使用游戏地图编辑器(TankGame_MapEditer.jar)必须存在img地图编辑器图片资源文件 4.游戏开始顺序 1).打开游戏服务器端(TankGame_Server.jar)服务器端显示“服务器启动成功”。 2).打开客户端(TankGame_Client.jar)输入用户名和密码,若为新用户可注册后登录。(系统管理 员用户名admin,密码admin)。 3).点击连接服务器,控制玩家P1的用户可以选择地图,选择地图完后点击准备按钮,准备开始游 戏。 4).第二位用户登录完服务器,并按下了准备就开始游戏。 5.地图编辑器的编辑地图 1).打开地图编辑器,按照帮助菜单内的说明完成地图编辑后,保存地图将在地图编辑器的同级 目录产生一个.MAP文件,该文件既为用户编辑产生的地图。 2).将该地图放到服务器端(TankGame_Server.jar)的map文件内重启服务器端即可使用地图进行 游戏。 6.服务器端在关闭时,将在服务器端的同级目录下生产服务器日志(ServerLog.txt),该日志记录服务器 启动和关闭的时间,以及用户的登录和退出。 7.用户可在服务器配置文件(ServerData.xml)中修改服务器端口号
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值