HTML 5游戲制作之五彩連珠(尋路)
上節(jié)主要做了動畫的實現(xiàn),感覺還是比較有意思的。游戲的性能好不好,重繪應(yīng)該比較重要吧,菜鳥瞎想了下呵呵。
本節(jié)就要做對泡泡的操作,上節(jié)后面提到了點擊泡泡后泡泡要做出閃動響應(yīng),那我們我們?nèi)绾潍@得被點擊了哪個泡泡呢?
其實Canvas也是html的一個元素而已,所以我們可以給Canvas加click事件。來查看click時鼠標(biāo)的坐標(biāo),這樣就等得出點擊了map的哪個位置。
我們給game增加一個click方法,當(dāng)Canvas點擊時調(diào)用此方法。
要實現(xiàn)的效果是: 當(dāng)Canvas時被點擊時有幾種可能:
1、沒點到map 那就不作響應(yīng);2、點到了泡泡,那該泡泡要做出響應(yīng)(閃);3、如果之前有點擊過其他的泡泡,則取消之前的泡泡的響應(yīng)(clicked.stop),如果之前的泡泡是自己,則不作響應(yīng)。并把clicked作為自己,以體后面的操作;4、如果點擊到的是空格,如果之前點擊了泡泡,那就嘗試移動這個泡泡,如果clicked為null(之前沒泡泡)那就不作任何響應(yīng)。如果可以移動,則取消閃動,并清除clicked,開始移動。
- 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) {
- //同一個泡不做反映
- 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();
- //移動clicked
- game.map.move(this.clicked, bubble);
- }
- }
- //console.log("x:" + x + " y:" + y);
- },
尋路的代碼還沒寫,因為這個需要考慮怎么實現(xiàn)。 我絞盡腦汁終于想到了一個辦法。暫且撇開游戲的代碼,單獨實現(xiàn)下兩點的尋路代碼。
先給定一個棋盤,假如如下:
1 1 1 1 1
0 0 1 0 1
0 0 1 0 1
1 0 0 1 1
要想從 最下面一行中間的點(2,3)移動到左上角的(0,1),該如何設(shè)計呢?
一個棋子能否移動,要看他相鄰的4個子是否為0,如果是0則可以移動。 所以我們可以通過遞歸來獲得所有相連的0的記錄。 這個記錄用樹結(jié)構(gòu)來存儲,直到我們無法繼續(xù)探測為0的格子或到達(dá)目的地。 我們把當(dāng)前的棋子的格子設(shè)為 root,他相鄰的棋子是他的孩子。這樣的話,我們會得到一棵樹的結(jié)果如下:
是不是?這樣的畫我們就可以直接看到了整個路徑(2,3 -> 1,3 -> 1,2 -> 0,2 -> 0,1)。思路很清晰,只要遞歸構(gòu)建子節(jié)點就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);
我加了一個parent,就是指向父親的指針,那樣就不用再去遍歷這棵樹了??梢灾苯訌膅oalNode的結(jié)果得到整個路徑:) 雖然偷懶,但也是要復(fù)出代價的,因為這樣走的路徑不是最短路線,比較傻,怎么選擇最優(yōu)路線呢? 最笨的方法就是 把所有的路徑都得到(深度優(yōu)先遍歷樹N遍- -)然后比較。這個顯然效率不高。開始我也不知道效果會這么差,等一運行(你運行下就知道了),我發(fā)現(xiàn),是代碼寫的不好(廢話)。因為我們每次的判斷順尋都是 左上右下,這樣路徑總是這個方向探索,而最優(yōu)的路徑應(yīng)該是朝目標(biāo)點的方向探索。 由于是遞歸查找,所以,對當(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];
- else
- child = [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];
- else
- child = [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));
- }
代碼雖然寫的比較傻,但這種方式不得不說好就一個字:)
既然尋路已經(jīng)實現(xiàn)了,那么下面就交給map了,map來負(fù)責(zé)讓泡泡走起來。其實就是根據(jù)路徑給泡泡著色- - ,代碼也不復(fù)雜。
- move: function (bubble, target) {
- var path = this.search(bubble.x, bubble.y, target.x, target.y);
- if (!path) {
- //顯示不能移動s
- alert("過不去");
- return;
- }
- //map開始播放當(dāng)前泡的移動效果
- //兩種實現(xiàn)方式,1、map按路徑染色,最后達(dá)到目的地 2、map生成一個臨時的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];
- else
- child = [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];
- else
- child = [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)容了。
原文鏈接:http://www.cnblogs.com/mad/archive/2012/03/18/2404660.html
【編輯推薦】