diff --git a/public/index.html b/public/index.html index fdc713e..1f9d708 100644 --- a/public/index.html +++ b/public/index.html @@ -1,8 +1,9 @@ - + - + + Amogus factory @@ -14,8 +15,9 @@ + - + diff --git a/src/App.svelte b/src/App.svelte index 0e323a6..cab2516 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -74,9 +74,6 @@ ambientSound = s.ambient; musicSound = s.music; }); - - import {search} from "./Test"; - search();
diff --git a/src/Game/Game.js b/src/Game/Game.js index 49496c4..4f62baf 100644 --- a/src/Game/Game.js +++ b/src/Game/Game.js @@ -20,6 +20,7 @@ import { WorldChunksVisibilityUpdater } from "./WorldGeneration/WorldChunksVisib import { NPCProto } from "./NPC/NPCProto/NPCProto"; import { NPCController } from "./NPC/NPCController/NPCController"; import { PathFinder } from "./Utils/PathFinding.utils"; +import { getNavigationGridTile } from "./World/NavigationGrid/NavigationGrid"; export function generateWorld() { @@ -63,10 +64,11 @@ function setupInGameSelector() { BC_VIEWPORT.onpointerdown = (e) => { let t = screenToWorldCoordinates(e.data.global.x, e.data.global.y); + // console.log(getNavigationGridTile(t.x, t.y)); // npccc.moveTo(new Point2D(getTileAt(t.x, t.y, ChunkStorageTypes.TYPE_TERRAIN).worldPosition.getX(), getTileAt(t.x, t.y, ChunkStorageTypes.TYPE_TERRAIN).worldPosition.getY())); - let tile = getTileAt(t.x, t.y, ChunkStorageTypes.TYPE_TERRAIN); - if(tile.props.navigationCost > 100) return; - npccc.moveTo(new PointInt2D(tile.worldPosition.getX(), tile.worldPosition.getY()), + let tile = getNavigationGridTile(t.x, t.y); + if(tile.isObstacle) return; + npccc.moveTo(new PointInt2D(tile.position.getX(), tile.position.getY()), (cb)=>{ console.log(cb); }); diff --git a/src/Game/NPC/NPCController/NPCController.js b/src/Game/NPC/NPCController/NPCController.js index 6274b32..5ecc741 100644 --- a/src/Game/NPC/NPCController/NPCController.js +++ b/src/Game/NPC/NPCController/NPCController.js @@ -1,6 +1,7 @@ import { GameObject } from "../../GameObject/GameObject"; import { PointInt2D } from "../../Utils/Math.utils"; import { NavigationPath, PathFinder } from "../../Utils/PathFinding.utils"; +import { findPathOnNavigationGridIfExists } from "../../World/NavigationGrid/NavigationGrid"; import { NPCProto } from "../NPCProto/NPCProto"; /** * NPCController defines NPC behavior. Many NPC can have same NPCController for the same behavior. @@ -33,24 +34,28 @@ export class NPCController extends GameObject */ moveTo(position, callback) { - let pf = new PathFinder(); - let nPath = pf.search(new PointInt2D(this.controlledNPC.worldPosition.getX(), this.controlledNPC.worldPosition.getY()), position); - console.log(nPath); - if(nPath.error) - { - callback("failed"); - return; - } - else if (nPath.result.path.length < 2) - { - callback("success"); - return; - } - for (let i = nPath.result.path.length-1; i > 0; i--) { - this.navigationPathQueue.push(nPath.result.path[i]); - } - this.navigationCallback = callback; - this.navigationInProgress = true; + // let pf = new PathFinder(); + // let nPath = pf.findPathIfExist(new PointInt2D(this.controlledNPC.worldPosition.getX(), this.controlledNPC.worldPosition.getY()), position); + let nPath = findPathOnNavigationGridIfExists(new PointInt2D(this.controlledNPC.worldPosition.getX(), this.controlledNPC.worldPosition.getY()), position); + nPath.then((r)=>{ + if(r.error) + { + callback("failed"); + return; + } + else if (r.result.path.length < 2) + { + callback("success"); + return; + } + for (let i = r.result.path.length-1; i > 0; i--) { + this.navigationPathQueue.push(r.result.path[i]); + } + this.navigationCallback = callback; + this.navigationInProgress = true; + }); + // console.log("boba"); + // console.log(nPath); } /** diff --git a/src/Game/Utils/PathFinding.utils.js b/src/Game/Utils/PathFinding.utils.js index c0bebe5..7c9ba61 100644 --- a/src/Game/Utils/PathFinding.utils.js +++ b/src/Game/Utils/PathFinding.utils.js @@ -4,14 +4,19 @@ import { getTileAt } from "../WorldGeneration/WorldGen"; import { PointInt2D } from "./Math.utils"; import { SceneObject } from "../SceneObjects/SceneObject"; import { getNavigationGridTile } from "../World/NavigationGrid/NavigationGrid"; +import { TerrainTile } from "../WorldGeneration/WorldObjects/TerrainTile/TerrainTile"; class PathFinderNode { position; - fScore = 0; + fScore = 1e16; gScore = 1e16; dScore = 1; // hScore = 1e16; id; + /** + * @type {TerrainTile} + */ + ref; /** * @@ -77,12 +82,32 @@ export class PathFinder { _goal = new PointInt2D(); _start = new PointInt2D(); + #MAX_PATH_ITERATIONS = 10000; + #iterationsCounter = 0; + + // findPathIfExist(start, goal) + // { + // let resultFromGoal = this.search(goal, start); + // if(resultFromGoal.state !== 1) + // { + // return resultFromGoal; + // } + // else + // { + // resultFromGoal.result.path.reverse(); + // return resultFromGoal; + // } + // } + /** * * @param {PointInt2D} start * @param {PointInt2D} goal */ search(start, goal) { + this._openSet = []; + this._closedSet = []; + this.#iterationsCounter = 0; start.divideBy(BC_TERRAIN_SETTINGS.totalSize); start.multiplyBy(BC_TERRAIN_SETTINGS.totalSize); this._start = start; @@ -90,6 +115,7 @@ export class PathFinder { goal.divideBy(BC_TERRAIN_SETTINGS.totalSize); goal.multiplyBy(BC_TERRAIN_SETTINGS.totalSize); this._goal = goal; + // getNavigationGridTile(goal.getX(), goal.getY()).optionalTerrainTileRef.drawObject.tint = 0x0000FF; this._openSet.push(new PathFinderNode(this._start, 0, this._heuristic(this._start, 0))); @@ -110,28 +136,29 @@ export class PathFinder { /** * @type Map */ - let cameFrom = new Array(); + let cameFrom = new Map(); let minFScoreNodeIndex = 0; //START LOOP WOOOOAAAW - // console.log(goal); while (this._openSet.length > 0) { + this.#iterationsCounter++; + if(this.#iterationsCounter >= this.#MAX_PATH_ITERATIONS) + { + return new NavigationResult(true, "reached limit", new NavigationPath(), 2); + } //find node with min f score //better to rewrite it to priority queue + minFScoreNodeIndex = 0; for (let i = 0; i < this._openSet.length; i++) { - // if(!this._openSet[i] || !this._openSet[minFScoreNodeIndex]){ - // return new NavigationResult(true, "Failed to access element in openSet", undefined); - // } if (this._openSet[i].fScore < this._openSet[minFScoreNodeIndex].fScore) minFScoreNodeIndex = i; } //wow! node is found and set!! currentNode = this._openSet[minFScoreNodeIndex]; - minFScoreNodeIndex = 0; + // if (currentNode.ref) currentNode.ref.drawObject.tint = 0xff0000; if (PointInt2D.isEqual(currentNode.position, this._goal)) { //wow!!! we have found an end of the path!!! this is so cool!!! - // console.log(cameFrom); - return new NavigationResult(false, "", new NavigationPath(this._reconstructPath(cameFrom, currentNode))); //return something weird stuff + return new NavigationResult(false, "", new NavigationPath(this._reconstructPath(cameFrom, currentNode)), 1); //return something weird stuff } //and now we must delete this node... what a sad situation... @@ -144,24 +171,20 @@ export class PathFinder { */ let currentNeighbors = this._getNeighbors(currentNode.position); for (const neighbor of currentNeighbors) { - let tentativeGScore = currentNode.gScore + this._manhattanDistance(currentNode.position, neighbor.position); if (this._existsInClosedSet(neighbor)) continue; - this._closedSet.push(neighbor); - if (tentativeGScore < neighbor.gScore) { - cameFrom.push({ id: neighbor.id, node: currentNode }); + let tentativeGScore = currentNode.gScore + this._manhattanDistance(currentNode.position, neighbor.position); + if (tentativeGScore < neighbor.gScore && !this._existsInOpenSet(neighbor)) { + cameFrom.set(neighbor.id, currentNode); neighbor.gScore = tentativeGScore; - neighbor.fScore = tentativeGScore + this._heuristic(neighbor.position, neighbor.dScore)*0; - - if(!this._existsInOpenSet(neighbor)) - { + neighbor.fScore = tentativeGScore + this._heuristic(neighbor.position, neighbor.dScore); + if (!this._existsInOpenSet(neighbor)) { this._openSet.push(neighbor); } } } - // console.log([...this._openSet]); } - return new NavigationResult(false, "", new NavigationPath()); + return new NavigationResult(false, "", new NavigationPath(), 0); } /** @@ -172,12 +195,12 @@ export class PathFinder { _reconstructPath(cameFrom, current) { let totalPath = [current.position]; console.log(cameFrom); - // let keys = [...cameFrom.keys()]; - // while (keys.includes(current.id)) { - // if (current.id === cameFrom.get(current.id).id) break; - // current = cameFrom.get(current.id); - // totalPath.push(current.position); - // } + let keys = [...cameFrom.keys()]; + while (keys.includes(current.id)) { + if (current.id === cameFrom.get(current.id).id) break; + current = cameFrom.get(current.id); + totalPath.push(current.position); + } return totalPath.reverse(); } @@ -194,38 +217,66 @@ export class PathFinder { let currentTile = getNavigationGridTile(root.getX(), root.getY() + BC_TERRAIN_SETTINGS.totalSize); let node; if (currentTile && !currentTile.isObstacle) { - currentTile.optionalTerrainTileRef.drawObject.tint = 0xff0000; + // currentTile.optionalTerrainTileRef.drawObject.tint = 0x0000ff; node = new PathFinderNode(currentTile.position, 1e16, 1e16, currentTile.movementCost); - if (/*!this._existsInClosedSet(node) &&*/ !currentTile.isObstacle) { - neighbors.push(node); - } + node.ref = currentTile.optionalTerrainTileRef; + neighbors.push(node); } + //north-e + // currentTile = getNavigationGridTile(root.getX() + BC_TERRAIN_SETTINGS.totalSize, root.getY() + BC_TERRAIN_SETTINGS.totalSize); + // if (currentTile && !currentTile.isObstacle) { + // // currentTile.optionalTerrainTileRef.drawObject.tint = 0x0000ff; + // node = new PathFinderNode(currentTile.position, 1e16, 1e16, currentTile.movementCost); + // node.ref = currentTile.optionalTerrainTileRef; + // neighbors.push(node); + // } + //north-w + // currentTile = getNavigationGridTile(root.getX() - BC_TERRAIN_SETTINGS.totalSize, root.getY() + BC_TERRAIN_SETTINGS.totalSize); + // if (currentTile && !currentTile.isObstacle) { + // // currentTile.optionalTerrainTileRef.drawObject.tint = 0x0000ff; + // node = new PathFinderNode(currentTile.position, 1e16, 1e16, currentTile.movementCost); + // node.ref = currentTile.optionalTerrainTileRef; + // neighbors.push(node); + // } //south currentTile = getNavigationGridTile(root.getX(), root.getY() - BC_TERRAIN_SETTINGS.totalSize); if (currentTile && !currentTile.isObstacle) { - currentTile.optionalTerrainTileRef.drawObject.tint = 0xff0000; + // currentTile.optionalTerrainTileRef.drawObject.tint = 0x0000ff; node = new PathFinderNode(currentTile.position, 1e16, 1e16, currentTile.movementCost); - if (/*!this._existsInClosedSet(node) &&*/ !currentTile.isObstacle) { - neighbors.push(node); - } + node.ref = currentTile.optionalTerrainTileRef; + neighbors.push(node); } + //south-e + // currentTile = getNavigationGridTile(root.getX() + BC_TERRAIN_SETTINGS.totalSize, root.getY() - BC_TERRAIN_SETTINGS.totalSize); + // if (currentTile && !currentTile.isObstacle) { + // // currentTile.optionalTerrainTileRef.drawObject.tint = 0x0000ff; + // node = new PathFinderNode(currentTile.position, 1e16, 1e16, currentTile.movementCost); + // node.ref = currentTile.optionalTerrainTileRef; + // neighbors.push(node); + // } + //south-w + // currentTile = getNavigationGridTile(root.getX() - BC_TERRAIN_SETTINGS.totalSize, root.getY() - BC_TERRAIN_SETTINGS.totalSize); + // if (currentTile && !currentTile.isObstacle) { + // // currentTile.optionalTerrainTileRef.drawObject.tint = 0x0000ff; + // node = new PathFinderNode(currentTile.position, 1e16, 1e16, currentTile.movementCost); + // node.ref = currentTile.optionalTerrainTileRef; + // neighbors.push(node); + // } //east currentTile = getNavigationGridTile(root.getX() + BC_TERRAIN_SETTINGS.totalSize, root.getY()); if (currentTile && !currentTile.isObstacle) { - currentTile.optionalTerrainTileRef.drawObject.tint = 0xff0000; + // currentTile.optionalTerrainTileRef.drawObject.tint = 0x0000ff; node = new PathFinderNode(currentTile.position, 1e16, 1e16, currentTile.movementCost); - if (/*!this._existsInClosedSet(node) &&*/ !currentTile.isObstacle) { - neighbors.push(node); - } + node.ref = currentTile.optionalTerrainTileRef; + neighbors.push(node); } //west currentTile = getNavigationGridTile(root.getX() - BC_TERRAIN_SETTINGS.totalSize, root.getY()); if (currentTile && !currentTile.isObstacle) { - currentTile.optionalTerrainTileRef.drawObject.tint = 0xff0000; + // currentTile.optionalTerrainTileRef.drawObject.tint = 0x0000ff; node = new PathFinderNode(currentTile.position, 1e16, 1e16, currentTile.movementCost); - if (/*!this._existsInClosedSet(node) &&*/ !currentTile.isObstacle) { - neighbors.push(node); - } + node.ref = currentTile.optionalTerrainTileRef; + neighbors.push(node); } return neighbors; } @@ -234,12 +285,24 @@ export class PathFinder { * * @param {PointInt2D} start * @param {PointInt2D} goal - * @returns + * @returns {Number} */ _manhattanDistance(start, goal) { return Math.abs(start.getX() - goal.getX()) + Math.abs(start.getY() - goal.getY()); } + /** + * + * @param {PointInt2D} start + * @param {PointInt2D} goal + * @returns {Number} + */ + _euclideanDistance(start, goal) { + let dx = Math.abs(start.getX() - goal.getX()); + let dy = Math.abs(start.getY() - goal.getY()); + return Math.sqrt(dx * dx + dy * dy); + } + /** * * @param {PointInt2D} current @@ -251,7 +314,8 @@ export class PathFinder { let x2 = this._start.getX() - this._goal.getX(); let y2 = this._start.getY() - this._goal.getY(); let cross = Math.abs(x1 * y2 - x2 * y1); - return this._manhattanDistance(current, this._goal) * d; + // console.log("cross", cross); + return this._manhattanDistance(current, this._goal) * d + cross * 0.00001; } /** diff --git a/src/Game/World/NavigationGrid/NavigationGrid.js b/src/Game/World/NavigationGrid/NavigationGrid.js index 69cbeb5..af32fc0 100644 --- a/src/Game/World/NavigationGrid/NavigationGrid.js +++ b/src/Game/World/NavigationGrid/NavigationGrid.js @@ -1,4 +1,5 @@ import { PointInt2D } from "../../Utils/Math.utils"; +import { PathFinder } from "../../Utils/PathFinding.utils"; import { getWorldChunkAt, worldCoordinatesToChunkLocalCoordinates } from "../../WorldGeneration/WorldGen"; import { TerrainTile } from "../../WorldGeneration/WorldObjects/TerrainTile/TerrainTile"; @@ -80,25 +81,30 @@ export class NavigationGridChunk } -export class NavigationGrid -{ +// export class NavigationGrid +// { - /** - * - * @param {Number} xWorld - * @param {Number} yWorld - */ - static getNavigationGridTile(xWorld, yWorld) - { - let chunk = getWorldChunkAt(xWorld, yWorld); - if(chunk) - { - return chunk.navigationGridChunk.getFromChunk(xWorld, yWorld); - } - return undefined; - } -} +// /** +// * +// * @param {Number} xWorld +// * @param {Number} yWorld +// */ +// static getNavigationGridTile(xWorld, yWorld) +// { +// let chunk = getWorldChunkAt(xWorld, yWorld); +// if(chunk) +// { +// return chunk.navigationGridChunk.getFromChunk(xWorld, yWorld); +// } +// return undefined; +// } +// } +/** + * @param {Number} xWorld + * @param {Number} yWorld + * @returns + */ export function getNavigationGridTile(xWorld, yWorld) { let chunk = getWorldChunkAt(xWorld, yWorld); @@ -107,4 +113,27 @@ export function getNavigationGridTile(xWorld, yWorld) return chunk.navigationGridChunk.getFromChunk(xWorld, yWorld); } return undefined; +} + +function NavigationGridPathFinderCallback(result) { + +} + +async function _findPathAsync(start, goal) +{ + let pf = new PathFinder(); + return pf.search(start, goal); +} +/** + * @param {PointInt2D} start + * @param {PointInt2D} goal + */ +export async function findPathOnNavigationGridIfExists(start, goal) { + let r0 = await _findPathAsync(goal, start); + if(!r0.error){ + r0.result.path.reverse(); + return r0; + } + let r1 = await _findPathAsync(start, goal); + return r1; } \ No newline at end of file diff --git a/src/Game/WorldGeneration/WorldGen.js b/src/Game/WorldGeneration/WorldGen.js index d754afd..60356f2 100644 --- a/src/Game/WorldGeneration/WorldGen.js +++ b/src/Game/WorldGeneration/WorldGen.js @@ -8,7 +8,7 @@ import { Point2D, PointInt2D, clampNumber } from "../Utils/Math.utils"; import { addGameObjectToGameState } from "../GameState/GameState"; import { VegetationTile, VegetationTileProps } from "./WorldObjects/VegetationTile/VegetationTile"; import { WorldChunksVisibilityUpdater } from "./WorldChunksVisibilityUpdater/WorldChunksVisibilityUpdater"; -import { NavigationGrid, NavigationGridChunk, NavigationGridTile } from "../World/NavigationGrid/NavigationGrid"; +import { NavigationGridChunk, NavigationGridTile } from "../World/NavigationGrid/NavigationGrid"; /** * @type Map @@ -30,8 +30,8 @@ const terrainTypeList = { }; const terrainNavigationCostList = { 0: 100000000000, //water - 1: 1, //sand - 2: 1, //grass + 1: 3, //sand + 2: 2, //grass 3: 1, //stone }; const grassVegetationSpriteList = { @@ -78,6 +78,7 @@ const grassVegResourcesList = { export function worldCoordinatesToChunkIndex(x, y) { let w = BC_CHUNKS_SETTINGS.width * BC_TERRAIN_SETTINGS.tileSize * BC_TERRAIN_SETTINGS.scale; let h = BC_CHUNKS_SETTINGS.height * BC_TERRAIN_SETTINGS.tileSize * BC_TERRAIN_SETTINGS.scale; + // console.log(w, h); return { x: Math.floor(x / w), y: Math.floor(y / h) }; } export function worldCoordinatesToChunkIndexesCoordinates(x, y) { @@ -126,7 +127,7 @@ export function getWorldChunkById(id) { * @param {Number} xWorld * @param {Number} yWorld * @param {ChunkStorageTypes} storageType - * @returns + * @returns {} */ export function getTileAt(xWorld, yWorld, storageType) { let c = getWorldChunkAt(xWorld, yWorld);