日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > HTML >内容正文

HTML

HTML5 Canvas编写五彩连珠(5):寻路

發(fā)布時(shí)間:2023/12/20 HTML 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HTML5 Canvas编写五彩连珠(5):寻路 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

上節(jié)主要做了動(dòng)畫的實(shí)現(xiàn),感覺還是比較有意思的。游戲的性能好不好,重繪應(yīng)該比較重要吧,菜鳥瞎想了下 呵呵。
本節(jié)就要做對(duì)泡泡的操作,上節(jié)后面提到了點(diǎn)擊泡泡后泡泡要做出閃動(dòng)響應(yīng),那我們我們?nèi)绾潍@得被點(diǎn)擊了哪個(gè)泡泡呢?
其實(shí)Canvas也是html的一個(gè)元素而已,所以我們可以給Canvas加click事件。來(lái)查看click時(shí)鼠標(biāo)的坐標(biāo),這樣就等得出點(diǎn)擊了map的哪個(gè)位置。
我們給game增加一個(gè)click方法,當(dāng)Canvas點(diǎn)擊時(shí)調(diào)用此方法。
要實(shí)現(xiàn)的效果是: 當(dāng)Canvas時(shí)被點(diǎn)擊時(shí)有幾種可能:
1、沒點(diǎn)到map ?那就不作響應(yīng)
2、點(diǎn)到了泡泡,那該泡泡要做出響應(yīng)(閃)
3、如果之前有點(diǎn)擊過其他的泡泡,則取消之前的泡泡的響應(yīng)(clicked.stop),如果之前的泡泡是自己,則不作響應(yīng)。并把clicked作為自己,以體后面的操作。
4、如果點(diǎn)擊到的是空格,如果之前點(diǎn)擊了泡泡,那就嘗試移動(dòng)這個(gè)泡泡,如果clicked為null(之前沒泡泡)那就不作任何響應(yīng)。如果可以移動(dòng),則取消閃動(dòng),并清除clicked,開始移動(dòng)。?

onclick: function (e) {var px = e.offsetX - game.map.startX;var py = e.offsetY - game.map.startY;if (px < 0 || py < 0 || px > game.map.width || py > game.map.height) {return;}var x = parseInt(px / game.cellWidth);var y = parseInt(py / game.cellWidth);var bubble = game.map.getBubble(x, y);if (bubble.color) {if (this.clicked) {//同一個(gè)泡不做反映if (this.clicked.x == x && this.clicked.y == y) {return;}this.clicked.stop();}this.clicked = bubble;bubble.play();}else {if (this.clicked) {this.clicked.stop();//移動(dòng)clickedgame.map.move(this.clicked, bubble);}}//console.log("x:" + x + " y:" + y);},

尋路的代碼還沒寫,因?yàn)檫@個(gè)需要考慮怎么實(shí)現(xiàn)。 我絞盡腦汁終于想到了一個(gè)辦法。暫且撇開游戲的代碼,單獨(dú)實(shí)現(xiàn)下兩點(diǎn)的尋路代碼。
先給定一個(gè)棋盤,假如如下:
1 1 1 1 1
0 0 1 0 1
0 0 1 0 1
1 0 0 1 1
要想從 最下面一行中間的點(diǎn)(2,3)移動(dòng)到左上角的(0,1),該如何設(shè)計(jì)呢?
一個(gè)棋子能否移動(dòng),要看他相鄰的4個(gè)子是否為0,如果是0則可以移動(dòng)。 所以我們可以通過遞歸來(lái)獲得所有相連的0的記錄。 這個(gè)記錄用樹結(jié)構(gòu)來(lái)存儲(chǔ),直到我們無(wú)法繼續(xù)探測(cè)為0的格子或到達(dá)目的地。 我們把當(dāng)前的棋子的格子設(shè)為 root,他相鄰的棋子是他的孩子。這樣的話,我們會(huì)得到一棵樹的結(jié)果如下:

是不是?這樣的畫我們就可以直接看到了整個(gè)路徑(2,3 -> 1,3 -> 1,2 -> 0,2 -> 0,1)。思路很清晰,只要遞歸構(gòu)建子節(jié)點(diǎn)就ok了。代碼如下:

var map = [ [1, 1, 1, 1, 1], [0, 0, 1, 0, 1], [0, 0, 1, 0, 1], [1, 0, 0, 1, 1]];var history = [];var goal = { "x": 0, "y": 1 }var goalNode = null;var getNode = function (x, y, parent) {if (x >= map.length || y >= map.length) {return;}if (map[y][x] == 1) {return;}var hasNode = false;history.forEach(function (n) {if (n.x == x && n.y == y) {hasNode = true;return;}});if (hasNode) {return;}var node = { "x": x, "y": y, "parent": parent, child: [] };history.push(node);if (node.x == goal.x && node.y == goal.y) {goalNode = node;return node;}if (x - 1 >= 0 && !map[y][x - 1]) {node.child.push(getNode(x - 1, y, node));}if (y - 1 >= 0 && !map[y - 1][x]) {node.child.push(getNode(x, y - 1, node));}if (x + 1 < map.length && !map[y][x + 1]) {node.child.push(getNode(x + 1, y), node);}if (y + 1 < map.length && !map[y + 1][x]) {node.child.push(getNode(x, y + 1, node));}return node;}console.log(getNode(2, 3));console.log(goalNode);

? ? ?我加了一個(gè)parent,就是指向父親的指針,那樣就不用再去遍歷這棵樹了。可以直接從goalNode的結(jié)果得到整個(gè)路徑:) 雖然偷懶,但也是要復(fù)出代價(jià)的,因?yàn)檫@樣走的路徑不是最短路線,比較傻,怎么選擇最優(yōu)路線呢? 最笨的方法就是 把所有的路徑都得到(深度優(yōu)先遍歷樹N遍- -)然后比較。這個(gè)顯然效率不高。開始我也不知道效果會(huì)這么差,等一運(yùn)行(你運(yùn)行下就知道了),我發(fā)現(xiàn),是代碼寫的不好(廢話)。因?yàn)槲覀兠看蔚呐袛囗槍ざ际?左上右下,這樣路徑總是這個(gè)方向探索,而最優(yōu)的路徑應(yīng)該是朝目標(biāo)點(diǎn)的方向探索。 由于是遞歸查找,所以,對(duì)當(dāng)前的node和目標(biāo)node進(jìn)行坐標(biāo)的方向判斷,然后調(diào)整判斷順序,這樣是得到的才是比較短的路徑。

