Observable Flutter: Hit boxes and pathfinding

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
CRAIG LABENZ: Hello, everyone, and welcome to another episode of Observable Flutter. My name is Craig, and I am your host. And, today, we're continuing my adventure-- our adventure, our collective adventure, building an infinite zombie shooter game. And in the previous two episodes, if you've not seen them, but are tuning in today, first of all, welcome. And in those previous two episodes, first one just set up some scaffolding, got that all going. And in the second one, actually uploaded an actual game map and got camera mechanics working correctly. The only problem with the game map was that nothing in it was real. So there were houses and you could just walk up the walls and on the roof and whatnot. And I really was tickled last week referring to that as climbing mechanics in the game. [LAUGHS] But, today, we're going to fix that and make those houses and objects like trees real so you can't just walk through them. And then, if there's time, might even add some pathfinding. But I don't have any zombies in the game yet. Honestly, pathfinding might have been a little bit ambitious of me to put in the card title. But we'll see how far we get. All right. Well, let's get started. So I think I'm going to begin with a little tour of the code that we have from these previous weeks. So let me blow this up so it's legible. Maybe this is too big. What do you all think? Is this too big? You can only see a couple lines. I'll leave it here for now. Let me know in the chat if this is cartoonishly big, or just right, or some such. NameTopSecret asks, what is the Flame version you're using? Well, the latest, 1.8.2. So a little tour of the code. In main.dart we have very standard stuff. I'm initializing a single instance of the game, passing it into the root app. And in that root app, basically just not even using a scaffold, just going straight into-- I don't even know if we needed the Material app, to be honest-- but basically going straight into this game widget. This comes right out of Flame. And then, we're excited about our game, which we just saw up here was called a zombie game. So the zombie game has in it a world. And this is not the movie. [LAUGHS] But this zombie world contains all the stuff, so all the terrain, all of any houses, fences, doors, eventually maybe zombie spawn points, stuff like that. And this is all going to be specified in the world. And last week, we got the camera component working to follow the player around the map. And in here, onload, we're adding some sprites and, honestly, not really using many of them. We are using the adventurer, but I don't think we're using this town one anymore. That's being loaded separately through a program called Tiled. And where is the Tiled implementation here? What's going on? What's happening? I thought that was in this file. Isn't it? I know I committed all the code at the end of the last episode. So it should be there somewhere. Yeah, land is a file we're not even using anymore. This was from week one. Can get rid of that. Yeah, world, it should have been-- oh, I was looking at the game. I thought I was looking at the world because I'd started talking about the world. There we go. So, in the world, this is what contains all the stuff. Here we set up just the size of everything based on how big our tile map is and then how large we're scaling it in the game. This land map, or a list, also just unnecessary. That's from week one. It's obsolete because we're now, again, using this Tiled component that I was talking about. And it's pretty fun. You use this other software called Tiled to build a map. You put down sprites where you want them. And then, this exports an XML file that is an industry standard. And so, Flame also supports it. And you can look right at the file itself. And it's just a bunch of really fun [LAUGHS] XML. But, actually, the fact that it is plain text here is pretty handy, because I was doing a little research for the first part of this, and you can figure out what the API is to consume all of this data by being able to read the XML. So that's actually quite handy. So in the world, we load up the map. We add a player. And we put those both in this world component. Again, in Flame, there's a component tree, just like in Vanilla Flutter there's a widget tree. And, ultimately, we tell the game-- we tell the camera component to follow the player. And that gets us to where we were last week. I added a little extra stuff here to make sure the camera couldn't go out of bounds. But that got our game feeling close to-- started to resemble a game. So it was pretty exciting. And I'm going to just run it real quick so we can see where the last stream ended up. And then we'll carry on. Oh nice, there's an error. [LAUGHS] What is this? Asset doesn't exist. Oh, yeah. I've been making this one modification. Every time I export the asset-- I have to figure this out-- it's exporting with a certain path that the Tiled program thinks is correct, and it's not the correct path for Flame and Flutter. So I've just been making this edit every time I export the map. Not a very robust build system, you might say. So now we have our map and our player can walk around. Nothing obstructs the player. Oh, hey, here we go. I was looking at this earlier and I wasn't seeing the castle. But now the castle is there. So that's great. So this is where the last two weeks got us. We can see the camera's following the player. When we go to the edge of the map, the player can go all the way to the edge, but the camera doesn't clip into nothingness. And you could design it differently. You could put half the camera's width worth of mountains or, obviously, impassable terrain so the player always stays centered. But we didn't opt for that. And here we are. So now we're going to make it so that you can't just climb this house, and you can't walk through trees, and stuff like that. So let's get started. Now last-- yesterday, I was doing a little prep for this. And because I knew I was going to have to read documentation and watch YouTube videos maybe. I ended up just watching-- or just reading documentation. But I didn't think that was going to make good television. So I did a little preparation to get us-- to get myself set up on the hitboxes stuff. And so, I've added, in this Tiled program, an objects layer here. If you've not used Tiled, then much of this jargon isn't going to make a lot of sense. But I learned it in a couple hours just by watching a YouTube series about it. So it's not too hard to get into. And you've got different layers here. So we've got the ground layer that also, apparently, not all my ground is in the ground layer. I found it very easy to put tiles in the wrong layer. [LAUGHS] And then, I have my objects layer. And, well, these are object sprites. And then, there's an actual objects layer. And this is for stuff. And here, this is where you designate certain spaces on the map and assign properties to them. So we can click on this object here. And I'll right click-- object properties? No. What do I want? There's a-- wait, how do I edit the object? Maybe we just add a new one if I can't figure out how to select it. OK, I think it's selected now. No. It just wants to delete it. I'm not going to lie. I do have a hard time with the menus. [LAUGHS] I'm not very good at navigating this. Where is the thing? What's going on? There's a way you can add custom tags to these. I was doing this yesterday. Yoopi asks, can I have the YouTube link or the title you want to learn about the objects? Yeah, I will share that. It was really good. Seven years old. So a couple of the buttons have changed places. But it is still quite good. Also, by the way, at the end of every episode, I just commit whatever code I have done and push it to this repository, which is open. And there's a branch for every episode. So, right now, there's two branches other than main, stream one, stream two, chapter one, chapter two, I forget what I call them. But, yeah, this is where you can check all this stuff out. We might just have to add a new object here because I'm failing to remember how to actually edit out the darn thing and see it's-- yeah. The biggest learning curve in this app is just figuring out what buttons to click. Let's try to add a new object. That'll be good. So I added both of the existing ones as a polygon, so a polygon around the house. And then, there's this little polygon at the base of the tree because I want the player to be able to walk behind the tree. So let's add a new one. I've got the polygon thing selected here. Nope, almost had it. Let's add another one around this tree, I guess? So basically just click here. Click. Decide what space should the player not be able to walk in, or what space should be whatever we're worrying about right now. So I've added this polygon. Right now it means nothing. But, here, I can click object properties and not have the thing show up. It was showing up yesterday. [LAUGHS] I don't know what-- yeah, none of these are it. Honestly, I don't know what these screens even do. It was just a pop-up. Oh, it's on my other monitor. Goodness gracious. Here we go. So you can name them so that you can find them in code that way. I haven't really thought too much about how I would super organize these. All I've done is add custom properties. So I'll add another one right now. And the thing I was naming this was blocks movement. And it's a Boolean. And then, of course, this does block movement. And now, I think-- oh, I'm about to draw another one. I don't want that. Now, I bet I can click another one-- object properties, there we go. It's on this menu again. So we see that that is actually-- this was set up yesterday. So now, both of these trees and this house have a polygon defined around them with the tag blocks movement set to true. Pretty exciting. Now I'm going to re-export this because, in my code, in what's in the app right now, this last one that I've added isn't there. So I'll say file export. Is that window going to another thing? Maybe I need to do export as. Here is the window. World TMX save, replace. Looking good. Now we'll go back to World TMX, make this edit again, assets. So we're ready to actually use these objects. And, again, I did a little homework on this yesterday. So I kind of know what to type for the first part of the stream today. So we have some polygons that are now defined in this map. And we can actually see them in the XML file, which is just really fun, I think. So we've got our different layers here that we were just looking at in the Tiled program itself. Here's layer one ground. We saw that. And this encodes all the squares, which asset is in them, I guess, is what this all means. And then, there was object sprites. So this specifies where the houses are and where the fences and that castle are or whatnot. So most of this layer is empty, which we can see a lot of zeros and then some other assets. And then, if you keep scrolling down, we get to the objects layer. Maybe you could name these better or something. But the objects layer just has the stuff that we were just talking about. So we've got a blocks movement object here. And it has its starting coordinates. Actually, it took me a minute to figure this out yesterday and it might take me a second to remember because it just deleted everything that I wrote. [LAUGHS] And then it has all of the points. And I believe, yeah, all of these points are relative to this point. So this specifies this whole polygon. And then there's just these other ones. I didn't give them classes or names or anything. That actually would have made this probably easier to read. But for now, I only have one kind of polygon, and that is unwalkable terrain. You can't walk through a tree and you can't-- we're deleting the climbing mechanics from last week. You can't just climb up the house. So this is the XML file. And we need to get this into game components in our Flame code. So let's get on with that. So it all exists in map. Then there's an object. So map is a Tiled component. But components are the primary thing in Flame. And then, all of the actual Tiled goodness sits on this attribute called Tiled map. So in Tiled map, we can see, there's a whole bunch of different things. But we want to look at its-- I think there's a get layer method. Yeah, get layer-- what was it? [LAUGHS] Get layer-- oh, what are you-- what were you called? Let's see. I already don't remember. Tiled component. Yeah, blah, blah, blah. I wonder if I read this in the documentation yesterday. Render. I should pull up some documentation. That's always really helpful. Let's go here. And Flame Tiled. And let's actually just read Flame titled. It's amazing how hard it is to remember what to type when it's not in front of you again. So, like always, Google takes you to-- well, not like always. But, often, Google takes you to the original version of Flame which, Flame folks, y'all's SEO game must have been on point for that initial release. All right. Flame Tiled. This is what I'm using. And layers, yeah, this is what we want. Oh, get layer. Is it just a floating method? Well, that's weird. Doesn't seem right. At Its simplest, layers can be retrieved from a tile map by invoking get layer. I wonder if we don't even have to do the .tileMap thing. I'm going to close this. So map-- .tileMap dot-- it wasn't from here, right? Get layer? No. Tiled map. Oh, tileMap. OK. Get layer. Oh, I think I wrote the wrong thing. So, yeah, the layer that we want to get is in object group, which I deduced yesterday, as I was doing prep, by seeing that they're called object groups. So I thought, well, I bet that's what we want. So object group. And then we give it a name, I think it was? Yeah, my blabbity blah. So for us, that name is objects. So we'll grab that. And there we go. Well, I guess they should be single-- single quotes. So we now have all of the stuff that we've defined in our map. So this will be the object layer. And the type of this should be object group. Yeah, and it's nullable. But we know it's there. So I'm just going to put this bang to get rid of it. Now, object layer has an objects array. So we'll say for object in object layer.objects. And objects itself is a list of Tiled object classes. So we can start to look at what is available on our object. Now, here, there's a bunch of different things that this could be. And I've already explored different parameter types here. So is polygon, of course, is a Boolean. And it tells us whether or not this is a polygon. Polygon itself is a list of points that presumably is null or empty or something if this is, in fact, not a polygon. And then, the other thing that we need is x and y here. And x and y are basically the global coordinates of our first point. So 0, 0 here, this first point, this is relative. It's zero pixels to the x away and zero y pixels away from this point. And these are our global coordinates. So we can say, for-- oh, wait, actually, we have to make a few checks. We'll say, if object is not a polygon-- so if you're not a polygon, then we'll continue and not examine this one in the loop. And we can also say if object-- now, there's the properties as well. Where were the properties? Oh, properties right here. And, remember, properties in Tiled itself, wherever that app is, looks like it's in this same window-- properties were where I said, can block-- or, no, blocks movement equals true. So we should find that in the properties array, so properties. And for this part, I did a little bit of digging because properties is just a list. And it's kind of annoying. But there is some really nice-- this was what, called a Tiled-- what are you objects? Tiled object. So Tiled object here has-- there's some nice little helper. All the properties are indexed by name. What was that called? (SINGING) Doo, doo, doo. Hmm. OK. I don't know where I found that yesterday. Maybe it was-- what's in properties? So what did we say this was? This is a Tiled object. So now it should be able to tell us what properties is-- custom properties. There we go. Oh, yeah, we have to click on the custom properties class. There it is, by name. So this will allow us to not loop through the list ourselves and whatnot. Yeah, Clint Purser is on it, custom properties. Thank you. So, yeah, custom properties there. So this means we have a by name object. And-- oh, wait, it didn't name them. What am I talking about? I think I just have to do first. No. Oh, should I give these names? Was there by tag or something? How did I do this yesterday? What was my thinking. Back in the TMX.dart file. Oh, the property has a name. Right. The object itself does not have a name. The property, of course, does have a name. Duh. One day. So we'll say, by name. And this was a map. So it's not a method. That means we can ask, do you have the key blocks movement-- which we should spell correctly. And if you do not have that key blocks movement, we will return. I'm assuming here that this will never be set to false because then I would just not have the property at all. So in some sense, this doesn't even need to be a Boolean. It's actually just its mere presence indicates truthiness. But we'll worry about that later. So if you don't have block movement return because you're some other property, and right now I'm just worried about the unmovable terrain stuff. And now, I think we're ready to start thinking about the points. And the first thing that I did yesterday was I just wanted to make sure I was understanding where they were and that I was measuring stuff correctly. So I started to make-- I was adding these really simple components to the game and-- oh, one second. I got to send someone a message here. So I was adding really simple components to the game and just drawing a little red square in spots to be like, yeah, those are where I put the components in the Tiled map. So let's do that again. So remember, object.polygon is a list of points. So we can say for final point in object.polygon. And these are points. And a point is basically like a vector two, similar to a vector two, in that it has two doubles. In fact, I don't know why it wasn't a vector two now that I think about it. So we can just add a component here. So we can say, add. And I was playing with a rectangle component yesterday. And the position of this is going to be a vector two, where the x value is the point .x and the y value is the point .y. And then, I wanted it to just be a little red square. So we can add a paint for that. And this is the same paint that comes from Flutter. So we'll add-- I mean, I could add just painting here. But I'm about to use a color as well. So we'll add material. And this paint is going to have a-- I think we just say color equals, like colors.red. So let's test this. I remember having more lines of code yesterday. But I don't know what they were. So we'll see here. And let's run it. Let's see what we get. So hot restart. It's thinking. We're back in the game. Now, we don't see any red squares here, which could be because I did something wrong, or it could be because I did it right, but I'm at the checkpoint that I expected to be at, which is that all the red squares are up in the corner. Nope, they're not even here. Oh, I do remember one thing. I had to give them a higher priority so they draw above the map. So priority-- the map has a priority of one, if I remember correctly. Actually, I think it's literally-- nope, don't remember where I specified that. Back to the game. Do we have red squares now? Oh, still no. Nice. Great, our first bit of debugging. What am I doing wrong? Vector square. [SIGHS] Oh, you probably need a size here. So this would also be a vector two. Oh, yeah. For some reason, I thought I had the size specified just from this position, which is, of course, bananas. So we've got a little thing there. Back to the game. Up to the corner. Aha. So we have some red squares. Clint again. You are really paying attention. You've eaten your Wheaties this morning, and it shows. So we have our red squares. They're all in the wrong spot though, which is a little annoying. So they're all zero indexed. But that makes sense, remember. I mean, they're really shoved in the corner here. There's two problems that we have to solve to correctly position these in the game. And the first one is that we observed that the first point always said 0.0. But it was relative to the actual global coordinates. So we're not yet taking into account object .xy, we have to do that. And that's one of two things that we have to do. So back to world.dart. Our position here is now going to have to be plus object .x. And for the y value, it's plus object .y. Now, if we reload, it will still be wrong, but it'll be less wrong. So I scroll over here. And we've got our points here. You can see that the house is outlined here. Then we have our two trees outlined. And they're positioned relatively to each other, but they ain't in the right spot. And that is because we are scaling up the entire rest of the game. And there was a file we set called constants. And there's three important values in here right now. One is the tile size. This is the actual physical amount of pixels in the tiles that we're using. Then the other one is how big we want them to actually look on the screen. So here, everything is just being blown up times four. But the Tiled program doesn't know about that. And so, that means that this file has no idea that all of these values should be multiplied by four. So we'll go back to world.dart and multiply all of these things by the world-- what is it called? World scale constant. So times world scale. Then here we'll say parentheses again and times world scale. And it'll format for us. And now, when I restart, we should have red dots on all the corners. Except they're still kind of not right because, you can see, they're just all offset. But that is a matter of their anchor. Now I'm remembering why I had more lines yesterday. [LAUGHS] So, by default, Flame positions things-- things have however much size they have. But then, their position is one coordinate. So that, of course, begs the question, well, where in the object should the coordinate be? And, by default, it puts that in the upper left. And you can specify that via the anchor property. So if we say anchor here is a anchor.center, which is the type of anchor that makes the most sense for my brain. And I was certainly clicking on the center of things when I did that. When I drew these polygons in Tiled, we can see that all of the polygons go back into place. These two are nicely on the corner here. And we're drawing stuff. Now, these polygons don't mean anything yet. Of course, the existence of these red squares will not prevent us-- they will not disable our climbing mechanics just yet. [LAUGHS] The climbing API is still alive and well. So, now, things already get pretty-- they get more fun. And I implemented this one way yesterday with hitboxes. But as I was thinking about it last night and this morning while I was walking my dog, I had another idea. And I don't even think we need hitboxes. And I want to talk through why. So if you've made games before, you probably are familiar with hitboxes. If not, then quick crash course, a hitbox is basically an amount of space around something that can be used to measure whether or not this object is colliding with something else. So if we had a hitbox around this house and a hitbox around this person, we can measure whether or not they contain any of the same pixels. And if they are, we can activate some collision logic. This is great for shooters and whatnot, where if you're playing "Space Invader," you want to know when your laser collides with an enemy or when their shots collide with the player. And when that happens, you blow up the enemy, or you take some health away from the player, or whatnot. But movement is different. Imagine I had a hitbox here and I walked onto the house. And so, the hitbox-- the collision detection detected a collision. And it calls a method on either the player or the house. And it's just like hey, by the way, you're colliding with this other thing. So the player gets its on collision start method called in the house. The house's hitbox is passed into that method. But what good does that do me here, because I've already walked in some unwalkable terrain. So I've already gone in an illegal space. And what I would have to do in that logic is eject myself. You'd have to move the player back. And what I really want to do is prevent the player from ever walking into the place-- ever walking onto illegal terrain in the first place. And so, I don't think we even need to use hitboxes here. But we do need to add more components and check for those components. So this is what I-- I cooked up some version of this yesterday. And then I'm going to try to do maybe a more streamlined version of it now. So we know that these are polygons. All of these points, in object.polygon, these constitute specific enclosed polygons. I've got some stuff in chat here. Let's see. Why not position it randomly on the map? Billu Boy, why not position it randomly in the map, excluding popping on the objects such as house, trees, et cetera. Can you say more about what you mean by position it? What's it? I'm curious what you're thinking here, Billu Boy. Billu Boy? Billu Boy. So we need to have polygons registered with the Flame component tree, just like we've now registered all of these individual rectangles. So let's, instead of adding these rectangles, let's start to store a list of vertices. So I'm going to grab the way we grabbed-- we got this point. We'll say here final vertices equals a list of vector twos. And I'm not going to add these rectangles anymore. I'm going to, instead, say vertices .add this point. So now, we are building up the information that we need to add an actual polygon to the game. So after-- where is it? Yeah, so after this loop, this is where we would add a polygon component. And now I'm freelancing. I'm improvising off the homework that I did yesterday. I presume there is a polygon component. Great. I knew there was a polygon hitbox. That's what I used yesterday. So this has an extremely similar signature to the polygon hitbox, which is great. So we need to give it its vertices. I don't remember if it's important to give it its position or size. Those feel mutually exclusive. I don't actually understand how that would work. But, anyway, we're just going to pass in the vertices. And I-- oh, yeah, there's one thing I really want to point out. In the polygon component-- oh, actually, no. I read this in the polygon hitbox. Nope, it's here as well. Cool. So the polygon hitbox and the polygon component have this really interesting note. Always define your polygon in a counterclockwise fashion in the screen coordinate system. And, you know what? I actually think I forgot to do that-- oh, no, I did luckily define this in a counterclockwise fashion. I'm remembering how I drew these blocks. But I also defined these two yesterday in a counterclockwise fashion. So I have not dug into the logic of what breaks if you don't do this, but you have to do it. Oh, you're talking about the red squares. So you said, why not position the red squares randomly in the map excluding popping on the objects such as house, trees, et cetera? So I think we may have had-- I may have under-communicated about what the purpose of the red squares were. I just wanted to make sure I was going to be able to correctly translate what I defined-- oh, I keep thinking Tiled is on that other screen. Maybe I should just put it there. I wanted to make sure I was going to be able to correctly translate all of these points to actual stuff in code. So I just wanted to draw them to make sure that they were in the right spot. But if you have another idea, or you think I'm still overlooking something interesting, let me know. So we've added a component here. Now, this component is not-- this is completely meaningless. If we rerun the game now, the fact that component exists, a, nothing's going to show up anymore. The red dots are gone. And we're obviously still able to walk around. But this is where I had my ideas. So let's add a new file. Why did-- did I save this as untitled one? So let's [LAUGHS] actually call this unwalkable terrain dot-- or, no. I guess it's a component. unwalkable_component.dart. And this will-- we need a cursor down here. Also, let's just add to the barrel file real quick. Yeah, components.dart. So we'll need unwalkable component. All right. Class unwalkable component extends-- I guess it extends the polygon component now, doesn't it? And in our constructor here, all we care about is the vertices. So this is super.vertices. Oh, it's interesting that you can use the underscore outside of the file. I guess they probably had-- this is a really interesting exception to the private variable rule. For a super constructor, you have to be able to see it. I'd never thought about this before. Interesting. So this unwalkable component has no purpose other than to identify the role of this polygon component. So now, we are not going to add polygon components anymore, we're going to add an unwalkable component. Now, the unwalkable component still doesn't block movement. So, again, this is pointless. But now we get to the exciting stuff as I close all these other files. In player.dart, where we move around, this is our movement code that we were working on last week or two. And, oh, yeah. So I guess we can revisit this real quick. When you press different keys, you update the movement vector. And then, when you release keys, you again update the movement vector by either zeroing out the value or setting it to the other direction if you're still pressing the other key. And then, in the end, you have a movement vector set. And the movement vector actually always exists. It starts at zero. Every tick of the game we figure out what direction are you moving, how fast are you going, and how much time has gone by. And we add that vector two to your position. Then we clamp the position to make sure you can't leave the game area. So this is it. And I reasoned yesterday and did wire up, but with a hitbox that I now think I don't need, that this is where we can prevent moving into an illegal square. So imagine-- remember I was talking about here, if I wait until there's a collision detection that gets triggered, that means I know I'm already in an illegal spot. And what would I do in-- it would be on collision start. And then you get your intersection points. And then the other hitbox that you're bumping into, and it'd be like, OK, cool. I'm colliding with something, and it's a house or whatever, and what am I going to do? I'm going to have position.add, and I'm going to have some negative movement vector of what I just did or something. And I hope that this is correct. And I hope that it's called in sync with this or something nasty. And I'm definitely not sure that I'm not going to stutter against unwalkable boundaries. So I don't want to do this. So, instead, let's see if we can prevent movement in the update method if you're trying to walk into an illegal space. And when I say, let's see if we can, I already did it yesterday. So I know that we can. So this is-- I'm going to try to remember what I typed yesterday. And my thought was, [SIGHS] back to the game. Oh, Bobek. Finally caught it live. Oh, you watch all of the videos and just wanted to say, thanks for the cool content? Oh, shucks. Well, you're very, very welcome. And I'm glad that you had a free Thursday morning, evening, afternoon, or night-- or I guess it could be-- could it be Friday? Is it Friday for someone in the world right now? I think so. Maybe it's Friday. I'm glad you're here. So what I thought we could do is decide-- well, if I'm heading up, then let's get the top center anchor of my character and query the game-- the component tree for what components are at that spot. And this is a thing that you can do in Flame. And if any of the components are unwalkable terrain, then I will simply zero out any of the up movement from my movement vector. And the first thing that I was doing was zeroing out the movement vector entirely. And that would mean if you were heading toward impassable terrain, like this diagonally, as soon as you hit it, you'd just stop and be stuck. And you wouldn't slide to the left like you'd intuitively think should happen. So that, immediately-- yeah, that was a really terrible experience. And then I realized, Oh, right, we don't zero out the movement vector. We just zero out the up component of it and allow the player to keep sliding across the impassable terrain. So this is just what we're going to implement. And I think it's going to work. So we'll say, if position-- or sorry-- if movement.y is less than zero, so this means we are moving up. Oh, you know what? We could add maybe a little helper on this. Oh, no. It's just a vector two. I'm not getting into that. So this means, we are moving up so we can grab the position of-- what's it, position of-- and then we give this an anchor, I think. What is position take? It takes a vector two. That's not what we want. So we probably want position of anchor. And this takes an anchor. Cool. So this is going to give us the global coordinates of this component's anchor.top center because we're heading up. And so, this is our final-- we'll call this new top. Oh, you know what? I actually need to add-- I wonder if this is going to not work. Oh, this might move one Pixel into-- OK, sorry, I'm thinking about a bunch of different things here. Let me slow down. I have not actually updated the position yet. So for the first frame that the player is clipping illegal terrain, I think this may not calculate-- this is the old position. And so, if we're just barely clipping into illegal terrain, this won't know about it yet because we're about to move into the illegal terrain. This change to our position value is what's going to move us up another one pixel into the illegal terrain. So this line won't find it on that first frame, then this line will move you into the illegal terrain. And then, the next frame through, we're actually clipping now at the start of the update function. So then this method would detect. And this is, by would detect, it's the code we haven't written yet. So then, everything would kick in and you wouldn't be able to move more than one Pixel into the illegal terrain. I think we can live with that for now. Actually, let's do it better. Let's do it better. So let's update our position. I think this will just do it-- this will prevent clipping one pixel. We're going to remove the value-- actually, I think, yeah, we don't need this anymore. We're going to remove the movement speed if-- oh, maybe we can save the original position. Yeah, original position equals vector two. Or maybe we can say position clone. Is there a clone method? Wonderful. So we have our original position here. Save this to use after we zero out movement for unwalkable terrain. And then, here we will fake update the position. So our anchor calculations take into account what movement we want to do this turn. I didn't do any of this last night. I'm just thinking of this now. Hopefully this is correct. Let's find out. So we're getting the position, the top center of us. And now, this is where we use this cool API that I hoped Flame would have, and it did, where we have a reference to the game by way of has game reference as a mixin on our component. So that allows us to say game, and then every component, it turns out-- so the game is a component, the world is a component-- all components have a-- I don't remember what it's called-- have some method components at point. There it is. And the point that we care about is the new top of where we're going to be. And so, here, we'll say for final component in all of these components. And now we just check, are any of these components unwalkable terrain? If component is unwalkable-- oh, yeah, it's a component, not terrain. I think I called it unwalkable terrain last night. Yeah, let's import all the components. If the component is an unwalkable component, then our movement vector can't go up anymore. Oh, you know what I've just realized? I don't want to actually zero out the movement. I want to have a value that is a movement this frame. Because the player is still pressing the up key. And I don't want to zero out that value because I'm going to get to the edge of the house. They should keep going up immediately. But if I zero out their movement here, they'd have to release the key and press it again to move up. So we're only going to manipulate the movement this frame variable. So movement this frame will now have its y-value set to zero. So we were going up negative y-coordinates. Remember, 0, 0 is always in the top left. So we were going up. But we concluded that our new top center position was going to intersect with an unwalkable component. And, thus, you cannot walk up on this frame. But we are not setting the movement this frame to zero because we want to still be able to slide along the house. We want to be able to slide along the house is just a pretty funny sentence. [LAUGHS] Let's quickly make these other changes. So this is, we are moving down. So our position of anchor is-- we'll call this one new bottom. And this is the bottom center. And here, again, we set the y-value to zero. Now, this is, if the x is less than the zero, that means we're going left, so new left. Suddenly our codebase is sounding a little political. Center left is the new left. And here we'll just set the x-value to zero. And x is greater than zero, that means we're moving right. New right. That's not how you spell right. Though it doesn't actually matter. You could put asdf here. Center right. And we set our x-value to zero. Then, for our last thing, we'll say that position equals original position plus movement this frame. I think this is going to work. Stalker Prod-- it's an interesting name you've chosen-- maybe a break when collision to not go through the whole loop? Hmm, interesting. So-- oh, yeah, absolutely. Great point. This is an excellent point. Just an excellent point. Nice. Thank you. I'm pressing Command S on Chrome. It's like, do you want to save streamyard.com? I do not. I'm continuing to press the wrong buttons. All right, restarting, switching to the game. Let's see. Can we move into this spot? We can't. [LAUGHS] Look at this. Can't enter over here. Can't enter from the bottom. We just cannot enter. And the tree, we should-- yeah, blocked on the tree. Try to enter from this side, from the left, cannot enter. But we can go around the tree. I have not yet programmed a way to have the person be rendered behind the tree here. That's going to be important for any amount of realism. And you can play with the hitboxes and make sure, here is-- it's kind of unrealistic that the person can't come closer than this. This hitbox actually isn't great. Why can't you even get up to the door? So let's actually just fix that right now. Let's go look at our-- oh, please stop. Actually, I don't know if I'm good at editing tiles. Oh, here we go. We're editing objects here. Let's bring this hitbox up to here, and same, and see if it's good? File export. Maybe I don't need to do the export as world.tmx. Did you get the change? You did. That means it was exported. Restart. The game is going again. And I don't-- maybe that was high enough? Maybe not. But you can see how cool it is to build your game world not purely in code, which is how I would have always done it in the past. Yeah, anyway, all right. We have added hitboxes. And now, we can think about pathfinding. And pathfinding is going to be really quite a bear, I think. So this, I've done no prep on, but I have thought about while walking my dog. And that is some form of prep. Obviously, for pathfinding, we're going to use a conventional pathfinding algorithm, like Dijkstra's or, of course, actually A-star. And, oh, Clint has another idea. Let's do it, because I skimmed it and I like this idea. If you enable debug mode in your Flame game, you can see the borders around the components. Nice for debugging this kind of stuff. Yeah, I've never actually done that, but I've read about it many times. Debug. Is that on the game? Is that where we do it? In your Flame game. Yeah, this is-- oh, yeah, of course, it would have to go into the super constructor. Doesn't look like it's there. Is it just a property that we set as opposed to pass through the debug? Nope. Nothing. Clint, I want to do this. Oh, set it onload. OK, we will do that. Debug mode. There we go. True. Restart. Oh, interesting. Why is it only on the player? Huh. Clint, why is it only on the player? Oh, that's our only-- but these are also components. Maybe it's positioned components. It's probably positioned components, because these other ones are, I guess, not positioned components. I don't know how the Flame Tiled extension is positioning them, but they seem to not be positioned components. Anyway, yeah, this is cool. I'm just going to leave this on for, basically, ever. It also is suggesting that we may want to trim the image because there's a lot of empty space here, or do something about that. Pathfinding, so if you've done pathfinding before, then this little crash course in how pathfinding works won't be very exciting. If you've not done pathfinding before, then this crash course in how pathfinding works might be confusing. Anyway, pathfinding is done through a graph. It's a set of nodes that can connect to some other nodes but not all other nodes. And this is how pathfinding works in games. It's how pathfinding works in navigation software. Obviously, the nodes are like corners on the streets, and the connections are the streets. And so, pathfinding is a way-- pretty clever algorithms. I once tried to write a pathfinding algorithm myself and just-- I'd never researched it or didn't know how they worked, and tried to reason about it myself. I was not successful. And then I looked up A-star and was like, oh, I wasn't close. [LAUGHS] I'll just use this. But if I'm remembering how A-star works correctly, it starts at your destination and starts to crawl through the graph, through the nodes, back to your origin. And it's keeping track of weights and how close it is for one node to get to the next node. It's like, you've got your starting-- you've got your destination node. So all the nodes around that have a weight to get to it of one. Let's say all the terrain has the same weight. So there's a weight of one from all of those. And then, you crawl out from those. And the weight from the next line of nodes says, well, to get back a layer, that's an additional one step. So my weight, I guess, is two. It's your weight plus my weight to you. How long does it take for me to get to you? And you've already calculated how long it takes from you to get to the end goal. And so, you just scan all the nodes like this. And then, once you get to the player, then you'll have-- and you scan exhaustively because you could miss a shortcut maybe. Oh, no, yeah, you always expand through the lowest weight as well. All of our weights here are one, so it's not going to be a huge issue for us. But, anyway, once you have a set of nodes that you've explored, you pick which one has the lowest weight, and you look for its neighbors. And you keep going until you eventually find the player. And then, the player is going to have nodes around it. And it will just walk into the one with the lowest weight. And then it walks into the next one with the lowest weight. And it walks into the next one with the lowest weight. And it eventually keeps going until it finds the player, or it finds the target. So, for us, that's going to ultimately be a zombie that's going to be heat-seeking the player. Now, I was talking about this graph. And this is the component-- or this is the aspect of pathfinding that I'm not really familiar with in games because we don't have an obvious graph. You could say every single pixel in this game is a node in the graph. And that would be a graph. But that is probably not a very performant way to calculate pathfinding, because our world is bigger than it needs to be. And, also, and lots of games have big worlds. So I think there's got to be some clever tricks that game developers do for this. And this is where I'm really just freelancing and guessing on the fly. But what I presume makes sense to do is pick a granularity and, I guess, playtest different granularities. So we have all these different tiles. If we go back to the tile map, we can see the tiles better. We have all these different land tiles coming together to make our game. Well, what if we said-- oh, Randal, what do you got? Maybe weighted vectors toward vertices? Yeah, I think-- so, Randal, brainstorm with me here-- and anyone else. The question is, where are the vertices in this game? Where are the nodes in this world? What I'm thinking-- and I'm open to better ideas, because I'm certain there are better ideas out there-- but I'm thinking we could say, for each square, like this, in actual pixels of the asset files there are 16 by 16 pixels. So the maximum amount of nodes in our graph, our pathfinding graph that we could have in every single square, is whatever 16 times 16 is, or 256. But, oh my gosh, that's so many across this whole thing. So we can't do that. That's wild. The minimum granularity that we can have is simply one node, like in the center of the square, or maybe top left corner, or whatever we pick. That would also be a possible node. That's a possible granularity we can have. It's a possible graph. But then, our players are going to move in really weird ways. They're going to go-- if you're here, and you can't go through this spot maybe. Oh, we moved the hitbox, so you could. But let's say you still couldn't. All the zombies would just maybe be taking these really staggered steps. And they're not going to move fluidly. It's not going to feel natural in this game. Everyone's going to be moving in really regimented ways. So we don't want one node in our pathfinding graph per game square. I don't know what the right number is. Maybe it's four per square. Maybe it's 16 per square. I'm not sure. Randal, what do you got? You could look for corners instead of lines in your source data or any non-walls. Yeah, so I feel like that would be a very-- if we could do that correctly, that would definitely be an elegant solution. And one thing that I was imagining, as I was walking my dog, let's say-- I'm going to add some more. Oh, no, I don't have them added to this. I'm going to get real silly and creative here. Oh, this is a perfect one. Ground, no, we'll put this in object sprites. So we've got an arrow here. So this is the target. [LAUGHS] This is where you're trying to get to. And there's nothing here that really represents a player. So I'm just going to be whatever this thing is. So this is a zombie. And this is a player. This is the target the zombie is seeking. If we had pathfinding at four by four intervals, it would say something like, maybe there's four nodes per block. We could have-- it would go to the middle here. Then it might go up here. Then it might go here. Then it might go here. Then it might go here. And it starts walking over like this. And it would be again still regimented. You'd see the zombie taking a bunch of 90-degree turns, which is just not going to be satisfying. But what you could do is say, how many turns-- how many of your steps have the same trajectory? And then, actually, just skip and make that your first point. So we're actually going to seek here, if our first thing is up and to the left, and then up and to the left again, and then up and to the left again, we'd say, well, don't go up and then don't go to the left and up and to the left again, just-- actually, no, that algorithm is going to be really tricky to implement. I'm definitely not going to be able to do it in the next hour. Hmm. So Randal, you're saying, look for corners. So another way we could do this is, you could draw a line between your target and the-- again, I feel like this is just going to be nuts. You could draw a target between your line, your actor and its target, what it's seeking, so the zombie and the player. And you could say, do we intersect any unwalkable terrain? If we do-- if we don't, that's easy. Just go on that line. If we do intersect walkable terrain, then this is where that algorithm would come in where we'd need to figure out, OK, which of our corners is closest to-- some mixture of closest to us, or maybe it's just-- no, wouldn't be closest to the target, because that would, of course, involve just cutting through again. Maybe we'd say closest to the target that doesn't involve any more intersections. So this doesn't involve any more-- if the zombie gets here, going to this point doesn't involve any more intersections. So that's cool. But at this point, which also involves no more intersections, doesn't-- it's closer to the target. So we'd actually say, the algorithm could be, draw a line from the zombie to the player. Oh, no. We clipped on walkable terrain. How far can we get? We get here. Now figure out all of the nodes, which one is closest to the target but has the least-- or has no clips, doesn't take us back into the unwalkable terrain. And then, what we'd actually do is we don't even walk to this spot and then cut over. We start here. We do this whole calculation. We figure out this corner. And we just walked to that corner. Is that what you're saying? The Bresenham algorithm takes a diagonal and turns it into a horizontal and vertical steps. Is that what I want? I mean, I kind of want the thing to move diagonally where appropriate. Michael Schwartz says, step one, go directly to the target and find out if a box prevents the next or present move. Yeah, that sounds like what we were saying. So we draw a line from the target to the player. And then we would calculate intersections, which I hope there is an algorithm for in Flame. And then, yeah, we look at all those points and figure out which one is closest to the player. But getting to that point doesn't involve intersecting the polygon again. And we actually just walk to that point as our first step. I think it's reasonable. Should we try it? I never thought I just wasn't going to use A-star. But maybe I'm not going to use A-star. I have no idea. I've got a little bit of time left. Definitely not enough. Let's see how far we get. This is going to be fun. So the question that we need to know-- oh, I don't even have a zombie. Oh boy. Not a good start. Oh, I will add-- yeah, I'm going to-- we gotta add a zombie. Here we go. Let's go to Finder. And in characters, there is a zombie. Oh, I wonder if this is already in that asset that I made. Zombie cheer, that's what we're going to go with. That's pretty fun. Oh, cheer two. Oh, what a festive little zombie here. Anyway, cheer one, that's what we're going with. Assets? Yeah, we had this generator here. Cheer, so adventurer. Do we have zombie? Yeah, zombie poses cheer one. Here we go. Great. So this asset, this is also from episode one, I just used the asset gen package. I think that's what it was called. Pubspec.yaml, what do we have here? Assets gen, really good. Love it. In our game, this is where we load all the things. So let's load assets. So we're going to cache our zombie pose one again. Now we need a new character. Did I just put-- oh, put player in components. That's interesting. Anyway, whatever, zombie.art. And I'm going to copy some code out of player. What do I need here for my constructor? It's a sprite component for sure. So this is a zombie. Please leave. Great you're gonna take a position, and a size, and an anchor, and a priority. So I'm going to grab all this. And you are the zombie. And you take a position instead. So this is required this dot position. Nope, super not position. And we have to import sprite component so this stops yelling at us. So we don't need to do this anymore. And I'll keep this size, keep this anchor, keep the priority. Looks great. Oh, this is closed. All right, there we go. We've got a zombie. And now, I think back in our world, we can add our first zombie, world.dart, of course. I'm going to get rid of this commented-out code. Let's add a single zombie. Oh, sure, we'll do it up here, I guess. Zombie, oh, yeah, probably need to make this like an actual instance variable and not just local to this method. Late final zombie, zombie, import. Oh, yeah, components.dart. This is the one I want. Let's add zombie. Now we can get rid of this redundant import. We've got a zombie. It needs a position. So let's-- I'm going to change the positions to what I just drew here. So our player is going to go in this spot. That's two pick-- it's one, two, three, four, five, six, seven, eight, nine, 10, and two down. So where does the player-- that was in the player's thing, yeah. So you're now 10 and 2. I don't know if we need the 0.5 stuff. We seemed to before, so I'll leave it. And then, for the zombie-- so that was 10, 11, 12, 13, 14, 15, 16 and two, three, four, five, six. I don't remember what numbers I just said. Where's the world? (ENGLISH ACCENT) Where's the world? Where's the zombie in the world? Oh, I'm in the wrong file. Here we go. So position is going to be a vector two. And how did I do that in the player? I should have a really-- I should have a convenience constructor for this. Yeah, it's like all of this shenanigans that we need. So this was 16 and five, something like that. All right, I don't know. Let's start. Do we have a zombie? Players on the house, zombie doesn't exist. But that's probably because I need to build properly. We also see that the player can-- I guess needs to be nine. So I have to rebuild I think because of the new asset or something. Oh my goodness. Harshit, you don't know how lucky you are with the person below you in chat. [LAUGHS] Said, can I ask a question regarding AdMobs and monetization of the application? I'm stuck, and can't seem to find any solution for it on the internet, or Stack Overflow, or any other place. And your question has been seen by ex-AdMob devrel, now Flutter devrel, Andrew Brogdon himself. Oh, I didn't add the zombie. Thank you, [? Fadi. ?] [LAUGHS] Yeah, man. That's like the old-- in the early days of Flutter, folks would change values in their widgets and not call the please render method. And that's why it was all wrapped in the set state method. So people would stop doing things like updating some values but forgetting to press the please render button. And I just have that all the time in Flame, always forgetting to add the component. Nice, we have an error here. We're moving forward. Sprite does not equal null. Is the assertion that failed? Indeed. So the player, where did I set the sprite in the player? In onload, yeah. So when the zombie-- here we go. Except, oh, you need the game ref. Does the player have the game ref? Yeah, it does. I'll add you to the zombie with has game ref. Probably have to import some stuff, like experimental, because has game reference is replacing has game ref. And we add the zombie game. There we go. Now we have the correct one. So let's go back to zombie game and grab this thing, make you not look like the character. Bring in the actual assets file itself. This might work. That's not the right one. Hey, we got a zombie. It's too far over. Let's bring it back to the left and down more on the y. So our zombie was added in the world. And this is way too much. Let's go 14, and then lower. So 6.5, refresh. This is going to be good. Ah, ha. All right. We are ready to have this zombie try to seek the player. Oh, it's getting exciting now. Also, Harshit, I'm just so happy for you. This is some high quality attention you're getting. We need to draw a line from the zombie to the player. And we need to figure out where does that line intersect any unwalkable terrain. Oh boy. Don't know if these APIs exist in Flame. Don't know what I'm doing. Void onload. Here we go. And it's an override situation. Let's Google it. Flame calculate line intersects components. And I'll add Flutter Flame so Google knows what I'm talking about. How to calculate the relect of a raycast? Oh, raycast, yeah. That seems like a word I want. Clint, you are right. That is correct. Magic happens when you tune in live. I think if you're reading, my English is not good. I bet your English is great. The formula for calculating a reflected vector-- that's already not what we want. But we might see some methods that we're interested in. So test game, create boundaries. What does create boundaries do? La, la, la. Oh, a lot of stuff here. Looking good, Mr. Game. I'm going to actually scan through these [INAUDIBLE].. So we do have a raycast method. Oh, this is a raycast method on a component. Does that mean we can raycast from our cells? Raycast, no. We don't seem to be able to do that. Where did this come from? Is it a mixin maybe that we have to add? Forge 2D game. Oh, that feels like a-- that is a 2D physics engine. It feels like a place that would add raycasting. Let's go to the bridge packages and click on Forge 2D. Nope, I don't think this is going to have what we want. Joints-- is that all that the Forge duty does? I thought it was a whole 2D physics thing. All the docs are about joints. Maybe I'm actually just not clear on what Forge 2D is. Interesting. Yeah, I don't see anything else here that's going to suggests it's going to add the capacity to do raycasting. Actually, let me go to the packages documentation itself just to make sure. Forge 2D, this is basically the same page I was just on. [LAUGHS] Oh, the publisher is Flame Engine. So I don't know if we're going to have any extra docs here. More docs can be found where we just were. All right, we're back to-- let's just look at our-- look at the API ourselves. Let's look at what can components-- oh, I'm re-adding onload. That's really smart of me. Zombie, I think it's going to be-- I mean, probably in position component? Let's see what we got here. This is probably where anchor-- position of anchor was. And then-- oh, where does the components at? How did I query? How did I do that? How did we figure out when we were clipping the house? That was done in the player's update method. So in the player update method I said, oh, just components at point. Yeah, so the question would be components in line or something. Absolute position of anchor to local, absolute to local, position of anchor. A lot of anchor logic here, very stable. Angle to, that could help us. Flip horizontally, flippity, flip, flip, flip. Render debug mode. Oh, nice. Render tree stuff. Absolute rect. Set by rect. No lines. It feels like it would be in a position component thing. But let's try component because maybe components at point-- yeah, components at point is actually defined here. So it could be in this file. Let's scroll through these functions, see what we get. From the top, component, looking good. A lot of docs, wish I had time to read them. Scrolling down, scrolling down, loaded, mounted, removed, parent, children, children factory. I love when programming yields funny names. Find parent, first child, last child, ancestors, descendants, propagate to children. Man, we need an estate tax in here. Find game, contains, on game resize, onload, on mount, on remove, parent resize, update, update tree, on children changed. Well, they are going to grow older, you know? Render, render tree, add, add to parent, add all, add child, remove, a bunch of remove. Change parent. Whoa, man. That's cold. That's real cold. You can't just swap parents like that. Contains local point. Contains point. Yeah, if what we want exists, it's going to be here. Components at point. Oh, no, now we get into priority. Hmm, interesting. Interesting. You know, I have an idea. I have an idea. The polygon component has collision detection mixin. Ooh, interesting. I'm going to-- I'm keeping that in mind. And I might combine it with what I'm thinking right now, which is that yesterday I was looking in the polygon component. And for that, I'm going to go back to world, because this is where I can-- oh, yeah, unwalkable. Now I can click on Polygon component. So this, I think, has its own contains point method. And the contains point involves lines, global vertices, render, render debug mode, contains point, get edge, is outside. Oh, no. They didn't go with the raycasting algorithm. I thought we might get to see some raycasting stuff. So the has collision detection mixin, that's quite a good idea. That's what we're going to do. Let's look there. Zombie world has game ref. I'm just going to say has collision detection so I can click on it. I'll have to remember to get rid of it. Actually, let's read a little bit. Keeps track of all the shape hitboxes in the component's tree and initiates collision detection every tick. Hitboxes are only part of the collision detection performed by-- you know what we could do? You know what we could do? Would this be silly? What if I added a line component from-- every update cycle, I add a line component from the zombie to the player and give the line component a hitbox. And then, when the hitbox collides with a polygon-- which, of course, it would also have to have a hitbox-- then we would change our navigation. Is that silly? [LAUGHS] I don't know. You can experiment with non-standard collision detection such as has quad tree collision detection, sometimes bring better performance, but it's not guaranteed. This mixin is useful. So it has generic collision detection. Oh, wow, there's not a lot here. So we need the other mixins. Yeah, let's look at shape hitbox. So this implements hitbox. A little mixin, OK. What are we in here? In the shape hitbox class? What other things are here? Collision pass through. Is that for tunneling? Collision callbacks, collision detection. Collision detection, add on. Doo, doo, doo. Hmm. So, yeah. I mean, another thing we could do-- oh, what? Moderator? Why? Can we unmoderate Andrew, please? Andrew is helping out. Oh, no. Andrew, did you lose what you typed? Ah, that's so painful. Mm. And the messages are gone? They're not even there? Oh, no. Moderator, can you unhide any messages? Is that a thing? I don't know if it's going to work. What we could do is we could cache-- there's no way this is going to happen in this episode. The rest of this episode may just be planning how we're going to handle collision detection and then implementing maybe a tiny bit of it. But let's think about this. Every time we add one of these unwalkable terrains, we know all of its points. So we could cache a list. Oh, we could cache all the vertices. And then, I can look up an algorithm for calculating if two lines intersect, because I definitely don't know it off the top of my head. And then, every time the zombie wants to move, we could draw a line from it to the player. And we just loop over all those vertices ourselves and run the check to see if they overlap. If they do, we'll cache them in such a way that it tells us we can immediately find which unwalkable terrain we have clipped. And then we'll run that check. That's what we're going to do. That's it. Great. So I'm closing a bunch of stuff, going back to the world.dart file. And, yeah, this is what we're going to do. So every single point we're going to add the last point. So we'll have a point last point. And it's nullable. And at the end of this, we say, point equals-- this is the next point. I don't know if this is going to work. I think it's going to work. I'm fairly confident it's going to work. Point next point goes here. I guess we should call this last point or something. Oh, I did call it last point. What are you complaining about? Vector two, what's the problem here? Vector two can't be assigned to type point. Indeed. So we had our last point. Yeah, we're just going to do this in vector twos, actually, now that I'm thinking about it. Vector two, blah, blah, blah, blah, blah, great, great, great. So every time here, we will say, if there is a last point, or this is the end of the list, we have a line to add to our cached list of lines. So there's probably a line class. But I'm just going to make one up real quick. And it's going to have a start and end vec two. So a final start vector two and final-- oh, that's not how you write code-- vector two, and final vector two end. And then we add the constructor. This dot start, this dot end. very. Simple. So if there is a last point, or this is the end of the list, then we have a line to add. So-- oh, we need a list of lines. We don't need this list of land anymore. So this will be final list. This is unwalkable component edges. And that's going to be a list of lines. Now, we add. So this says, lines.add. And the line is different points depending on what's up. So if we are in a-- oh, let's add-- let's make next point go up here too. That'll help us get the last one. Next point. So, in the list, we just say, if last point does not equal null, then we add this line of-- it doesn't really matter what the start and end is. Oh, maybe it will. We can figure that out when we look up the intersection algorithm. But last point and next point. Oh, I didn't call it lines. I gave it a better name-- unwalkable component edges. And then, at the end of the list, we add-- yeah, end of the loop-- we add another one, which is the last point that we had. So that's here. This is the end. Oh, no, it's the start. Last point. And then, this line ends at the very first point. So vector two first point. I'm just being lazy here because I don't want to calculate all of this again. Can you unselect, please? So the first point is going to be assigned only if it's null to next point. So the first point will be saved there. And then, this will allow us to add the first point. I'm gonna have to call add. And you have to be a line, of course. What's the problem with you? Oh, these are nullable, but they're good to go. So we have the lines. DrDraco says, can you show the performance of this game? Well, I can show walking around. I don't have an FPS overlay though. I do think Flame supports such a thing. There's not a lot going on right now. So the performance is stellar. You'd be very impressed. And did I create Flutter? No. [LAUGHS] I actually found Flutter around the 1.0 release. And I immediately realized I liked it a lot. And then I applied to Google so that I could work on it. And here we are. Andrew, I presume you will retype that stuff up and share the info, maybe on Twitter, so everyone can benefit. But I haven't really followed the questions. So maybe Twitter is not the appropriate place. So we have our lines. And I actually want to add them, just so we can see them drawn. Let's do that. Every time we add a line, let's add a component for it. And this will be add a rectangle component. Oh, no. Maybe we need a line component. Is there a line component? Hmm. So I need a rectangle component but skewed, that's the thing. Yeah, I'm not going to add these. Pretty sure the positions are correct. Now we need to look up calculate if two lines intersect, intersection of two lines. Let's consider the following case. The equation a1x, b1x, plus c1. What is c1? Where did c come into play here? I don't see a c. What is c? 8x plus b2y. What is c? Does anyone know what c is? Does anyone know how to math? I don't know how to math. Let's consider the following blabbity, blah, evaluate the point, intersecting, solving two simultaneous linear equations. Also, we do let the equations of the two lines be written in general form. So, yeah, I mean, they're the same, but we've got one and two in each of these. Now let the intersection point, x0, let this be-- wow, I'm really just not following this. Plus I have no idea what c is. Am I missing something? It doesn't say what c is. I don't think this is the tutorial for me. Let's look at another one. Is Bard-- oh, Bard's not joining the party here. Bard? Where is old Bardy? Hmm, hmm, hmm. Again, c. Oh, wait, this one actually said what c was. Why did it jump to some other page? Line, line, intersection. One of the most common line intersection. Despite the fact that it's so common, a lot of coders still have trouble with it, like me, right now. First question is, what form are we giving our lines in and what form would we like them in? Good question. Ideally, each of our lines will be in the form ax plus by equals c, where a, b, and c are the numbers which define the line. Is this like standard geometry? You're just supposed to know what c is? I think of a line as two points, xy1 and xy2. Definitely don't want this. Do you use chatGPT when you develop? No, but do I use Bard a lot. And it wasn't showing up on this search. c may mean the center of intersection. Yeah, but I'm calculating that. I guess we solve for it, is the idea maybe. Here-- may be sir c is a pure integer? Hmm, I'm not sure what that would mean. I'm not going to lie. Anyway, regardless of how the lines are specified, you should be able to generate two different points along the line and then generate a, b, and c. ax plus by equals c. What is c? Yeah, I don't know. Finding a circle from three points. Yeah, there's a bunch of fun stuff here. But it thinks I know what c is. So the only Bard tool that I use is an internal one. I don't actually know where to find Bard publicly. Maybe I'll just Google that. How ask Bard a question? Oh, see? This is what I wanted. Why didn't you pop up when I was asking about intersecting lines? Use Bard on Android. No, I just want to use it on Chrome. This is really funny. I don't know where to find Bard because I always use the internal one. [LAUGHS] Do any of you use Bard and know-- so it's just a constant. OK, great. And I guess we're solving for it or something. What is the constant? OK, someone else is continuing to help here. For example, 3x plus 2y plus 7 equals 10, where we want to find the x and y value. 3x plus 2y plus 7 equals 10. That's a line? I mean, that's an equation. That can represent a line? Man, my high school math is so rusty. I actually had-- I got a math minor in university. And [LAUGHS] it feels like it needs to be revoked. Standard linear equation format. Hmm. Open the Google app on your phone. Tap the chatbot icon. How to ask Bard a question on Chrome? Where do I go to ask Bard a question? Try Bard. There we go. Yeah, this is what I want. I use the internal one. Then I don't-- where's the link, man? Sign up to try Bard. Bard.google.com. Is that literally it? Oh my goodness. Oh, this is the internal one. Can't show it. There was no new UI there, it just gives different answers. It's the next release. All right, so we can't ask Bard because-- oh, I guess I could ask Bard on my personal. Bard.google.com. There we go. This looks promising. Try Bard. I don't know. Sure, whatever. Just [INAUDIBLE]. I know Bard's an experiment. Everything is an experiment. Please write a function in Dart to determine if two lines are intersecting. I'm pretty confident that the next release will-- oh, Bard was just updated. Oh, nice. Here we go. Are lines intersecting? Check if the lines have the same slope. If they have the same slope, they are not intersecting. A list of doubles is line one. So this is line one's double, but you can't-- a double is not a line. You need coordinates. I don't know if this is right. [LAUGHS] Let's see here. So line one, two, three, four. This isn't a line. Is this a line? This is a point. Am I missing something? This is not a line. I don't think this code is working. View other drafts. I would like to view other drafts. Well, this is kind of neat. This is the same. Oh, this is a line. This is a line. So what does this do? This is slope one. Oh, I like it. Check if the lines intersect by finding the point of intersection. Oh my goodness. This is absolutely impossibly-- there's no way a human brain could ever make sense of this. But I will bring it in and test it. Are lines intersecting? I think I'm just going to write a test for this, and then that'll be the end of the day. I might not even have time for that. So let's say, as-- so this will be a list of double. And as list, I'll call this, so it's friendly for that component. So our as list here is going to say, yeah, we just return start.x, start.y, and end.x, end.y. And then let's get our slope as well. And what is the slope of a line? Line one-- mm. OK. This is real, real lazy of me. In theory, that's the slope. Let's write a couple tests. We're [INAUDIBLE] this is going to have to be filled-- finished next week. So components, lib, test. Let's call this line test.dart. Import package, test, test.dart. Why is that-- why is that not love me? Package test doesn't exist. Do I not have it in my pubspec? Flutter dev dependencies, Flutter test. Oh, right. This is Flutter not Dart. Flutter test. All right, here we go. We're going to import the game itself. This was in components, right? It's in the world. So we'll just put it here. Here we go. We need to find out if this line class works. Line, slope should test return zero for-- we can't-- oh, yeah, for flat line. Yeah, we're going to have to check things like not having infinity slope not dividing by zero. Pretty sure there's no divide by zero check here. Return zero for a flat line. So we should say, just expect a line of-- let's have it start at 1, 0. So this is a vector two of 1, 0. Wow, can't type. Vector math 64. So we got our vector two here. This is x 1, 0. And it goes to 2, 0. That is flat. So that slope should have a value of zero. Oh. Can't type. So Flutter test? We've got one test. Does some generated code work? Two lines can be defined by four points. So the intersection will need four inputs. Yes, I agree with that 100%, which is why-- yeah, each of these lines needed to have two points. The tests failed. What happened? Negative 0.5. Let's look at the code. So 0.1 minus 0.0. This is the starting y minus the starting x. Oh, no. This needs to be three and two, right? Yep. Let's try it again. Classic-- oh, infinity. Divided by zero. One minus zero-- zero minus one is negative one, divided by zero minus two. I don't know. I'm going to do this later. This will be next week. Actually, it might be a guest next week. I'm not sure. But, folks, pathfinding is proving to be very interesting and involves a lot of geometry that I am not good at. But I think we have a good strategy. I'm actually pretty excited. I'm going to do more homework before getting into this next time. I'm pretty excited about this fundamental strategy of draw a line from a zombie to a player. This log represents a zombie because they have a similar IQ. And the target, of course, represents the player because the player will be the zombie's target. So the-- draw a line from the player-- or from the zombie to the player. Check out, does that line intersect any edges of unwalkable terrain? If it does, just visualizing this, I think the correct next part of the algorithm will be find the corner of the unwalkable terrain that is reaching from this spot that we're currently calculating. Reaching does not add any more intersections and is closest to the target of all such edges. So the two-- the line from the zombie to the player is going to intersect here. Of course, we have to go back through the unwalkable terrain to get to this one, this one, this one, and this one. So this algorithm would produce these two as the eligible corners to track. And then, this one is, of course, closest. So we're not even going to have the zombie walk to this square and then go over. We'll just skip straight to having this be the first target for the zombie to get to. Once it gets here, it's going to run the same algorithm the whole time. Once it gets to this corner, assuming the player hasn't left, then the same algorithm will continue. It will draw a line to the player. And it will finally just not have-- it will not intersect any unwalkable terrain. And so, the zombie will be able to go straight. I feel like that seems right. Oh, someone is also telling the actual line-- the actual thing for slope. That's great. So y1 minus y2. y1 is-- so now using this points just feels silly. So y1 is end.y minus end-- no, minus start.y. And this is over x. So n.x minus end dot-- or start.x. Thank you. Negative one. Do I know how to think of lines? This is sitting at one on x and zero on y. So it's on the x-axis and the next one is two on x and also zero on y, so still on the x-axis. Shouldn't that have a slope of zero? What? End.y minus start.y. End.x, start.x. And that, I mean, this looks familiar. So end.y is zero. Minus start.y is zero. So this is zero over some value, zero over negative one. x-- I don't know. I'll do it later. It feels like it should have worked. [LAUGHS] Can't you debug the numbers? I seem to not be able to. [LAUGHS] All right, everyone. Got decently far today. And I'm going to also talk to the Flame folks, just see if they have any raycasting stuff in Flame. They've had everything that I've wanted in the past. And they probably will today as well for this. So who knows. The next time I pick this up, there might be just-- we might be pressing an easy button that basically tells us the answer. But if not, we're going to do some line intersection logic. I'll probably enter the next stream with that part figured out so we can just get to the good stuff where we know if lines are intersecting, and what slopes are, and boring things like that. But that will be an adventure for another Thursday, another Observable Flutter. And until then, everybody, have fun.
Info
Channel: Flutter
Views: 14,376
Rating: undefined out of 5
Keywords:
Id: mooauGN4AjQ
Channel Id: undefined
Length: 108min 55sec (6535 seconds)
Published: Thu Aug 17 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.