Placeholder Image

字幕列表 影片播放

  • hello, and it's been a long time since I've done any of this, but I thought it's time for a part.

  • Two to the path finding video Siri's.

  • We originally did the A style with him, and he can look at that video by clicking on the link in the top, right.

  • But this time we're going to look at a slightly different algorithm, perhaps simpler than a star in certain situations, or but also maybe less useful, too.

  • I think it's quite a debatable one.

  • We're going to look at wave propagation, and this is what I'm going to be developing in this video.

  • And if it looks eerily familiar than well, yes, it should, because you've probably seen the programming a star video.

  • In fact, it looks exactly the same.

  • We have a starting point marked in green, which we can move around on.

  • We have an ending point marked in red, which I can't move around.

  • In this particular video, we could see an array of nodes, which are blue for empty space and gray.

  • Four solid obstacles on, just like with the A star video.

  • If you start placing obstacles, it finds different paths or in this case, it's trying to find the shortest path, and it has done on.

  • I've marked it with a yellow circles.

  • It's time to make the path a little bit more visible.

  • You also see that I've assumed eight way connective ity so things could move in diagonals.

  • We'll see in this video that that doesn't have to be the case.

  • We can also use four waken activity on just with a star.

  • If there is no path, you could have just blocked it off here than there is no path.

  • There's no solution that can be found trying.

  • Make it go a long way round and we see the performance is actually quite fine on here.

  • It's running about 900 frames per second now.

  • This is a very small area.

  • It has two path over.

  • But that's one of the nice things about the wave propagation approach to path.

  • Finding is it's relatively computation.

  • Lee Simple.

  • Now, just before I get too stuck into explaining the algorithm, you'll notice also that this seems to be quite a high resolution, and that's because I'm doing it in the pixel game engine instead of the console game engine on.

  • I wanted the high resolution to demonstrate an effect later on.

  • And for the first time ever in a one lone code video, I'm going to make a little personal announcement.

  • I've had a surprising number of requests that people would in some way like to support me.

  • As a result, I've created a patriotic page.

  • You can find the link to the pantry on below on what I will say is it is entirely up to you.

  • There is no obligation at all, I probably wrote Be producing any exclusive content, and it is unlikely that I'll even mention your names in any videos.

  • But if you'd like to buy me a cup of tea, then pop on over to the pantry on page.

  • When I looked at the A star algorithm, one of the fundamental assumptions we made was that the locations, the waypoints, didn't necessarily have to be organized in any particular way.

  • We just had a graph off interconnected notes, and so the algorithm was really tasked with trying to find the shortest path from one note to the other, knowing some sort of heuristic onto the distance between the notes.

  • The A star algorithm itself was computational.

  • Quite simple, but conceptually it was a little tricky.

  • And as part of that demonstration, I made another fundamental assumption.

  • Even though we could use a star on generic graphs like that.

  • In fact, what we did was have routinely position nodes in a grid on dhe.

  • We applied the star algorithm to nose with different types of connectivity.

  • So in this case, we've got four waken activity between all of the notes, and this was quite convenient and used in many real time strategy games and path planning applications for robots.

  • It's quite nice to represent your world as an array such as this, but this begs the question.

  • If we've got routine structure like this, do we need most of the A star facilities in order to find the shortest path?

  • And the answer is no.

  • For a long time, there's been an alternative algorithm based upon wave propagation.

  • I'm quite sure all of you will have used wave propagation, and some of you may not know that you've used it.

  • If you ever used the flood fill tool in paint or in photo shop or gimp, then you have used a form off wave propagation fundamentally What we're trying to achieve is the transmission off information from a starting point across the array on this way front carries the information.

  • The contents of the raid itself can change what happens to that way front.

  • So let's assume we've got an area of solid on our map on we cast out the same wave just like a sound wave.

  • It propagates around the corner.

  • This is a very useful dynamic of wave propagation.

  • Okay, so path finding by wave propagation is a little bit different.

  • And I'm gonna hand walk through an example here.

  • I'll start off with quite a bit of detail, but as we get the hang of it are probably speeded up a little bit.

  • The advantage to doing wave propagation is it works very nicely on to D grids, and we can see here I've got a two D array off well, effectively, billions.

  • Either an obstacle exists it shaded grey, or it doesn't.

  • And whereas you can implement wave propagation on nodes, it means more when you're operating on a grid.

  • So what I'm going to show is how we get from a start location to an end location, which I've marked with green and red dots.

  • Red is the end.

  • Green is the start.

  • Now we can solve this problem very quickly by looking at it, we can see which Siri's of cells do we need to pass through to get from one to the other.

  • But of course, the computer doesn't have this top down perspective.

  • It needs to generate some method of passing from one location to the other, and ideally, we want to try and find the shortest path.

  • Now I'm going to prime our two D array with some information already.

  • So if the cell contains an obstacle or a block, I'm going to fill it with the value minus one on for cells that represent empty space to fill it with the values.

  • Zero.

  • The objective here is to fill the map with the distance away from the target location.

  • On will do this by emitting the distance in Italian location and watching it flood fill the map.

  • The boundary of the flood fill is effectively are wave propagation.

  • Now that I've set up the array with information to help us work out where the obstacles are, I'm also going to maintain a couple of lists on these lists of notes.

  • Now I'm going to use the word node or sell interchangeably here.

  • Typically, Node wouldn't really be implied on the two d matrix like this, but that's what I mean.

  • Now, to start the algorithm, I'm going to prime it with the target location.

  • So in this list on the left, I'm going to put in the coordinates of where we want the path to stop.

  • That's five in the X axis, one in the Y axis on.

  • I'm going to set another value called De Tau one, and D is going to represent the distance.

  • And so here we go.

  • I'll step through the algorithm, quite verbose.

  • Flee to start with.

  • We take the first entry in our list of discovered nodes, and we look at the devalue on.

  • We write the devalue to that location.

  • So 51 is.

  • Here is where we end the path, so we'll change that zero to a one.

  • That's the first step.

  • The next step is to look at our immediate neighbors and see if the value they contain is non zero.

  • If it is non zero, we're not interested.

  • So let's have a look.

  • We'll take the northern neighbor first appear, we can see its value is minus one.

  • I'm not interested in that note.

  • Well, look at the eastern neighbor here.

  • Well, that's minus one.

  • Also, that's not zero.

  • Now we'll look at the southern neighbor.

  • We can see the value is zero.

  • And so this means we want to do something.

  • We have discovered a new note.

  • Nothing has touched this note before, but it's a valid know that we're interested in.

  • So I'm going to add the coordinates off this new note to my second list here.

  • So this is a five in the X two in the why on dhe To calculate the devalue, we take our current devalue, which is one and add one to it.

  • So D in this instance becomes, too, on al drawer square around it to say that that's a newly discovered node.

  • The final neighbor left a check is our western neighbor on Indeed, Again, the value is zero.

  • So that means we have discovered and no, that's not been discovered before, so we'll add that to our list as well.

  • In this case, it's 41 on two again because we take the value of our current node at one to it and stall that is the distance in our newly discovered node list.

  • So for our first node here, which is the end location for the path we've now finished the algorithm.

  • Let's move on to step two.

  • Step two is quite simple.

  • We look at our list of newly discovered nodes on we remove any duplicates.

  • Well, we haven't got any duplicates here.

  • And so what I'm going to do is remove the note that we have now processed copy over our newly discovered notes and clear the newly discovered node list.

  • So let's continue with the algorithm we're now up to here.

  • So the first thing we're going to do is find this note.

  • This is five and two.

  • So that's this node.

  • Here on first stage is to take the Devalue and write it into the cell.

  • So we'll replace the zero here with two.

  • We then start checking our neighbors who are looking our northern neighbor.

  • Well, that's non zero.

  • That's a one, so that neighbor has been seen before.

  • It's not new, so we're not interested in it.

  • Let's look towards our eastern neighbor.

  • Well, that's a minus one that's non zero.

  • Not interesting towards our southern neighbor.

  • Well, that is a zero.

  • We are interested in that one somewhere.

  • To add that to our newly discovered node list.

  • In this case, it's location five by three on we take our current devalue wish in this case is, too.

  • We add one to it and store that as the new devalue.

  • Now all we've got left is our western neighbor, which again is zero.

  • It's a cell we've not discovered.

  • I'll just draw in the newly discovered cells on.

  • We'll add that one also to our lists.

  • In this case, it's for two and again three because we take our current devalue and add one to it.

  • We have now completed at this note so we can smooth shit out.

  • There we go on, we look at the next load that's in our list for want to, which is here.

  • The first thing we want to do is take our devalue and write that into the location of the cell.

  • So get rid of that zero and put two.

  • And now we check our neighbors well north is minus one.

  • No good East is one that's not a zero either self.

  • Now we have discovered this note before, but as far as we're concerned, it's still a zero at this point in time.

  • So I'm going to add that again to the list of newly discovered note.

  • This is four and a two, and we calculate day in exactly the same way.

  • It's our current devalue plus one, 4 to 3.

  • We've only got one note left to check.

  • Now that's our western neighbor.

  • Well, that's a minus one.

  • That's not very interesting.

  • And then we're done.

  • So the next phase is to look at our newly created nodes and remove the duplicates.

  • And we could see we did have a duplicate, therefore two and three.

  • So we don't want that.

  • Once we've got rid of the duplicates we copy over the newly discovered nodes into our list of notes to process, and we'll clear this list, right?

  • We're up to 533 now, so that's here.

  • The first thing we want to do is write in our Devalue into this cell that we check our northern neighbors, not zero eastern neighbor, not zero.

  • Southern neighbor is zero.

  • So I'll flag that as a newly discovered note, get its coordinate.

  • 54 Take my devalue and add one to it.

  • So we're storing this is it for this time.

  • And finally check my western neighbor against one is zero.

  • So it's a newly discovered node going to capture that it's Ah, 434 this time and then we've run out of neighbors we've done north, South, East and west were done.

  • Squid that one out.

  • We're now going to process node 4 to 3.

  • So Step one is to write in The Devalue replaced the zero without three.

  • And now we check our neighbors Well to the north, I've gotten non zero to the East, her non zero to the south.

  • I have got to zero, so I'm going to add that to the list.

  • It's 43 and four.

  • Again.

  • We've got a duplicate for enough.

  • We know we'll get rid of that in a minute and finally got my western neighbor, which is a newly discovered node wishing this case is 3 to 4.

  • I've got no neighbors left now, so I've done processing that note.

  • It's time to look for duplicates and copy them over and remove them.

  • Well, I can see here I have got some duplicates for three and four.

  • So I'm going to remove one of them.

  • A copy over the new nodes.

  • 54 and four, 43 and four, 32 and four.

  • Going to run out of space at the bottom.

  • But we'll worry about that in a minute.

  • All right?

  • One last time.

  • We should be able to do this really quick now and then I'll finish it off by hand and speed up the footage.

  • Take the first note in our list of nose to be process 544 Which is this note?

  • Down here, we take our devalue on.

  • We're going to replace that value that's already in the cell with the Devalue, so that zero becomes a four.

  • I now check my neighbors to the north.

  • I've got nothing to the east.

  • I've got nothing to the South, I've got nothing.

  • Nothing means non zeros.

  • Oddly, in this context and to the West, well, I've got a newly discovered sell.

  • It is a zero.

  • It's something that we've not seen before.

  • So I'm going to capture that and capture its location.

  • In this case, four and four.

  • I take my current devalue and add one to it for the new devalue sets of five, and I move on to the next note.

  • 434 So the first thing we'll do is replace the zero with the note.

  • New Devalue, which is four.

  • We'll check our neighbors well.

  • To the north, it's a three that's non zero to the east.

  • It's a theory that's non zero to the south.

  • We've discovered it before, but it's still considered a zero at this point in time.

  • So will capture that note again for 45 onto the West.

  • Well, that's non zero, so we don't add anything either.

  • Got our last note to process.

  • Now smash that one out.

  • 3 to 4, well, north.

  • Nothing East.

  • Nothing South.

  • Nothing but West New node.

  • And before I capture the new order to quickly forget to do, we forgot to put in the new devalue.

  • There we go.

  • So I got a new note here at 22 and it's my devalue four plus one to give me a new devalue off five.

  • And then we're done with that note.

  • The final thing to do is to look at our list of newly discovered nodes removed the duplicates and continue to process the notes.

  • Now, I've run out of space somewhere to start with my list again up here.

  • But I'm also not going to do the commentary through this one at this point.

  • You see, I've discovered quite a few new nodes and I've got quite a lot of duplication, so I don't need this one on.

  • I don't need this one.

  • And I don't need this one.

  • That's okay.

  • It's quite common for this album to discover nodes that have already been discovered on the same cycle.

  • But just carry on.

  • Regardless, it'll still work.

  • We finally come to the situation.

  • We've only got one node left now.

  • And it's this bottom no, down here, 14 with a value of eight.

  • While carrying on with the procedure is just fine will replace the value with the D value of the node, which in this case is an eight.

  • We can't discover any new nodes now.

  • So at this point, are discovered knows list is completely empty, the algorithm has completed, and now that we have filled the ray up with numbers, we can see that the pattern is that we have propagated away from the target destination an increasing value.

  • So as we see the target destination was this one around that we've got to?

  • Then we've got some threes, then we've got force, and then we've got five.

  • But these numbers increased depending upon the geometry of the level.

  • As we come through the opening here, we can see that the next step in any direction from this cell will incriminate the distance by one.

  • It goes from 66 and six.

  • And it's due to this distribution of numbers that we can calculate the shortest path.

  • So if I take are starting location appear I can create a path as follows.

  • Let's assume I can only move north, south, east and west.

  • I look at my immediate neighbors.

  • I look at the value off them and I see which is the lowest.

  • Well, in this case, I've only got a choice of six and six.

  • Anything less than zero doesn't count, so it doesn't matter which one I choose.

  • But let's say for argument's sake, I choose this one.

  • I can start to assemble my path now in this cell, I look at my immediate neighbors and see which is the lowest value.

  • Well, my value is six.

  • I've got a neighbor, which is seven, and I got a neighbor, which is five.

  • I could only really go one way.

  • Now at the five location look of my neighbors.

  • I've got a 666 and a four while I want to go to the lowest number.

  • And this is why it's called sometimes a potential field approach because were effectively moving downhill towards the finish line.

  • And so the height of a cell or it's D number can be considered its potential in Cell four.

  • I've got a choice of a five on the left or three on the right.

  • Well, I follow the lowest in this cell Mark three.

  • Now my neighbors, heir to fourth and two twos.

  • Well, I can choose randomly either of the twos.

  • The path length is going to be the same each way, so I'll choose this one.

  • And finally I can choose between a three and a one, and I found the end of my path.

  • So by flooding the map with information relating to the distance away from the target location, we can then easily as if we dropped a ball bearing on the map.

  • Watch its path as it descend towards the target location.

  • We needn't just assume that we have immediate neighbour connectivity either.

  • We've been looking at four waken activity.

  • We could easily just have eight wake and activity too, in which case we'd get some nice diagonals.

  • But the result is the same and you'll see there was never a chance of this algorithm ever choosing this long path here.

  • And so that completes the wave propagation part of the algorithm.

  • Let's replicate this in code.

  • I'm going to code this one up pretty quickly because we've already done something similar with the A star algorithm.

  • Is that this time I'm doing it in the pixel game engine.

  • I'm going to start with a basic pixel game engine template, and I created a class derived from the pixel game engine called Path Finding Flow fields, and I'm creating a game engine of 5 12 pixels wide by 4 80 pixels high on each pixel in the game.

  • Space is two pixels on your screen, so let's get some of the boiler plate out the way I know I'm going to need a two dimensional map.

  • We'll have map wit and map height variables.

  • I want the map to look like a grid, so we know which cells were selecting.

  • So I'm going to have a cell size variable, and I won't have a thickness, which represents the border between the cells.

  • So if we specify cell size of 32 on, we have aboard width of to the actual drawn cell size is 30 pixels on.

  • I'm going to need my first array, which you're going to be a Boolean array called Be Obstacle map.

  • And it's going to be true for an obstacle exists and false for Doesn't our populate these variables in the on user create function there were to specify, to have a border width of four in a cell size of 32 on I'm going to make the map width and height.

  • Variables depend on how big the game engine screen has been created in the first place, and I'll also create my array, have defined the array to be defaulted to false.

  • In our news update, I want to draw the array, including the obstacles.

  • Now I'm going to also draw a few of the things as well as the video progresses.

  • But to start with.

  • I want to just clear this screen and then I'll create two nested four loops, which it a rate through all of the cells in our map.

  • The default color for a cell is going to be blue and fridge cell location.

  • I'm going to call the Phil Recht function with some scaling parameters to specify the top left, top, right width and height of the cell.

  • And you can see I'm only going to draw these sell to the cell size minus the border with that gives this black border on the color is going to be blues we've just defined.

  • So let's just take a quick look.

  • Perfect.

  • Now I'm willing to bet that in probably 95% of my videos, we do the same thing we do.

  • Why Times Width plus X to convert a to D coordinate into a wandy index for an array.

  • Since we're working primarily with a raise on neighboring cells of other cells in the race, I'm going to create a little convenience Lambda utility function.

  • To do this for me, it's just going to be called Pee on will pass along the X and y, and it just performs that calculation.

  • It'll make the code a little bit more visibly tidy.

  • This means I contest for things inside my obstacle map array.

  • Quite simply, I just called the Lambda Function with X and Y and use the return of that as the index into my obstacle map.

  • If it's true, I'm going to set the color to Grey on will draw it differently.

  • Now most interaction is pretty critical for this application, so I'm going to capture the current mouse coordinates at the start of every update cycle.

  • I'm going to scale the mouse cornet into the same space as the grid that we've created.

  • So it takes the screen coordinate X and Y divided by the cell size, and it's an interview divide.

  • So we get the actual cell that the cursor is hovering over.

  • And if the user clicks the mouse button, I'm going to respond to the released state off the get mouse zero, which is the left mouse book.

  • I'm going to toggle the state of that particular cell.

  • Does it?

  • Is it an obstacle?

  • Or isn't it?

  • Using our newly found Maris coordinates, I'm going to use our Lambda function to Index into the obstacle map array and simply invert the state of that cell using the exclamation mark here.

  • This will allow me to toggle the cells.

  • Very nice.

  • As part of the demonstration, I'm going to need a starting and ending location for our path was going to store these interview coordinates on Denon User create.

  • I'll give them some default values in our news update.

  • I'm going to make it that if the user right click somewhere within the array, it changes the start location.

  • So the one here in the get mouse function corresponds to the right mouse button.

  • I'm not going to put in a way to set the end location.

  • We'll need to draw starting and locations.

  • But this is quite simple now, just by quickly changing our loop to also include if the loop coordinate is the same as our star quarterback will set the cooler too green.

  • And if it's the same as the end, coordinate will set it to read.

  • We don't have a path to draw yet, so that's the next step Now, just like I did with the A Star video.

  • It's very difficult to actually do this algorithm in stages it kind of all needs to be there to work.

  • So we will be looking at most of the code to begin with before running any visual tests going to add an additional map.

  • And this time it's going to be an integer are colic flow field zed and might be wondering why I said that this represents the devalue that we've just seen when we calculated it by hand on.

  • In many ways, that devalue can be considered the height off the map at that location, so I'm going to create an array off heights.

  • Naturally, this needs to be initialized on will create the same width and height as our obstacle map.

  • Now I'm going to run the path, finding every single frame, which is quite redundant because really should only do the path finding when you need to do it.

  • AII the path has changed in some way or other, but before we can do any path thing, we need to initialize our flow field, said Array according to what obstacles Aaron the map.

  • And so I'm going to do this by having again two nested four loops and I want to set to minus one the corresponding location in our flow field map.

  • If we're on the border or there is an obstacle in that location to the borders affiliated to check for if X or Y is equal to zero, then it's going to be an obstacle.

  • Likewise, if X or Y is equal to the dimensions of the map minus one, that's the other side and finally will dip into the obstacle map array itself the array of billions at Location X y.

  • If any of these return true, we're going to consider that location to be an obstacle.

  • And so I'm going to sense the flow, Field said.

  • For that location to minus one in all of the situations, I'm going to set it to zero.

  • Once the flow field has been prepared, we can now start the algorithm.

  • We're going to need a little bit of boiler plate to get this going.

  • We've seen in the manual example that I'm going to maintain a couple of lists.

  • Well, I'm going to go all modern, see on you for the first time ever, you're going to see me actually use STD Cola Cola in my code.

  • And so it seems only fitting that will modern c++ this application to death?

  • I'm going to create a list off topples There we go on, we can see the list is here on the list will contain a type topple on the topple itself is of type int int int where the first interview is the X coordinate off the new node that we've added The second interview is the Y coordinate of than you know that we've added onto the third interview is the distance of the new node that we've added.

  • Remember, we have the columns on the right and screen X y d those correspond to these three interviews and so this is going to be our list off discovered note on.

  • If you remember, the first thing that we add to this list is the end location and set it toe one.

  • We now need to repeatedly run the algorithm until they're no nodes left in this list.

  • And each time we process a note in this list, we're going to develop some new nodes.

  • Now I'm going to maintain these in a separate list of topples.

  • Uh, vintage is exactly the same four matters before.

  • Now it's time to iterated through the nodes in our list and process them.

  • So I'm going to use a little auto four loop to do that, just so things can remain slightly readable for the video, I'm going to actually extract X, Y and D independently from the topple.

  • And you do that.

  • Using these standard get function on the first stage of the algorithm is to write the current devalue to that location so we can do that with a single line.

  • We take our ray flow, Field said.

  • We use our little Lambda function to index into the array, that location, and we're writing the de value to it.

  • Now.

  • We want to check each of our compass, point neighboring nodes or sells.

  • I'm going to start by checking the eastern neighbor.

  • That is, of course, the one to the right, but I don't want to go out of bounds, So the first thing I'm going to check for is the current X location plus one, a valid location to be sampling from our Rafer.

  • And the nice thing about it is it.

  • It's guaranteed to execute these conditions in order, so if any of them fail the year four fail straight away that means I can per this up with Honore.

  • Check on this will allow me to safely check this array regardless of what the X and Y coordinates are going to be.

  • I'm not going to go out of bounds for this array on what's on checking for is, is my neighbor equal to zero?

  • Because any non zero we're not interested in it's already been discovered or it's an obstacle on by checking for X Plus one.

  • I'm checking for the one on the right outside of me, my eastern neighbor.

  • So in this case, if this returns true, then I want to push this node back onto my new nodes list, which is currently empty, and I'm going to push it back with the correct location X plus one and why?

  • And if you remember, we take the current D and add one to it in a very similar way.

  • I can check the western neighbor, except this time I'm checking to see that the Western neighbor is, of course, equal to or more than zero.

  • And it goes without saying that North and South neighbors are also very similar indeed, So this auto full loop has now gone through all of the nodes in our node list to be processed and created a whole bunch of new notes.

  • We may have duplicates in there, though, so we want to sort that out now.

  • If you really keen on performance the easiest ways to avoid duplicates altogether, simply check as you're pushing the note into the new note of Ray that it doesn't already exist.

  • However, I'm flexing my modern c++ muscles today, and so I'm going to use some standard Libra utility functions to help me out.

  • It's important that we do remove the duplicate notes else the algorithm will never complete.

  • So how will we remove duplicates?

  • And it's a little trickier in this case because it's not like our nodes are just represented by single number.

  • They represented by three different things.

  • Well, the first thing I'm going to do is sort the list of new nodes, and you might think, Oh, no, hang on.

  • We've got assorted here, but on the whole list of new node is going to be quite small on.

  • I'm going to use thesaurus function that comes as part of the link list.

  • Implementation on you can pass to that a land of function as one of its arguments.

  • So what will work through this?

  • Don't worry if it looks a little bit overwhelming at first.

  • And so the two arguments that get passed into the sort function are basically references to the topples that we've been using the topples of the stored in the list of that location.

  • And you'll get two of them because what this sort function is asking you to do is to compare them.

  • And if you return a true it will swap them.

  • And if you return false, it doesn't do anything.

  • So this is sort of the fundamental sorting bit off its internal sort algorithm it'sthe condition upon which things are sorted.

  • So we need to provide it with an implementation to do this.

  • And I'm going to do this a slightly strange way.

  • Fundamentally, my condition is quite simple.

  • I'm just checking to see if something is less than something else on that something is actually going to be the result of our P Lambda function, which we started with at the beginning on the arguments I'm going to pass to that of the X and Y locations for that specific topple that's come in.

  • And the reason I'm doing this is that the Topple will have an X and y coordinate.

  • I can't compare those very readily, but what I can do is convert them into a unique one.

  • Devalue on.

  • I can compare those one devalues with the less than operator.

  • So I do that for both sides of that operator corresponding to both topples and you might think, Well, what kind of nonsense sort is this on?

  • The truth is, I don't really care what the order is once they've been sorted.

  • What I care about is that they are stacked up in this particular pattern, appealed to assume it s so it doesn't matter how messy things are once the sort is finished.

  • Notes that are similar are grouped together, and I want them grouped in that particular way because I'm going to use another modern c++ miracle.

  • I'm going to use the unique function on the unique function looks through lists or containers for duplicate objects in Siri's.

  • And if it finds them, it removes them so we can see here that group off bees and the list will get found and then we'll get removed on it works in pretty much the same way as the sort.

  • In this case, I'm again passing in two references to the two topples in the list.

  • But instead of returning whether one is greater than or less than the other this time I want to compare if they are the same.

  • Because if they are the same and this returns true, then it removes it.

  • And so it's very similar to the sort function.

  • Except this time I'm just checking the equality.

  • You could argue I could check the individual parameters of the Topple as well, but I just like the symmetry that's going on here.

  • And so, admittedly, they're completely overkill mechanism for removing duplicates.

  • But nonetheless, I thought, quite interesting.

  • It allowed me to explore these functions in a little bit of detail.

  • Now that I know I've got a unique list of newly discovered nodes, I'm going to clear the list of nodes that we've just been processing and replace that list with the newly discovered sorted note list.

  • When there's nothing else to do, the algorithm is complete.

  • And so yes, quite a watch of code.

  • But most importantly, our flow field Zed array has now been populated with distance values, something to draw those into our final map in the routine where I'm drawing the cells, I'm going to add in an additional Coulter drawstring.

  • I'm going to draw it in the same location as the top left of the cell, and all I'm doing here is converting thief Low Field said value at that location to a string on, coloring it in white.

  • So now let's take a look at what's going to happen.

  • Well, what we can see is our start location in Green and are en location read on.

  • We see a bunch of numbers and we can see minus one around the boundary, and that's because we set our boundary.

  • Don't forget as being an obstacle.

  • I could draw that in.

  • It doesn't make any difference, but I'm not going to because I'll be clicking forever.

  • I'll tell you what, I'll just do it anyway.

  • Give me a minute.

  • Perfect.

  • But what we see is from our target location that's marked with one on the immediately adjacent cells north south eastern western marked with two and then three and then four and then five so emanating away from the target location.

  • We see the distance is increasing on because of the symmetry of our map.

  • It's got no obstacles.

  • At the moment.

  • We can see the furthest away locations is 18 and that implies it takes 18 steps in order to get from the target location to that location.

  • In fact, we can see that by following it just very simply.

  • 123456789 10 11 12 13 boy, 15 16 17 18.

  • There we go all the way to one of the corners.

  • So let's start putting in some obstacles and seeing what happens to start randomly.

  • This pose an obstacle in here.

  • We see nothing changes, really in the distance map, but the shortest path so far is 123456789 10 from one side to the other.

  • Now I've put an obstacle in the way we can see that the distances are changing.

  • And if we follow one of the past 12345 we see now we can go either way along the wall.

  • 6789 10 11 12 13 14 15.

  • And this ability to follow the wall is because the wave has propagated and clung to the edge.

  • Let's contain the target location so we can see to actually get up here now to the top right takes quite a number of steps, which is what you'd expect.

  • It has to go basically all the way around the map.

  • There's no solution now, so most of the map is set to zero.

  • So let's just break a quick hole in there.

  • With this little hole in the bottom, we can see the wave starts to propagate outwards.

  • We're just basically following numbers that are the same.

  • So the Elevens curve out.

  • Then the twelves go out in a bigger radius around it, and the 13 starts to form what looks like a circle.

  • You know what?

  • This is very difficult to verbalize.

  • So I'm going to hack in a way of displaying the wave.

  • There we go.

  • Perfect.

  • So what I've done now is told it to maintain a number on if the number is equal to the distance value that we can see on the screen to shade in that seltzer.

  • Currently, the wave is set at one.

  • I set it to two, said it 23456 And so we can see from the target.

  • The wave has propagated outwards, so let's bring that back in.

  • You have to draw all these walls again.

  • It now, and I try and replicate what we had before and to create a little room with the door.

  • So emanates a wave.

  • You see, the wave has passed out, but it's only got to the door with one little sell.

  • It opens, but as it goes through the door, a new wave is created, and we can see now here at the bottom that the wave hugs the corner.

  • Bring it back in.

  • Let's try and make this quite complicated and see how it pans out.

  • So each time there's an opening, a new wave.

  • Get spawn and you can see the wave basically explores all of the available space until it reaches the target destination, it has to explore all available space.

  • That's the only way this algorithm will know that it's found the complete path.

  • We can visually see the path, but now we need to make the computer understand where the path it's, so we'll have to program the algorithm to know extract the path from our height map.

  • So this is the next face.

  • Create the path.

  • I'm going to represent my path again, using some modern.

  • See, I'm going to use a standard list off pears, obviously.

  • Now the Paris just in x and y coordinate, I don't really care about the distance so much.

  • I'm going to straight away.

  • Add to the path The starting location went to maintain three additional variables whilst I'm Iterating through trying to find the path first is location X and location.

  • Why?

  • Which is the location I'm currently investigating to work out which direction I need to move for the path onda bullion, which is no path, which means if there is no viable solution, the algorithm shuts down early on.

  • So I want to assemble path segments until I either reach the end location.

  • So my location X and location wise, equal to the end location or there is no viable path.

  • So for each neighbor, I'm going to generate Get this another list of couples.

  • I'm going to look at my immediate north, south, east and West neighbors on.

  • I'm going to rank them.

  • I eat sort them based upon which one is the minimum because if you remember, we want to follow the path backwards.

  • Basically rolling downhill.

  • We want to follow minimum heights.

  • So that's the value we're going to be sorting on later on.

  • I'm going to assume to start with four way connective ity.

  • That's the simple one is in a very similar monitor.

  • Before I'm doing a boundary check to make sure that I don't read memory I shouldn't on.

  • I'm adding to the list of neighbors a new topple, which is the location X and location.

  • Why onto the height at that location?

  • So I've done that for North.

  • I'll now do the same for East, South and west.

  • Well, now have a very small list of only valid neighboring topples on.

  • I want to solve this list based on the height value.

  • In fact, I want to sort it from smallest to largest.

  • Therefore, the item at the front of the list should be the path that I want to take.

  • Indeed, if I have no neighbors that are valid a tall then I have no path and I want to exit prematurely.

  • Otherwise I'm going to isolate the location X and Y from the topple and push that location into my path list, and that's it.

  • So let's work on how we visualize the path.

  • I can't draw the path as part off the cell drawing routine because the craft itself is now just a list in its own right.

  • So I'm going to create an auto four loop to iterated through the path and draw the locations.

  • Now I want this to be a little bit aesthetically pleasing.

  • Yes, I'm going to draw yellow circles where the coordinates are for each path element.

  • But I want to join them up with a line.

  • And to do this, I need to, firstly extract the first point separately, which I do here.

  • So I have an original X and an original.

  • Why value?

  • Because the list of points is really a list of pears this time and not a topple.

  • I can access the X coordinate with the first parameter on the Y.

  • Coordinate with the second parameter.

  • I only want to do this for the very first point because I need a starting point for my line.

  • The remaining points.

  • I could just use the point from the previous iteration of this loop that's all used the draw line function to help me do just this on it is going to be a little bit of a mouthful for each coordinate simply because I want to.

  • Firstly, scale.

  • Don't forget, this will be in sort of node space, so this is a very small coordinate system.

  • Anita's now scale it to this screen appropriately, so I'll take my ex location.

  • Which of the start of the line I want to multiply it by cell size to get it up into screen space.

  • But then I want to offset it by half the cell size minus the border with because I want to draw it from the middle location off the cell.

  • That's for the X coordinate.

  • Do exactly the same for the Y Coordinate on.

  • I do exactly the same for the new court It, except this time I'm using the current points first and second parameters.

  • It's important that I remember to update my O X and A Y values with those parameters for the next iteration.

  • And finally, on top of all that, I am going to draw a big yellow circle to show which cell has been path.

  • So let's take a look well, we can see the path has been discovered.

  • This is what we saw at the start of the video.

  • Except this time, we've only got four way connective ity.

  • Now that might be desirable in certain applications where you want things to behave in a very cellular manner.

  • And I can also propagate the wave outwards to see the path that was chosen eight waken activity is quite a simple addition.

  • We don't need to change the base algorithm that we created.

  • All we need to do is add in an additional four checks in the path creation part of the algorithm.

  • Effectively, we need to at northwest, southwest, northeast and southeast.

  • So I've done that is a water here.

  • I'm going to just quickly remove the text being drawn, and we'll just see if that works very good.

  • So it might not always be appropriate to have diagonals in your path finding, but you can have both with a very simple change of the algorithm.

  • Now, you may be wondering why some people call this potential fields.

  • And indeed, why have I even called some of my variables field the information that we currently have in our height map is enough to start giving us some Grady INTs in the X and Y directions, so I'm going to create two additional maps flow Field X and flow field.

  • Why, actually, I want to create these to be the same size as the rest of the maps and initialize them to zero by looking at the height values in the height map, we can extract information about local radiance.

  • And so, for a given cell, I can work out a resultant vector which represents the Grady into that location.

  • Very simply, I just look at my neighbors.

  • Look at the direction north, south, east and west.

  • Look at the differences and some them up.

  • Well, it's not quite that simple, because the minus one here will clearly by us the result of the calculation.

  • So if we have a minus one, which is an obstacle, I'm going to set that to be the same as the cell I'm currently testing.

  • So in this eastern direction, there is no resultant change here.

  • We can see we've got a plus one in the X on this side.

  • It's a zero in the X appear we've got the plus one in the UAE And down here we've got a plus one in the Why If we call this one left on this one right on this one up on this one down, we can see that we get the D X value would be equal to our minus.

  • L A.

  • D y value would be equal to down, minus up.

  • So for this particular cell, we could say that D X is equal to minus one on D Y is equal to zero.

  • We could go one step further and include eight way connective itI as well.

  • Although I'm not going to in this example, you'd also have to include some root two's in there.

  • But having a DX and a D Y value is incredibly useful.

  • So after we've created the path, I'm going to add in the optional step of creating the fields.

  • And again, this will require me to.

  • It's a rate through our height map, but I'm going to ignore the boundaries.

  • I don't want to get into any boundary issues this time.

  • I'm doing this in a slightly long winded manner, but I'm going to create the individual x and Y components on some them up now because I don't want to include minus ones.

  • I'm using the turn of the operator to make that check for me.

  • So if it is less than or equal to zero than the value that I am adding is equal to my current location value.

  • Once we've got that result in Vector, I can quickly work out the magnitude of it and populate my corresponding flow Field X and flow field.

  • Why array locations with my Grady in Vector?

  • I'm going to modify the cell drawing loops to include a way of visualizing this factor.

  • And this is one of those times where wish had copied some functionality over from the council game engine into the pixel game engine, and I have from neglected to do so.

  • I will rectify that.

  • At some point.

  • What I want to do is draw a little arrow to show the vector, and I only want to draw an arrow if indeed there is a narrow to draw.

  • So I'm going to check that the height value is not an obstacle.

  • Now my arrow is going to be a fairly standard arrow, is going to consist of four points knowing that I've got a D X and Andy.

  • Why value?

  • I can use the Aten to function to give me the angle which will call theater.

  • That's basically the angle that I've drawn in the dashed line here.

  • I'm going to assume that my arrow fits in to a circle drawn by that dash line.

  • There with radius are now that I know theater on our I can use some simple modifications to co sign and sign functions to give me the coordinates of these four points of my arrow, between which I will draw the arrow lines.

  • So I used the Aten to F function to calculate the angle of the field at that particular location using the Deac senti Why components on the radius is a function off the cell size and border width.

  • I'll also need to calculate an offset, which is where I'm going to draw the arrow in screen space onto the four arrow locations.

  • I'm just using co sign and sign on slight modifications to the angle to give me the offsets for the left and right hand side of the arrow on a slight modification off the radius.

  • Then I'm just going to draw the three lines that make up the arrow between the relevant points.

  • Let's take a look.

  • So now what we can see with our arrows is something that indicates the field of flow towards the target location.

  • Let me put in some obstacles.

  • We go on, we can see that the field changes is necessary on what this reveals, if you haven't already picked up on it already.

  • Is that one passive?

  • The algorithm essentially gives you a mapping from any location in the space to the target location.

  • So it doesn't matter that my object is starting at this location.

  • I could choose any location for it to start at.

  • We can see that the flow field doesn't change it all, and so this becomes very useful.

  • But if you've got many, many agents that you want to get to a particular location, so imagine you've got a flock of sheep, for example.

  • The sheep are scattered all over the map.

  • They can all gradually flow to that particular target location with one pass off the path finding algorithm.

  • I wouldn't like to use this algorithm in a situation where I've got multiple agents trying to reach multiple destinations simply because I'd have to create the entire wave propagated field map for every single unit I'd prefer in that case, to use something like a star simply because a star is a guided version of

hello, and it's been a long time since I've done any of this, but I thought it's time for a part.

字幕與單字

單字即點即查 點擊單字可以查詢單字解釋

B1 中級

路徑規劃#2 波的傳播,勢場和現代(就是)C++ (Path Planning #2 Wave Propagation, Potential Fields & Modern(ish) C++)

  • 8 0
    林宜悉 發佈於 2021 年 01 月 14 日
影片單字