1. 游戏逻辑
1.1. 定义初始化地图变量

1.2. 定义活动地图, 因为人物每移动一次, 地图都发生了变化

1.3. 坐标类, 定义人物位置

1.4. 定义当前人物坐标变量

1.5. 绘制场景时, 记录人物坐标

1.6. 获取人物前面的两个坐标位置来进行判断人物是否能够移动

1.7. 判断人物是否能够移动
1.8. 如果人物可以移动重新绘制场景

1.9. 移动人物效果图

2. 设置关卡数据
2.1. 定义人物移动步数变量

2.2. 获取显示数据对象

2.3. 显示当前关卡数据

2.4. 调用显示当前关卡数据

2.5. 效果图

3. 判断是否推成功
3.1. 判断是否推成功逻辑

3.2. 延迟检测是否通关, 如果不延迟, 绘画没完成

3.3. 效果图

4. 切换关卡
4.1. 定义全局的初始化方法

4.2. 自动切换下一关

4.3. 切换到下一关后重新初始化游戏

4.4. index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>推箱子</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
body {
background-color: #000;
}
#game {
width: 560px;
margin: 0 auto;
}
#canvas {
background: #fff;
}
#msg {
color: #fff;
font-size: 16px;
height: 40px;
line-height: 40px;
background-color: #000;
}
#btn {
height: 50px;
line-height: 50px;
background-color: #FFF;
}
#btn input {
height: 30px;
width: 100px;
-webkit-appearance: button;
}
</style>
</head>
<body>
<div id="game">
<canvas id="canvas" width="560px" height="560px"></canvas>
<div id="msg">第1关, 移动次数: 0</div>
<div id="btn">
<input type="button" id="previous" value="上一关" />
<input type="button" id="next" value="下一关" />
<input type="button" id="replay" value="重玩本关" />
<input type="button" id="descript" value="游戏说明" />
</div>
</div>
<script type="text/javascript" src="mapdata.js"></script>
<script type="text/javascript">
// 坐标类, 定义人物位置
function ClassPoint(x,y) {
this.x = x;
this.y = y;
}
// 加载图片
function loadImages(){
var imageSrc = {
"block": "images/block.gif",
"wall": "images/wall.png",
"box": "images/box.png",
"ball": "images/ball.png",
"up": "images/up.png",
"down": "images/down.png",
"left": "images/left.png",
"right": "images/right.png"
};
var exports = {};
exports.images = {};
exports.succeed = false;
exports.init = function(callback){
var count = 0, imgCount = 0;
for (let key in imageSrc) {
imgCount++;
}
for(let key in imageSrc){
this.images[key] = new Image();
this.images[key].onload = function(){
count++;
if(count == imgCount){
exports.succeed = true;
}
callback(count, imgCount);
};
this.images[key].src = imageSrc[key];
}
};
return exports;
}
// 初始化场景
function initScene(level, images){
var block = images["block"];
for(let i = 0, ilen = level.length; i < ilen; i++){
for(let j = 0, jlen = level[i].length; j < jlen; j++){
let data = level[i][j];
let img = block;
switch(data){
case dataItem.wall:
img = images["wall"];
break;
case dataItem.ball:
img = images["ball"];
break;
case dataItem.box:
img = images["box"];
break;
case dataItem.down:
img = curManImg; // 人物初始化使用向下的图片, 看起来是面向屏幕外
curManPoint.x = i;
curManPoint.y = j;
break;
}
// 画砖块
ctx.drawImage(block, j * w, i * h, block.width, block.height);
// 画墙、小球、箱子和人物
if(level[i][j] != 0){
ctx.drawImage(img, j * w + (w - img.width) / 2, i * h + (h - img.height), img.width, img.height);
}
}
}
}
// 事件方法
function doEvent(images){
// 键盘按键值对象
var keyCode = {
up: 38, // 键盘上的上键, 人物向上走
w: 87, // 键盘上的w键, 人物向上走
down: 40, // 键盘上的下键, 人物向下走
s: 83, // 键盘上的s键, 人物向下走
left: 37, // 键盘上的左键, 人物向左走
a: 65, // 键盘上的a键, 人物向左走
right: 39, // 键盘上的右键, 人物向右走
d: 68 // 键盘上的d键, 人物向右走
};
var exports = {};
// 初始化事件方法
exports.init = function(){
var previous = document.getElementById('previous');
var next = document.getElementById('next');
var replay = document.getElementById('replay');
var descript = document.getElementById('descript');
document.body.addEventListener('keyup', this.bodyKeyup, false);
this.addEventListener(previous, 'click', this.nextLevel, -1);
this.addEventListener(next, 'click', this.nextLevel, 1);
this.addEventListener(replay, 'click', this.nextLevel, 0);
this.addEventListener(descript, 'click', this.descript);
// 获取显示数据对象
this.msg = document.getElementById('msg');
};
// 给body元素添加按键方法
exports.bodyKeyup = function(e){
switch(e.keyCode){
case keyCode.up:
case keyCode.w:
console.log('人物向上走');
exports.go('up');
break;
case keyCode.down:
case keyCode.s:
console.log('人物向下走');
exports.go('down');
break;
case keyCode.left:
case keyCode.a:
console.log('人物向左走');
exports.go('left');
break;
case keyCode.right:
case keyCode.d:
console.log('人物向右走');
exports.go('right');
break;
}
};
// 添加事件方法, 可以传递参数
exports.addEventListener = function(obj, name, fn, args){
var addfn = function(){
fn.call(obj, args);
};
obj.addEventListener(name, addfn, false);
};
// 下一关卡方法
exports.nextLevel = function(l){
curLevel += l;
if(curLevel < 0) {
curLevel = 0;
}
if(curLevel >= levels.length){
curLevel = levels.length - 1;
}
console.log('当前关卡: ' + curLevel);
// 切换到下一关后重新初始化游戏
init();
};
// 帮助方法
exports.descript = function(){
alert("用键盘上的上、下、左、右键移动人物, 把箱子全部推到小球的位置即可过关。箱子只可向前推, 并且人物一次只能推动一个箱子。");
};
// 人物行走的方法
exports.go = function(dir){ // 人物图片key、方向
curManImg = images[dir];
var p1, p2; // 获取人物前面的两个坐标位置来进行判断人物是否能够移动
switch (dir) {
case "up":
p1 = new ClassPoint(curManPoint.x - 1, curManPoint.y);
p2 = new ClassPoint(curManPoint.x - 2, curManPoint.y);
break;
case "down":
p1 = new ClassPoint(curManPoint.x + 1, curManPoint.y);
p2 = new ClassPoint(curManPoint.x + 2, curManPoint.y);
break;
case "left":
p1 = new ClassPoint(curManPoint.x, curManPoint.y - 1);
p2 = new ClassPoint(curManPoint.x, curManPoint.y - 2);
break;
case "right":
p1 = new ClassPoint(curManPoint.x, curManPoint.y + 1);
p2 = new ClassPoint(curManPoint.x, curManPoint.y + 2);
break;
}
// 如果人物可以移动重新绘制场景
if (this.trygo(p1, p2)) {
initScene(curMap, images);
moveSteps++;
this.showCurLevelData();
// 延迟检测是否通关, 如果不延迟, 绘画没完成
setTimeout(this.checkSuccess, 50);
}
};
// 判断人物是否能够移动
exports.trygo = function(p1, p2){
if(p1.x < 0) return false; // 若果超出地图的上边, 不通过
if(p1.y < 0) return false; // 若果超出地图的左边, 不通过
if(p1.x > curMap.length) return false; // 若果超出地图的下边, 不通过
if(p1.y > curMap[0].length) return false; // 若果超出地图的右边, 不通过
if(curMap[p1.x][p1.y] == dataItem.wall) return false; // 若果前面是墙, 不通过
if (curMap[p1.x][p1.y] == dataItem.box) { // 若果人物前面是箱子, 那就还需要判断箱子前面有没有障碍物(墙/箱子)
if (curMap[p2.x][p2.y] == dataItem.wall || curMap[p2.x][p2.y] == dataItem.box) {
return false;
}
// 如果人物可以移动, 箱子前进一步
curMap[p2.x][p2.y] = dataItem.box; // 更改当前地图人物前2的坐标值为箱子的值
}
// 如果人物可以移动, 人物前进一步
curMap[p1.x][p1.y] = dataItem.down;// 更改当前地图人物前1的坐标值为人物的值
// 若果人物前进了一步, 人物原来的位置不是小球的话, 更改为砖块的值
var value = initMap[curManPoint.x][curManPoint.y];
curMap[curManPoint.x][curManPoint.y] = (value != dataItem.ball) ? 0 : value;
// 若果判断人物前进了一步, 更新人物坐标值
curManPoint = p1;
// 返回true, 指代能够移动人物
return true;
};
// 显示当前关卡数据
exports.showCurLevelData = function(){
this.msg.innerHTML = "第" + (curLevel+1) + "关, 移动次数: " + moveSteps;
}
// 判断是否推成功逻辑
exports.checkSuccess = function() {
for (var i = 0; i < curMap.length; i++) {
for (var j = 0; j < curMap[i].length; j++) {
// 当前移动过的地图和初始地图进行比较, 若果初始地图上的小球参数在移动之后不是箱子的话就指代没推成功
if (initMap[i][j] == dataItem.ball && curMap[i][j] != dataItem.box) {
return false;
}
}
}
alert("恭喜您, 通关了!");
// 自动切换下一关
exports.nextLevel(1);
}
return exports;
}
(function(global){
// 获取画布
global.can = document.getElementById('canvas');
// 获取画笔(实际上是CanvasRenderingContext2D对象)
global.ctx = global.can.getContext("2d");
global.w = 35, global.h = 35; // w矩阵元素的宽度, h矩阵元素的高度
// 0: 代表砖块; 1: 代表墙壁; 2: 代表小球; 3: 代表箱子; 4: 代表人物
global.dataItem = {block: 0, wall: 1, ball: 2, box: 3, down: 4};
global.curLevel = 0; // 当前关卡
var li = loadImages();
li.init(function(count, imgCount){
console.log(count / imgCount * 100 + '%');
if(li.succeed){
// initScene(curMap, li.images);
init();
}
});
// 初始化事件
var e = doEvent(li.images);
e.init();
global.curManImg = li.images['down']; // 当前人物图片, 默认向下的人物图片
// 定义初始化地图变量
// global.initMap = levels[curLevel];
// 定义活动地图, 因为人物每移动一次, 地图都发生了变化
// global.curMap = function(){
// var b = []; // 每次移动更新地图数据都先清空再添加新的地图
// for (var i = 0; i < levels[curLevel].length; i++) {
// b[i] = initMap[i].concat(); // 链接数组, 返回数组副本
// }
// return b;
// }();
// 定义当前人物坐标变量, 默认(0,0)的位置
// global.curManPoint = new ClassPoint(0, 0);
// 定义人物移动步数变量
// global.moveSteps = 0;
// 定义全局的初始化方法
global.init = function() {
this.initMap = levels[curLevel];
this.curMap = function(){
var b = []; // 每次移动更新地图数据都先清空再添加新的地图
for (var i = 0; i < levels[curLevel].length; i++) {
b[i] = initMap[i].concat(); // 链接数组, 返回数组副本
}
return b;
}();
this.curManPoint = new ClassPoint(0, 0);
this.moveSteps = 0;
initScene(curMap, li.images);
e.showCurLevelData();
};
})(window);
</script>
</body>
</html>
4.5. mapdata.js
// 0: 代表砖块; 1: 代表墙壁; 2: 代表小球; 3: 代表箱子; 4: 代表人物
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,0,1,1,1,0,0,0,0,0,0,0],
[0,0,0,0,0,0,1,2,1,0,0,0,0,0,0,0],
[0,0,0,0,0,0,1,0,1,1,1,1,0,0,0,0],
[0,0,0,0,1,1,1,3,0,3,2,1,0,0,0,0],
[0,0,0,0,1,2,0,3,4,1,1,1,0,0,0,0],
[0,0,0,0,1,1,1,1,3,1,0,0,0,0,0,0],
[0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,0],
[0,0,0,0,0,0,0,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]];
levels[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.47元/天 解锁文章
4462

被折叠的 条评论
为什么被折叠?



