jdiperla Posted June 3, 2017 Share Posted June 3, 2017 Good afternoon, I was hoping that the community could look over my code and see if something needs to be adjusted. I recently started learning Javascript and I just started learning Phaser and easystar yesterday so this might all look ridiculous. At the moment all I am trying to accomplish is pathfinding the way it is done in a point and click adventure game such as Monkey Island or Day of the Tentacle from Lucasarts. I am trying to achieve it much how AGS(Adventure Game Studio) allows the user to set it up. Basically, you import a room image and then you draw over the room with a chosen color(Sort of like drawing a mask) all the area's that a character can walk and find a path to and from. What I am trying to achieve in my code is the same principle above and I am trying to use easystar and Phaser to accomplish this. Let me explain my code: I have three images: the 'background' image which will load up the image of the room the character will be in. 'walkablepath' will be the image that contains the image mask. Anything that is hot pink is where the character can walk. 'maincharacter' is the player character that will find the path when we click on a part of the screen we want the player to go to. At start we will create a 'bmd' object, create the walkable grid and then destroy the bmd object. The 'bmd' object is what will hold the walkable mask information. It will match the same size as the room image. It will have complete transparency and will be overlaid over the background image, but not visible to the user. 'walkableGrid' will be the grid data that easystar will use to calculate the walkable paths. 'walkableRGB' will contain the RGB value of Hot Pink so that we can find the hot pink pixels. 'gridCollection' will collect the X and Y pixel data in the 'bmd' object and push it to the 'walkableGrid' as it goes through each pixel line from top to bottom. The code will do this by iterating through each X and Y pixel in a loop. After that is completed, the mask will be destroyed, easystar will have a setup to determine the acceptable tiles in the grid. Function calculateWalkPath() will be called each time the user clicks on the screen and the game will try and calculate the path for the user to walk and move him to his destination. Please see the code below: //Set the initial game paramters - Will start with 800x600 resolution and will use WebGL as a renderer and default back to Canvas if WebGL is not present. var game = new Phaser.Game(800,600, Phaser.AUTO, '', { preload: preload, create: create, update: update}); var easystar = new EasyStar.js(); //Lets get easy star in here. var bmd; //This will be the object that will take the pixel data of the scene. //Assets that will be preloaded at start function preload(){ game.load.image('background', 'assets/room1.png'); //The game room background that will be loaded. game.load.image('walkablepath', 'assets/walkablepath.png'); //The room's walkable area. game.load.image('maincharacter', 'assets/character.png', 32, 48); //The main characters animated spritesheet who will be walking around the room. } //The first function called when we start our game function create(){ //We are going to obtain the width and height of the background room. var backWidth = game.cache.getImage("background").width;var backHeight = game.cache.getImage("background").height; bmd = game.make.bitmapData(backWidth, backHeight); //Getting ready to determine the room size and get the pixel data of the walkable path. game.add.sprite(0,0,'background'); // Will add the room background to the desktop. It will place the upper left part of the image to the upper left part of the screen. bmd.alpha = 0; //Let's make sure the image is completely invisible to the users eyes. bmd.draw('walkablepath', 0, 0); //Will overlay the transparent walkable path over the background image. var walkableGrid = new Array(); //Lets make the grid that easy star will define as the walkable points. var gridCollection; //This will collect the 2 dimensional array grids and push it to the walkableGrid. var walkableRGB = "255105180"; //This is the RGB value of the area's the user can walk on. - Hot Pink is the RGB Color //Following code will begin at the top left corner of the walkable area and check each pixel for the hot pink color. If it finds it, it will add a 0. If not, 1. for (i = 0; i < backWidth; i++) { gridCollection = "["; for (j = 0; j < backWidth; j++) { if (bmd.getPixelRGB(i, j) == "255105180"){ gridCollection = gridCollection + "0"; } else { gridCollection = gridCollection + "1"; } //If there is still more width in the image, it will add a comma. Otherwise it won't and the array can be closed off. if (j != backWidth) { gridCollection = gridCollection + ","; } } //Close up and then Push the Array to the walkable grid gridCollection = gridCollection + "]"; walkableGrid.push(gridCollection); } bmd.kill(); //let's destroy the walkable area path we created from view - we need to find a better way to do this process. easystar.setGrid(walkableGrid); //Let's add the 2 dimensional grid array we just created to Easy star's pathfinding grid. easystar.setAcceptableTiles([0]); //Let's also make sure that easy star is aware that the 0 tiles are the tiles that the player can walk on. } function update(){ } function calculateWalkPath() { //This function will be called every time the user clicks on a path to walk to. //Now let's calculate the path and presumably use tweening to move the character from it's current x and y position to it's next calculated position easystar.findPath(xClickDest, yClickDest, playerXpos, playerYpos, function( path ) { if (path === null) { //Do something like a shrug animation from the character for not being able to find a path. } else { game.add.tween(maincharacter).to( { x: path[0].x }, 2000, Phaser.Easing.Linear.None, true); game.add.tween(maincharacter).to( { y: path[0].y }, 2000, Phaser.Easing.Linear.None, true); } }); //Let's get this party started. easystar.setIterationsPerCalculation(1000); easystar.calculate(); } I have to admit, I did not test this code yet. I rather have a fresh pair of eyes on this as I spent a good half hour trying to figure this out today and feel rather brain dead. Now, my questions are these: Will this code operate correctly? Did I use Phaser and Easystar correctly? What about memory management and speed and what is a better way to manage this? How would you improve it? Also, can I set more than one acceptable tile for easystar and how? Thanks for looking and for your assistance. Link to comment Share on other sites More sharing options...
samme Posted June 3, 2017 Share Posted June 3, 2017 I think setGrid's argument must be an array of arrays: var grid = [ [0,0,1,0,0], [0,0,1,0,0], [0,0,1,0,0], [0,0,1,0,0], [0,0,0,0,0] ]; easystar.setGrid(grid); Make sure you declare your iterator variables. I think you want to compare j to backHeight, not backWidth. getPixelRGB returns an object so you have to compare its rgba property (a number). for (var i = 0; i < backWidth; i++) { for (var j = 0; j < backHeight; j++) { if (bmd.getPixelRGB(i, j).rgba === 255105180) { // … } } } When I have scripts at this stage I often find it helpful to step through execution line by line and examine the values, revise, and repeat. Link to comment Share on other sites More sharing options...
jdiperla Posted June 3, 2017 Author Share Posted June 3, 2017 So part of the issue is that 'bmd' only loads it up as a black image and nothing more, even if I remove the alpha part of the code. Any thoughts? Link to comment Share on other sites More sharing options...
samme Posted June 4, 2017 Share Posted June 4, 2017 A BitmapData itself isn't a display object. It needs an image or a sprite to display it. bmd = game.make.bitmapData(backWidth, backHeight); bmd.draw('walkablepath', 0, 0); var bmdImage = bmd.addToWorld(); bmdImage.alpha = 0.5; Link to comment Share on other sites More sharing options...
jdiperla Posted June 4, 2017 Author Share Posted June 4, 2017 Actually I literally just figured it out. I was able to do bmd.load('walkablepath') and it loaded it into background and I was able to parse the data appropriately... I believe anyway. Now I just have to test it with EasyStar. My only other current issue is that after it get's all the row data in the X grid, it should cut off the current dimensional array without the comma at the end of it. Instead, right now it is still adding it, which results in it looking like this: [0,1,1,1,1,1,1,1,1,1,1,] instead of like this [0,1,1,1,1,1,1,1,1,1] <- notice no comma after the last 1. That is what I want to achieve, but it is not working as intended. This is my revised code if you have any suggestions: //Set the initial game paramters - Will start with 800x600 resolution and will use WebGL as a renderer and default back to Canvas if WebGL is not present. var game = new Phaser.Game(800,600, Phaser.AUTO, '', { preload: preload, create: create, update: update}); var easystar = new EasyStar.js(); //Lets get easy star in here. var bmd; //This will be the object that will take the pixel data of the scene. //Assets that will be preloaded at start function preload(){ game.load.image('background', 'assets/room1.png'); //The game room background that will be loaded. game.load.image('walkablepath', 'assets/walkablepath.png'); //The room's walkable area. game.load.image('maincharacter', 'assets/character.png', 32, 48); //The main characters animated spritesheet who will be walking around the room. } //The first function called when we start our game function create(){ //We are going to obtain the width and height of the background room. var backWidth = game.cache.getImage("background").width;var backHeight = game.cache.getImage("background").height; bmd = game.make.bitmapData(backWidth, backHeight); //Getting ready to determine the room size and get the pixel data of the walkable path. bmd.load('walkablepath'); //This will load the walkable path into memory. game.add.sprite(0,0,'background'); // Will add the room background to the desktop. It will place the upper left part of the image to the upper left part of the screen. var walkableGrid = new Array(); //Lets make the grid that easy star will define as the walkable points. var gridCollection; //This will collect the 2 dimensional array grids and push it to the walkableGrid. var walkableRGB = "rgba(255,105,180,1)"; //This is the RGB value of the area's the user can walk on. - Hot Pink is the RGB Color var color; //Will contain the pixel color of where the walkablepath search index is on. //Following code will begin at the top left corner of the walkable area and check each pixel for the hot pink color. If it finds it, it will add a 0. If not, 1. for (i = 0; i < backWidth; i++) { gridCollection = "["; for (j = 0; j < backHeight; j++) { color = bmd.getPixelRGB(i, j); //Store the color date of X and Y pixel /*if (color.rgba == "rgba(255,105,180,1)") { console.log("X: " + j + " Y: " + i + " = " + color.rgba); }*/ if (color.rgba == walkableRGB){ gridCollection = gridCollection + "0"; } if (color.rgba != walkableRGB) { gridCollection = gridCollection + "1"; } //If there is still more width in the image, it will add a comma. Otherwise it won't and the array can be closed off. if (i != backWidth) { gridCollection = gridCollection + ","; } } //Close up and then Push the Array to the walkable grid gridCollection = gridCollection + "]"; walkableGrid.push(gridCollection); } bmd.destroy(); //let's destroy the walkable area path we created from view - we need to find a better way to do this process. easystar.setGrid(walkableGrid); //Let's add the 2 dimensional grid array we just created to Easy star's pathfinding grid. easystar.setAcceptableTiles([0]); //Let's also make sure that easy star is aware that the 0 tiles are the tiles that the player can walk on. } function update(){ } function calculateWalkPath() { //This function will be called every time the user clicks on a path to walk to. //Now let's calculate the path and presumably use tweening to move the character from it's current x and y position to it's next calculated position easystar.findPath(xClickDest, yClickDest, playerXpos, playerYpos, function( path ) { if (path === null) { //Do something like a shrug animation from the character for not being able to find a path. } else { game.add.tween(maincharacter).to( { x: path[0].x }, 2000, Phaser.Easing.Linear.None, true); game.add.tween(maincharacter).to( { y: path[0].y }, 2000, Phaser.Easing.Linear.None, true); } }); //Let's get this party started. easystar.setIterationsPerCalculation(1000); easystar.calculate(); } Thank you! Link to comment Share on other sites More sharing options...
jdiperla Posted June 4, 2017 Author Share Posted June 4, 2017 Well I figured almost everything out correctly. Seems either I am not creating the array correctly or easy star is not being used correctly to determine the path. Here is my updated code: //Set the initial game paramters - Will start with 800x600 resolution and will use WebGL as a renderer and default back to Canvas if WebGL is not present. var game = new Phaser.Game(800,600, Phaser.AUTO, '', { preload: preload, create: create, update: update}); var easystar = new EasyStar.js(); //Lets get easy star in here. var bmd; //This will be the object that will take the pixel data of the scene. //Assets that will be preloaded at start function preload(){ game.load.image('background', 'assets/room1.png'); //The game room background that will be loaded. game.load.image('walkablepath', 'assets/walkablepath.png'); //The room's walkable area. game.load.image('maincharacter', 'assets/character.png', 32, 48); //The main characters animated spritesheet who will be walking around the room. } //The first function called when we start our game function create(){ //We are going to obtain the width and height of the background room. var backWidth = game.cache.getImage("background").width;var backHeight = game.cache.getImage("background").height; bmd = game.make.bitmapData(backWidth, backHeight); //Getting ready to determine the room size and get the pixel data of the walkable path. bmd.load('walkablepath'); //This will load the walkable path into memory. game.add.sprite(0,0,'background'); // Will add the room background to the desktop. It will place the upper left part of the image to the upper left part of the screen. mainchar = game.add.sprite(200,516,'maincharacter'); // Will add the room background to the desktop. It will place the upper left part of the image to the upper left part of the screen. var walkableGrid = new Array(); //Lets make the grid that easy star will define as the walkable points. var gridCollection; //This will collect the 2 dimensional array grids and push it to the walkableGrid. var walkableRGB = "rgba(255,105,180,1)"; //This is the RGB value of the area's the user can walk on. - Hot Pink is the RGB Color var color; //Will contain the pixel color of where the walkablepath search index is on. //Following code will begin at the top left corner of the walkable area and check each pixel for the hot pink color. If it finds it, it will add a 0. If not, 1. for (i = 0; i < backWidth; i++) { gridCollection = "["; for (j = 0; j < backHeight; j++) { color = bmd.getPixelRGB(i, j); //Store the color date of X and Y pixel if (color.rgba == walkableRGB){ gridCollection = gridCollection + "0"; } if (color.rgba != walkableRGB) { gridCollection = gridCollection + "1"; } //If there is still more width in the image, it will add a comma. Otherwise it won't and the array can be closed off. if (i != backWidth) { gridCollection = gridCollection + ","; } } //Close up and then Push the Array to the walkable grid gridCollection = gridCollection + "]"; walkableGrid.push(gridCollection); } bmd.destroy(); //let's destroy the walkable area path we created from view - we need to find a better way to do this process. easystar.setGrid(walkableGrid); //Let's add the 2 dimensional grid array we just created to Easy star's pathfinding grid. easystar.setAcceptableTiles([0]); //Let's also make sure that easy star is aware that the 0 tiles are the tiles that the player can walk on. game.input.onDown.add(calculateWalkPath, this); } function update(){ } function calculateWalkPath() { //This function will be called every time the user clicks on a path to walk to. console.log(game.input.x + ", " + game.input.y + "/"); console.log(mainchar.x + ", " + mainchar.y + "/"); //Now let's calculate the path and presumably use tweening to move the character from it's current x and y position to it's next calculated position easystar.findPath(game.input.x, game.input.x, mainchar.x, mainchar.y, function( path ) { if (path === null) { //Do something like a shrug animation from the character for not being able to find a path. } else { mainchar.x = path[0].x; mainchar.y = path[0].y; console.log(path[0].x); console.log(path[0].y); } }); //Let's get this party started. easystar.setIterationsPerCalculation(1000); easystar.calculate(); } You can look at an example I am trying to run here: http://sw-bfs.com/examples/udemy/ Link to comment Share on other sites More sharing options...
samme Posted June 5, 2017 Share Posted June 5, 2017 You can make walkableGrid like this: var walkableGrid = []; for (var i = 0; i < backWidth; i++) { var row = walkableGrid[i] = []; for (var j = 0; j < backHeight; j++) { row[j] = (bmd.getPixelRGB(i, j).rgba === walkableRGB) ? 0 : 1; } } easystar.setGrid(walkableGrid); Link to comment Share on other sites More sharing options...
jdiperla Posted September 30, 2017 Author Share Posted September 30, 2017 I am just re-bumping this as I can not seem to find a solution to those and I Would really like to find one. Here is my demo: http://www.sw-bfs.com/examples/udemy/ You can use the Java console to see what is happening. This is the room background: http://www.sw-bfs.com/examples/udemy/assets/room1.pngThe walkable path I am using as a path: http://www.sw-bfs.com/examples/udemy/assets/walkablepath.pngThe character image: http://www.sw-bfs.com/examples/udemy/assets/character.png My last post in this thread is the actual code I am still using. Can't figure this out for the life of me. Any help? Consider the walkable path image as the image file that considers the Hot Pink colored area the navigation Mesh. I suspect the error is where I move my character perhaps or in translating the hot pink area's into a walkable grid with Easy Star. Please help! Link to comment Share on other sites More sharing options...
samid737 Posted October 1, 2017 Share Posted October 1, 2017 Two things to consider: 1. easystar.findPath(game.input.x, game.input.y, mainchar.x, mainchar.y, function( path ) {//game.input.y instead of x 2. Reverse the array dimension , for example your image is 200*400, but you pass an 400*200 array to easystar. So reverse backWidth and backHeight in the for loop. Im not sure how easyStar implements this so can't help you with that. Link to comment Share on other sites More sharing options...
jdiperla Posted October 1, 2017 Author Share Posted October 1, 2017 Hi Samid737, I did both things. Still not working. I messed around some more with the console to see where the error may lie. The best I Can tell is that Path is returning Null. So easy star is definitely not finding the path. So either I am not doing something right with the grid, or I am not calculating the path correctly with Easy Star for some reason. Might be the grid is too large maybe? Either way, it is a little unnerving. Any other idea's guys? Link to comment Share on other sites More sharing options...
emmanuelnk Posted April 10, 2018 Share Posted April 10, 2018 I faced a very similar problem. I just solved it in my case. I'm posting this for any future researchers that run into this problem. easystar will most likely return null on a path because it hasn't calculated that path. Most likely, the problem is your easystar.calculate() method is unreachable. The basic is like this: in your pixi setup() you should define all functions and events related to easystar pathfinding. Then, you call your gameloop() function from the setup() function such that it becomes a closure. The important thing is that you easystar.calculate() is running in a ticker or setInterval. Otherwise, I'm pretty sure your arrays are correct. Easystar just isn't calculating on them thus returning null. /* other pixi application, container setup code */ let easystar = new easystarjs.js(); let grid = Array(50).fill().map(() => Array(50).fill(0)); setup() { app.ticker.add(delta => gameLoop(delta)); } function gameLoop(delta) { // 60fps easystar.calculate(); easystar.enableDiagonals(); } function getPath() { initPath(); let path = findPath(); } function initPath() { easystar.setGrid(grid); easystar.setAcceptableTiles([0]); } function findPath (startX, startY, endX, endY) { return new Promise ((resolve, reject) =>{ console.log('path find init:',startX, startY, endX, endY); easystar.findPath(startX, startY, endX, endY, function( path ) { if (path === null) { console.log("Path was not found."); reject(path); } else { console.log("Path was found. The first Point is " + path[0].x + " " + path[0].y); resolve(path); } }); }); } Link to comment Share on other sites More sharing options...
Recommended Posts