推箱子游戏JS实现

松波
2023-12-01

参考以下教学视频编写

教学视频:Canvas画布实现推箱子游戏-HTML5前端设计JavaScript原生开发_哔哩哔哩_bilibili

箱子地图

Boxdata.js

//1:围墙 2:目标点 3:箱子 4:角色 5:在目标点的箱子
var levels = [];
levels[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,1,1,1,1,1,1,0,0,0,0,0],
    [0,0,0,0,0,1,2,0,0,0,1,0,0,0,0,0],
    [0,0,0,0,0,1,0,3,0,4,1,0,0,0,0,0],
    [0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
    [0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
    [0,0,0,0,0,1,1,1,1,1,1,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],
]

1.地图绘制

pushBox.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="Boxdata.js"></script>
</head>
<body>
    <canvas id="mycanvas" height="800" width="800">     
    </canvas>
    <script>
        /** @type {HTMLCanvasElement} */
        var canv = document.getElementById("mycanvas");
        var ctx = canv.getContext("2d");

        var level = levels[0];

        var g={
            wall:{color:'#333',data:[]},
            target:{color:'#9f3',data:[]},
            box:{color:'rgba(255,50,0,0.8)',data:[]},
            player:{color:'rgba(255,220,0,0.8)',data:[]}
        }

        var objects = ['wall','target','box','player'];

        for(let i=0;i<16;i++){
            for(let j=0;j<16;j++){
                let d=level[i][j];
                let id = i*16+j;
                if(d>0){
                    if(d==5){
                        g['target'].data.push(id);
                        g['box'].data.push(id);
                    }else{
                        g[objects[d-1]].data.push(id);
                    }
                }
            }
        }

      g['wall'].data.forEach(n=>drawRRect('wall',n))
      g['target'].data.forEach(n=>drawRRect('target',n))
      g['box'].data.forEach(n=>drawRRect('box',n))
      g['player'].data.forEach(n=>drawRRect('player',n))
      
     
// 绘制圆角矩形
       function drawRRect(object,id){
           let w = 800/16,r=5,m=2;
           let [x,y] = [id%16*w,~~(id/16)*w]; //~~取整
           ctx.save();
           if(object=='target'){
               r=24              
           }
           ctx.beginPath();
           ctx.moveTo(x,y+r);
           ctx.arcTo(x,y+w-m,x+w-m,y+w-m,r);
           ctx.arcTo(x+w-m,y+w-m,x+w-m,y+w-m-r,r);
           ctx.arcTo(x+w-m,y,x,y,r);
           ctx.arcTo(x,y,x,y+w-m,r);
           ctx.fillStyle=g[object].color;
           ctx.fill();
           ctx.restore();
       }
    </script>
</body>
</html>

2.移动玩家角色

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="Boxdata.js"></script>
</head>
<body>
    <canvas id="mycanvas" height="800" width="800">     
    </canvas>
    <script>
        /** @type {HTMLCanvasElement} */
        var canv = document.getElementById("mycanvas");
        var ctx = canv.getContext("2d");
 
        var level = levels[0];
 
        var g={
            wall:{color:'#333',data:[]},
            target:{color:'#9f3',data:[]},
            box:{color:'rgba(255,50,0,0.8)',data:[]},
            player:{color:'rgba(255,220,0,0.8)',data:[]}
        }
 
        var objects = ['wall','target','box','player'];
 
        for(let i=0;i<16;i++){
            for(let j=0;j<16;j++){
                let d=level[i][j];
                let id = i*16+j;
                if(d>0){
                    if(d==5){
                        g['target'].data.push(id);
                        g['box'].data.push(id);
                    }else{
                        g[objects[d-1]].data.push(id);
                    }
                }
            }
        }
 
      g['wall'].data.forEach(n=>drawRRect('wall',n))
      g['target'].data.forEach(n=>drawRRect('target',n))
      g['box'].data.forEach(n=>drawRRect('box',n))
      g['player'].data.forEach(n=>drawRRect('player',n))
      
     
        // 绘制圆角矩形
       function drawRRect(object,id){
           let w = 800/16,r=5,m=2;
           let [x,y] = [id%16*w,~~(id/16)*w]; //~~取整
           ctx.save();
           if(object=='target'){
               r=24              
           }
           ctx.beginPath();
           ctx.moveTo(x,y+r);
           ctx.arcTo(x,y+w-m,x+w-m,y+w-m,r);
           ctx.arcTo(x+w-m,y+w-m,x+w-m,y+w-m-r,r);
           ctx.arcTo(x+w-m,y,x,y,r);
           ctx.arcTo(x,y,x,y+w-m,r);
           ctx.fillStyle=g[object].color;
           ctx.fill();
           ctx.restore();
       }

       //添加键盘监听
       document.addEventListener('keydown',ev=>{
           //方向键,根据方向键,dir移动的id
           let dir = [-1,-16,1,16][ev.keyCode-37]
           console.log(ev.code,ev.keyCode,dir)
           let player = g.player.data[0]
           let index = g['player'].data.indexOf(player)
           console.log(player,index)
           move('player',player,dir)
       })

       //移动玩家角色和箱子
       function move(who,id,dir){
            let next = id+dir
            let index = g[who].data.indexOf(id)
            g[who].data[index]=next
            //重绘地图
            ctx.clearRect(0,0,800,800)
            objects.forEach(obj=>g[obj].data.forEach(n=>drawRRect(obj,n)))
       }
    </script>
</body>
</html>

3. 添加撞墙判断,移动箱子和过关判断

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="Boxdata.js"></script>
</head>
<body>
    <canvas id="mycanvas" height="800" width="800">     
    </canvas>
    <script>
        /** @type {HTMLCanvasElement} */
        var canv = document.getElementById("mycanvas");
        var ctx = canv.getContext("2d");
 
        var level = levels[0];
 
        var g={
            wall:{color:'#333',data:[]},
            target:{color:'#9f3',data:[]},
            box:{color:'rgba(255,50,0,0.8)',data:[]},
            player:{color:'rgba(255,220,0,0.8)',data:[]}
        }
 
        var objects = ['wall','target','box','player'];
 
        for(let i=0;i<16;i++){
            for(let j=0;j<16;j++){
                let d=level[i][j];
                let id = i*16+j;
                if(d>0){
                    if(d==5){
                        g['target'].data.push(id);
                        g['box'].data.push(id);
                    }else{
                        g[objects[d-1]].data.push(id);
                    }
                }
            }
        }
 
      g['wall'].data.forEach(n=>drawRRect('wall',n))
      g['target'].data.forEach(n=>drawRRect('target',n))
      g['box'].data.forEach(n=>drawRRect('box',n))
      g['player'].data.forEach(n=>drawRRect('player',n))
      
     
        // 绘制圆角矩形
       function drawRRect(object,id){
           let w = 800/16,r=5,m=2;
           let [x,y] = [id%16*w,~~(id/16)*w]; //~~取整
           ctx.save();
           if(object=='target'){
               r=24              
           }
           ctx.beginPath();
           ctx.moveTo(x,y+r);
           ctx.arcTo(x,y+w-m,x+w-m,y+w-m,r);
           ctx.arcTo(x+w-m,y+w-m,x+w-m,y+w-m-r,r);
           ctx.arcTo(x+w-m,y,x,y,r);
           ctx.arcTo(x,y,x,y+w-m,r);
           ctx.fillStyle=g[object].color;
           ctx.fill();
           ctx.restore();
       }
 
       //添加键盘监听
       document.addEventListener('keydown',ev=>{
           //方向键,根据方向键,dir移动的id
           let dir = [-1,-16,1,16][ev.keyCode-37]
           
           let playerid = g.player.data[0]

	   let next = playerid+dir
           
	   //判断是否撞墙	
	   if(g.wall.data.includes(next)) return

	   //判断是否移动箱子	 
	   if(g.box.data.includes(next)){

		let boxid=next
		
		let boxnext = boxid+dir
		
                //判断箱子是否撞墙或撞箱子
		if(g.wall.data.includes(boxnext)||g.box.data.includes(boxnext)) return
		
		move('box',boxid,dir)
			
           }

           //移动玩家角色
           move('player',playerid,dir)

	   //判断游戏结束
	   checkOver()
       })
 
       //移动玩家角色和箱子
       function move(who,id,dir){
            let next = id+dir
            let index = g[who].data.indexOf(id)
	    
            g[who].data[index]=next
	    
            //重绘地图
            ctx.clearRect(0,0,800,800)
            objects.forEach(obj=>g[obj].data.forEach(n=>drawRRect(obj,n)))
       }

	//判断游戏结束
	function checkOver(){
        let win = g.box.data.every(b=>g.target.data.includes(b))
	if(win){
        	setTimeout(()=>{
			alert("恭喜过关!")	
		},100)
	  }
        }
    </script>
</body>
</html>

 类似资料: