Home > Article > Web Front-end > Writing a push box game with JS and H5
The push box game is a popular game many years ago (even now many people play it). The purpose of the game is very simple, that is, push the box and push all the boxes. When you reach the destination, the game is successful: it seems to be a simple logic, but in fact it is still difficult. I also relied on the help of others to complete it. Now I will introduce how to use js and html5 to write the game (for convenience, we will Replace it with a pink circle):
1. Ability requirements
JavaScript, HTML canvas, basic object-oriented ideas, reasonable programming logic.
2. Writing sequence
1.pushBox.html file
2.pojo.js file (used to store all objects)
3.paint.js file (used to write Drawing statements)
4.game.js file (used to write the running logic part)
5.allLevels.js file (used to store levels)
*Note: This is my writing habit. According to content and function, Each category is separated. If you have a better way to write it, please comment
3. Start writing
1. Create a basic pushBox .html file:
The content is very simple. The only needs the
<body> <canvas id='can1' width=1536 height=733></canvas> <audio id="walk" autoplay></audio> <audio id="push" autoplay></audio> <audio id="win" autoplay></audio> </body>
<script> There are four other .js files introduced, and one that calls the run() method: </script>
<script type="text/javascript" src="pojo.js"></script> <script type="text/javascript" src="game.js"></script> <script type="text/javascript" src="paint.js"></script> <script type="text/javascript" src="allLevels.js"></script> <script> window.onload = function(){ run(); } </script>
2. Write the pojo class:
First we need to know that there are these classes:
People, boxes, target points, bricks And walls, it's very simple. All classes have attributes such as color (color), size (side length/radius), x (abscissa), and y (ordinate). Then we have to remember that people and bricks may coincide with the target point, so we need to add the isOnTarget (whether it coincides with the target point) attribute to the classes of the box and the person, and we are done:
//人类 function Person(x, y){ this.color = 'pink'; this.size = 20; this.x = x; this.y = y; //判断这个人是否在目标点上 this.isOnTarget = false; } //箱子类 function Box(x, y){ this.color = 'yellow'; this.size = 40; this.x = x; this.y = y; //判断某个箱子是否在目标点上 this.isOnTarget = false; } //目标点类 function Target(x, y){ this.size = 12; this.x = x; this.y = y; this.color = 'lime'; } //地砖类 function Brick(x, y){ this.x = x; this.y = y; this.size = 40; } //围墙类 function Wall(x, y){ this.x = x; this.y = y; this.size = 40; }
3. Write the paint.js class
We need to write painting methods for all the classes we just wrote in the pojo.js class:
It is important to note: we If you use certain x, y coordinates, for example, to draw a box:
ctx.fillRect(x, y, size, size);
If it is called as follows, we draw the box like this in allLevels:
ctx.fillRect(500, 500, 40, 40);
We cannot be sure It's hard to tell what's next to this box, so there's no way to write a logic layer.
So, we conceive like this: we use a two-dimensional array to construct this level, and each object (box, person, target point, brick or wall) is placed in this array, arr1[] [], like this:
var arr1 = [ ['','wall','wall','wall','wall','wall','',''], ['','wall','brick','person','wall','wall','wall',''], ['','wall','brick','box','brick','brick','wall',''], ['wall','wall','wall','brick','wall','brick','wall','wall'], ['wall','target','wall','brick','wall','brick','brick','wall'], ['wall','target','box','brick','brick','wall','brick','wall'], ['wall','target','brick','brick','brick','box','brick','wall'], ['wall','wall','wall','wall','wall','wall','wall','wall'] ];
For example, arr1[0][1] is the wall, so that the logic layer can be written. And another advantage is that it is very convenient for us to create new levels later. We only need to write them out in the array in the order of the coordinates.
So, we write like this in paint.js. Each method performs some operations on x and y so that it can be drawn correctly on the web page:
//首先还是清屏 function clearScreen(ctx){ ctx.clearRect(0,0,1536,750); } //画人 function paintPerson(ctx, x, y, size,color){ ctx.beginPath(); ctx.fillStyle = color; //我们在内部就写好该往哪里画,传过来的x,y坐标值就可以直接在里面计算了 ctx.arc(530+x*size*2+20, 180+y*size*2+20, size, 0, 2*Math.PI); ctx.fill(); } //画箱子 function paintBox(ctx, x, y, size,color){ ctx.beginPath(); ctx.fillStyle = color; ctx.fillRect(530+x*size, 180+y*size, size, size); ctx.strokeStyle = 'black'; ctx.lineWidth = 2; ctx.moveTo(530+x*size, 180+y*size); ctx.lineTo(530+x*size+size, 180+y*size+size); ctx.stroke(); ctx.beginPath(); ctx.moveTo(530+x*size+size, 180+y*size); ctx.lineTo(530+x*size, 180+y*size+size); ctx.stroke(); ctx.strokeRect(530+x*size, 180+y*size, size, size); } //画目标点 function paintTarget(ctx, x, y, size,color){ ctx.beginPath(); ctx.fillStyle = color; ctx.arc(530+x*size*4+20, 180+y*size*4+20, size, 0, 2*Math.PI); ctx.fill(); } //画地砖,其中用了for循环简化了代码量 function paintBrick(ctx, x, y, size){ ctx.beginPath(); ctx.fillStyle = 'blue'; ctx.fillRect(530+x*size, 180+y*size, size, size); ctx.strokeStyle = 'lightblue'; for(var i = 0; i <= 3; i++){ ctx.beginPath(); ctx.moveTo(530+x*size, 180+y*size+0.25*(i+1)*size); ctx.lineTo(530+x*size+size, 180+y*size+0.25*(i+1)*size); ctx.stroke(); } for(var i = 0; i < 4; i++){ ctx.beginPath(); if(i%2 == 0){ ctx.moveTo(530+x*size+0.5*size, 180+y*size+0.25*i*size); ctx.lineTo(530+x*size+0.5*size, 180+y*size+0.25*(i+1)*size); ctx.stroke(); }else{ ctx.moveTo(530+x*size+0.25*size, 180+y*size+0.25*i*size); ctx.lineTo(530+x*size+0.25*size, 180+y*size+0.25*(i+1)*size); ctx.stroke(); ctx.beginPath(); ctx.moveTo(530+x*size+0.75*size, 180+y*size+0.25*i*size); ctx.lineTo(530+x*size+0.75*size, 180+y*size+0.25*(i+1)*size); ctx.stroke(); } } } //画围墙 function paintWall(ctx, x, y, size){ ctx.beginPath(); ctx.fillStyle = 'gray'; ctx.fillRect(530+x*size, 180+y*size, size, size); ctx.strokeStyle = 'white'; ctx.beginPath(); ctx.moveTo(530+x*size+0.5*size, 180+y*size); ctx.lineTo(530+x*size+0.5*size, 180+y*size+size); ctx.stroke(); ctx.beginPath(); ctx.moveTo(530+x*size, 180+y*size+0.5*size); ctx.lineTo(530+x*size+size, 180+y*size+0.5*size); ctx.stroke(); }
Then, We call it like this when calling the painting method, and draw according to the position of each block in the array:
unction getMap(ctx, person){ clearScreen(ctx); for(var i = 0; i<arr1.length; i++){ for(var j = 0; j<arr1[0].length; j++){ //画初始图 if(arr1[i][j] == 'wall'){ //这样我们就把坐标作为参数,引入到那些paint方法里,经过计算,在网页中正确显示了 paintWall(ctx,j,i,40); }else if(arr1[i][j] == 'brick'){ paintBrick(ctx, j, i,40); }else if(arr1[i][j] == 'target'){ paintBrick(ctx, j, i,40); paintTarget(ctx, j, i,10,'lime'); }else if(arr1[i][j] == 'box'){ paintBrick(ctx, j, i,40); var index = getBoxIndex(boxes,i,j); if(boxes[index].isOnTarget){ paintBox(ctx, j, i,40,'red'); }else{ paintBox(ctx, j, i,40,'yellow'); } }else if(arr1[i][j] == 'person'){ paintBrick(ctx, j, i,40); paintPerson(ctx, j, i,20,'pink'); } } } }
After calling the getMap() method in run() in game.js, the effect is like this :
4.game.js class:
First we still need to create the objects of people and boxes:
var person = new Person(0,0);
var boxLevel1Count = 3; var boxes = new Array(boxLevel1Count); for(var i = 0;i<boxLevel1Count;i++){ boxes[i] = new Box(0,0); }
Then we have to Knowing the position (coordinates) of the person in the two-dimensional array and the coordinates of the three boxes in it requires the following two methods:
//找到人的坐标 function findPerson(){ for (var i = 0; i < arr1.length; i++) { var tmp = arr1[i]; for (var j = 0; j < tmp.length; j++) { if (arr1[i][j] == 'person') { //使用json传变量 return {personX:i,personY:j}; } } } } //找箱子的坐标,并把他们放到数组里 function findBox(){ var count = 0; for (var i = 0; i < arr1.length; i++) { var tmp = arr1[i]; for (var j = 0; j < tmp.length; j++) { if (arr1[i][j] == 'box') { boxes[count].x = i; boxes[count].y = j; count++; } } } return boxes; }
After finding the coordinates of the person, we need to use the x, y and The i and j in the two-dimensional array are related, which is:
//接收person的坐标 var position = findPerson(); //i是person的横坐标 var i = position.personX; //j是person的纵坐标 var j = position.personY; //使对象的属性和人在二维数组的坐标关联 person.x = i; person.y = j;
<br/>
Then we can start writing logic. For example, if the user presses the left arrow key, he needs to determine what is on the left. If it is a box, Determine what is on the left side of the box:
The correct logic is as follows (let’s take a person moving to the left as an example, and then the top, bottom and right are the same):
The specific syntax is very simple: for example, according to the first example, there are bricks on the left, and the person steps on something other than the target point:
if(arr1[i][j-1] == 'brick'){ arr1[i][j-1] = 'person'; arr1[i][j] = 'brick'; Audio1.src = '走路emm.wav'; }
If we take the next step, the person steps on the target point , we will set person.isOnTarget to true, and when the person moves away, we will set this property to false
当人左边是箱子的时候,比较麻烦,首先必须明白有一点:我们到底推的是哪个箱子?之前已经有了一个存放所有箱子的数组了,所以现在需要一个方法,可以让我们知道我们推的是哪个箱子:
function getBoxIndex(boxes, i,j){ var index = 0; for(var k = 0;k<boxes.length;k++){ if(boxes[k].x == i && boxes[k].y == j){ //找到了这个箱子的下标 index = k; } } return index; }
在实际调用中,里面的参数(i,j)就写下一步要走的那个位置,比如说向左走,就是
var index = getBoxIndex(boxes, i,j-1);
这个index就是我们要找的第i个箱子了,接下来就很好办了,我们按照刚才的逻辑一步一步写,一堆的if、else,只需注意两点,当人踩到目标点时,把person.isOnTarget = true,移开之后false;箱子踩到目标点时boxes[index].isOnTarget = true,移开之后false,然后再整理一下,简化代码量,就是:
//玩家操作 document.onkeydown = function(ev){ var oCan = document.getElementById('can1'); var ctx = oCan.getContext('2d'); var oEvent = ev || event; var Audio1 = document.getElementById('walk'); var Audio2 = document.getElementById('push'); //接收person的坐标 var position = findPerson(); //i是person的横坐标 var i = position.personX; //j是person的纵坐标 var j = position.personY; //使对象的属性和人在二维数组的坐标关联 person.x = i; person.y = j; if(oEvent.keyCode == 37){ if(person.isOnTarget){ if(arr1[i][j-1] == 'brick'){ arr1[i][j-1] = 'person'; arr1[i][j] = 'target'; person.isOnTarget = false; }else if(arr1[i][j-1] == 'box' && arr1[i][j-2] != 'wall' && arr1[i][j-2] != 'box'){ var index = getBoxIndex(boxes, i,j-1); if(!boxes[index].isOnTarget){ if(arr1[i][j-2] == 'brick'){ arr1[i][j-2] = 'box'; arr1[i][j-1] = 'person'; arr1[i][j] = 'target'; person.isOnTarget = false; }else if(arr1[i][j-2] == 'target'){ arr1[i][j-2] = 'box'; arr1[i][j-1] = 'person'; arr1[i][j] = 'target'; person.isOnTarget = false; boxes[index].isOnTarget = true; } }else if(boxes[index].isOnTarget){ if(arr1[i][j-2] == 'brick'){ arr1[i][j-2] = 'box'; arr1[i][j-1] = 'person'; arr1[i][j] = 'target'; boxes[index].isOnTarget = false; }else if(arr1[i][j-2] == 'target'){ arr1[i][j-2] = 'box'; arr1[i][j-1] = 'person'; arr1[i][j] = 'target'; } } boxes[index].y--; }else if(arr1[i][j-1] == 'target'){ arr1[i][j-1] = 'person'; arr1[i][j] = 'target'; } }else if(!person.isOnTarget){ if(arr1[i][j-1] == 'brick'){ arr1[i][j-1] = 'person'; arr1[i][j] = 'brick'; }else if(arr1[i][j-1] == 'box' && arr1[i][j-2] != 'wall' && arr1[i][j-2] != 'box'){ var index = getBoxIndex(boxes, i,j-1); //箱子踩的不是目标点 if(!boxes[index].isOnTarget){ //箱子左边是地面 if(arr1[i][j-2] == 'brick'){ arr1[i][j-2] = 'box'; arr1[i][j-1] = 'person'; arr1[i][j] = 'brick'; }//箱子左边是目标点 else if(arr1[i][j-2] == 'target'){ arr1[i][j-2] = 'box'; arr1[i][j-1] = 'person'; arr1[i][j] = 'brick'; boxes[index].isOnTarget = true; } }else if(boxes[index].isOnTarget){ if(arr1[i][j-2] == 'brick'){ arr1[i][j-2] = 'box'; arr1[i][j-1] = 'person'; arr1[i][j] = 'brick'; boxes[index].isOnTarget = false; person.isOnTarget = true; }else if(arr1[i][j-2] == 'target'){ arr1[i][j-2] = 'box'; arr1[i][j-1] = 'person'; arr1[i][j] = 'brick'; person.isOnTarget = true; } } boxes[index].y--; }else if(arr1[i][j-1] == 'target'){ arr1[i][j-1] = 'person'; arr1[i][j] = 'brick'; person.isOnTarget = true; } } }
这样,向左走的所有逻辑就完成了,然后是上,右,下,接着else if 就行,照猫画虎,把里面的i和j一更换就完成了。
所有逻辑写完,验证无误后,我们要判断通关条件,这个很简单,当所有的箱子都isOnTarget时候,成功,当然是在每次按完方向键之后都要判断:
function judgeWin(boxes){ var count = 0; for(var p = 0;p<boxes.length;p++){ if(boxes[p].isOnTarget) count++; }if(count == boxes.length){ var Audio3 = document.getElementById('win'); Audio3.src = '鼓掌.mp3'; alert('You Win! 一共走了'+countStep+'步'); } }
通关的画面如下:
最后,我们加入音效,主要划分成以下几类:人走到砖块的,人推箱子的,人碰到墙的(和人推箱子碰到墙的),成功后的掌声。
总结:
1.我开始以为这个很简单,和我之前做的那个flappy bird 差不多,没想到这里面的逻辑其实很复杂,我的flappy bird链接如下:点击打开链接。所以,一定要在刚开始的时候要构思好大局,别越写越麻烦,容易产生放弃心里。
2.利用二维数组存放地图,然后在paint()方法里面写画的位置,大小等,调用的时候paint()里面就填坐标,这样有两个好处:(1)可以知道每个物块的上下左右都是什么,易于判断;(2)新建关卡的时候易于创建,只需要按照坐标位置,把wall,brick,person,target,box放进去即可
3.在创建人的对象后,我们需要在地图数组中把人的坐标找出来,然后将对象的x,y属性和坐标关联;在创建箱子的对象数组后,我们需要getIndex()方法,找到人到底推的是哪个箱子,才能使这个箱子的isOnTarget改成true或者false,这两点很关键。
The above is the detailed content of Writing a push box game with JS and H5. For more information, please follow other related articles on the PHP Chinese website!