Observable Flutter: Bonfire

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
CRAIG LABENZ: Welcome back, everybody, to another episode of "Observable Flutter." my name is Craig Labenz. And I am your host-- so far, as always. Who knows? Maybe one day there'll be an adventurous other primary host. And today I'm pretty excited to talk about an extension built on top of Flame. And it's called Bonfire. And Bonfire is specifically a bunch of helpers that you need to build an RPG-style game in Flame and Flutter. Think if you were trying to make "Stardew" or some other-- "Vampire Survivors" or some of those really super-popular indie games, you might start with Bonfire. And I'm joined today-- oh, wait. I always get into this too early. One second. First of all, remember, code of conduct. Folks, this is the Flutter community. Kindness always-- I really-- I actually do enjoy, on "Hump Day Q&A"-- if you don't watch "Hump Day Q&A," you should. They have this whole thing, spells out all the stuff. And the one thing they talk about is, if you're unpleasant in the chat on "Hump Day Q&A," then they share that around with other people. Same is true here, though I don't think we've ever had anyone rise to that level of earning themselves the banhammer. So that's always good. But yes, I am joined today by Trey Hope, a Flutter YouTuber, who has made some content on Flame and on Bonfire, specifically. So of course, I had to reach out and collaborate on that. So let me switch back here. And Trey, welcome to "Observable Flutter." TREY HOPE: What's going on, man? Thank you for having me. I appreciate it. CRAIG LABENZ: Yeah, appreciate you taking the time, and not only the time to be here today, but the time to make all those YouTube videos that you've made. For folks who haven't seen Trey's YouTube channel, Trey, how would they find you? What would they search? TREY HOPE: Yeah, you can find me on pretty much all platforms, YouTube, TikTok, Instagram. But just type in Trey, T-R-E-Y, dot codes, C-O-D-E-S. And then I should pop up. CRAIG LABENZ: All right, yeah. I'm just grabbing the link to your YouTube channel right now. And I'm going to make a banner with it, Add Banner. There it is. There you go. TREY HOPE: Thank you. CRAIG LABENZ: So this is where you can find today's Flutter content. That's where I found it. And Trey, you came recommended to me. I'd seen some of your videos before. But when I got started on the Flame series recently, someone was like, have you seen Trey's things? And I was like, the time is now. So your reputation precedes you. TREY HOPE: I'm glad to hear that. CRAIG LABENZ: All right, and it was Steph, actually, who did the recommending. And I'm glad-- TREY HOPE: Hey, Steph. CRAIG LABENZ: --that you're here, Steph. Yeah, Steph, the author of the Flutter-- or technical reviewer of "The Flutter Apprentice." TREY HOPE: I've actually talked to Steph before. Yeah, she's good people. CRAIG LABENZ: Nice, nice, nice. God, I love all of this. I love folks in the Flutter community collaborating, here. OK, so Trey, you're the Bonfire expert. So I'm going to be driving today. You're going to be coaching. But I'm going to lean on you for expertise and a little bit of know how, in terms of getting things wired up. I've so far run-- actually, let's switch to the correct settings, here. I'm going to have to do this real quick. I want this one. Here we go. Get in there. OK, so I have created a fresh Flutter Create. I've added Flame and Bonfire, and Flame Tiled, which I presume I'll need. And then I have downloaded an off-the-shelf map, and added it to Assets. But I haven't actually gotten this to load yet. So I think I still have to do some tweaking to something in this. Last time I ran it, it was a black screen. But so here we can see, pretty darn fresh. And how do you get started with Bonfire? TREY HOPE: Yeah, so I guess the first thing, I guess, would be to get the actual map loaded. So it sounds like you have the assets in there. So if you go to the main.dart, where you have the Bonfire widget. Let's see, here. CRAIG LABENZ: Oh, so I'm not even using the Bonfire widget yet. I suppose I need to do that, huh? TREY HOPE: I'm trying to think. CRAIG LABENZ: I'm pulling up the Bonfire docs on another slide. And then I'm going to bring it into the main screen so everyone can look at it. All right, so looking at the Bonfire docs here, yeah, I always like to start from total ignorance, so we can all learn together here. So let's look at their getting started, which is linked out here. Here we go. I would like to-- yeah, know more. And now we need to make this text bigger. That's interesting. Why did this load in a new tab? Oh, I didn't Command-click. That's what happened. All right, so created with the purpose-- that creating Flutter games is easy, objective, and fast. So built on top of the Flame, as we see, dah, dah, dah, there's examples. Let's look at getting started. Oh, right, the joystick. Yeah, yeah, yeah. That's such a great-- all right, so let's get to here. So WorldMapbyTiled is-- is this a widget from Bonfire? TREY HOPE: I believe it is. Yeah, I believe it is. Or it's either from Bonfire or from the Flutter tile package. But that is-- I think that what you have right now, you have a world object, being set to the map. CRAIG LABENZ: Correct. TREY HOPE: --Bonfire world-- CRAIG LABENZ: --but if we're going to scrap all this-- Yeah. TREY HOPE: Yeah, so yeah, we just replaced that with the Bonfire world. And-- CRAIG LABENZ: I need one more, I think. All right, so let's add some imports here, Bonfire widget and then we need this Joystick, I guess. Oh, this is also from Bonfire. Great. So this one's probably redundant. So we're bringing in all of bonfire. All right, what do you need here-- TREY HOPE: So they're here for-- CRAIG LABENZ: --for the map. TREY HOPE: Yeah, that's where the map would equal that world map by tile. CRAIG LABENZ: OK. So we got that going. Now this is where I will use my actual path, which is assets/dungeon/tiled, and then the map file is sampleMap.tmx for me, sampleMap.tmx. And this is the last thing that I was playing with. In sampleMap.tmx, it points to this source, which I think needs to also be the absolute path to sampleSheet.tmx. And in sampleSheet.tmx, we get the source to the full, actual tile map, the sprite. And this, I was just editing. But I haven't run it yet. So this may work, but it probably won't. TREY HOPE: I don't think that you need the assets/dungeons path, or part in the path, when specifying it in the main.dart file. CRAIG LABENZ: Oh. TREY HOPE: I think you can just do-- CRAIG LABENZ: Really? TREY HOPE: Yeah, I think it's just sampleMap.tmx, if you have it in the right directory. That could be-- I could be wrong. I know usually when I set it up, I just specify the actual name of the file. CRAIG LABENZ: All right. Well, let's dive into that as well. Where do you put it? Because I've right now got it in Assets. So this is my project bonfire example. Folder name is Bonfire. In the assets dungeon directories, there's all of these assets. TREY HOPE: Right. So I noticed-- it's weird. I noticed-- well, I guess you can use either JSON or TMX. When it's a JSON file, it would be in the Images directory, but then for TMX files it would be in the Tiles directory. I think that's right. So either/or, just depending on what file-- if you're using TMX, we'll put in the Tile directory under Assets. CRAIG LABENZ: So you basically just skip this dungeon layer. TREY HOPE: Yeah. CRAIG LABENZ: Well, we're getting a-- TREY HOPE: You may be able to put it in there still-- CRAIG LABENZ: [INAUDIBLE] error. TREY HOPE: --but I'm not sure. Oh, only supports JSON. CRAIG LABENZ: Oh, it only supports JSON files. Oh. TREY HOPE: Yeah, I think, if I'm not mistaken, Bonfire only does JSON and then Flame lets you do TMX and the other one. CRAIG LABENZ: Ah. Trey, do you potentially have a map with a JSON file that I could borrow? [LAUGHS] I didn't anticipate that. TREY HOPE: Yeah, let me see here. I probably have one for Mario that you could use. CRAIG LABENZ: OK. TREY HOPE: Let me see if I can get this populated. I have to redownload it. CRAIG LABENZ: Yeah, so world map I tiled-- let's see here. So this does come from Bonfire. Now, the other thing I could try to do is load this up in Tiled and see if I can then export it as JSON. I don't know-- well, I haven't used Tiled since I ran-- since I was building the world for the zombie game. So let's make a new project. I've got to say, the tiled UI is a little-- TREY HOPE: It's a little weird. CRAIG LABENZ: Leaves a little to be desired. [LAUGHS] TREY HOPE: Yeah. Hold on, I'm getting it right now. JSON. And grab that from here. How should I send this to you? CRAIG LABENZ: Do you want to email it? TREY HOPE: Yeah, I'll do that right now. CRAIG LABENZ: All right. Folks, thanks for bearing with us here. I'm going to pull this out of my email real quick. TREY HOPE: All right, just sent. CRAIG LABENZ: You're a hero, Trey. This is when keeping it real goes wrong. [LAUGHS] When staying ignorant leads to big surprises. All right, waiting for-- there we go. Got the stuff. Love it. Now, this is just the JSON file. I'm going to need your assets as well, right? TREY HOPE: I'm trying to think. CRAIG LABENZ: Can you zip up the structure that you have with those? TREY HOPE: Yeah, I guess that would make sense. Let me do that. Hold on. CRAIG LABENZ: All right, well I'll still bring that in for now. Who's got a either interesting Flutter joke or just thing that you learned or something to share during-- from the last week? Put it in the chat, and we'll pop it up and talk about it while Trey and I fumble around with these maps. All right, I'm going to Downloads. That's what I'm doing. There it is. TREY HOPE: Let's see. Audio, no. Images. I think I can just send you the tiles, but I'm not sure. Let's see, Share. Compress. All right, I'm going to email the files that come along with that JSON file. I may need to send over another directory as well, but maybe this is all you need. But we'll see. CRAIG LABENZ: New release of Flutter yesterday shortly after Hump Day ended, Randall says. Oh, it was a hotfix? Or was it a new beta? Probably a new hotfix. Do you know what was fixed, Randall? All right, awaiting the second email over here. Minor fix on stable. TREY HOPE: One second. CRAIG LABENZ: For more stability. Ah, a few iOS rendering things. Were they in-- what's the thing that I'm thinking of here? Maybe Impeller related? Is this the Boring Show? Well, it is boring right now, so in a way, yes. TREY HOPE: I just sent it over to you. CRAIG LABENZ: OK, great. This is a-- TREY HOPE: Let me know if that works. CRAIG LABENZ: --similar thing. Awaiting that email's arrival. If this has an attachment, it might take a second longer to get to me. Yeah, this is a live stream that has Boring Show vibes for sure, including today where our-- my desire to come-- TREY HOPE: I like the Boring Show vibe. CRAIG LABENZ: --in here not knowing anything about Bonfire has meant I don't have the right map type, which is delaying our launch. OK. Come on, internet. Get me this email. I'd love-- oh, there's a website with the release notes. What do you think? What do you think I'm working on the team supposed to know about that? A lot of pressure. I love that character in "South Park," Tweek. Oh, it's a lot of pressure. He's just always freaking out. All right, got this Images directory. Do you have the Images directory next to world map JSON? TREY HOPE: World map JSON is in the Images directory. CRAIG LABENZ: In the Images directory, OK. And I see you've reincluded that wonderful "OK." TREY HOPE: I've also sent over a Tiles directory, but I'm not sure if that's necessary or not, but it shouldn't be. CRAIG LABENZ: OK. So we're bringing the screen back in. Screen, get in the game. Pubspec. OK. So my new setup is in Assets we've got this images directory that has all the stuff. So I'm just going to say assets/images, and that's it. Then I'm going to press Command-M and minimize instead of open main.dart. Now in here, we can actually use-- oh, what was it called? It's got a full name, world_1_1_map. This is great naming. So you believe we can do this, and then I am not using this at all. And I can begin. We could run this. TREY HOPE: Right. Should work. CRAIG LABENZ: OK. In the words of-- what's his name-- Samuel Jackson in "Jurassic Park," hold onto your butts. I love this giant red error. Oh, no directionality widget found. Let's wrap it in a material app. TREY HOPE: Oh, is that what that means? CRAIG LABENZ: Material app will add that, yeah. So this is, what, Home? Is that what we give this? And then OK. Restart. All right, we got the joystick. That's good, but there is another error. Wait. Why does it still say it can only support JSON files? We are super duper using a JSON file. Does that make any sense to you? TREY HOPE: Do you need-- I'm trying to think. You weren't debugging it, so that shouldn't be an issue. I was wondering if you had to restart it completely, but you already did. CRAIG LABENZ: Yeah. I'm trying that now, but I'm not optimistic this is going to help. Wow. Invalid tileset source. Only supports JSON files. Let's look up that error and figure out where it's happening. It's not in-- oh, tiled world builder. I'm just going to search in here. JSON. No, it's not in this file either. I think I'm going to clone Bonfire and-- [LAUGHS] I'm going to clone Bonfire and just search for this error. Do you have any other thoughts? TREY HOPE: What if you search in the search bar-- CRAIG LABENZ: Oh, here we go. This is actually it right here. Well, this isn't going to search in the dependencies I don't think. So we found one here. It says print(TiledWorldMap) Error error. And so that's what we're getting down here. And then it says-- so there's an exception coming from probably this read map method. My guess is that's it. Oh, is it just having a JSON-- this sure seems like valid JSON. Weird. Oh, let's try giving it-- I have an idea. Let's try Assets, Images. Let's try that. Maybe the error is not that precise. TREY HOPE: Right. I see what you're saying. CRAIG LABENZ: Oh, OK. The asset doesn't exist. No, we're moving. This is good. So it loaded the file, and now-- oh, I keep moving this up as if that's going to raise this text. So it loaded the file, but now this is getting double concatenated, so that's not really what we want. TREY HOPE: So we don't need the assets images on that. CRAIG LABENZ: Well, we seem to have needed it on-- so it was able to actually-- oh, wait a minute. No, you're right. Now it says unable to-- no, you're totally right. TREY HOPE: I'm not sure why the JSON isn't popping up, though, for the map. That's weird. CRAIG LABENZ: Yeah. So Yefri says, it's possible that there's a syntax error in the JSON, which is looking more and more likely. But boy, I don't-- oh, there's-- so look at this. There is also-- there's other errors that we're going to get to. This isn't right. Oh yeah, you were mentioning the Tiles directory that you sent that you weren't sure if I was going to need. I think I am going to need it. TREY HOPE: Yeah. So I sent over the Tiles directory. I'm glad I did that, OK. You should have that in your email too then. CRAIG LABENZ: All right. Pulling it in. Thank you. Download. TREY HOPE: It should be two files. CRAIG LABENZ: OK, grabbing that out of my Downloads folder. And Tiles. Unzipping. Yeah, there it is. Oh, this is so funny. All that's in here is the TMX. Oh yeah, that is literally what it specifies. OK. Screen back in the game. TREY HOPE: Hopefully, that works. CRAIG LABENZ: Let's try it again. So what I did-- because this says it wants to go up a level and then into the Tiles folder, I put it up a level and next to Images. I hope that's appropriate. TREY HOPE: Yeah, that's how it should. CRAIG LABENZ: Anyway, we're back to the same error, only supports JSON files. So that is coming from-- we saw that was here. And I presume it's in read map, so I'm going to print and we'll just see. I think we're going to get flag one but not flag two. And while that's happening, I'm going to go back into read map and poke around some more. So from server, that's not it, so we're not in that statement. Otherwise, it's just reader.read. Yeah, we got flag one, not flag two. So we go into this reader.read method, and that's here. So this says route bundle load string path file, so let's see if we get print data. And then JSON decode the data. This is already going to be enough to be interesting, in all likelihood. Yeah, I know. [LAUGHS] ApllArte Tutoriales says, I thought I was the only one who got errors. No, no. I am an error machine. So which was the last thing? It's obviously loading the file, and a lot of looks like Base64 encoded stuff there. So result. So we're even JSON decoding the result, and then there's just going to be-- there's some exception in here. Oh, here we go. This is thrown literally. So if the tileset.source doesn't contain JSON or TSJ, this is the specific problem. Tilset.soure. TREY HOPE: Do you still have the TSX fileset somewhere? CRAIG LABENZ: Well, I haven't gotten rid of anything that you've sent. TREY HOPE: OK. Well, I mean in the code where you're calling the path. Is that still referencing TMX or something by chance in main.dart? CRAIG LABENZ: I don't think so. Yeah, I've gotten rid of basically everything else. TREY HOPE: OK. CRAIG LABENZ: So if we go back to this read method-- so tileset, where are you? So map, tilesets. Oh, and then we're looping over. I see. So we're in an async loop here. So map.tilesets. And then this tileset has to have a source that is a string, and it has to have-- So let's look at the source key and see if we can figure out what tilesets is in the-- and we're still reading-- this is all from the JSON file. So in the JSON file, tilesets. Here we go. So I'm expecting somewhere-- here is a source, and it has TSX not JSON. That's the error. But you made a video about this. How did you-- do you remember bumping into this error at all? TREY HOPE: It looks like it's referencing tiles though, so that should work because you have that in the tiles directory, right? The TSX file? CRAIG LABENZ: So I agree that this should-- this seems like it would work. But this is the code that's complaining. TREY HOPE: Got you. CRAIG LABENZ: So it says, in the given tileset, the source key, if it's not null, it has to contain these two extensions. And we just definitely don't. And yeah, folks in the chat are noticing this as well. I don't know how they saw it before I put it on the screen, but that was mighty fine sleuthing. [LAUGHS] So we need a JSON map here. Interesting. Why is this being so-- TREY HOPE: I think, if I'm not mistaken, I noticed-- OK, so the file I sent over-- I think this might be a rookie mistake, but I've been working on converting the Mario game from Flame to Bonfire. So it was originally in Flame, so I might have sent over a file that I was using for Flame, but the JSON file is what I've been using most recently for Bonfire. But that should still work. I'm just thinking out loud why it's not working. CRAIG LABENZ: Yeah. So you sent something that runs on your machine? TREY HOPE: Yes. And I'm using Bonfire right now, so that should work. But let me see. CRAIG LABENZ: Oh, you know what we can do potentially? I just had an idea that I think might work pretty well. Let's go to Bonfire, Flutter, and download one of their examples and steal the map. TREY HOPE: That works too. That's a good idea. That would save a lot of time. CRAIG LABENZ: So I guess we can go to GitHub. And I'll make this bigger. And in Examples, Lib, let's see what they've got here. Shared? Oh, no. Is it going to be in Assets? TREY HOPE: You want to take their-- yeah, the assets. CRAIG LABENZ: All right. So Assets, Images. They've got a ton of stuff. TREY HOPE: It would probably be in Maps. Is there a Map directory? CRAIG LABENZ: So they do have Tile. So here's the Floors. And then I wonder if we're going to have to just take all of this. TREY HOPE: Probably would have to. CRAIG LABENZ: All right, that's what I'm going to do. So I'm going to go back to Bonfire and I'm going to just download this. I'm going to download the zip for now. TREY HOPE: I also have-- maybe this would work. I'm going to send over another map for another game that I made for the "Green Ninja." There's less files, so maybe this will translate a little bit easier coming over. CRAIG LABENZ: Yeah, that was your YouTube video. TREY HOPE: Yeah. So we'll see. This one's a very basic map, but hopefully this works better for what we're trying to do. CRAIG LABENZ: Yeah, basic will do. TREY HOPE: Let's see here. I'll send. CRAIG LABENZ: All right, so I'm going to hold off on using the downloaded Bonfire example, and I'm going to try to use the "Green Ninja" map as well. TREY HOPE: All right, let's see. Then email it. CRAIG LABENZ: So while we're doing this, heard a great joke the other day. Actually, heard it several times, but heard it again recently. Oh, I think I told this joke on stream recently, actually, so some of you are going to know the answer. What do you call a widget that lives in Washington, DC? This is a very US-centric joke. What do you call a widget that lives in Washington, DC? I'm waiting for an answer in the chat. TREY HOPE: I think I know the answer. If I do, that's funny. That's pretty funny. CRAIG LABENZ: Right? It's not bad. Randall recalls the joke. And you're being a good citizen and letting other people guess. All right, I'm putting the Images directory in-- I've moved Images over, so this Images directory is now everything from your game TREY HOPE: Yes. CRAIG LABENZ: I'm going to delete this, which was also from the old thing. TREY HOPE: The map name on this one is going to be different. It's going to be-- CRAIG LABENZ: map_one.JSON? TREY HOPE: Yes. CRAIG LABENZ: OK. So close a bunch of stuff. map_one.JSON. Here we go. We've got our first answer, a capital widget. You're thinking in the right direction, but-- oh, Widgeton, DC. That was actually the first answer. Both of them not quite what we're looking for, but you are on the right track. Hey, look at us. We have a map. It's not the fanciest thing in the world. TREY HOPE: Very basic map, just empty space. CRAIG LABENZ: Yeah. It'll do. OK, it's time to start doing some Bonfire things. So Trey, one of the things that I was impressed by in your video was just how easy Bonfire makes it to control a character and swap to the correct-facing sprite every time they move, every time you change their direction and whatnot. Oh, we've got another answer here. Is Center the answer? Washington, DC, does think it's the center of the universe, so that's also a pretty good answer, but not the one we're looking for. So Trey, I would love to begin by adding the main character to this game and moving it around and seeing the asset swap because I thought that was really strong from Bonfire. TREY HOPE: Got you, OK. So is this-- we had to figure out what type of player we're creating because if we're doing it from a perspective of 45 degrees or top-down level, then if I'm not mistaken, you would need to add specific animations for going towards the left. I don't think that you can just swap it as easily if you're doing it from a certain type of player because there's three different types of players you can have in the game-- simple player, rotation player, and platform player. And they're all based on how you're viewing the character. So some characters, you wouldn't necessarily be flipping. You have to have a specific direction for what you want to do. But we can figure that out as we're going along with it, as we're creating the player. CRAIG LABENZ: OK. Are there any player assets in the stuff that you just sent? TREY HOPE: Yes, there's an asset for the Green Ninja, which is the main character. Is there-- I don't see Green Ninja. [INAUDIBLE] CRAIG LABENZ: Oh, here we go. Yep. TREY HOPE: Yeah, OK. Yeah, there's that one. CRAIG LABENZ: OK. So yeah, let's add this Green Ninja. Oh, there's lots of different ninjas. Let's add the-- oh man, these are kind of terrifying. TREY HOPE: Yeah, those are some enemies. CRAIG LABENZ: What do you say we just pick whatever perspective is appropriate for this asset and add the Green Ninja to the game? TREY HOPE: Yep, that sounds good. CRAIG LABENZ: OK. We also did get it. It is a stateless widget because Washington, DC, the District of Columbia, is not a state. TREY HOPE: Oh, wow. CRAIG LABENZ: All right, how do I get started adding the Green Ninja, Trey? TREY HOPE: So we can create a new class called I guess Green Ninja or whatever we want to name the player. And then we would extend a simple player widget. CRAIG LABENZ: Simple player? TREY HOPE: Yeah. CRAIG LABENZ: OK. So that looks like it's already imported. TREY HOPE: It should have a-- CRAIG LABENZ: Oh, we have to call the constructor. Oh, geez. That was nice. So it needs a position. It needs a size. Now, one thing I am comfortable with in Vanilla Flame is where-- I would probably extend one of these widgets or I would extend the world. And in the onload method of that new class is when I would call the add method to bring in the Green Ninja here. Does it work the same way in Bonfire or does it work differently? TREY HOPE: So in Bonfire, you actually just assign an instance of the Green Ninja to the player property that's on the Bonfire widget. CRAIG LABENZ: OK. So there's a player right here, so we'll say player equals Green Ninja. TREY HOPE: And then we need to specify the position, which is going to be where we want it on the map. Just starting out, we could do something like that. And then the size, which I think I typically had them at 32-by-32. Yeah, you can change that to 32. That should look good. CRAIG LABENZ: OK. Oh, I've still got all of those print statements happening. And I don't really want them anymore, so I'm going to very quickly kill those. Actually, I'll restart the app while this happens. It was in the builder, and then it was in read map, so I got rid of these two. And then in read map, we'll look for these other print statements. We're going to have to click on Read. Read map. All right, I can't find it. Worry about it in a second. So we didn't assign the asset to our Green Ninja yet, so I'm actually in hindsight not surprised-- TREY HOPE: You're right. CRAIG LABENZ: --that we don't have anything. TREY HOPE: You're right. And so for that, that's where we would pass in on the Green Ninja. In the super constructor, there should be a-- actually, what we can do is add a super constructor to the simple player to specify the animation. CRAIG LABENZ: Yeah, so I'm seeing this simple direction animation thing, which I think, if I remember right from your video, this is where a lot of the heavy lifting starts to happen. TREY HOPE: It gets pretty technical at that point. But the thing is, the only thing-- there's only two methods required for the simple direction animation, I believe, and that's facing right and running right. So you don't have to have animations for every direction on the map as far as where the player can go. So if you hover under that-- so only idleRight and runRight are required. CRAIG LABENZ: Interesting. So we'll say idleRight and-- oops. TREY HOPE: So in this instance, if you were to have-- for this character, I believe we could do the flip rotation thing if you wanted to. But since it's already a field here where we can specify what it looks like going left, that might help too. CRAIG LABENZ: No, I think that would totally-- yeah, I think that would be totally fine. So how do I define this-- oh, this is just a flame sprite animation? TREY HOPE: Mm-hmm. CRAIG LABENZ: Oh, nice. I'm not going to lie, I've actually never used a sprite animation in Flame, so you can tell that I'm not actually a professional game developer yet. TREY HOPE: I think there's a method like spriteanimation.load or sprite.load-- yeah, sprite.load. And then this is where you can specify I believe the actual path to the gif. But now that I think about it, I think that only loads-- it'll load an animation for one specific-- Let's say that Green Ninja didn't have 36 pieces and it was just one, then sprite animation load would load that one. But loading all 36 of those wouldn't give us what we need. So I think that there is another method in there. It's called something different. I think it's sprites-- the name slips me, but it should be in here somewhere. It's where you take a list of different GIFs at a specific position. CRAIG LABENZ: So there was a-- there are a few that involve lists. There's sprite lists and variable sprite list. But I didn't see in sprite list-- create an animation from a list of sprites, all having the same-- TREY HOPE: I think that might be it. CRAIG LABENZ: --transition. So where would we specify in this which subset of the sprite we need? TREY HOPE: I believe that on that-- so in the method, we're taking a list of sprites. I think that's where we would call sprite.load and then-- there's a method-- let me just do some quick research. I think there's a method where you can actually specify the row or column on an image when creating the animation. CRAIG LABENZ: Got you. So this was spriteanimation.spritelist, we're thinking. And then sprite list takes a list of sprites. And that was positional? That is positional. TREY HOPE: So we actually need to create a sprite sheet that represents the image I sent over with the Green Ninjas. CRAIG LABENZ: Got it. TREY HOPE: There's going to be a method that we can call to load that sprite sheet. And then from there, we'll get instance of that sprite sheet and say get sprite at index or column 0, row 1. And that'll be how we know which Green Ninja we're getting on the map. CRAIG LABENZ: Got it. Now, should I-- I feel like maybe I should turn this into a stateful widget, and we can do that asset loading in its init method. How does that sound? TREY HOPE: That should work. Yeah, that should work. CRAIG LABENZ: All right. So we're converting real quick. I'm going to add an initState. And this will be loadAssets. loadAssets. So in here, we should be able to do that. So you've mentioned loading. We need to create a sprite sheet. TREY HOPE: Which would tend to be, I guess, like-- not a constant variable, but like a global variable that we'll be using. CRAIG LABENZ: And this is just pure Flame, right? TREY HOPE: Yeah. So we could do-- it should be flame.images.load. There it is right there. CRAIG LABENZ: Yep. TREY HOPE: And that'll give us the image that we then pass into the sprite sheet constructor to give us that sprite sheet. CRAIG LABENZ: Amazing. So this was Green Ninja, and it's .png. And do we need to give it the whole asset path of assets, images, sprite sheets, Green Ninja? TREY HOPE: No, we don't need to do that. CRAIG LABENZ: Really? TREY HOPE: If it's in-- let me see. CRAIG LABENZ: So all I put in the pubspec was-- TREY HOPE: Actually, since the file is-- the Green Ninja is on the sprite sheet, so we just need sprite_sheets/green_ninja-- CRAIG LABENZ: Sprite sheet. TREY HOPE: Yeah. CRAIG LABENZ: OK. All right. What are you whining about? Image is not defined. Whoops. What's it complaining about? Value of type image where image is defined and-- can't be defined-- oh, there is a collision of the types of images. So I think, first of all, we're not using Flame tiled anymore. Oh, there's also just a crazy amount of incomplete code down here. I'm going to comment all this out. I think that's throwing the thing for a loop. So this is unnecessary, apparently. I do want to import the correct-- yeah, so which one did it say? TREY HOPE: Maybe you could just take the image out-- not the image itself, but the constructor part. Just say var image to ignore the red error. And then for what we need to do, we can just keep going. But there should be a method-- what is it called? Spritesheet from-- well, we can create the sprite sheet. So create an instance. CRAIG LABENZ: So the image that it actually pulls in is from Flutter Painting. So I am going to just grab that real quick. And then, now I think we can say painting.image. Nope. [LAUGHS] Really? TREY HOPE: It was a good attempt. CRAIG LABENZ: [LAUGHS] Why is that not doing a thing? Dart UI. Where's the image in here? Oh, this is just a barrel file. Wait, let's go back. I want to get there. I want to-- because we're going to-- I don't want to use dynamic. TREY HOPE: Yeah, you don't like using dynamic either? CRAIG LABENZ: Yeah, there be dragons. So the value of image is defined-- yeah. And then it's also defined in widgets image. That's fine. I'm actually not quite working out what the issue is here. If I got rid of-- I don't need flame game either. That's not needed. So if I get rid of Bonfire, of course, other things will error, but then it knows-- oh, image extends stateful widget. So that's the issue that we're getting. It's that image from Material. And then if we get rid of Material and show Bonfire, now image is going to come from somewhere else. TREY HOPE: Dart UI. CRAIG LABENZ: And this says now it's coming from painting. So this is Flutter. Lib. UI. Painting. Back to main.dart. I've never had this issue before. This is also kind of weird. I wonder if it's-- TREY HOPE: I never had this issue because I usually do my file initiation in a separate file, so I never use anything Material related. CRAIG LABENZ: Yeah. Well, let's do that. So let's add another thing, which is green_ninja.dart. And the Green Ninja will go into that file. And we'll add Bonfire in here. Now the Green Ninja is-- oh, I don't think we want to actually put the Green Ninja in there. TREY HOPE: You just want to have the sprite sheet initialization in there, right? CRAIG LABENZ: Right. So I'm going to delete this. We'll make a new file. And this will just be load assets, I guess, dot dart. And in here, this will say future void, I guess. loadAssets. async. And it's going to do this. And now we'll import the things that we need, which is Bonfire. And that all works just fine. And let's attach this to the global variable Image greenNinjaImage. OK, there we go. TREY HOPE: And that looks good. CRAIG LABENZ: So now we don't need any of this anymore, but we do need to add-- we need to import load assets, and we need to call will await loadAssets. Oh! I just looked at chat. People were trying to help us out. I appreciate that. So we've loaded the image, and now we still need to turn it into a sprite, right? TREY HOPE: Yes. So I guess in load assets, what we can do is we really-- I don't think we need to make Green Ninja global because is the reason you make the Green Ninja image global is so we can represent other files? CRAIG LABENZ: Correct. TREY HOPE: So we really would be using the sprite sheet version because we still have to do some conversion on that image to make it a sprite sheet. CRAIG LABENZ: Got it. TREY HOPE: So there's a method called SpriteSheet.load, I believe. If you just type in SpriteSheet.load. Load from-- is it load from-- oh wait. SpriteSheet.fromColumnsAndRows. So that's where we're-- CRAIG LABENZ: Exciting. TREY HOPE: passing that image. CRAIG LABENZ: All right. And now these columns and rows are-- TREY HOPE: Those are going to be the columns and rows in that image. CRAIG LABENZ: --a single int. TREY HOPE: Yeah. CRAIG LABENZ: Interesting. TREY HOPE: I forgot what the columns and rows was on that image, though. We had to look again. CRAIG LABENZ: So to walk to the right is-- I actually don't quite understand this constructor. We just give it one integer for columns and rows. TREY HOPE: Right. CRAIG LABENZ: Do you understand this? TREY HOPE: So we're making the sprite sheet based off that image, and then we're specifying how many rows or columns are in it. So that way, when we need to call in that simple-- the Green Ninja class, we can say, give me the sprite at index 04. So that'll mean-- CRAIG LABENZ: I see. TREY HOPE: --we're walking at 04. CRAIG LABENZ: I see. OK, that makes more sense. Sorry. I was thinking-- thank you, Trey. I was like-- I thought we were going to be giving it a slice, a subset of this or something, which in hindsight makes absolutely no sense. Anyway, so it's four columns and one, two, three, four, five, six, seven rows. Amazing. And now this is the Green Ninja asset, right? TREY HOPE: Yeah. So I think that's the sprite sheet. CRAIG LABENZ: Or sprite sheet I meant, yeah. TREY HOPE: Perfect. And so then from here on the simple direction animation, we should be able to now create those sprites based off that sprite sheet because we're going to be passing in a list of sprites. So we can say greenNinjaSprite.getSprite. CRAIG LABENZ: OK, love it. I do have a bunch of stuff to close here. Let me go through that real quick. Oh man, there's a lot of stuff. OK, I'm going to close sprite sheet as well. So we're back in main.dart, and this is where-- so the first thing is a list of sprites. And like you were just saying, it's going to be greenNinjaSpriteSheet.getSprite. TREY HOPE: GetSprite. CRAIG LABENZ: Row, column. And this is idleRight. So which ones do you think we should use here? TREY HOPE: So the thing is with these games, idleRight could really be almost any of them where he's not making too much movement. But it's funny because you can use an idle one within one where he's running because he would eventually have a walk posture while running at some point. So it's really up to you, but I'd say maybe the second one down all the way to the right. No, it looks like he's moving. CRAIG LABENZ: If we gave it a couple, would he just kind of bounce? TREY HOPE: Yeah, that's exactly how it would be. CRAIG LABENZ: So what do you say we try to give it these first four? TREY HOPE: OK. Let's see what it looks like. CRAIG LABENZ: So this would be row, and these are probably zero indexed, I'm guessing. So column 3 in rows 1 through 3. So rows 0 through 3 and column 3. And then we just do 1, 2, 3. And then this needs step time, and that is a double-- I'm guessing that's milliseconds? TREY HOPE: I don't think so. I think it's actual seconds. I think. CRAIG LABENZ: All right, I'm going to give it-- TREY HOPE: I'm not exactly sure. CRAIG LABENZ: I'm going to give it 1. It's either going to be unbelievably fast or unbelievably slow. TREY HOPE: I think it's actually seconds, so I think that will be slow, but we'll check it out and see. CRAIG LABENZ: OK so now we need to copy all this for runRight, but I think we want to give-- where is he actually running? You know what? I think I realized what you were saying. I think I only want to do this one at index 2, and then we're going to do the same set of ones for the running. So I'm going to leave running alone. That should be good. And then I'm going to delete all of them except for 2 for the idleRight. TREY HOPE: Two sprites or just one sprite? CRAIG LABENZ: One sprite at row 2. I think it was the one you were saying. It looks like he's just totally standing there. It's actually-- yeah, it's very similar to the one above it. Is the game-- do we have any chance that this is going to work? TREY HOPE: Real quick, getSprite, is that asking for the column first or the row first? CRAIG LABENZ: Row. TREY HOPE: OK. I was just making sure we had the coordinates right. CRAIG LABENZ: Yeah, otherwise our character would look like they're doing some nutty dancing. We've got an error, and I have to call the thing. We got a good question here. So why is Bonfire not just Flame? And Trey, I certainly want you to take a crack at this as well. But just like-- I heard someone describe Flame in a really useful way. They were fielding a question why use Flame and not just Flutter. And they said, well, if you make a game in Flutter, you will eventually arrive at many of the solutions that are in Flame. And I just thought like, oh, that's, A, correct, and B, a very succinct way to put it. And I think the same thing is still true for Bonfire. Of course, there's no magic in here. There's nothing that you can't do. We're getting-- I seem to have a bad column here. So it's assets, images, sprite sheets, and it has assets/images/sprite sheets/green ninja.png. green_ninja.png. Wow, that really does look like it's there. [LAUGHS] Unable to load assets. assets/images/sprite sheets/green ninja.png. Trey, are you seeing the error? TREY HOPE: Yeah. I'm not-- [INAUDIBLE]. Did you completely restart and-- that wouldn't matter. I'm not sure why it's doing that. CRAIG LABENZ: Oh, yeah, sometimes with assets maybe. But I thought I did completely restart. I don't think it's going to be an issue. Anyway-- TREY HOPE: Whenever I have issues with assets, I always think it's just something related to restarting again. CRAIG LABENZ: Right. And it rarely is. It's normally I actually genuinely did something wrong. But boy, here I'm really not seeing what it is. TREY HOPE: Sprite sheets. CRAIG LABENZ: So unable to load asset. assets/images/sprite_sheets. So I'm going to copy this string, and then I'm going to say ll. And there it is, 5.1kb image. That's from the root of the file-- or root of the project. So is it because this assets directory-- is there something else that we have to do to populate this? TREY HOPE: No, not to my knowledge. There shouldn't be anything else. CRAIG LABENZ: So folks in chat are asking, is there something we need to do in pubspec.yaml? So honestly-- TREY HOPE: That could be it, actually. CRAIG LABENZ: Getting assets to load is always the-- TREY HOPE: That's a very good point. You do have to specify the sprite sheets the right-- you got to specify every directory you use under Images, unfortunately. CRAIG LABENZ: Right, yeah. TREY HOPE: I forgot about that. CRAIG LABENZ: I picked the wrong thing. So chat to the rescue. Thank you, the many people who-- Eren had it right. This name that I am too illiterate to read had it right. Another one. Another one. [LAUGHS] Thanks, everybody. TREY HOPE: Yeah, they were on top of it. CRAIG LABENZ: Yep, not recursive. OK, so there we go. Let's run again. So to wrap up that other thing Eric said, OK, got it. But for anyone else who's wondering, this is just a bunch of helpers on top of Flame that if you're making an RPG game in Flame you're probably going to build. TREY HOPE: And real quick, I want to piggyback off that answer. As far as using Bonfire over Flame, I recently ran into something where, when I was creating a Mario game in Flame, I was trying to create physics that allowed him to run into boundaries. That way, he can stand on the ground. And I had to do a lot of extra steps that Bonfire already took care of because they come with different gravitational pulls, blocking, callbacks, and things like that to where I could allow Mario to stand on the ground out the gate. So like Craig said, it's just coming to solutions that have already been built when using certain packages. CRAIG LABENZ: Yep, absolutely. TREY HOPE: And that's good that we got the game in here, though. CRAIG LABENZ: Yeah. Especially with physics-based stuff, collision detection, any of those really tricky kinds of things, man, I appreciate packages that deliver that for us. So now he's-- I think 0.1 seconds is too fast. Is there a time that you remember feeling good? TREY HOPE: I don't know. 0.1 looks accurate to me. CRAIG LABENZ: Oh, you think? TREY HOPE: You feel like he's moving too fast? Yeah, I think-- because he's small so I think his arms are moving. CRAIG LABENZ: OK. He's moving slowly. I feel like his feet-- he's really shuffling. TREY HOPE: Yeah. CRAIG LABENZ: But all right. Now, here's a cool thing. You mentioned that they only said you had to do the Right and idleRight, and that's because here's another great trick from Bonfire. They're just flipping it for us if we're going left. TREY HOPE: Mm-hmm, looks like it. CRAIG LABENZ: That's pretty good. That's pretty good. And this joystick widget is nice if we were on mobile, but it's not my favorite on desktop. Do they have a similarly convenient WASD widget? TREY HOPE: No, not to my knowledge. The joystick is I think the only UI component that they have within Bonfire. CRAIG LABENZ: Got it. Maybe we can build one in and contribute. So the dude is moving. And honestly, this wouldn't take forever in Flame, and it kind of took us forever because we didn't know what we were doing and we had the wrong map type and lots of things that aren't really Bonfire's fault. But you can I think already see that if you've gotten past a little bit of learning curve with Bonfire, you start whipping stuff together pretty darn quick here. So I think that's pretty neat. What's another thing that might make sense for us to do? TREY HOPE: So now we could potentially add some enemies or objects. I guess that would make sense to have an enemy in the game. CRAIG LABENZ: Yep. It's not too rich of a game experience just yet. So I'm going to go back to the spreadsheet. And you got this crazy thing here of a demon cyclops thing. TREY HOPE: Demon cyclops. CRAIG LABENZ: Is that-- oh yeah, that is what you named it. So let's add this-- TREY HOPE: There was some random, I guess, sprite package that I found for a ninja game, and it came with all these assets. CRAIG LABENZ: Nice. I love it. I use Kenney.nl a lot, so I'm no stranger to random sprite packets. I do love Kenney.nl's art style. So I'm going to add this. I'm guessing there's probably-- just like we had tons of helpers, simple player and whatnot, I'm guessing there's similar helpers for enemies? TREY HOPE: Yeah, there's a simple enemy. CRAIG LABENZ: I love it. So this is going to be Demon extends SimpleEnemy. And let's see what this needs. Again, called a constructor. Looking very similar. Now, here's another thing. I'm guessing, on Bonfire widget, there's a place for-- there's a thing for enemies. Oh, there's already-- there's actually not. Well, that's not bad because I wanted to make enemies through some kind of spawning mechanism. TREY HOPE: Oh, I see what you're saying, like on a map and have multiple and specify where they're at, actual locations and things like that? CRAIG LABENZ: Yeah. And maybe when you kill one, another one spawns, stuff like that. So how does Bonfire think about adding enemies? TREY HOPE: So with enemies, I think you just rolled past it, but there's a param called objects builder. So objects builder lets you put-- you can put enemies on the map, decorations, other items, just any type of object. But this is within-- this callback in here is where you specify, all right, I want five demons at these positions on the map. CRAIG LABENZ: That sounds like exactly what I want. I don't think it's called objects builder. Let's look at the actual super constructor again. Joystick. Player interface. Debug. Collision. Lighting. Components? overlayBuilderMap. I don't think that would be it. What is components? List of game components. So that could be it, but I do want-- oh, I guess we could have a game component that is just our enemy spawner or something. Does that make sense? TREY HOPE: Oh, you know what it is? The object builder is within the map. So if you go back to the main.dart file and go within world or by tile, it should be-- yeah, objectsBuilder. CRAIG LABENZ: Ah-ha. And this is a map of strings to object builders. And that is just a function that takes this TiledObjectProperties thing. That's unknown to me, but we'll learn together. Well, you probably already know. And it just returns a game component. This seems good, so let's get into that. objectsBuilder. Now, the key that you give it, is this how you access it later? So could this be like demon spawner or something? TREY HOPE: So this would be, within the JSON file that we made, on that map is where we would specify where we want that demon at. And so it's pretty much reading that JSON file for how many occurrences of demon spawner did we see. Put them at these locations. So I should-- if you go to the JSON file real quick, we can just look up what the name is I have for the demon. It might be-- what is it called? Type in demon. dark_ninja. OK, it's just demon. And there should only be one right now, but if we come back to the main.dart file-- CRAIG LABENZ: OK. So now we just put "demon" here. TREY HOPE: Yeah, change that to "demon." And then I think on the objects builder there should be an extra param. CRAIG LABENZ: I think I see how this is going maybe. So I'm going to have a-- actually, let's make it a static Demon buildDemon or something. And this is going to take that tiled object properties thing and it's going to return a demon. And we're going to give it the position from the tiled object properties is what I think is going to happen. TREY HOPE: Yes. CRAIG LABENZ: So Demon.buildDemon I'm guessing goes here. TREY HOPE: So what you can do right there-- actually what you want to do is first there's a param that comes with the objects builder. So take the Demon.buildDemon out and put properties. CRAIG LABENZ: What's this? TREY HOPE: Oh OK, that's how you went. OK, got you. That works. CRAIG LABENZ: And then what I'm thinking we're going to do is just take-- TREY HOPE: That's exactly it. CRAIG LABENZ: --the x and the y out of this, right? TREY HOPE: Exactly. CRAIG LABENZ: And that's going to be its coordinates. TREY HOPE: So you can really just props.proposition. CRAIG LABENZ: Oh, love that. Amazing. And then the size is probably also specified. Oh, no. I guess that's just known-- oh, well, we've got width and height, so we do have size. TREY HOPE: You can do the same thing here. CRAIG LABENZ: Nice Now, the demon needs some sprites. So we're going to get back into the same game we were playing with the Green Ninja and again close some of these other files. Load assets. So here we'll have the demonSpriteSheet. And I'm going to copy all of this. And you are the demonImage. sprite sheets. Oh, we actually have two of them because there's Idle and Walking. So there's demonIdle and Walking. So this was demon_cyclop_walk. And this will be demon_walk_image. And then this will be the demonWalkingSpriteSheet will come from the demonWalkImage. And there's just one row, but how many columns was it? For walking is six. So one row and six columns. Now we duplicate this for idle demon-- get out of here, emoji popup-- idle image comes from demon_cyclops-- cyclop-- singular-- idle. And then this is the IdleSpriteSheet. And this comes from demonWalkIdle-- that's not it. demonIdleImage, and it's five columns this time. Now we're back to the demon super constructor, and we're just going to steal right out of the animation, grab all of this. How much did I copy? I don't know what I copied. I'm grabbing the animation parameter in the pot. There we go. So right now, it's going to look a lot like the ninja. That'd be confusing, but maybe an advanced demon type. So this was idleRight, so that is-- basically, just we want all five because I think he's got a little bounce to him here. So this is demonIdleSpriteSheet. And let's say the row for us here is 0 and the column is 1. And then we're going to add five of those. And that wasn't correct. That was really-- TREY HOPE: You got them all in an array. CRAIG LABENZ: Yeah. [LAUGHS] Try it again. 2, 3, 4, 5. So that's our idle dude, and he's really going to be popping here. I think we're going to want to slow him down. Let's try a 1/3 of a second even. I don't know. We'll find out together. Now then for walking, again we're just going to want all six of them. So this is the DemonWalkingSpriteSheet, and it's all row 1 and the different columns. So we're going to grab six of these. 2, 3, 4, 5, 6. What do you think? Are we going to have a dude? TREY HOPE: Looks good. CRAIG LABENZ: Oh! Oh, Randall says he thinks we can do assets thingy thing thing. I did actually try to use a single star for the blob earlier, and it wasn't working. But I of course should have tried two stars, so maybe that would work. I do want to speed up our Ninja because Ninjas are supposed to be fast. Oh, they're blinking in and out. That's interesting. Oh, why are they doing that? TREY HOPE: So they're probably getting an incorrect GIF while they're stepping through. CRAIG LABENZ: Yeah. Also, so they're moving way too slow. So demon idle, we got to get them popping here. Oh, of course, it's 0 indexed. TREY HOPE: Oh, yeah. Good. Didn't even notice that myself. CRAIG LABENZ: How silly of me. And this time, instead of renaming them all, I'm going to be smarter. OK, that should be better. So here we go. The game is building. Why does the column start from 0? Because all counting in computing starts from 0. TREY HOPE: That's the best answer to give to that question. Everything starts from 0. CRAIG LABENZ: Yeah. Hey, now they don't blink out of existence. Though if they were interdimensional demons, the blinking in and out wasn't all bad. I think-- oh man, that's a really subtle bounce. I don't know how much it's even going to be coming through on the stream here. They're pretty-- TREY HOPE: Yeah, that bounce time is still kind of slow. CRAIG LABENZ: Yeah. So we got it at 0.2. I guess let's double the speed. I wish it was a little more pronounced, but maybe just all the characters need to be bigger. TREY HOPE: Could be. CRAIG LABENZ: Oh, Randall verified, can't do it. TREY HOPE: I was about to look at up for myself. CRAIG LABENZ: OK. Next thing I want to do is make the Green Ninja run faster after we confirm. Now they just look like they're hyperventilating, but we're going with it. They're hyperventilating demons. Why it started from 1, not 0. My bad. Oh, that's just because I typed total nonsense. It wasn't ever going to work. OK. How do we make the Ninja run faster? That's what I want to do next. Here we go, speed. Wonderful. So there's probably some default speed which we're going to find as we keep drilling around. We still don't know the speed. Speed, speed, speed. Still don't know the speed. Movement maybe. Default speed, 80. Great. Oh, this is very precise something. Oh, diagonal reduction. Oh, nice. OK, that's how you have moving diagonally not be a speed boost. So the speed was 80. I guess we'll go after animation. Speed. I really want to be much faster. I want to get to those demons on the map. TREY HOPE: Double the speed. CRAIG LABENZ: All right, what should we do next after we test this out? Oh, that's a ninja. That's a ninja. Look at that. He's just scooting. It's like Percy Harvin out here. Watched the Florida Gators documentary on Netflix recently. Anyway. The enemies need to follow us, right? TREY HOPE: Yes. So that's where we would need to modify the demon class. There is a mixin that we add to the demon class. It's automatic movement or something like that. But it'll give us some callback methods for what we do when we see the player close to us. AutomaticRandomMovement. CRAIG LABENZ: You have AutomaticRandomMovement. TREY HOPE: And it provides just a bunch of different callbacks. CRAIG LABENZ: We're adding random movement like enemy walking through the scene. OK. So run and movement. Method that should be used in update. So this has a lot of parameters here. Is this going to add-- it looks like this is definitely doing a lot on its own, so should we just try it right now? TREY HOPE: Yeah. CRAIG LABENZ: Yeah, this method looks like it has some serious logic in it already. There's some target distance stuff now. So we're going to have to do something to activate this, it looks like. They did say to call the method in update, so maybe that's what we have to do. We actually have to call the method in update. The doc said that. So void update. That takes the double dt. And then we'll call this method, which itself takes the dt and override. Oh no, still mad. Must call super. I still have that print statement coming somewhere. It's printing the whole JSON map every time, and I'm super annoyed by it. But I couldn't find it. TREY HOPE: Did you type in-- was it a print? What if you searched for the print statement in the code? CRAIG LABENZ: Yeah, I've closed the files. We just have to find the right file. So they are moving. They're not following us yet. But they're moving, so that was certainly part of the issue. And the movement is random, so the mixin I think has lived up to the billing. Wonder if there's any parameters-- checkPositionWithRaycast. onStartMove. So there's nothing-- there doesn't seem to be-- TREY HOPE: There should be a method for seek and move to player. CRAIG LABENZ: Oh. I'm not seeing it in here. I'm guessing it's a different thing, a different mixin. So I'm going to search Bonfire, SimpleEnemy. Oh, I didn't really find it. Wait, what is this? SimpleEnemy. Oh, it is in Bonfire. Oh, the Google result here hid that it was Bonfire. Can't see it, other than I guess now I'm noticing it here. Interesting. Let's also make this bigger. So we're in Bonfire. Oh, I wanted to see also documentation. Does this have random? So this has AutomaticRandomMovement. So this should also list whatever the mixin is. Will this mixin have a see also? No. See kids, always write your see also. Really handy. Just skimming here. Do you remember what the other mixin's called? TREY HOPE: The way I have it it's just called that mixin AutomaticRandomMovement. But it came with a method called See And Move To Player. CRAIG LABENZ: Interesting TREY HOPE: I don't think they removed it that quick. CRAIG LABENZ: It seems to be somewhere else. I feel like I want to just open the-- I'm going to open all of Bonfire in another window so we can search for stuff. Bonfire. Loading. There we are, into VS Code. TREY HOPE: I'm not sure why that's not in there, in the AutomaticRandomMovement. But it doesn't look like it's in there anymore. It's weird. CRAIG LABENZ: All right, I'm in. Got Bonfire pulled down. Oh, interesting. So Randall did say there's an issue about this, the assets thing. It's not going to be a thing. And Bard was how he found that. Way to go, Bard. Way to go. All right, we're looking for-- what was it, seek? TREY HOPE: See, S-E-E. CRAIG LABENZ: Oh, see. TREY HOPE: And Move To Player. CRAIG LABENZ: Interesting. So these are in the examples. I don't really want-- oh, you know what? I should have loaded this up. Oh no, it's fine. OK, back to the search. I don't want rotation_enemy_extension. npc_extension. TREY HOPE: I think that's the one that you want, npc_extension. CRAIG LABENZ: seeAndMoveToPlayer returns a shape? Interesting. So we're back in main.dart in our game, and on the demon class we're going to add npc_movement. Nope. We're going to add npc_extensions on NPC. I wonder if an enemy is an NPC. They're not. There's also rotate-- oh, enemy_extensions, this is what we want. seeAndMoveToAttackRange. seeAndMoveToPlayer. Critter. I'll see how they do it in this goblin class. Oh, so it's seeAndObserveToAttackRange. Interesting. So seePlayer is what they call. So first of all, Goblin is a simple enemy. And they've got AutomaticRandomMovement, and then that's it. So that's fine. Now, in their update method they got a kill switch here. That's good. Is the game actually running? If not-- and this is a whole other kill switch. It's good. So they call seePlayer, and this is going to basically look at its range and maybe like not see through walls or something. That would be pretty cool. And then we've got observed and notObserved options. So that's pretty cool. And I'm going to copy all of this. So seePlayer is going to go into-- we're not calling random movement anymore. We're calling all of this. So we don't have an attack yet. radiusVision. Let's just give it 100 or something. tileSize. So 100 for us is going to be several tiles. And the attack range here-- so minDistanceFromPlayer. This is the attack range. Sure, that's fine. So we'll say 32 or something. And then, once we're in position, there's a ranged attack that we're not going to have. The vision for them is-- wait a minute, I thought that's what I was specifying up here. TREY HOPE: Yeah, there's a lot of parameters on this method. CRAIG LABENZ: We'll just give this 100 again. I'm not really sure. notObserved-- TREY HOPE: I think the difference between those two are one of them is for the radius that it would take for them to go into attack mode. The other one is for them to just notice you because they can notice you without doing an attack yet, I believe-- a certain attack. CRAIG LABENZ: I see. TREY HOPE: But yeah, there's a lot of customization when it comes to these methods. I don't think you need that. CRAIG LABENZ: Oh, you're right. We don't need anything for that. Oh right, because I just grabbed a bunch of actual code. We weren't leaning on a mixin anymore. TREY HOPE: It's coming together, though. CRAIG LABENZ: I'm moving around. Hey, hey, hey. So if we get close, are they going to follow us? No. Boo. TREY HOPE: They still got the random movement, it looks like. CRAIG LABENZ: So they call random movement in this update method. They're seePlayer. So I'm guessing we're not observed because they're-- well, move towards target and then not observed. They do eventually run some random movement. TREY HOPE: I think that radius vision might be throwing something off. CRAIG LABENZ: Yeah, let's look at the docs for that. NpcExtension. SeePlayer. So this method we notify when we detect the player in radiusVision configuration. It doesn't really say much more about what that is. radiusVision goes into the seeComponent method. radiusVision defaults to 32, and then it gets use shape vision. radiusVision. _canSee. RectangleShape. otherShape. So we draw a thing, and then just check for a collision. So that all makes sense. It feels like those should just be normal-- here's some more why you'd use Bonfire. They're going to check to make sure he can't see through walls, so that's pretty good. Anyway. Yeah, it feels like we haven't seen any evidence that this would be anything other than game pixels. I'm going to-- TREY HOPE: What happens if you take out the move to random call? CRAIG LABENZ: Oh, that'll be a good test. Yeah, that'll be a good test because then we'll know-- are they just going to sit still? I think if we can get them following the player, that could be a good breaking point. What do you think? TREY HOPE: I think that's good. CRAIG LABENZ: Great thinking. They're not moving. So we're pretty sure they are sitting in notObserved and seeAndMoveToAttackRange. I wonder-- well, we know they were getting down here because we commented out and they stopped moving. So the question is just, why are they not seeing the player and getting into observed? TREY HOPE: I wonder-- I don't think that there's anything with collisions needed here. CRAIG LABENZ: (WHISPERING) gameRef.player. Well, this is interesting. We did-- there is-- we set up the player correctly, right? TREY HOPE: Yes. CRAIG LABENZ: OK. So return see component. Yeah, I'm just looking at how this code works. See component has the observed and not observed. And this is what we were just looking at. So if we're moving-- that's not us. Can see observed. Not observed. Right. So we're definitely getting into not observed, and I want to see why. So I have one thought here. We thought there was one demon. There are two, it seems. Demon. Demon. There we go. So I'm going to get rid of one of them so I can put print statements in here, and we'll know that each print statement is just for that one demon. We won't have to worry about what's what. So in vision, in canSee, there's inShape on a first check. And I also want to see, how big is this rectangle? And I guess the size is what we really want. And then we've got inShape. So if inShape and checkWithRaycast-- what was the default on that? And checkWithRaycast is-- where did that come from? Is set to true. So we are going to make this check. Then we've got our raycast results. I'm going to check that as well. Print raycast result. And we're going to get all of this. See what we see. TREY HOPE: Raycast-- CRAIG LABENZ: Py Workshops asks, "Will Flutter support 3D games in the future?" TREY HOPE: It's a good question. CRAIG LABENZ: Yeah, I think eventually. So when we're really far away, we're seeing that this rectangle is pretty small. It's like one square. And then we're not in the shape, so that's expected. So let's get down by this demon here. So now inShape is true, but we're getting false on raycast. So you mentioned hitboxes, and is it that we need a hitbox? TREY HOPE: I think that's exactly what it is. So we can just do that in both of the constructors of the player, the Green Ninja, and the demon. CRAIG LABENZ: Got it. TREY HOPE: So yeah, you got it. No, it'll be-- actually, it should be add hitbox. Should be a native method on the player class. So if you create a constructor on this class-- CRAIG LABENZ: In on update I think we would, right? I wasn't even doing the right thing. TREY HOPE: We're just assigning it, so we would need to put it in the update. We can just, in the constructor-- CRAIG LABENZ: Oh, sorry. TREY HOPE: --just add a collision. CRAIG LABENZ: OnLoad is what I meant, but I'm-- so we've got positions, size. Oh. No, there doesn't seem to be-- I think we are going to have to do it in onLoad. But you're definitely right, not on not update. TREY HOPE: Yeah. Actually, I think there's-- OK, so there's a mixin called object collision that we can add. I think with that mixin, then we'll have a method called setup collision. CRAIG LABENZ: Oh, is this base Flame? TREY HOPE: Yes. CRAIG LABENZ: Got it. So it's like-- yeah, Has-- oh, how'd the cursor get there? With HasCollisionDetection. I can never remember whether it's-- there's one you put on the root game, and then there's another one you put on the actual-- TREY HOPE: I think HasCollisionDetection is on the game. And then ObjectCollision is on the character. CRAIG LABENZ: So that one didn't come up for me, but I don't know if I-- are you sure it's called ObjectCollision? TREY HOPE: ObjectCollision, yeah. CRAIG LABENZ: Now, there is a new version of Flame. TREY HOPE: OK. Let's have it just-- well yeah, we can look here too, but I was going to just type in collision and see what pops up. CRAIG LABENZ: Oh, in-- TREY HOPE: In Flutter for a mixin just see what pops up when we type in. CRAIG LABENZ: Am I in the right one here? Up on-- oh, that's not even the right one at all. So sorry, you were saying-- TREY HOPE: Yeah, just type in CollisionCallback, HasCollisionDetection. Yeah, I feel like it's one of those two, HasCollisionDetection or CollisionCallbacks. CRAIG LABENZ: Can be used to get callbacks from the collision detection system. No, I don't think that's-- this isn't the one we want. Collision callbacks on component. Oh, maybe it's this. What's the error here? Private name _activeCollisions conflicts. Conflicts with what? TREY HOPE: Maybe it's-- is it already in the simple player? CRAIG LABENZ: Oh. Attackable. GameComponent. Is that from Bonfire as well? It is. So PositionComponent, CollisionCallbacks. Ah-ha! But we still need to add the hitbox, I think. TREY HOPE: I think what we could do is call SetupCollision inside the Green Ninja, and that would give us what we need. CRAIG LABENZ: And where does SetupCollision happen? TREY HOPE: In the on load or the constructor. CRAIG LABENZ: Oh, sorry. Where is it defined? Sorry, my bad. TREY HOPE: Got you. CRAIG LABENZ: Because yeah, all the way through GameComponent I'm not seeing SetupCollision. TREY HOPE: It looks like it's in Collision on the Bonfire package. I see we have lib, base, game_component. I think it's lib, collision. CRAIG LABENZ: So I see collision_util. No, that's not it. Collision.dart. In lib you saw it? TREY HOPE: Yeah, but now that I think about it, I'm using an older version of Bonfire, 2.1.2. You're on 3 it looks like. CRAIG LABENZ: Yeah. So I'm going to-- oh wait, that was-- then the collision-- so callbacks, this does just come from Flame. CollisionCallbacks. So I think we just need to follow base, flame, collision stuff, which I never remember. 100% always have to look it up. Look at the latest stuff. Collision Detection. Very good. So MyGame, looking good. MyCollidable. So CollisionCallbacks, that's what it was using. So there's just the onCollision, but we still have to add the hitbox. There we go. This is what we want. So now we'll return to the player. No, to main.dart, see? And we're almost all the way there. What's your problem? Isn't a valid override. Oh, future or. Fine. So now it might work. TREY HOPE: Do you have it in the demon as well? CRAIG LABENZ: I don't, but I don't know if it needs that because in the raycasting code it was just looking for a collision with the-- oh, we are now invisible. But he's following us now. He's got better vision than we do. [LAUGHTER] What a crafty enemy. TREY HOPE: I'm not sure what happened, though, why you disappeared. CRAIG LABENZ: Yeah. Oh, probably because we have to call super.onLoad. I bet I just ruined everything. [LAUGHS] TREY HOPE: Everything else is working, though. CRAIG LABENZ: Yeah. Well, this feels pretty good. Get back here, game. There we go. It helps when you don't delete all the code or short circuit the code that the framework has. Yeah, look at that. TREY HOPE: Perfect. CRAIG LABENZ: And it looks pretty good with this walking animation you found. Of course, this and so much more is in your video on Bonfire. Oh, look, it got lost now. Oh, boy. There he goes. Come back! [LAUGHTER] And we have no boundaries, so he's just going to leave the game world. Oh, interesting. He kind of got stuck in this motion as we left his radius because we're faster than him. Then he just got stuck in that direction. TREY HOPE: Yeah, what is that? CRAIG LABENZ: It's interesting. TREY HOPE: That's interesting. CRAIG LABENZ: Yeah. Potentially some refinement to be made on the simple enemy class. Maybe a little too simple. So I do want to just remind everybody, you got to check out Trey's channel here where there's-- you got a ton of videos. And the Green Ninja-- yeah, I think it's this one right here. So this is a video that we just did the simplest little version of. But you've got a ton of stuff going here. Oh, you have a backend one too with Nakama? Trey, you're killing it. TREY HOPE: Yeah, I just got hip to Nakama. I really-- I want to see what Nakama can do for me with a lot of the games I'm working on, but I just got hip to it. It's pretty cool. CRAIG LABENZ: Nice. Yeah, I've heard great things about it. What's your initial impression? TREY HOPE: I think that it's a very-- just a simple way to set up some online gaming features like leaderboards or online matches, friend groups. It's pretty much just a simple API to get all that integrated. I haven't seen any other type of gaming package that integrates as well with apps and stuff like that. So Nakama, from what I can tell, it's a really good contestant for adding online gaming. CRAIG LABENZ: Nice. Did they have leaderboard-- or you said leaderboards. Do they have matchmaking and individual dedicated instances to actually run a single real-time match? TREY HOPE: I know that they have matchmaking. I don't know how detailed they get into what they're providing, but I know they do provide matchmaking though. CRAIG LABENZ: Nice. Awesome. Yeah, Nakama's been on my list of things to look at more as well, but I just haven't yet. Well, man, Trey, thanks so much for coming on. Thanks so much for walking me through this and showing everybody that you don't just have to stop with Flame and there's a whole bunch of super helpful utilities in Bonfire as well. Any last thoughts from you, Trey? TREY HOPE: I want to thank you personally for reaching out to me. I've definitely been watching your videos for a long time, so I'm definitely honored once I got the invite, so thank you for having me. And thank you to everyone out there who watches my videos. Thank you for the entire Flutter community itself. Just very grateful to be a part of something that's still growing and learning every day. So thank you so much. CRAIG LABENZ: Well, man, very well said. I just put my computer to sleep on accident. I don't know if the stream actually flickered or not. [LAUGHS] Very well said, Trey. The pleasure was all mine, I assure you of that. Everybody, thanks for tuning in, whether you're watching live or in the future. Hopefully Bonfire looks pretty exciting. If you've been into Flame, hopefully you feel like you're now into Bonfire as well. And folks, we'll see you all on the next episode next week. TREY HOPE: See you later, everybody.
Info
Channel: Flutter
Views: 10,597
Rating: undefined out of 5
Keywords:
Id: mp5N0ENPuOw
Channel Id: undefined
Length: 103min 2sec (6182 seconds)
Published: Fri Oct 20 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.