UNPKG

shaku

Version:

A simple and effective JavaScript game development framework that knows its place!

264 lines (224 loc) 9.19 kB
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Shaku</title> <meta name="description" content="Shaku - a simple and easy-to-use javascript library for videogame programming."> <meta name="author" content="Ronen Ness"> <link href="css/style.css" rel="stylesheet" type="text/css" media="all"> </head> <body> <div class="noselect"> <button class="view-code-btn" onclick="showSampleCode();">View Code</button> <h1 class="demo-title">Shaku Miscs Demo: Path Finding</h1> <p>This demo show how to use the built-in PathFinder utility.<br /> <b>Click</b> on tiles to change their type and a new path will be calculated few moments after (path displayed as red dots). <br/> <b>Hold Right Mouse Button</b> to paint blocking tiles.<br/> <br /><b>Tile colors:</b> <ul> <li>Black tiles are blocking.</li> <li style="color:green">Green tiles are cheapest to walk on.</li> <li style="color:purple">Purple tiles slightly more expensive.</li> <li style="color:orange">Orange tiles are expensive to walk on.</li> </ul> </p> <div> <input type="checkbox" id="allowDiagonal" name="allowDiagonal" checked> <label for="allowDiagonal">Allow Diagonal</label> </div> <br /> <!-- include shaku --> <script src="js/demos.js"></script> <script src="js/shaku.js"></script> <!-- demo code --> <script id="main-code"> async function runGame() { // make shaku only listen to gfx canvas Shaku.input.setTargetElement(() => Shaku.gfx.canvas); // init shaku await Shaku.init(); // add shaku's canvas to document and set resolution to 800x600 document.body.appendChild(Shaku.gfx.canvas); Shaku.gfx.setResolution(900, 900, true); // create shapes batch let shapesBatch = new Shaku.gfx.ShapesBatch(); // some consts const tilemapSize = 45; const tileSize = 900 / 45; const tileColors = [Shaku.utils.Color.black, Shaku.utils.Color.green, Shaku.utils.Color.purple, Shaku.utils.Color.orange]; // create tilemap let tiles = []; for (let i = 0; i < tilemapSize; ++i) { tiles.push([]); for (let j = 0; j < tilemapSize; ++j) { tiles[i].push(1); } } // a grid provider that simple use a 2d array of numbers to represent tile types. // tile type 0 = walls / out of bounds. // other types = walkable. class GridProvider extends Shaku.utils.PathFinder.IGrid { constructor(grid) { super(); this.grid = grid; } // blocking tiles = type 0, or out of bounds. isBlocked(_from, _to) { return !Boolean(this.getType(_to)); } // get price: price = tile type getPrice(_index) { return this.getType(_index) * 5; } // get type from index. // return 0 (block) for out of bounds. getType(index) { if (!this.grid[index.x]) { return 0; } return this.grid[index.x][index.y] || 0; } } const gridProvider = new GridProvider(tiles); // current path let currPath = null; // changing diagonal will recalculate document.getElementById("allowDiagonal").onclick = () => { currPath = null; } // time to recalc path let recalcPathTimeout = null; // do a single main loop step function step() { // start new frame and clear screen Shaku.startFrame(); Shaku.gfx.clear(Shaku.utils.Color.gray); shapesBatch.begin("opaque"); // check if need to calculate a new path if (!currPath) { const allowDiagonal = document.getElementById("allowDiagonal").checked; currPath = Shaku.utils.PathFinder.findPath(gridProvider, {x:4, y:4}, {x:40, y:40}, {allowDiagonal: allowDiagonal}) || []; } // draw the map for (let i = 0; i < 45; ++i) { for (let j = 0; j < 45; ++j) { let type = tiles[i][j]; let rect = new Shaku.utils.Rectangle(i * tileSize + 1, j * tileSize + 1, tileSize - 2, tileSize - 2); let color = tileColors[type]; shapesBatch.drawRectangle(rect, color); } } // draw start and end position shapesBatch.drawCircle(new Shaku.utils.Circle(new Shaku.utils.Vector2(tileSize * 4.5, tileSize * 4.5), tileSize / 2), Shaku.utils.Color.red); shapesBatch.drawCircle(new Shaku.utils.Circle(new Shaku.utils.Vector2(tileSize * 40.5, tileSize * 40.5), tileSize / 2), Shaku.utils.Color.red); // draw path if (currPath) { for (let point of currPath) { let currDot = new Shaku.utils.Circle(new Shaku.utils.Vector2(tileSize * (point.x + 0.5), tileSize * (point.y + 0.5)), tileSize / 3); shapesBatch.drawCircle(currDot, Shaku.utils.Color.red); } } // change tiles if (Shaku.input.released('mouse_left') || Shaku.input.down('mouse_right')) { // remove current path currPath = []; // set tile let indexX = Math.floor(Shaku.input.mousePosition.x / tileSize); let indexY = Math.floor(Shaku.input.mousePosition.y / tileSize); if (indexX >= 0 && indexX < tilemapSize && indexY >= 0 && indexY < tilemapSize) { if (Shaku.input.down('mouse_right')) { tiles[indexX][indexY] = 0; } else { tiles[indexX][indexY]++; if (tiles[indexX][indexY] >= tileColors.length) { tiles[indexX][indexY] = 0; } } } // reset timeout to calc new path if (recalcPathTimeout) { clearTimeout(recalcPathTimeout); } recalcPathTimeout = setTimeout(() => { currPath = null; recalcPathTimeout = null; }, 100); } // draw path shapesBatch.end(); // end frame and request next frame Shaku.endFrame(); Shaku.requestAnimationFrame(step); } // start main loop step(); } runGame(); </script> </div> <!-- code example part --> <div id="sample-code-modal" class="modal"> <div class="modal__overlay jsOverlay"></div> <div class="modal__container"> <p class="noselect">The following shows a basic path finding example code.</p> <pre><code id="code-box" class="language-js">// a grid provider that simple use a 2d array of numbers to represent tile types. // tile type 0 = blocking / out of bounds. // other types = walkable, price = type index. class GridProvider extends Shaku.utils.PathFinder.IGrid { constructor(grid) { super(); this.grid = grid; } // blocking tiles = type 0, or out of bounds. isBlocked(_from, _to) { return !Boolean(this.getType(_to)); } // get price: price = tile type getPrice(_index) { return this.getType(_index); } // get type from index. // return 0 (block) for out of bounds. getType(index) { if (!this.grid[index.x]) { return 0; } return this.grid[index.x][index.y] || 0; } } // create grid let grid = [[1,2,1,1,1,1,1,1,1,1,1,1], [1,2,1,1,1,1,1,1,1,1,1,1], [1,1,1,0,1,1,1,1,1,1,1,1], [1,1,1,0,0,0,1,1,1,1,1,1], [1,1,1,1,1,0,1,1,1,1,1,1], [1,1,1,2,2,0,1,0,0,0,1,1], [1,1,1,2,1,0,1,0,1,1,1,1], [1,1,1,1,1,1,1,0,1,1,1,1], [1,1,0,1,1,1,1,0,1,2,1,1], [1,1,0,1,1,0,0,0,1,2,2,1], [1,1,0,1,1,1,1,1,1,2,2,1], [1,1,0,1,1,1,1,1,1,1,2,1]]; let gridProvider = new GridProvider(grid); // find path from top-left to bottom-right let path = Shaku.utils.PathFinder.findPath(gridProvider, {x:0, y:0}, {x:11, y:11}, {allowDiagonal: true});</code></pre> <button class="modal__close" onclick="closeModal('sample-code-modal')">&#10005;</button> </div> </div> <link href="prism/prism.css" rel="stylesheet" /> <script src="prism/prism.js"></script> </body> </html>