In the previous section, we mainly implemented animation, which is quite interesting. The performance of the game is good. re-painting should be more important.
This section describes how to operate on bubbles. As we mentioned later, after clicking on bubbles, the bubble will trigger a response. How can we obtain the bubble we have clicked?
Canvas is actually an element of html, so we can add a click event to Canvas. To view the coordinates of the mouse when you click the map.
We add a click method to game. This method is called when Canvas is clicked.
The following figure shows the possible effects when a Canvas is clicked:
1. No response will be made if there is no point in map
2. Click the bubble to respond to the bubble (flash)
3. If you have already clicked another bubble, you can cancel the response of the previous bubble (clicked. stop). If the previous bubble is your own, no response will be made. And use clicked as your own.
4. If a space is clicked, if a bubble is clicked before, try to move the bubble. If clicked is null (no bubble before), no response will be made. If it can be moved, cancel the flash and clear clicked to start moving.
[Javascript]
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) {// The same bubble does not reflect 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 ();
// Move clicked game. map. move (this. clicked, bubble );
} // Console. log ("x:" + x + "y:" + y) ;}, 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) {// The same bubble does not reflect 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 ();
// Move clicked game. map. move (this. clicked, bubble );
} // Console. log ("x:" + x + "y:" + y );},
The code for finding the path has not been written yet, because how to implement it needs to be considered. I finally came up with a solution. Currently, the code of the game is removed to implement the path-finding code for the next two points.
First, a chessboard is given, for example:
1 1 1 1
0 0 1 0 1
0 0 1 0 1
1 0 0 1 1
How do I design a point (2, 3) in the middle of the row to be moved to (0, 1) in the upper left corner?
Whether a piece can be moved depends on whether the four adjacent parts are 0. If it is 0, it can be moved. So we can use recursion to obtain records of all connected 0. This record is stored in the tree structure until we cannot continue to detect grids with 0 or reach the destination. We set the current chess piece lattice as root, and his adjacent chess pieces are his children. In this case, we will get the result of a tree as follows:
Right? In this way, we can directly see the entire path (-> 1, 3-> 1, 2-> 0, 1 ). The idea is clear. It is okay to build a subnode recursively. The Code is as follows:
[Javascript]
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 );
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 );
I added a parent, which is a pointer to my father, so that I don't have to traverse this tree any more. The entire path can be obtained directly from the goalNode result: although it is lazy, it also has to come back at a cost, because the path is not the shortest path, it is silly, how to choose the optimal route? The most stupid way is to obtain all the paths (traverse the tree N times in depth-) and then compare them. This is obviously inefficient. At first, I didn't know that the effect would be so bad. I found that the Code is not well written (nonsense ). Because every time we judge that the search direction is top left and bottom right, the path is always explored in this direction, and the optimal path should be explored in the direction of the target point. Because it is a recursive search, the current node and the target node are judged in the direction of the coordinates, and then the judgment order is adjusted. In this way, the short path is obtained.
[Javascript]
Var child = [];
Var left, top, right, buttom;
// The Rough Determination of the shortest path is the general direction of the preferred target location 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 ));}
Var child = [];
Var left, top, right, buttom;
// The Rough Determination of the shortest path is the general direction of the preferred target location 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 ));}
Although the Code is silly, you have to say a word in this way :)
Now that path search has been implemented, we will hand it over to map, which is responsible for bringing bubbles up. In fact, the bubble is colored by path-and the code is not complicated.
[Javascript]
Move: function (bubble, target) {var path = this. search (bubble. x, bubble. y, target. x, target. y );
If (! Path) {// The display cannot move s alert ("cannot go ");
Return;
} // Map starts to play the moving effect of the current bubble
// Two implementation methods: 1. map is stained by path and finally reaches the destination
2. map to generate a temporary bubble for display and remove it from the destination.
// 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 } 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;
// The Rough Determination of the shortest path is the general direction of the preferred target location 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;
}},
Move: function (bubble, target ){
Var path = this. search (bubble. x, bubble. y, target. x, target. y );
If (! Path ){
// The display cannot move s alert ("cannot move ");
Return;
} // Map starts to play the moving effect of the current bubble
// Two implementation methods: 1. map is stained by path and finally reaches the destination
2. map to generate a temporary bubble for display and remove it from the destination.
// 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 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;
// The Rough Determination of the shortest path is the general direction of the preferred target location 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;
}
},
Look at the animation effect http://jsfiddle.net/maddemon/khAJB/4/embedded/result/
The rest is to determine how to eliminate, add points, and prevent misoperations.
From Jun Zhiqiang