var child = []; var left, top, right, buttom; //最短路徑的粗略判斷就是首選目標(biāo)位置的大致方向 if (x - 1 >= 0 && map.isEmpty(x - 1, y))left = { "x": x - 1, "y": y }; if (x + 1 < map.length && map.isEmpty(x + 1, y))right = { "x": x + 1, "y": y }; if (y + 1 < map.length && map.isEmpty(x, y + 1))buttom = { "x": x, "y": y + 1 }; if (y - 1 >= 0 && map.isEmpty(x, y - 1))top = { "x": x, "y": y - 1 };if (x > x2) {if (y > y2)child = [left, top, right, buttom];else if (y < y2)child = [left, buttom, right, top];elsechild = [left, top, right, buttom]; } else if (x < x2) {if (y > y2)child = [right, top, left, buttom];else if (y < y2)child = [right, buttom, left, top];elsechild = [right, top, left, buttom]; } else if (x == x2) {if (y > y2)child = [top, left, right, buttom];else if (y < y2)child = [buttom, left, right, top]; }for (var i = 0; i < child.length; i++) {var c = child[i];if (c) node.child.push(getnode(c.x, c.y, node)); }

代碼雖然寫的比較傻,但這種方式不得不說好就一個(gè)字:)


?既然尋路已經(jīng)實(shí)現(xiàn)了,那么下面就交給map了,map來(lái)負(fù)責(zé)讓泡泡走起來(lái)。其實(shí)就是根據(jù)路徑給泡泡著色- - ,代碼也不復(fù)雜。

move: function (bubble, target) {var path = this.search(bubble.x, bubble.y, target.x, target.y);if (!path) {//顯示不能移動(dòng)salert("過不去");return;}//map開始播放當(dāng)前泡的移動(dòng)效果//兩種實(shí)現(xiàn)方式,1、map按路徑染色,最后達(dá)到目的地 2、map生成一個(gè)臨時(shí)的bubble負(fù)責(zé)展示,到目的地后移除//console.log(path);var me = this;var name = "move_" + bubble.x + "_" + bubble.y;var i = path.length - 1;var color = bubble.color;game.play(name, function () {if (i < 0) {game.stop(name);return;}path.forEach(function (cell) {me.setBubble(cell.x, cell.y, null);});var currentCell = path[i];me.setBubble(currentCell.x, currentCell.y, color);i--;}, 50);},search: function (x1, y1, x2, y2) {var history = [];var goalCell = null;var me = this;getCell(x1, y1, null);if (goalCell) {var path = [];var cell = goalCell;while (cell) {path.push({ "x": cell.x, "y": cell.y });cell = cell.parent;}return path;}return null;function getCell(x, y, parent) {if (x >= me.bubbles.length || y >= me.bubbles.length)return;if (x != x1 && y != y2 && !me.isEmpty(x, y))return;for (var i = 0; i < history.length; i++) {if (history[i].x == x && history[i].y == y)return;}var cell = { "x": x, "y": y, child: [], "parent": parent };history.push(cell);if (cell.x == x2 && cell.y == y2) {goalCell = cell;return cell;}var child = [];var left, top, right, buttom;//最短路徑的粗略判斷就是首選目標(biāo)位置的大致方向if (x - 1 >= 0 && me.isEmpty(x - 1, y))left = { "x": x - 1, "y": y };if (x + 1 < me.bubbles.length && me.isEmpty(x + 1, y))right = { "x": x + 1, "y": y };if (y + 1 < me.bubbles.length && me.isEmpty(x, y + 1))buttom = { "x": x, "y": y + 1 };if (y - 1 >= 0 && me.isEmpty(x, y - 1))top = { "x": x, "y": y - 1 };if (x > x2) {if (y > y2)child = [left, top, right, buttom];else if (y < y2)child = [left, buttom, right, top];elsechild = [left, top, right, buttom];}else if (x < x2) {if (y > y2)child = [right, top, left, buttom];else if (y < y2)child = [right, buttom, left, top];elsechild = [right, top, left, buttom];}else if (x == x2) {if (y > y2)child = [top, left, right, buttom];else if (y < y2)child = [buttom, left, right, top];}for (var i = 0; i < child.length; i++) {var c = child[i];if (c) cell.child.push(getCell(c.x, c.y, cell));}return cell;}},

?試玩地址:http://zhengliangjun.sinaapp.com/colorline.html

?后面剩下的就是判斷如何消除、加分、防止誤操作之類的內(nèi)容了。

總結(jié)

以上是生活随笔為你收集整理的HTML5 Canvas编写五彩连珠(5):寻路的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。