用 html 做了一款推箱子的小游戏(只做了一个关卡),屏幕触控和键盘方向键控制小人推箱子、重新开始、上一步及通关等功能,直接上图:
效果链接(只看看不如亲自体验一下,因为写了键盘监听的缘故,键盘事件都被停用了):
http://h5demo.yyfuncdn.com/res/gameDemo/sokoban/
手机扫码运行:
下面是所有的项目文件:
game.html 文件是项目的运行文件,里面包含了创建舞台、创建游戏场景、人物等内容的操作,最后还设置了键盘操控
.
游戏源码被放到下面公众号里面啦,有需要的小伙伴可以扫码关注,发送 “推箱子” 获取哦
感谢大家支持……^ ^
.
<!DOCTYPE html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<head>
<script src='js/pixi.js'></script>
<script src='js/background.js'></script>
<script src="js/wall.js"></script>
<script src="js/box.js"></script>
<script src="js/ball.js"></script>
<script src="js/role.js"></script>
<script src="js/text.js"></script>
<script src="js/reset.js"></script>
<meta name="viewport" content="width=device-width,height=device-height"/>
<style>
body{
background-color:black;
}
canvas{
width:100%;
max-width:500px;
position: absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%);
}
</style>
</head>
<body>
<script>
//创建舞台
var app = new PIXI.Application(560,800);
document.body.appendChild(app.view);
//生成背景
var bg = new Background(560,800);
//创建地图数组
var mapArr = [];
//地图二维数组初始化
function init(){
var width = 40;
var height = 40;
var left = 20;
var up = 200;
for(var i = 0;i < 14;i++) {
mapArr.push([]);
for(var j = 0;j < 9;j++) {
mapArr[i].push([]);
mapArr[i][j] = {type:null};
}
}
//墙方块在数组中的位置
var wallx = [0,0,0,0,0,0,0,1,1,2,2,2,2,2,3,3,3,4,4,4,5,5,5,5,5,5,5,6,6,7,7,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,11,11,11,12,12,12,13,13,13,13,13,13,13,13,13];
var wally = [0,1,2,3,4,5,6,0,6,0,6,7,8,9,0,6,9,0,6,9,0,1,2,3,5,6,9,0,9,0,3,5,6,8,9,0,3,6,9,0,3,4,9,0,3,4,9,0,1,9,1,5,9,1,2,3,4,5,6,7,8,9];
//生成墙方块
for(var i = 0;i < wallx.length;i++) {
var x = wallx[i];
var y = wally[i];
var wall = new Wall();
wall.pos(left+x*width,up+y*height);
wall.posArr = {x:x,y:y};
mapArr[x][y] = {type:"wall"};
}
//箱子在数组中的位置
var boxx = [4,6,7,7,9,9,10,10,11,11];
var boxy = [7,3,2,7,6,7,2,5,6,7];
//生成箱子
for(var i = 0;i < boxx.length;i++) {
var x = boxx[i];
var y = boxy[i];
var box = new Box();
box.pos(left+x*width,up+y*height);
box.posArr = {x:x,y:y};
mapArr[x][y] = {type:"box",obj:box};
boxArr.push(box);
}
//球体在数组中的位置
var ballx = [1,1,1,1,1,2,2,2,2,2];
var bally = [1,2,3,4,5,1,2,3,4,5];
//生成球体
for(var i = 0;i < ballx.length;i++) {
var x = ballx[i];
var y = bally[i];
var ball = new Ball();
ball.pos(left+x*width,up+y*height);
ball.posArr = {x:x,y:y};
mapArr[x][y] = {type:"ball",obj:ball};
ballArr.push({obj:ball,type:false});
}
}
//用于保存球体对象的数组
var ballArr = [];
//用于保存箱子对象的数组
var boxArr = [];
var moveArr = [];
init();
//生成小人
var role = new Role();
role.pos(20+7*40,200+4*40);
role.posArr = {x:7,y:4};
bg.setobj(role);
//生成文字
var text = new Text();
text.setPos(280,100,280,700);
//生成重置按钮
var reset = new Reset();
reset.setPos(100,100);
reset.setPos1(450,100);
//用于控制移动
var control = true;
document.onkeydown = keyDown;
function keyDown(event) {//键盘监听
var event = event || window.event;
switch (event.keyCode) {
case 37:
bg.wasd("a");
break;
case 39:
bg.wasd("d");
break;
case 38:
bg.wasd("w");
break;
case 40:
bg.wasd("s");
break;
}
return false;
}
</script>
<div style="display:none;">
<script type="text/javascript" src="https://s4.cnzz.com/z_stat.php?id=1279820148&web_id=1279820148"></script>
</div>
</body>
</html>
背景被封装进了 Background 对象,用于触控控制小人的移动,监测是否触墙、箱子是否入库、一起推动箱子的操作
//背景对象
function Background(width,height) {
var self = this;
this.view = new PIXI.Sprite.fromImage("res/background.png");
app.stage.addChild(this.view);
this.view.height = height;
this.view.width = width;
//获取到控制对象
var obj = null;
this.setobj = function(dx) {
obj = dx;
}
//定义变量 pos 用于记录鼠标按下的位置
var pos;
//开启交互
this.view.interactive = true;
//绑定鼠标按下事件
this.view.on("pointerdown",function(event) {
pos = event.data.getLocalPosition(app.stage);
})
//撤销事件
this.revocation = function() {
if(moveArr.length == 0)return;
switch (moveArr[moveArr.length-1].direction) {
case "a":
self.move(role,"d");
if(moveArr[moveArr.length-1].obj != null){
self.shiftOut(moveArr[moveArr.length-1].obj);
self.moveChangeMapArr(moveArr[moveArr.length-1].obj,"d");
self.immigrate(moveArr[moveArr.length-1].obj);
}
break;
case "d":
self.move(role,"a");
if(moveArr[moveArr.length-1].obj != null) {
self.shiftOut(moveArr[moveArr.length-1].obj);
self.moveChangeMapArr(moveArr[moveArr.length-1].obj,"a");
self.immigrate(moveArr[moveArr.length-1].obj);
}
break;
case "w":
self.move(role,"s");
if(moveArr[moveArr.length-1].obj != null) {
self.shiftOut(moveArr[moveArr.length-1].obj);
self.moveChangeMapArr(moveArr[moveArr.length-1].obj,"s");
self.immigrate(moveArr[moveArr.length-1].obj);
}
break;
case "s":
self.move(role,"w");
if(moveArr[moveArr.length-1].obj != null) {
self.shiftOut(moveArr[moveArr.length-1].obj);
self.moveChangeMapArr(moveArr[moveArr.length-1].obj,"w");
self.immigrate(moveArr[moveArr.length-1].obj);
}
break;
default:
break;
}
moveArr.splice(moveArr.length-1,1);
}
//用于判断角色的移动
this.wasd = function(type) {
if(!control) {
return;
}
var dx = self.roleCrash(obj,type);
if(dx.type == false) {
return;
}
moveArr.push({direction:type,obj:null});
if(dx.obj != null){
var state = self.boxCrash(dx.obj,type);
if(state == false) {
moveArr.splice(moveArr.length-1,1);
return;
}
moveArr[moveArr.length-1].obj = dx.obj;
self.shiftOut(dx.obj);
self.moveChangeMapArr(dx.obj,type);
self.immigrate(dx.obj);
}
self.move(obj,type);
}
//检查箱子是否移出球体位置
this.shiftOut = function(obj) {
for(var i = 0;i < ballArr.length;i++){
if(obj.posArr.x == ballArr[i].obj.posArr.x && obj.posArr.y == ballArr[i].obj.posArr.y) {
ballArr[i].type = false;
mapArr[obj.posArr.x][obj.posArr.y] = {type:"ball",obj:ballArr[i].obj};
return;
}
}
}
//检查箱子是否移动至球体位置
this.immigrate = function(obj) {
for(var i = 0;i < ballArr.length;i++) {
if(obj.posArr.x == ballArr[i].obj.posArr.x && obj.posArr.y == ballArr[i].obj.posArr.y) {
ballArr[i].type = true;
}
}
for(var i = 0;i < ballArr.length;i++) {
if(ballArr[i].type == false) {
return;
}
}
reset.win();
}
//检查角色触碰到的物体并返回物体类型及物体对象
this.boxCrash = function(obj,type) {
var state = true;
switch (type) {
case "a":
if(mapArr[obj.posArr.x-1][obj.posArr.y].type == "wall") {
state = false;
}
if(mapArr[obj.posArr.x-1][obj.posArr.y].type == "box") {
state = false;
}
break;
case "d":
if(mapArr[obj.posArr.x+1][obj.posArr.y].type == "wall") {
state = false;
}
if(mapArr[obj.posArr.x+1][obj.posArr.y].type == "box") {
state = false;
}
break;
case "w":
if(mapArr[obj.posArr.x][obj.posArr.y-1].type == "wall") {
state = false;
}
if(mapArr[obj.posArr.x][obj.posArr.y-1].type == "box") {
state = false;
}
break;
case "s":
if(mapArr[obj.posArr.x][obj.posArr.y+1].type == "wall") {
state = false;
}
if(mapArr[obj.posArr.x][obj.posArr.y+1].type == "box") {
state = false;
}
break;
default:
break;
}
return state;
}
//检查角色触碰到的物体并返回物体类型及物体对象
this.roleCrash = function(obj,type) {
var state = {type:true,obj:null};
switch (type) {
case "a":
obj.wl(0);
if(mapArr[obj.posArr.x-1][obj.posArr.y].type == "wall") {
state = {type:false,obj:mapArr[obj.posArr.x-1][obj.posArr.y]};
}
if(mapArr[obj.posArr.x-1][obj.posArr.y].type == "box") {
state = {type:true,obj:mapArr[obj.posArr.x-1][obj.posArr.y].obj};
}
break;
case "d":
obj.wl(1);
if(mapArr[obj.posArr.x+1][obj.posArr.y].type == "wall") {
state = {type:false,obj:mapArr[obj.posArr.x+1][obj.posArr.y]};
}
if(mapArr[obj.posArr.x+1][obj.posArr.y].type == "box") {
state = {type:true,obj:mapArr[obj.posArr.x+1][obj.posArr.y].obj};
}
break;
case "w":
obj.wl(2);
if(mapArr[obj.posArr.x][obj.posArr.y-1].type == "wall") {
state = {type:false,obj:mapArr[obj.posArr.x][obj.posArr.y-1]};
}
if(mapArr[obj.posArr.x][obj.posArr.y-1].type == "box") {
state = {type:true,obj:mapArr[obj.posArr.x][obj.posArr.y-1].obj};
}
break;
case "s":
obj.wl(3);
if(mapArr[obj.posArr.x][obj.posArr.y+1].type == "wall") {
state = {type:false,obj:mapArr[obj.posArr.x][obj.posArr.y+1]};
}
if(mapArr[obj.posArr.x][obj.posArr.y+1].type == "box") {
state = {type:true,obj:mapArr[obj.posArr.x][obj.posArr.y+1].obj};
}
break;
default:
break;
}
return state;
}
//控制箱子移动并改变箱子在地图数组中的位置
this.moveChangeMapArr = function(obj,type) {
switch (type) {
case "a":
if(mapArr[obj.posArr.x][obj.posArr.y].type != "ball") {
mapArr[obj.posArr.x][obj.posArr.y] = {type:null};
}
obj.view.x += -40;
obj.posArr.x += -1;
mapArr[obj.posArr.x][obj.posArr.y] = {type:"box",obj};
break;
case "d":
if(mapArr[obj.posArr.x][obj.posArr.y].type != "ball") {
mapArr[obj.posArr.x][obj.posArr.y] = {type:null};
}
obj.view.x += 40;
obj.posArr.x += 1;
mapArr[obj.posArr.x][obj.posArr.y] = {type:"box",obj};
break;
case "w":
if(mapArr[obj.posArr.x][obj.posArr.y].type != "ball") {
mapArr[obj.posArr.x][obj.posArr.y] = {type:null};
}
obj.view.y += -40;
obj.posArr.y += -1;
mapArr[obj.posArr.x][obj.posArr.y] = {type:"box",obj};
break;
case "s":
if(mapArr[obj.posArr.x][obj.posArr.y].type != "ball") {
mapArr[obj.posArr.x][obj.posArr.y] = {type:null};
}
obj.view.y += 40;
obj.posArr.y += 1;
mapArr[obj.posArr.x][obj.posArr.y] = {type:"box",obj};
break;
default:
break;
}
}
//角色移动方法
this.move = function(obj,type) {
switch (type) {
case "a":
obj.view.x += -40;
obj.posArr.x += -1;
break;
case "d":
obj.view.x += 40;
obj.posArr.x += 1;
break;
case "w":
obj.view.y += -40;
obj.posArr.y += -1;
break;
case "s":
obj.view.y += 40;
obj.posArr.y += 1;
break;
default:
break;
}
}
//绑定鼠标抬起事件&&用抬起的位置与按下的位置做比对来辨别行动的方向
this.view.on("pointerup",function(event) {
if(pos == undefined || obj == null){
return;
}
var posup = event.data.getLocalPosition(app.stage);
if(Math.abs(posup.x - pos.x) > Math.abs(posup.y - pos.y)) {
if(posup.x - pos.x > 0) {
self.wasd("d");
}else if(posup.x - pos.x < 0) {
self.wasd("a");
}
}else if(Math.abs(posup.x - pos.x) < Math.abs(posup.y - pos.y)) {
if(posup.y - pos.y > 0) {
self.wasd("s");
}else if(posup.y - pos.y < 0) {
self.wasd("w");
}
}
})
}
从新开始按钮单独封装在了 Reset 对象中
function Reset(){
var self = this;
var style1 = {
fontFamily: 'Arial',
fontStyle: 'normal',
fontSize:30,
fontWeight: 'bold',
fill: '#bf8735',
}
this.view = new PIXI.Text("重新开始",style1);
app.stage.addChild(this.view);
this.view.anchor.set(0.5,0.5);
var style2 = {
fontFamily: 'Arial',
fontStyle: 'normal',
fontSize:30,
fontWeight: 'bold',
fill: '#bf8735',
}
this.revocation = new PIXI.Text("上一步",style2);
app.stage.addChild(this.revocation);
this.revocation.anchor.set(0.5,0.5);
var style = {
fontFamily: 'Arial',
fontStyle: 'normal',
fontSize:40,
fontWeight: 'bold',
fill: '#f5286e',
}
var text = new PIXI.Text("恭 喜 过 关",style);
app.stage.addChild(text);
text.anchor.set(0.5,0.5);
text.x = 280;
text.y = 300;
text.visible = false;
this.win = function() {
text.visible = true;
self.setPos(280,400);
self.revocation.visible = false;
control = false;
}
//设置位置方法
this.setPos = function(x,y) {
self.view.x = x;
self.view.y = y;
}
this.setPos1 = function(x,y) {
self.revocation.x = x;
self.revocation.y = y;
}
//开启重置按钮的交互
this.view.interactive = true;
this.revocation.interactive = true;
//绑定点击事件
this.revocation.on("pointertap",function() {
bg.revocation();
})
this.view.on("pointertap",function() {
self.revocation.visible = true;
text.visible = false;
moveArr = [];
self.setPos(100,100);
var boxx = [4,6,7,7,9,9,10,10,11,11];
var boxy = [7,3,2,7,6,7,2,5,6,7];
for(var i = 0;i < boxArr.length;i++) {
var box = boxArr[i];
mapArr[box.posArr.x][box.posArr.y] = {type:null};
mapArr[boxx[i]][boxy[i]] = {type:"box",obj:box};
box.posArr = {x:boxx[i],y:boxy[i]};
box.pos(20+boxx[i]*40,200+boxy[i]*40);
}
role.pos(20+7*40,200+4*40);
role.posArr = {x:7,y:4};
for(var i = 0;i < ballArr.length;i++) {
var ball = ballArr[i];
ball.type = false;
mapArr[ball.obj.posArr.x][ball.obj.posArr.y] = {type:"ball",obj:ball.obj};
}
})
}
以下其他对象没有什么特别的,只是单独的对象,每次使用调用
球对象(蓝色的目标点)
//球对象
function Ball(){
var self = this;
this.view = new PIXI.Sprite.fromImage("res/blueball.png");
app.stage.addChild(this.view);
this.view.anchor.set(0.5,0.5);
this.view.alpha = 0.7;
this.posArr;
//设置位置方法
this.pos = function(x,y) {
self.view.x = x;
self.view.y = y;
}
}
箱子对象
//箱子对象
function Box(){
var self = this;
this.view = new PIXI.Sprite.fromImage("res/bluebox.png");
app.stage.addChild(this.view);
this.view.anchor.set(0.5,0.5);
this.posArr;
//设置位置方法
this.pos = function(x,y){
self.view.x = x;
self.view.y = y;
}
}
角色对象
//角色对象
function Role() {
var self = this;
var urlArr = ["res/left.png","res/right.png","res/up.png","res/down.png"];
this.view = new PIXI.Sprite.fromImage(urlArr[3]);
app.stage.addChild(this.view);
this.view.anchor.set(0.5,0.5);
this.posArr;
//设置角色纹理
this.wl = function(num) {
var url = new PIXI.Texture.fromImage(urlArr[num]);
self.view.texture = url;
}
//设置位置方法
this.pos = function(x,y) {
self.view.x = x;
self.view.y = y;
}
}
文本对象
function Text(){
var self = this;
//设置标题样式
var style = {
fontFamily: 'Arial',
fontSize: 40,
fontStyle: 'normal',
fontWeight: 'bold',
fill:'#bf8735',
stroke: '#000fff',
strokeThickness: 5
}
//设置说明样式
var style2 = {
fontFamily: 'Arial',
fontStyle: 'normal',
fontWeight: 'bold',
fill: '#000fff'
}
this.view = new PIXI.Text("推 箱 子",style);
this.view.anchor.set(0.5,0.5);
app.stage.addChild(this.view);
this.explain = new PIXI.Text("滑动屏幕或键盘方向键控制小人移动",style2);
this.explain.anchor.set(0.5,0.5);
app.stage.addChild(this.explain);
//设置位置方法
this.setPos = function(viewx,viewy,explainx,explainy) {
self.view.x = viewx;
self.view.y = viewy;
self.explain.x = explainx;
self.explain.y = explainy;
}
}
墙对象
function Text(){
var self = this;
//设置标题样式
var style = {
fontFamily: 'Arial',
fontSize: 40,
fontStyle: 'normal',
fontWeight: 'bold',
fill:'#bf8735',
stroke: '#000fff',
strokeThickness: 5
}
//设置说明样式
var style2 = {
fontFamily: 'Arial',
fontStyle: 'normal',
fontWeight: 'bold',
fill: '#000fff'
}
this.view = new PIXI.Text("推 箱 子",style);
this.view.anchor.set(0.5,0.5);
app.stage.addChild(this.view);
this.explain = new PIXI.Text("滑动屏幕或键盘方向键控制小人移动",style2);
this.explain.anchor.set(0.5,0.5);
app.stage.addChild(this.explain);
//设置位置方法
this.setPos = function(viewx,viewy,explainx,explainy) {
self.view.x = viewx;
self.view.y = viewy;
self.explain.x = explainx;
self.explain.y = explainy;
}
}
数组在上一篇的专栏,中我们进行了回顾和刷题。 链表 趁热打铁,我们来对比数组...
看到一篇博客:HTTP协议详解 , 图1 开始不明白 "(CRLF)" 是什么意思! 查了一些...
Base64编码的深入认识与理解 之前在很多业务中都有见过或者用到过Base64编码,但...
本文实例讲述了Java Web开发之图形验证码的生成与使用方法。分享给大家供大家参...
Linux 有很多用于查看图像的 GUI 应用。但我从来没有尝试过用任何命令行应用来查...
主要是针对低版本的浏览器 !-- --是html的注释标签,高版本的浏览器会识别style...
开始使用现代方法配置 Linux 网络接口。 在很长一段时间内, ifconfig 命令是配...
问题 如何在ASP.NET Core 2.0中实现网址重定向? 答案 新建一个空项目,在Startu...
话不多说,请看下面代码 var row = [{ "code": "1", "model": "APOLLO" }, { "co...
其实出现Microsoft VBScript 编译器错误 错误 '800a03e9' 内存不够的错误一般是...