├── LICENSE
├── README.md
├── assets
├── gridtiles.png
├── map.json
├── map.tmx
└── phaserguy.png
├── index.html
└── js
├── easystar.js
├── game.js
├── main.js
└── phaser.js
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Jerome Renaux
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pathfinding_tutorial
2 |
3 | Demo for a tutorial showing how to perform pathfinding and move a sprite accordingly with Phaser 3.
4 |
5 | The demo [can be found here](https://jerenaux.github.io/pathfinding_tutorial/).
6 |
7 | The tutorial is available [here](http://www.dynetisgames.com/2018/03/06/pathfinding-easystar-phaser-3/).
8 |
9 | ## Running the demo ##
10 |
11 | Place all the files on your local web server and navigate to index.html.
12 |
13 | You can move around by clicking on the map. The colored areas are tiles that have a cost attached to them, so the pathfinding algorithm witll try to avoid them if possible (unless all alternative paths are much longer).
14 |
--------------------------------------------------------------------------------
/assets/gridtiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jerenaux/pathfinding_tutorial/1cefa2ef883c19b7335c0e4eb2729cc97c49b8f1/assets/gridtiles.png
--------------------------------------------------------------------------------
/assets/map.json:
--------------------------------------------------------------------------------
1 | { "height":20,
2 | "layers":[
3 | {
4 | "data":[20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 1, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 20, 20, 1, 20, 1, 20, 20, 20, 20, 20, 20, 1, 62, 62, 62, 62, 1, 1, 20, 1, 20, 20, 1, 20, 1, 20, 1, 1, 1, 1, 1, 1, 58, 58, 58, 62, 1, 1, 20, 1, 20, 20, 1, 20, 1, 90, 1, 20, 20, 20, 20, 20, 20, 20, 58, 90, 1, 1, 20, 1, 20, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 20, 1, 20, 20, 1, 1, 20, 20, 1, 20, 20, 20, 20, 20, 20, 20, 1, 1, 20, 1, 20, 1, 20, 20, 1, 20, 20, 20, 1, 20, 1, 20, 1, 1, 20, 1, 1, 1, 20, 1, 20, 1, 20, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 20, 1, 20, 1, 20, 20, 1, 1, 20, 20, 20, 20, 20, 1, 1, 1, 20, 20, 20, 1, 20, 20, 20, 1, 20, 20, 1, 1, 20, 20, 20, 20, 20, 1, 20, 1, 1, 1, 20, 1, 20, 1, 20, 1, 20, 20, 1, 72, 72, 72, 72, 72, 1, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 20, 1, 72, 62, 72, 62, 72, 1, 1, 20, 1, 1, 1, 20, 1, 20, 1, 20, 1, 20, 20, 1, 20, 20, 72, 20, 1, 1, 1, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 20, 20, 1, 1, 20, 20, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 20, 20, 1, 1, 1, 1, 1, 1, 1, 20, 20, 20, 20, 20, 20, 20, 1, 20, 1, 1, 20, 20, 1, 1, 20, 1, 1, 20, 1, 48, 47, 1, 1, 1, 1, 1, 1, 20, 1, 1, 20, 20, 1, 20, 20, 20, 20, 20, 1, 34, 34, 34, 30, 34, 34, 34, 34, 34, 1, 1, 20, 20, 1, 1, 1, 1, 1, 1, 1, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20],
5 | "height":20,
6 | "name":"ground",
7 | "opacity":1,
8 | "type":"tilelayer",
9 | "visible":true,
10 | "width":20,
11 | "x":0,
12 | "y":0
13 | }],
14 | "nextobjectid":1,
15 | "orientation":"orthogonal",
16 | "renderorder":"right-down",
17 | "tiledversion":"1.0.3",
18 | "tileheight":32,
19 | "tilesets":[
20 | {
21 | "columns":14,
22 | "firstgid":1,
23 | "image":"gridtiles.png",
24 | "imageheight":320,
25 | "imagewidth":448,
26 | "margin":0,
27 | "name":"tiles",
28 | "spacing":0,
29 | "tilecount":140,
30 | "tileheight":32,
31 | "tileproperties":
32 | {
33 | "103":
34 | {
35 | "collide":true
36 | },
37 | "19":
38 | {
39 | "collide":true
40 | },
41 | "29":
42 | {
43 | "cost":2
44 | },
45 | "33":
46 | {
47 | "collide":true
48 | },
49 | "47":
50 | {
51 | "collide":true
52 | },
53 | "57":
54 | {
55 | "cost":4
56 | },
57 | "61":
58 | {
59 | "collide":true
60 | },
61 | "71":
62 | {
63 | "cost":3
64 | },
65 | "89":
66 | {
67 | "collide":true
68 | }
69 | },
70 | "tilepropertytypes":
71 | {
72 | "103":
73 | {
74 | "collide":"bool"
75 | },
76 | "19":
77 | {
78 | "collide":"bool"
79 | },
80 | "29":
81 | {
82 | "cost":"int"
83 | },
84 | "33":
85 | {
86 | "collide":"bool"
87 | },
88 | "47":
89 | {
90 | "collide":"bool"
91 | },
92 | "57":
93 | {
94 | "cost":"int"
95 | },
96 | "61":
97 | {
98 | "collide":"bool"
99 | },
100 | "71":
101 | {
102 | "cost":"int"
103 | },
104 | "89":
105 | {
106 | "collide":"bool"
107 | }
108 | },
109 | "tilewidth":32
110 | }],
111 | "tilewidth":32,
112 | "type":"map",
113 | "version":1,
114 | "width":20
115 | }
--------------------------------------------------------------------------------
/assets/map.tmx:
--------------------------------------------------------------------------------
1 |
2 |
76 |
--------------------------------------------------------------------------------
/assets/phaserguy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jerenaux/pathfinding_tutorial/1cefa2ef883c19b7335c0e4eb2729cc97c49b8f1/assets/phaserguy.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Pathfinding tutorial
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/js/easystar.js:
--------------------------------------------------------------------------------
1 | var EasyStar =
2 | /******/ (function(modules) { // webpackBootstrap
3 | /******/ // The module cache
4 | /******/ var installedModules = {};
5 |
6 | /******/ // The require function
7 | /******/ function __webpack_require__(moduleId) {
8 |
9 | /******/ // Check if module is in cache
10 | /******/ if(installedModules[moduleId])
11 | /******/ return installedModules[moduleId].exports;
12 |
13 | /******/ // Create a new module (and put it into the cache)
14 | /******/ var module = installedModules[moduleId] = {
15 | /******/ exports: {},
16 | /******/ id: moduleId,
17 | /******/ loaded: false
18 | /******/ };
19 |
20 | /******/ // Execute the module function
21 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22 |
23 | /******/ // Flag the module as loaded
24 | /******/ module.loaded = true;
25 |
26 | /******/ // Return the exports of the module
27 | /******/ return module.exports;
28 | /******/ }
29 |
30 |
31 | /******/ // expose the modules object (__webpack_modules__)
32 | /******/ __webpack_require__.m = modules;
33 |
34 | /******/ // expose the module cache
35 | /******/ __webpack_require__.c = installedModules;
36 |
37 | /******/ // __webpack_public_path__
38 | /******/ __webpack_require__.p = "";
39 |
40 | /******/ // Load entry module and return exports
41 | /******/ return __webpack_require__(0);
42 | /******/ })
43 | /************************************************************************/
44 | /******/ ([
45 | /* 0 */
46 | /***/ (function(module, exports, __webpack_require__) {
47 |
48 | /**
49 | * EasyStar.js
50 | * github.com/prettymuchbryce/EasyStarJS
51 | * Licensed under the MIT license.
52 | *
53 | * Implementation By Bryce Neal (@prettymuchbryce)
54 | **/
55 |
56 | var EasyStar = {};
57 | var Instance = __webpack_require__(1);
58 | var Node = __webpack_require__(2);
59 | var Heap = __webpack_require__(3);
60 |
61 | const CLOSED_LIST = 0;
62 | const OPEN_LIST = 1;
63 |
64 | module.exports = EasyStar;
65 |
66 | var nextInstanceId = 1;
67 |
68 | EasyStar.js = function () {
69 | var STRAIGHT_COST = 1.0;
70 | var DIAGONAL_COST = 1.4;
71 | var syncEnabled = false;
72 | var pointsToAvoid = {};
73 | var collisionGrid;
74 | var costMap = {};
75 | var pointsToCost = {};
76 | var directionalConditions = {};
77 | var allowCornerCutting = true;
78 | var iterationsSoFar;
79 | var instances = {};
80 | var instanceQueue = [];
81 | var iterationsPerCalculation = Number.MAX_VALUE;
82 | var acceptableTiles;
83 | var diagonalsEnabled = false;
84 |
85 | /**
86 | * Sets the collision grid that EasyStar uses.
87 | *
88 | * @param {Array|Number} tiles An array of numbers that represent
89 | * which tiles in your grid should be considered
90 | * acceptable, or "walkable".
91 | **/
92 | this.setAcceptableTiles = function (tiles) {
93 | if (tiles instanceof Array) {
94 | // Array
95 | acceptableTiles = tiles;
96 | } else if (!isNaN(parseFloat(tiles)) && isFinite(tiles)) {
97 | // Number
98 | acceptableTiles = [tiles];
99 | }
100 | };
101 |
102 | /**
103 | * Enables sync mode for this EasyStar instance..
104 | * if you're into that sort of thing.
105 | **/
106 | this.enableSync = function () {
107 | syncEnabled = true;
108 | };
109 |
110 | /**
111 | * Disables sync mode for this EasyStar instance.
112 | **/
113 | this.disableSync = function () {
114 | syncEnabled = false;
115 | };
116 |
117 | /**
118 | * Enable diagonal pathfinding.
119 | */
120 | this.enableDiagonals = function () {
121 | diagonalsEnabled = true;
122 | };
123 |
124 | /**
125 | * Disable diagonal pathfinding.
126 | */
127 | this.disableDiagonals = function () {
128 | diagonalsEnabled = false;
129 | };
130 |
131 | /**
132 | * Sets the collision grid that EasyStar uses.
133 | *
134 | * @param {Array} grid The collision grid that this EasyStar instance will read from.
135 | * This should be a 2D Array of Numbers.
136 | **/
137 | this.setGrid = function (grid) {
138 | collisionGrid = grid;
139 |
140 | //Setup cost map
141 | for (var y = 0; y < collisionGrid.length; y++) {
142 | for (var x = 0; x < collisionGrid[0].length; x++) {
143 | if (!costMap[collisionGrid[y][x]]) {
144 | costMap[collisionGrid[y][x]] = 1;
145 | }
146 | }
147 | }
148 | };
149 |
150 | /**
151 | * Sets the tile cost for a particular tile type.
152 | *
153 | * @param {Number} The tile type to set the cost for.
154 | * @param {Number} The multiplicative cost associated with the given tile.
155 | **/
156 | this.setTileCost = function (tileType, cost) {
157 | costMap[tileType] = cost;
158 | };
159 |
160 | /**
161 | * Sets the an additional cost for a particular point.
162 | * Overrides the cost from setTileCost.
163 | *
164 | * @param {Number} x The x value of the point to cost.
165 | * @param {Number} y The y value of the point to cost.
166 | * @param {Number} The multiplicative cost associated with the given point.
167 | **/
168 | this.setAdditionalPointCost = function (x, y, cost) {
169 | if (pointsToCost[y] === undefined) {
170 | pointsToCost[y] = {};
171 | }
172 | pointsToCost[y][x] = cost;
173 | };
174 |
175 | /**
176 | * Remove the additional cost for a particular point.
177 | *
178 | * @param {Number} x The x value of the point to stop costing.
179 | * @param {Number} y The y value of the point to stop costing.
180 | **/
181 | this.removeAdditionalPointCost = function (x, y) {
182 | if (pointsToCost[y] !== undefined) {
183 | delete pointsToCost[y][x];
184 | }
185 | };
186 |
187 | /**
188 | * Remove all additional point costs.
189 | **/
190 | this.removeAllAdditionalPointCosts = function () {
191 | pointsToCost = {};
192 | };
193 |
194 | /**
195 | * Sets a directional condition on a tile
196 | *
197 | * @param {Number} x The x value of the point.
198 | * @param {Number} y The y value of the point.
199 | * @param {Array.} allowedDirections A list of all the allowed directions that can access
200 | * the tile.
201 | **/
202 | this.setDirectionalCondition = function (x, y, allowedDirections) {
203 | if (directionalConditions[y] === undefined) {
204 | directionalConditions[y] = {};
205 | }
206 | directionalConditions[y][x] = allowedDirections;
207 | };
208 |
209 | /**
210 | * Remove all directional conditions
211 | **/
212 | this.removeAllDirectionalConditions = function () {
213 | directionalConditions = {};
214 | };
215 |
216 | /**
217 | * Sets the number of search iterations per calculation.
218 | * A lower number provides a slower result, but more practical if you
219 | * have a large tile-map and don't want to block your thread while
220 | * finding a path.
221 | *
222 | * @param {Number} iterations The number of searches to prefrom per calculate() call.
223 | **/
224 | this.setIterationsPerCalculation = function (iterations) {
225 | iterationsPerCalculation = iterations;
226 | };
227 |
228 | /**
229 | * Avoid a particular point on the grid,
230 | * regardless of whether or not it is an acceptable tile.
231 | *
232 | * @param {Number} x The x value of the point to avoid.
233 | * @param {Number} y The y value of the point to avoid.
234 | **/
235 | this.avoidAdditionalPoint = function (x, y) {
236 | if (pointsToAvoid[y] === undefined) {
237 | pointsToAvoid[y] = {};
238 | }
239 | pointsToAvoid[y][x] = 1;
240 | };
241 |
242 | /**
243 | * Stop avoiding a particular point on the grid.
244 | *
245 | * @param {Number} x The x value of the point to stop avoiding.
246 | * @param {Number} y The y value of the point to stop avoiding.
247 | **/
248 | this.stopAvoidingAdditionalPoint = function (x, y) {
249 | if (pointsToAvoid[y] !== undefined) {
250 | delete pointsToAvoid[y][x];
251 | }
252 | };
253 |
254 | /**
255 | * Enables corner cutting in diagonal movement.
256 | **/
257 | this.enableCornerCutting = function () {
258 | allowCornerCutting = true;
259 | };
260 |
261 | /**
262 | * Disables corner cutting in diagonal movement.
263 | **/
264 | this.disableCornerCutting = function () {
265 | allowCornerCutting = false;
266 | };
267 |
268 | /**
269 | * Stop avoiding all additional points on the grid.
270 | **/
271 | this.stopAvoidingAllAdditionalPoints = function () {
272 | pointsToAvoid = {};
273 | };
274 |
275 | /**
276 | * Find a path.
277 | *
278 | * @param {Number} startX The X position of the starting point.
279 | * @param {Number} startY The Y position of the starting point.
280 | * @param {Number} endX The X position of the ending point.
281 | * @param {Number} endY The Y position of the ending point.
282 | * @param {Function} callback A function that is called when your path
283 | * is found, or no path is found.
284 | * @return {Number} A numeric, non-zero value which identifies the created instance. This value can be passed to cancelPath to cancel the path calculation.
285 | *
286 | **/
287 | this.findPath = function (startX, startY, endX, endY, callback) {
288 | // Wraps the callback for sync vs async logic
289 | var callbackWrapper = function (result) {
290 | if (syncEnabled) {
291 | callback(result);
292 | } else {
293 | setTimeout(function () {
294 | callback(result);
295 | });
296 | }
297 | };
298 |
299 | // No acceptable tiles were set
300 | if (acceptableTiles === undefined) {
301 | throw new Error("You can't set a path without first calling setAcceptableTiles() on EasyStar.");
302 | }
303 | // No grid was set
304 | if (collisionGrid === undefined) {
305 | throw new Error("You can't set a path without first calling setGrid() on EasyStar.");
306 | }
307 |
308 | // Start or endpoint outside of scope.
309 | if (startX < 0 || startY < 0 || endX < 0 || endY < 0 || startX > collisionGrid[0].length - 1 || startY > collisionGrid.length - 1 || endX > collisionGrid[0].length - 1 || endY > collisionGrid.length - 1) {
310 | throw new Error("Your start or end point is outside the scope of your grid.");
311 | }
312 |
313 | // Start and end are the same tile.
314 | if (startX === endX && startY === endY) {
315 | callbackWrapper([]);
316 | return;
317 | }
318 |
319 | // End point is not an acceptable tile.
320 | var endTile = collisionGrid[endY][endX];
321 | var isAcceptable = false;
322 | for (var i = 0; i < acceptableTiles.length; i++) {
323 | if (endTile === acceptableTiles[i]) {
324 | isAcceptable = true;
325 | break;
326 | }
327 | }
328 |
329 | if (isAcceptable === false) {
330 | callbackWrapper(null);
331 | return;
332 | }
333 |
334 | // Create the instance
335 | var instance = new Instance();
336 | instance.openList = new Heap(function (nodeA, nodeB) {
337 | return nodeA.bestGuessDistance() - nodeB.bestGuessDistance();
338 | });
339 | instance.isDoneCalculating = false;
340 | instance.nodeHash = {};
341 | instance.startX = startX;
342 | instance.startY = startY;
343 | instance.endX = endX;
344 | instance.endY = endY;
345 | instance.callback = callbackWrapper;
346 |
347 | instance.openList.push(coordinateToNode(instance, instance.startX, instance.startY, null, STRAIGHT_COST));
348 |
349 | var instanceId = nextInstanceId++;
350 | instances[instanceId] = instance;
351 | instanceQueue.push(instanceId);
352 | return instanceId;
353 | };
354 |
355 | /**
356 | * Cancel a path calculation.
357 | *
358 | * @param {Number} instanceId The instance ID of the path being calculated
359 | * @return {Boolean} True if an instance was found and cancelled.
360 | *
361 | **/
362 | this.cancelPath = function (instanceId) {
363 | if (instanceId in instances) {
364 | delete instances[instanceId];
365 | // No need to remove it from instanceQueue
366 | return true;
367 | }
368 | return false;
369 | };
370 |
371 | /**
372 | * This method steps through the A* Algorithm in an attempt to
373 | * find your path(s). It will search 4-8 tiles (depending on diagonals) for every calculation.
374 | * You can change the number of calculations done in a call by using
375 | * easystar.setIteratonsPerCalculation().
376 | **/
377 | this.calculate = function () {
378 | if (instanceQueue.length === 0 || collisionGrid === undefined || acceptableTiles === undefined) {
379 | return;
380 | }
381 | for (iterationsSoFar = 0; iterationsSoFar < iterationsPerCalculation; iterationsSoFar++) {
382 | if (instanceQueue.length === 0) {
383 | return;
384 | }
385 |
386 | if (syncEnabled) {
387 | // If this is a sync instance, we want to make sure that it calculates synchronously.
388 | iterationsSoFar = 0;
389 | }
390 |
391 | var instanceId = instanceQueue[0];
392 | var instance = instances[instanceId];
393 | if (typeof instance == 'undefined') {
394 | // This instance was cancelled
395 | instanceQueue.shift();
396 | continue;
397 | }
398 |
399 | // Couldn't find a path.
400 | if (instance.openList.size() === 0) {
401 | instance.callback(null);
402 | delete instances[instanceId];
403 | instanceQueue.shift();
404 | continue;
405 | }
406 |
407 | var searchNode = instance.openList.pop();
408 |
409 | // Handles the case where we have found the destination
410 | if (instance.endX === searchNode.x && instance.endY === searchNode.y) {
411 | var path = [];
412 | path.push({ x: searchNode.x, y: searchNode.y });
413 | var parent = searchNode.parent;
414 | while (parent != null) {
415 | path.push({ x: parent.x, y: parent.y });
416 | parent = parent.parent;
417 | }
418 | path.reverse();
419 | var ip = path;
420 | instance.callback(ip);
421 | delete instances[instanceId];
422 | instanceQueue.shift();
423 | continue;
424 | }
425 |
426 | searchNode.list = CLOSED_LIST;
427 |
428 | if (searchNode.y > 0) {
429 | checkAdjacentNode(instance, searchNode, 0, -1, STRAIGHT_COST * getTileCost(searchNode.x, searchNode.y - 1));
430 | }
431 | if (searchNode.x < collisionGrid[0].length - 1) {
432 | checkAdjacentNode(instance, searchNode, 1, 0, STRAIGHT_COST * getTileCost(searchNode.x + 1, searchNode.y));
433 | }
434 | if (searchNode.y < collisionGrid.length - 1) {
435 | checkAdjacentNode(instance, searchNode, 0, 1, STRAIGHT_COST * getTileCost(searchNode.x, searchNode.y + 1));
436 | }
437 | if (searchNode.x > 0) {
438 | checkAdjacentNode(instance, searchNode, -1, 0, STRAIGHT_COST * getTileCost(searchNode.x - 1, searchNode.y));
439 | }
440 | if (diagonalsEnabled) {
441 | if (searchNode.x > 0 && searchNode.y > 0) {
442 |
443 | if (allowCornerCutting || isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y - 1, searchNode) && isTileWalkable(collisionGrid, acceptableTiles, searchNode.x - 1, searchNode.y, searchNode)) {
444 |
445 | checkAdjacentNode(instance, searchNode, -1, -1, DIAGONAL_COST * getTileCost(searchNode.x - 1, searchNode.y - 1));
446 | }
447 | }
448 | if (searchNode.x < collisionGrid[0].length - 1 && searchNode.y < collisionGrid.length - 1) {
449 |
450 | if (allowCornerCutting || isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y + 1, searchNode) && isTileWalkable(collisionGrid, acceptableTiles, searchNode.x + 1, searchNode.y, searchNode)) {
451 |
452 | checkAdjacentNode(instance, searchNode, 1, 1, DIAGONAL_COST * getTileCost(searchNode.x + 1, searchNode.y + 1));
453 | }
454 | }
455 | if (searchNode.x < collisionGrid[0].length - 1 && searchNode.y > 0) {
456 |
457 | if (allowCornerCutting || isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y - 1, searchNode) && isTileWalkable(collisionGrid, acceptableTiles, searchNode.x + 1, searchNode.y, searchNode)) {
458 |
459 | checkAdjacentNode(instance, searchNode, 1, -1, DIAGONAL_COST * getTileCost(searchNode.x + 1, searchNode.y - 1));
460 | }
461 | }
462 | if (searchNode.x > 0 && searchNode.y < collisionGrid.length - 1) {
463 |
464 | if (allowCornerCutting || isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y + 1, searchNode) && isTileWalkable(collisionGrid, acceptableTiles, searchNode.x - 1, searchNode.y, searchNode)) {
465 |
466 | checkAdjacentNode(instance, searchNode, -1, 1, DIAGONAL_COST * getTileCost(searchNode.x - 1, searchNode.y + 1));
467 | }
468 | }
469 | }
470 | }
471 | };
472 |
473 | // Private methods follow
474 | var checkAdjacentNode = function (instance, searchNode, x, y, cost) {
475 | var adjacentCoordinateX = searchNode.x + x;
476 | var adjacentCoordinateY = searchNode.y + y;
477 |
478 | if ((pointsToAvoid[adjacentCoordinateY] === undefined || pointsToAvoid[adjacentCoordinateY][adjacentCoordinateX] === undefined) && isTileWalkable(collisionGrid, acceptableTiles, adjacentCoordinateX, adjacentCoordinateY, searchNode)) {
479 | var node = coordinateToNode(instance, adjacentCoordinateX, adjacentCoordinateY, searchNode, cost);
480 |
481 | if (node.list === undefined) {
482 | node.list = OPEN_LIST;
483 | instance.openList.push(node);
484 | } else if (searchNode.costSoFar + cost < node.costSoFar) {
485 | node.costSoFar = searchNode.costSoFar + cost;
486 | node.parent = searchNode;
487 | instance.openList.updateItem(node);
488 | }
489 | }
490 | };
491 |
492 | // Helpers
493 | var isTileWalkable = function (collisionGrid, acceptableTiles, x, y, sourceNode) {
494 | var directionalCondition = directionalConditions[y] && directionalConditions[y][x];
495 | if (directionalCondition) {
496 | var direction = calculateDirection(sourceNode.x - x, sourceNode.y - y);
497 | var directionIncluded = function () {
498 | for (var i = 0; i < directionalCondition.length; i++) {
499 | if (directionalCondition[i] === direction) return true;
500 | }
501 | return false;
502 | };
503 | if (!directionIncluded()) return false;
504 | }
505 | for (var i = 0; i < acceptableTiles.length; i++) {
506 | if (collisionGrid[y][x] === acceptableTiles[i]) {
507 | return true;
508 | }
509 | }
510 |
511 | return false;
512 | };
513 |
514 | /**
515 | * -1, -1 | 0, -1 | 1, -1
516 | * -1, 0 | SOURCE | 1, 0
517 | * -1, 1 | 0, 1 | 1, 1
518 | */
519 | var calculateDirection = function (diffX, diffY) {
520 | if (diffX === 0 && diffY === -1) return EasyStar.TOP;else if (diffX === 1 && diffY === -1) return EasyStar.TOP_RIGHT;else if (diffX === 1 && diffY === 0) return EasyStar.RIGHT;else if (diffX === 1 && diffY === 1) return EasyStar.BOTTOM_RIGHT;else if (diffX === 0 && diffY === 1) return EasyStar.BOTTOM;else if (diffX === -1 && diffY === 1) return EasyStar.BOTTOM_LEFT;else if (diffX === -1 && diffY === 0) return EasyStar.LEFT;else if (diffX === -1 && diffY === -1) return EasyStar.TOP_LEFT;
521 | throw new Error('These differences are not valid: ' + diffX + ', ' + diffY);
522 | };
523 |
524 | var getTileCost = function (x, y) {
525 | return pointsToCost[y] && pointsToCost[y][x] || costMap[collisionGrid[y][x]];
526 | };
527 |
528 | var coordinateToNode = function (instance, x, y, parent, cost) {
529 | if (instance.nodeHash[y] !== undefined) {
530 | if (instance.nodeHash[y][x] !== undefined) {
531 | return instance.nodeHash[y][x];
532 | }
533 | } else {
534 | instance.nodeHash[y] = {};
535 | }
536 | var simpleDistanceToTarget = getDistance(x, y, instance.endX, instance.endY);
537 | if (parent !== null) {
538 | var costSoFar = parent.costSoFar + cost;
539 | } else {
540 | costSoFar = 0;
541 | }
542 | var node = new Node(parent, x, y, costSoFar, simpleDistanceToTarget);
543 | instance.nodeHash[y][x] = node;
544 | return node;
545 | };
546 |
547 | var getDistance = function (x1, y1, x2, y2) {
548 | if (diagonalsEnabled) {
549 | // Octile distance
550 | var dx = Math.abs(x1 - x2);
551 | var dy = Math.abs(y1 - y2);
552 | if (dx < dy) {
553 | return DIAGONAL_COST * dx + dy;
554 | } else {
555 | return DIAGONAL_COST * dy + dx;
556 | }
557 | } else {
558 | // Manhattan distance
559 | var dx = Math.abs(x1 - x2);
560 | var dy = Math.abs(y1 - y2);
561 | return dx + dy;
562 | }
563 | };
564 | };
565 |
566 | EasyStar.TOP = 'TOP';
567 | EasyStar.TOP_RIGHT = 'TOP_RIGHT';
568 | EasyStar.RIGHT = 'RIGHT';
569 | EasyStar.BOTTOM_RIGHT = 'BOTTOM_RIGHT';
570 | EasyStar.BOTTOM = 'BOTTOM';
571 | EasyStar.BOTTOM_LEFT = 'BOTTOM_LEFT';
572 | EasyStar.LEFT = 'LEFT';
573 | EasyStar.TOP_LEFT = 'TOP_LEFT';
574 |
575 | /***/ }),
576 | /* 1 */
577 | /***/ (function(module, exports) {
578 |
579 | /**
580 | * Represents a single instance of EasyStar.
581 | * A path that is in the queue to eventually be found.
582 | */
583 | module.exports = function () {
584 | this.pointsToAvoid = {};
585 | this.startX;
586 | this.callback;
587 | this.startY;
588 | this.endX;
589 | this.endY;
590 | this.nodeHash = {};
591 | this.openList;
592 | };
593 |
594 | /***/ }),
595 | /* 2 */
596 | /***/ (function(module, exports) {
597 |
598 | /**
599 | * A simple Node that represents a single tile on the grid.
600 | * @param {Object} parent The parent node.
601 | * @param {Number} x The x position on the grid.
602 | * @param {Number} y The y position on the grid.
603 | * @param {Number} costSoFar How far this node is in moves*cost from the start.
604 | * @param {Number} simpleDistanceToTarget Manhatten distance to the end point.
605 | **/
606 | module.exports = function (parent, x, y, costSoFar, simpleDistanceToTarget) {
607 | this.parent = parent;
608 | this.x = x;
609 | this.y = y;
610 | this.costSoFar = costSoFar;
611 | this.simpleDistanceToTarget = simpleDistanceToTarget;
612 |
613 | /**
614 | * @return {Number} Best guess distance of a cost using this node.
615 | **/
616 | this.bestGuessDistance = function () {
617 | return this.costSoFar + this.simpleDistanceToTarget;
618 | };
619 | };
620 |
621 | /***/ }),
622 | /* 3 */
623 | /***/ (function(module, exports, __webpack_require__) {
624 |
625 | module.exports = __webpack_require__(4);
626 |
627 | /***/ }),
628 | /* 4 */
629 | /***/ (function(module, exports, __webpack_require__) {
630 |
631 | var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Generated by CoffeeScript 1.8.0
632 | (function () {
633 | var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup;
634 |
635 | floor = Math.floor, min = Math.min;
636 |
637 | /*
638 | Default comparison function to be used
639 | */
640 |
641 | defaultCmp = function (x, y) {
642 | if (x < y) {
643 | return -1;
644 | }
645 | if (x > y) {
646 | return 1;
647 | }
648 | return 0;
649 | };
650 |
651 | /*
652 | Insert item x in list a, and keep it sorted assuming a is sorted.
653 |
654 | If x is already in a, insert it to the right of the rightmost x.
655 |
656 | Optional args lo (default 0) and hi (default a.length) bound the slice
657 | of a to be searched.
658 | */
659 |
660 | insort = function (a, x, lo, hi, cmp) {
661 | var mid;
662 | if (lo == null) {
663 | lo = 0;
664 | }
665 | if (cmp == null) {
666 | cmp = defaultCmp;
667 | }
668 | if (lo < 0) {
669 | throw new Error('lo must be non-negative');
670 | }
671 | if (hi == null) {
672 | hi = a.length;
673 | }
674 | while (lo < hi) {
675 | mid = floor((lo + hi) / 2);
676 | if (cmp(x, a[mid]) < 0) {
677 | hi = mid;
678 | } else {
679 | lo = mid + 1;
680 | }
681 | }
682 | return [].splice.apply(a, [lo, lo - lo].concat(x)), x;
683 | };
684 |
685 | /*
686 | Push item onto heap, maintaining the heap invariant.
687 | */
688 |
689 | heappush = function (array, item, cmp) {
690 | if (cmp == null) {
691 | cmp = defaultCmp;
692 | }
693 | array.push(item);
694 | return _siftdown(array, 0, array.length - 1, cmp);
695 | };
696 |
697 | /*
698 | Pop the smallest item off the heap, maintaining the heap invariant.
699 | */
700 |
701 | heappop = function (array, cmp) {
702 | var lastelt, returnitem;
703 | if (cmp == null) {
704 | cmp = defaultCmp;
705 | }
706 | lastelt = array.pop();
707 | if (array.length) {
708 | returnitem = array[0];
709 | array[0] = lastelt;
710 | _siftup(array, 0, cmp);
711 | } else {
712 | returnitem = lastelt;
713 | }
714 | return returnitem;
715 | };
716 |
717 | /*
718 | Pop and return the current smallest value, and add the new item.
719 |
720 | This is more efficient than heappop() followed by heappush(), and can be
721 | more appropriate when using a fixed size heap. Note that the value
722 | returned may be larger than item! That constrains reasonable use of
723 | this routine unless written as part of a conditional replacement:
724 | if item > array[0]
725 | item = heapreplace(array, item)
726 | */
727 |
728 | heapreplace = function (array, item, cmp) {
729 | var returnitem;
730 | if (cmp == null) {
731 | cmp = defaultCmp;
732 | }
733 | returnitem = array[0];
734 | array[0] = item;
735 | _siftup(array, 0, cmp);
736 | return returnitem;
737 | };
738 |
739 | /*
740 | Fast version of a heappush followed by a heappop.
741 | */
742 |
743 | heappushpop = function (array, item, cmp) {
744 | var _ref;
745 | if (cmp == null) {
746 | cmp = defaultCmp;
747 | }
748 | if (array.length && cmp(array[0], item) < 0) {
749 | _ref = [array[0], item], item = _ref[0], array[0] = _ref[1];
750 | _siftup(array, 0, cmp);
751 | }
752 | return item;
753 | };
754 |
755 | /*
756 | Transform list into a heap, in-place, in O(array.length) time.
757 | */
758 |
759 | heapify = function (array, cmp) {
760 | var i, _i, _j, _len, _ref, _ref1, _results, _results1;
761 | if (cmp == null) {
762 | cmp = defaultCmp;
763 | }
764 | _ref1 = function () {
765 | _results1 = [];
766 | for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--) {
767 | _results1.push(_j);
768 | }
769 | return _results1;
770 | }.apply(this).reverse();
771 | _results = [];
772 | for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
773 | i = _ref1[_i];
774 | _results.push(_siftup(array, i, cmp));
775 | }
776 | return _results;
777 | };
778 |
779 | /*
780 | Update the position of the given item in the heap.
781 | This function should be called every time the item is being modified.
782 | */
783 |
784 | updateItem = function (array, item, cmp) {
785 | var pos;
786 | if (cmp == null) {
787 | cmp = defaultCmp;
788 | }
789 | pos = array.indexOf(item);
790 | if (pos === -1) {
791 | return;
792 | }
793 | _siftdown(array, 0, pos, cmp);
794 | return _siftup(array, pos, cmp);
795 | };
796 |
797 | /*
798 | Find the n largest elements in a dataset.
799 | */
800 |
801 | nlargest = function (array, n, cmp) {
802 | var elem, result, _i, _len, _ref;
803 | if (cmp == null) {
804 | cmp = defaultCmp;
805 | }
806 | result = array.slice(0, n);
807 | if (!result.length) {
808 | return result;
809 | }
810 | heapify(result, cmp);
811 | _ref = array.slice(n);
812 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
813 | elem = _ref[_i];
814 | heappushpop(result, elem, cmp);
815 | }
816 | return result.sort(cmp).reverse();
817 | };
818 |
819 | /*
820 | Find the n smallest elements in a dataset.
821 | */
822 |
823 | nsmallest = function (array, n, cmp) {
824 | var elem, i, los, result, _i, _j, _len, _ref, _ref1, _results;
825 | if (cmp == null) {
826 | cmp = defaultCmp;
827 | }
828 | if (n * 10 <= array.length) {
829 | result = array.slice(0, n).sort(cmp);
830 | if (!result.length) {
831 | return result;
832 | }
833 | los = result[result.length - 1];
834 | _ref = array.slice(n);
835 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
836 | elem = _ref[_i];
837 | if (cmp(elem, los) < 0) {
838 | insort(result, elem, 0, null, cmp);
839 | result.pop();
840 | los = result[result.length - 1];
841 | }
842 | }
843 | return result;
844 | }
845 | heapify(array, cmp);
846 | _results = [];
847 | for (i = _j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
848 | _results.push(heappop(array, cmp));
849 | }
850 | return _results;
851 | };
852 |
853 | _siftdown = function (array, startpos, pos, cmp) {
854 | var newitem, parent, parentpos;
855 | if (cmp == null) {
856 | cmp = defaultCmp;
857 | }
858 | newitem = array[pos];
859 | while (pos > startpos) {
860 | parentpos = pos - 1 >> 1;
861 | parent = array[parentpos];
862 | if (cmp(newitem, parent) < 0) {
863 | array[pos] = parent;
864 | pos = parentpos;
865 | continue;
866 | }
867 | break;
868 | }
869 | return array[pos] = newitem;
870 | };
871 |
872 | _siftup = function (array, pos, cmp) {
873 | var childpos, endpos, newitem, rightpos, startpos;
874 | if (cmp == null) {
875 | cmp = defaultCmp;
876 | }
877 | endpos = array.length;
878 | startpos = pos;
879 | newitem = array[pos];
880 | childpos = 2 * pos + 1;
881 | while (childpos < endpos) {
882 | rightpos = childpos + 1;
883 | if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) {
884 | childpos = rightpos;
885 | }
886 | array[pos] = array[childpos];
887 | pos = childpos;
888 | childpos = 2 * pos + 1;
889 | }
890 | array[pos] = newitem;
891 | return _siftdown(array, startpos, pos, cmp);
892 | };
893 |
894 | Heap = function () {
895 | Heap.push = heappush;
896 |
897 | Heap.pop = heappop;
898 |
899 | Heap.replace = heapreplace;
900 |
901 | Heap.pushpop = heappushpop;
902 |
903 | Heap.heapify = heapify;
904 |
905 | Heap.updateItem = updateItem;
906 |
907 | Heap.nlargest = nlargest;
908 |
909 | Heap.nsmallest = nsmallest;
910 |
911 | function Heap(cmp) {
912 | this.cmp = cmp != null ? cmp : defaultCmp;
913 | this.nodes = [];
914 | }
915 |
916 | Heap.prototype.push = function (x) {
917 | return heappush(this.nodes, x, this.cmp);
918 | };
919 |
920 | Heap.prototype.pop = function () {
921 | return heappop(this.nodes, this.cmp);
922 | };
923 |
924 | Heap.prototype.peek = function () {
925 | return this.nodes[0];
926 | };
927 |
928 | Heap.prototype.contains = function (x) {
929 | return this.nodes.indexOf(x) !== -1;
930 | };
931 |
932 | Heap.prototype.replace = function (x) {
933 | return heapreplace(this.nodes, x, this.cmp);
934 | };
935 |
936 | Heap.prototype.pushpop = function (x) {
937 | return heappushpop(this.nodes, x, this.cmp);
938 | };
939 |
940 | Heap.prototype.heapify = function () {
941 | return heapify(this.nodes, this.cmp);
942 | };
943 |
944 | Heap.prototype.updateItem = function (x) {
945 | return updateItem(this.nodes, x, this.cmp);
946 | };
947 |
948 | Heap.prototype.clear = function () {
949 | return this.nodes = [];
950 | };
951 |
952 | Heap.prototype.empty = function () {
953 | return this.nodes.length === 0;
954 | };
955 |
956 | Heap.prototype.size = function () {
957 | return this.nodes.length;
958 | };
959 |
960 | Heap.prototype.clone = function () {
961 | var heap;
962 | heap = new Heap();
963 | heap.nodes = this.nodes.slice(0);
964 | return heap;
965 | };
966 |
967 | Heap.prototype.toArray = function () {
968 | return this.nodes.slice(0);
969 | };
970 |
971 | Heap.prototype.insert = Heap.prototype.push;
972 |
973 | Heap.prototype.top = Heap.prototype.peek;
974 |
975 | Heap.prototype.front = Heap.prototype.peek;
976 |
977 | Heap.prototype.has = Heap.prototype.contains;
978 |
979 | Heap.prototype.copy = Heap.prototype.clone;
980 |
981 | return Heap;
982 | }();
983 |
984 | (function (root, factory) {
985 | if (true) {
986 | return !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
987 | } else if (typeof exports === 'object') {
988 | return module.exports = factory();
989 | } else {
990 | return root.Heap = factory();
991 | }
992 | })(this, function () {
993 | return Heap;
994 | });
995 | }).call(this);
996 |
997 | /***/ })
998 | /******/ ]);
--------------------------------------------------------------------------------
/js/game.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Jerome Renaux (jerome.renaux@gmail.com) on 25-02-18.
3 | */
4 | var Game = {};
5 |
6 | Game.preload = function(){
7 | Game.scene = this; // Handy reference to the scene (alternative to `this` binding)
8 | this.load.image('tileset', 'assets/gridtiles.png');
9 | this.load.tilemapTiledJSON('map', 'assets/map.json');
10 | this.load.image('phaserguy', 'assets/phaserguy.png');
11 | };
12 |
13 | Game.create = function(){
14 | // Handles the clicks on the map to make the character move
15 | this.input.on('pointerup',Game.handleClick);
16 |
17 | Game.camera = this.cameras.main;
18 | Game.camera.setBounds(0, 0, 20*32, 20*32);
19 |
20 | var phaserGuy = this.add.image(32,32,'phaserguy');
21 | phaserGuy.setDepth(1);
22 | phaserGuy.setOrigin(0,0.5);
23 | Game.camera.startFollow(phaserGuy);
24 | Game.player = phaserGuy;
25 |
26 | // Display map
27 | Game.map = Game.scene.make.tilemap({ key: 'map'});
28 | // The first parameter is the name of the tileset in Tiled and the second parameter is the key
29 | // of the tileset image used when loading the file in preload.
30 | var tiles = Game.map.addTilesetImage('tiles', 'tileset');
31 | Game.map.createStaticLayer(0, tiles, 0,0);
32 |
33 | // Marker that will follow the mouse
34 | Game.marker = this.add.graphics();
35 | Game.marker.lineStyle(3, 0xffffff, 1);
36 | Game.marker.strokeRect(0, 0, Game.map.tileWidth, Game.map.tileHeight);
37 |
38 | // ### Pathfinding stuff ###
39 | // Initializing the pathfinder
40 | Game.finder = new EasyStar.js();
41 |
42 | // We create the 2D array representing all the tiles of our map
43 | var grid = [];
44 | for(var y = 0; y < Game.map.height; y++){
45 | var col = [];
46 | for(var x = 0; x < Game.map.width; x++){
47 | // In each cell we store the ID of the tile, which corresponds
48 | // to its index in the tileset of the map ("ID" field in Tiled)
49 | col.push(Game.getTileID(x,y));
50 | }
51 | grid.push(col);
52 | }
53 | Game.finder.setGrid(grid);
54 |
55 | var tileset = Game.map.tilesets[0];
56 | var properties = tileset.tileProperties;
57 | var acceptableTiles = [];
58 |
59 | // We need to list all the tile IDs that can be walked on. Let's iterate over all of them
60 | // and see what properties have been entered in Tiled.
61 | for(var i = tileset.firstgid-1; i < tiles.total; i++){ // firstgid and total are fields from Tiled that indicate the range of IDs that the tiles can take in that tileset
62 | if(!properties.hasOwnProperty(i)) {
63 | // If there is no property indicated at all, it means it's a walkable tile
64 | acceptableTiles.push(i+1);
65 | continue;
66 | }
67 | if(!properties[i].collide) acceptableTiles.push(i+1);
68 | if(properties[i].cost) Game.finder.setTileCost(i+1, properties[i].cost); // If there is a cost attached to the tile, let's register it
69 | }
70 | Game.finder.setAcceptableTiles(acceptableTiles);
71 | };
72 |
73 | Game.update = function(){
74 | var worldPoint = this.input.activePointer.positionToCamera(this.cameras.main);
75 |
76 | // Rounds down to nearest tile
77 | var pointerTileX = Game.map.worldToTileX(worldPoint.x);
78 | var pointerTileY = Game.map.worldToTileY(worldPoint.y);
79 | Game.marker.x = Game.map.tileToWorldX(pointerTileX);
80 | Game.marker.y = Game.map.tileToWorldY(pointerTileY);
81 | Game.marker.setVisible(!Game.checkCollision(pointerTileX,pointerTileY));
82 | };
83 |
84 | Game.checkCollision = function(x,y){
85 | var tile = Game.map.getTileAt(x, y);
86 | return tile.properties.collide == true;
87 | };
88 |
89 | Game.getTileID = function(x,y){
90 | var tile = Game.map.getTileAt(x, y);
91 | return tile.index;
92 | };
93 |
94 | Game.handleClick = function(pointer){
95 | var x = Game.camera.scrollX + pointer.x;
96 | var y = Game.camera.scrollY + pointer.y;
97 | var toX = Math.floor(x/32);
98 | var toY = Math.floor(y/32);
99 | var fromX = Math.floor(Game.player.x/32);
100 | var fromY = Math.floor(Game.player.y/32);
101 | console.log('going from ('+fromX+','+fromY+') to ('+toX+','+toY+')');
102 |
103 | Game.finder.findPath(fromX, fromY, toX, toY, function( path ) {
104 | if (path === null) {
105 | console.warn("Path was not found.");
106 | } else {
107 | console.log(path);
108 | Game.moveCharacter(path);
109 | }
110 | });
111 | Game.finder.calculate(); // don't forget, otherwise nothing happens
112 | };
113 |
114 | Game.moveCharacter = function(path){
115 | // Sets up a list of tweens, one for each tile to walk, that will be chained by the timeline
116 | var tweens = [];
117 | for(var i = 0; i < path.length-1; i++){
118 | var ex = path[i+1].x;
119 | var ey = path[i+1].y;
120 | tweens.push({
121 | targets: Game.player,
122 | x: {value: ex*Game.map.tileWidth, duration: 200},
123 | y: {value: ey*Game.map.tileHeight, duration: 200}
124 | });
125 | }
126 |
127 | Game.scene.tweens.timeline({
128 | tweens: tweens
129 | });
130 | };
--------------------------------------------------------------------------------
/js/main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Jerome Renaux (jerome.renaux@gmail.com) on 25-02-18.
3 | */
4 | var config = {
5 | type: Phaser.AUTO,
6 | width: 20*32,
7 | height: 20*32,
8 | parent: 'game',
9 | scene: [Game]
10 | };
11 |
12 | var game = new Phaser.Game(config);
13 |
--------------------------------------------------------------------------------