使用JS,H5編寫經典遊戲-推箱子
初學JS,使用JS、H5編寫推箱子小遊戲簡易版
推箱子小遊戲是一款多年前很流行的小遊戲(即使現在也有很多人玩),遊戲目的很簡單,就是人推箱子,把所有的箱子推到目的地,就遊戲成功:
看似跟簡單的邏輯,其實還是有一定難度的,我也是依靠了別人的幫助才完成的,現在開始介紹如何用js,html5編寫該遊戲(方便起見我們把人用一個粉圓形代替):
一.能力要求
JavaScript,HTML畫布,面向物件的基本思想,合理的程式設計邏輯。
二.編寫順序
1.pushBox.html檔案
2.pojo.js檔案(用來存放所有的物件)
3.paint.js檔案(用來寫畫畫的語句)
4.game.js檔案(用來寫執行邏輯部分)
5.allLevels.js檔案(用來存放關卡)
*注:這是我的書寫習慣,按內容和功能將個各類分開,如有更好地寫法歡迎評論
三.開始編寫
1.建立基本的pushBox.html檔案:
內容很簡單,<body>裡面僅僅需要<canvas>標籤,設定id,其後的<audio>檔案在邏輯部分做完後再加進去:
<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>有引入其他四個.js檔案,還有一個呼叫run()方法:
<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類:
首先我們需要知道總共有這些類:
人,箱子,目標點,磚塊和圍牆,很簡單,所有的類都有color(顏色),size(邊長/半徑),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.編寫paint.js類
我們需要對剛剛在pojo.js類中寫的所有類寫出畫的方法:
需要注意一點很重要:我們如果使用確定的x,y座標,比如說要畫箱子:
ctx.fillRect(x, y, size, size);
如果如下面的方式呼叫的話,我們在allLevels裡面這樣畫這個箱子:
ctx.fillRect(500, 500, 40, 40);
我們就無法確定這個箱子的旁邊是什麼,不好判斷,於是沒有辦法寫邏輯層。
所以,我們這樣構思:我們用一個二維陣列來構造這個關卡,每一個物塊(箱子,人,目標點,磚塊或者牆)都放到這個陣列當中,arr1[][],像這樣:
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]就是牆了,這樣一來就能寫邏輯層了。而且還有一個優點,就是我們在之後建立新關卡的時候很方便,只需要按照座標順序在數組裡寫出來即可。
所以,我們在paint.js裡面這樣寫,每個方法裡面都把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);
}
然後我們要知道人在二維陣列中的位置(座標),以及三個箱子在其中的座標需要以下兩個方法://找到人的座標
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;
}
找到人的座標之後,我們要把person物件的x,y和二維數組裡的i,j關聯起來,就是://接收person的座標
var position = findPerson();
//i是person的橫座標
var i = position.personX;
//j是person的縱座標
var j = position.personY;
//使物件的屬性和人在二維陣列的座標關聯
person.x = i;
person.y = j;
然後我們就可以開始寫邏輯了,比如說使用者按方向鍵左,要判斷左邊是什麼,如果是箱子的話,還要判斷箱子的左邊是什麼:
正確的邏輯如下所示(我們就拿人往左移動為例,然後上下右都是一樣的):
具體的語法很簡單:比如說,按照第一個為例,左邊是磚塊,並且人踩的不是目標點:
if(arr1[i][j-1] == 'brick'){ arr1[i][j-1] = 'person'; arr1[i][j] = 'brick'; Audio1.src = '走路emm.wav'; }
如果我們下一步,人踩到了目標點,我們就要把person.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,這兩點很關鍵。
注:我只做了一關,也沒有做關卡的跳轉,有興趣的夥伴們可以繼續把跳轉關卡完成。
最後,希望大家有所收穫。本文為原創,其中若有更好的寫法請大佬留言評論。
音樂,音效提供:愛給網
程式創作時間:2018.3.12
部落格撰寫時間:2018.3.16
相關推薦
使用JS,H5編寫經典遊戲-推箱子
初學JS,使用JS、H5編寫推箱子小遊戲簡易版推箱子小遊戲是一款多年前很流行的小遊戲(即使現在也有很多人玩),遊戲目的很簡單,就是人推箱子,把所有的箱子推到目的地,就遊戲成功:看似跟簡單的邏輯,其實還是有一定難度的,我也是依靠了別人的幫助才完成的,現在開始介紹如何用js,ht
使用前端原生 js,貪吃蛇小遊戲
oct 新的 生成 方便 描述 logs turn asc 轉動 好久好久,真的是好久好久沒來寫過了,因為最近有點小忙。不過即使是忙,也也還是寫了兩個小遊戲,其中一個就是這個,貪吃蛇啦。 算是一個小練手了,這個是在有點太簡單了,只是第一次寫這種小遊戲,還是零零星星花了三
小遊戲推箱子Java程式碼(實現遊戲的空介面+玩家)
介面的各個圖片,自己做好哦建立一個窗體import javax.swing.JFrame; public class GameFrame extends JFrame {//extends繼承 GamePanel gp; //構建GameFrame方法 public G
three.js 製作一個三維的推箱子游戲
今天郭先生髮現大家更喜歡看我發的three.js小作品,今天我就發一個3d版本推箱子的遊戲,其實webGL有很多框架,three.js並不合適做遊戲引擎,但是可以嘗試一些小遊戲。線上案例請點選部落格原文。 要製作一個推箱子游戲,正常要有以下4個步驟 定義一些陣列,要有開始箱子陣列、結束箱子陣列、地面陣列還
用C++寫的推箱子小遊戲2.2(支援步數顯示,回撤,穿牆,闖關,輸入等功能)
歡迎來到jiedai的推箱子。 jiedai用C++寫的推箱子游戲支援步數顯示,回撤,穿牆,闖關,輸入等操作。 1.1的更新增加了步數的輸出,對玩家移動的箭頭進行了改進。 1.2的更新推出了小編提供的地圖,希望大家遊戲愉快。 1.3的更新修正了除錯過程中的bug。 經過玩家
例項,js實現的推箱子游戲
<SCRIPT LANGUAGE="JavaScript"> <!-- var mappath = new Array( "11111111111111111111", "10000000000000000001", "100100000000000000
推箱子小遊戲 C語言 共寫了四十關,遊戲有多種模式,適合新手學習交流 有全部完整程式碼可直接編譯執行
}void middleMap(int Map[20][20],int *n,int *m,char *ch) {//中等關卡 static int cs=0; int guan; //fscanf(fp1, "%d", &guanshu2); //fclose(fp1);//
推箱子小遊戲《格魯的實驗室》13關 - bfs最短路徑
rst ace clu end 路徑 我們 prior top other 下載了一款推箱子小遊戲,第13關的時候怎麽也破不了最佳紀錄(最少步數是9而我們最好的方案是10步),因為數據比較小(6*8的方陣),所以寫了個BFS來找最短路。 遊戲的目標是把小黃人推到黃色球,小綠
C++學習(九)之 項目 推箱子遊戲
bgm 控制臺 兩個 分享 加載圖片 eat gethwnd put init 遊戲制作 推箱子 步驟分析 1、模板 2、模板分析 組成元素: 空地 墻 人 目的地 箱子 背景 3、如何操作 通過WASD鍵盤操作人,推著箱子,到達目
網易2017遊戲研發面試題 —— 推箱子(廣搜BFS判斷)
#include<iostream> #include <queue> #include <map> using namespace std; typedef pair<int, int> point; typedef pair<point,
Java版推箱子(搬箱子)遊戲開發入門示例及原始碼
推(搬)箱子,又名Sokoban,倉庫番等,是一款堪稱古玩級的電腦遊戲。提起它,筆者相信沒什麼人會感覺到陌生,更沒什麼生物會連聽都沒聽說過。它的發展歷史之久遠,甚至超越了俄羅斯方塊(1988年電腦遊戲化)。這款遊戲最初起源於日本,是個很難爭辯的事實(我知道有人反對,但筆者確實找
原生js用ajax編寫省市聯動,二級聯動
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>城市聯動</
【騰訊優測】騰訊優測是備受客戶信賴的移動雲測試平臺,為應用、遊戲,H5混合應用的研發團隊提供產品質量檢測與問題解決服務。不僅在線上平臺提供「全面相容測試」、「原始碼缺陷分析」、「遠端真機租用」等多種質量檢測工具
騰訊優測 騰訊優測是備受客戶信賴的移動雲測試平臺,為應用、遊戲,H5混合應用的研發團隊提供產品質量檢測與問題解決服務。不僅在線上平臺提供「全面相容測試」、「原始碼缺陷分析」、「遠端真機租用」等多種質量檢測工具...
js與h5的結合,初步學習。
初步學習拖放事件 首先了解一下與拖放事件有關的標籤,拖動某元素時依次觸發: dragstart drag dragend 其次瞭解某元素拖動到一個你想要拖動的目標上,依次發生: drage
C#遊戲程式設計:《控制檯小遊戲系列》之《推箱子》
##編輯一下看看csdn支援markdown沒有 `測試` 經典的《推箱子》遊戲: /* * ///////////////////////////////////////////////////////////////////// * Program : C
小夥利用Python自制一個推箱子小遊戲!
導語 月初更波python製作小遊戲系列吧用python寫了個推箱子小遊戲,在這裡分享給大家,讓我們愉快地開始吧 進群進群:943752371可以獲取Python各類入門學習資料! 這是我的微信公眾號【Python程式設計之家】各位大佬用空可以關注下,每天更新Python學習方法,感謝!
(微控制器原理與應用)大液晶屏的遊戲設計(推箱子)
圖十二 遊戲程式介面 系統的優點與不足 優點:遊戲容易上手,而且可以鍛鍊大腦思維 不足:有些關卡設計簡單 參考文獻 [1] 陳海宴. 51微控制器原理及應用--基於Keil C與Proteus. 北京航空航天大學出版,2010-07-01,第1版. [2] 彭偉. 微控制器C語言程式設計實訓100例——基
【休閒遊戲 實戰1】推箱子PC端小遊戲(附原始碼)
效果圖:第100關有些難度,用了449步才過關(我用的是可跳關版的,直接玩的最後一關)原始碼解讀原始碼一共3個檔案:index.html(遊戲介面載入,核心功能),js/mapdata100.js(100個16階矩陣,通過0,1,2,3,4分別對應遊戲中5個元素的圖片來定義每
微信HTML5小遊戲之推箱子
經典的推箱子是一個來自日本的古老遊戲,目的是在訓練你的邏輯思考能力。在一個狹小的倉庫中,要求把木箱放到指定的位置,稍不小心就會出現箱子無法移動或者通道被堵住的情況,所以需要巧妙的利用有限的空間和通道,合理安排移動的次序和位置,才能順利的完成任務。推箱子游戲是一種老少皆宜的益
uni-app 是一個使用 Vue.js 開發跨平臺應用的前端框架,開發者編寫一套程式碼,可編譯到iOS、Android、微信小程式等多個平臺。
uni-app 是一個使用 Vue.js 開發跨平臺應用的前端框架,開發者編寫一套程式碼,可編譯到iOS、Android、微信小程式等多個平臺。 uni-app在跨端數量、擴充套件能力、效能體