ホームページ >ウェブフロントエンド >jsチュートリアル >JS と H5 を使用してプッシュ ボックス ゲームを作成する
プッシュボックスゲームは何年も前に人気のあるゲームです(今でも多くの人がプレイしています)ゲームの目的は非常にシンプルです。つまり、ボックスを押して目的地まですべてのボックスを押します。ゲームは成功します: 単純なロジックに見えますが、実際には非常に困難です。私も他の人の助けを借りて完成させました。ここで、js と html5 を使用してゲームを記述する方法を紹介します (便宜上、人をピンクの丸で置き換えます):
1. 能力要件
JavaScript、HTML キャンバス、基本的なオブジェクト指向のアイデア、および合理的なプログラミング ロジック。
2. 書き込み順序
1.pushBox.html ファイル
2.pojo.js ファイル (すべてのオブジェクトの保存に使用)
3.pa整数ファイル (描画ステートメントの作成に使用)
4.game.js ファイル (実行ロジック部分の作成に使用)
5.allLevels.js ファイル (レベルの保存に使用)
*注: これは私の執筆習慣であり、より良い書き方があればコメントしてください
1. 基本を確立します。 PushBox.html ファイル:内容は非常に単純です。
は、ロジック部分が完了した後に追加されます。 :
<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> は他の 4 つの .js ファイルを導入しており、そのうちの 1 つは run() メソッドを呼び出します: </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. pojo クラスを作成します:
まず、合計を知る必要があります。これらのクラスの数:
人、ボックス、ターゲット ポイント、レンガと壁、これは非常に単純です。すべてのクラスには、色 (色)、サイズ (辺の長さ/半径)、x (横座標)、y (縦軸)。次に、人やレンガがターゲット ポイントと一致する可能性があることを覚えておく必要があるため、ボックスと人のクラスに isOnTarget (ターゲット ポイントと一致するかどうか) 属性を追加する必要があります。これで完了です:
//人类 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. ペイントの js クラスを記述します pojo.js クラスに記述したすべてのクラスの描画メソッドを記述する必要があります:
注意することが重要です: たとえば、特定の x、y 座標を使用する場合、ボックスを描画するには:
ctx.fillRect(x, y, size, size);
次の方法で呼び出された場合、すべてのレベルで次のようにボックスを描画します:
ctx.fillRect(500, 500, 40, 40);
ボックスの隣に何があるか判断できず、判断するのが難しいため、ロジック層を記述する方法がありません。
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'] ];たとえば、arr1[0][1]が壁なので、ロジック層を書くことができます。もう 1 つの利点は、後で新しいレベルを作成する際に、座標の順序で配列に書き出すだけで済むということです。 そこで、paint.js で次のように記述し、Web ページ上で正しく描画できるように、x と y に対していくつかの操作を実行します。
//首先还是清屏 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(); }次に、ペイント メソッドを呼び出すときに、次のようにします。これを実行します: 配列内の各オブジェクト ブロックの位置に従って呼び出し、描画します:
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'); } } } }game.js の run() で getMap() メソッドを呼び出した後の効果は次のようになります:
4. game.js クラス:
まず、人とボックスのオブジェクトを作成する必要があります:
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); }
次に、2 次元配列内の人の位置 (座標) と、その中の 3 つのボックスの座標を知る必要があります。次の 2 つのメソッドが必要です:
//找到人的坐标 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; }人の座標を見つけた後、人物オブジェクトの x、y を 2 次元配列の i、j に関連付ける必要があります。たとえば、ユーザーが左矢印キーを押した場合、左側にあるものを決定し、それがボックスの場合は、ボックスの左側にあるものも決定します。 正しいロジックは次のとおりです。 (例として、人が左に移動すると、上、下、右が同じになります):
具体的な構文は非常に簡単です。 たとえば、最初の例によれば、次のようになります。左側にレンガがあり、その人がターゲット ポイント以外のものを踏んだ場合:
//接收person的坐标 var position = findPerson(); //i是person的横坐标 var i = position.personX; //j是person的纵坐标 var j = position.personY; //使对象的属性和人在二维数组的坐标关联 person.x = i; person.y = j;
次のステップに進み、その人がターゲット ポイントを踏んだ場合、その人を .isOnTarget が true に設定されている必要があります。人が離れていく場合、このプロパティを 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,这两点很关键。
以上がJS と H5 を使用してプッシュ ボックス ゲームを作成するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。