Godot Top-down Shooter Tutorial - Part 4 (Bullets and Signals)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everyone my name is Joe and welcome to part 4 of the Godot top-down shooter tutorial in this video we're gonna be talking about how to spawn bullets how to use signals and connect to them from the UI and from code and how to destroy bullets that you've spawned after a certain amount of time so as a recap of what we did in our last video we had a player controller where we can move our character around and we could shoot by clicking and it all works and feels good except for the fact that bullets will actually move with our mouse which is obviously now what we're going for so the first thing I want to do today is fix that I want to create a bullet manager that our bullets will be children of rather than the player so that they won't be dependent on our mouse move so right now if I come into our player we're spawning all of our bullets from this script right here we're spawning them as children of our player and that's happening because of this line right here where we're creating our bullet and since and then adding as a child so the first thing I want to do is get rid of this line and you know if we run it right now bullets won't appear but that's ok because we're gonna fix that in just a second so what we want to do is whenever the player attacks we need to communicate something we basically need to create an event to tell whoever cares that a bullet needs to get spawned this is a pretty common paradigm in programming called event-driven programming and the way we do it in Godot is through something called signals so in order to declare a signal you can just come up to the top of a file so I'll do it up here and what we're gonna say is just signal and then we'll say player fired bullets so when you're declaring a signal you are describing an action that has already taken place so you usually want it to be in the past tense and it's perfectly fine to have a signal like this right now where there's no parameters we do want to have a parameter here though because we're gonna create our bullet and then we need to pass it alongside the signal with whatever we end up whatever ends up taking care of it so we can do that by saying just similar to a function we can just add a parameter in parenthesis here so we'll say bullets and do that and now down here so instead of adding the bullet as a child to our player now I'm actually gonna call emit signal and you'll see it'll autocomplete it'll detect that we added this new player fired bullet signal and it'll be right there and I'm also just after you give it the name it's a you know you just add any parameters you want to pass in so since we added a bullet parameter up here I need to have that in here as well so I'm gonna say bullet instance so we're gonna emit this signal and we're gonna pass along our bullet instance so this is all well and good but now we need something that will actually listen and and handle this signal that we're sending up and how we're gonna do that is come over to our main scene over here and I'm gonna add an ode to D and so it's a pretty common use case in Godot or in programming where you'll want something that is in the scene but not actually visible you want it to either just contain data or have some functionality or do some things you know every frame or whatever you just want some functionality to happen but maybe that doesn't like show up visibly on the screen which is the case for our bullet manager we want it to contain all of our bullets as children but not actually do anything itself visibly and so no two DS are a great you know bass they're the base note for all to dinos and so they serve that purpose really well of just something that you can you know use to have some logic to it without actually showing up and so I'm gonna call this bullet manager okay there we go and I'm gonna add a script to this so I'm gonna do a tad script and we'll just call it bullet manager for now eventually I'm gonna refactor and move around our folders just to organize it a bit better but for now we'll just put things in the root directory so we have our bullet manager and before we get to that we actually don't have a main script right now so I'm gonna add a main script to I'll create that and so basically what we need to do is at the beginning of the game we need to get our player and we need to get our bullet manager and we need to connect the bullet fired script error signal excuse me from the player to a function on the bullet manager and the way we're gonna do that is in our main script we're gonna get references to both those things so we'll say I'm ready VAR bullets manager and we will call this bullet manager it will do the same for the player player equals bullet manager okay so now when basically our game starts up when everything is ready we will have references to our bullet manager and our player and what we can do when both those are true we do have those references let me get rid of some of those scaffolded stuff we can say bullet manager and that or whoops sorry player dot connect and we'll see a list of all the signals on the player it won't pick up the one we added since it's not typed but we know it's there so I said player fired bullet and then we need to say well do bullet manager and we'll say handle bullets spawned okay so let me break down what this line is doing whenever you connect a signal you call connect from the node that has the signal so because our player fired bullet signal is on our player we have to call player dot connect so we do that and then the second parameter is your passing in which object is going to have the function that will be called as a result of that connection so our player has the signal and our bullet manager has this function which we will create that should get called so what's gonna happen now is every time that the player clicks this signal is gonna get fired and our main script is going to get it here and because of this connection it's gonna get the bullet manager and call this function but right now that function doesn't exist so we need to implement that so I'm gonna copy and paste the name of this and come over to our bullet manager here gonna get rid of this real quick there's a way you can turn off all the like default comments and scaffolding and everything and I've just been too lazy to do that but so what I'm going to do is funk and a bullet spawned and will save bullets okay so now what we can do since we're getting our pull our bullet passed in Arbil and manager we can actually add that bullet as a child of the bullet manager so I can say add child bullet and now what's gonna happen is that whenever we click that signals gonna get fired our main is gonna send it over here and then this handle bullet spawn function is gonna get called and we're actually gonna add the bullet as a child of our bullet manager and the thing that we're going for is because this bullet manager is not a child of the player our bullets are not gonna be are not gonna move relative to the players rotation or transform or position whatever so before we test this there's one other change that we're gonna want to make if I come back over to our bullet or to our player excuse me and our script here so you'll notice that previously we were trying to set the direction of our bullet before it's added to the scene tree because now we're adding it after this single get submitted so we're gonna get some errors here if we try doing this and what we really want to do is actually add the speed or I mean the position and the direction of our bullet is part of our signal so we're basically saying hey we fire like a bullet needs to be spawned going this way starting at this position and it kind of makes sense one of the nice things that this will let us do is something we call separation of concerns and what that is is you want to make sure that your code is broken up into blocks or chunks that are just focused about the things they need to be and if we look at our player for example our player doesn't need to know how bullets are spawned or like what information about needs a bullet or a player should really just be responsible for saying hey the player attacked and we should let something else know the logic for how our bullet actually needs to get spawned so now only is this gonna make it a little bit cleaner for our player it's also gonna let us separate nicely the things that our player doesn't really need to know about and let our bullet manager handle those because that's what it's there for so what I'm gonna do is just take out pretty much all of this code right here I'm just gonna command X to cut it and then what I'm gonna do is come up to our signal and not only add a bullet but also add a position and a direction okay so now we have not just the bullet but the position in the directions so we actually need to add that down here when we're setting off the signal and I'm realizing I actually got rid of some lines we needed so we need to give it the position in the direction so what we're gonna give it is the position is end of gun position because that's where we want to spawn let me get rid of this one here and then we need to give it a direction so we still need this direction to mousseline we have here so I'm gonna do direction to Mouse and then because our bullet instance is not actually readied it's not added to the scene yet it's not added as a child of our manager it's not actually gonna have a position so we're gonna get an error if we try using this line here so I need to change this to also be out of gun that global position position and then I can get rid of this one because we're not doing that yet and so now we're sending out our position and our direction as part of our signal which is perfect and the thing we need to do now is actually come to our bullet manager to where we're calling this function and come into here and actually add those parameters into so we'll need a position and we'll need direction and in just a sec once we finish this I'm gonna come back through and we'll refactor this a bit to use static types here which are a cool feature of Gd script but we'll just get it working first I did just notice to a bug in May I don't know okay we're all good take that back anyway so now that we have this what we need to do is we can set our bullets global position to be equal to position and we can set our bullets this is where we added that one let me go back in here and check we added that set Direction function so we can say bullet that set direction and so speaking of static typing hypothetical question would it be great if we had good auto completion for stuff like this like we know that set Direction is a function on our bullet so won't it be nice if I were to just say start typing set and it popped up well you actually can get that in GD script and we'll do that in just a second here so let me do set direction and we'll say direction okay Oh real quick I'm noticing I needed to write bullet here to set the bullets global position not our manager okay so let's try running that now and if I shoot you'll see it's still messed up but it looks like it's going in the right direction I think I know why this is happening I bet yeah okay so the reason that's happening is because I was accidentally setting our bullet to spawn at the end of guns relative position rather than its global position and so it was spawning if I come down to our end of gun it was spawning 31 degree units to the right and 10 degrees down of right here this is of the player these are relative coordinates to the center of the player but then when we set it as a child of our bullet manager it was actually getting spawn you know 31 degrees to the right and 10 degrees down of Maine so it's always getting spawned in this corner which is what we don't want so all we need to do set this to global and we'll have fixed that bug if I dare claim victory ahead of time and sure enough what do you know but it works oh this is sweet guys it's so cool this looks great it feels great nice awesome think we're good with that so now that we have the firing working correctly I want to make sure that we have our bullets rotated the right way it just you know just so it looks better and so if you go to our bullet you'll notice that it's actually rotated a different way than our player you know our player is rotated facing right by default which like we talked about before is kind of like B default you know zero degrees is kind of facing to the right so we want our bullets to be similarly angled so we can select our bullet come over here to its rotation degrees and set that to be 90 and just save that and then what we can do so there's a couple different ways that you can get the direction you know that you want something to fire in this case just an easy kind of it's not maybe the best way but super easy and simple way to do it is create another position to D I'm gonna call this gun direction and what I'm gonna do is set it just a couple units further out than our end of gun so I'll do like you know 34 in this case and so what this is gonna do is it means that no matter which direction our player is rotated we're always gonna have the same like we're always gonna have the correct direction like the angle the vector between these two positions is always gonna be correct because they'll it'll rotate with our player and so this is kind of like a quick and easy way to be able to calculate that angle at all times is just by getting the direction between these two getting the difference vector between both these positions so I'm gonna create this gun Direction thing here I'll make a reference to it here so I'm ready of our key gun direction direction and so what I want to do down here is we're actually gonna replace the previous things we were using that got our global mouse position because one issue with that is that our player rotates at its center and so the rotation if we try to use the mouse rotation is going to be lined up with from our center and so our bullet is always going to be slightly angled because it's gonna be trying to go to our mouse and if our mouse is right here and our players facing this direction you know this is the angle we're gonna get which is what we don't want so that's one reason why we have to make this separate direction calculation so I'm gonna get rid of what we're currently doing and I'm gonna change this let's say for our direction and this is going to be our gun direction that global position - our end of gun that global position so this is gonna get us the angle between basically or the vector between both of these positions and we can actually pass this end so we'll have a direction vector coming through and now on our bullet we can actually say rotation equals direction dot angle and because we've set our rotation to 90 we actually want this to be a plus equals so that we are you know it whatever rotation we're setting it to it's additive to what's already there and now if I run this we should see that our bullets are facing the correct direction they've also picked up quite a bit of velocity through that change which is unintended but I think it's just because we're getting this direction here and then we're not normalizing it so if I do this we should be good to go oh yeah there we go and look at that that looks so good it's a lot better even the small things you know it just adds another kind of touch of quality to what we want to do this is great I think we've got the rotation part good to go and now the thing I want to focus on is to come back to what we talked about before real quick and talk about class names in Godot so we're gonna probably do some of this refactoring plenty of times throughout the way but what I want to focus on now is what we are seeing just a little bit ago in our bullet manager where we were saying man it wouldn't be nice if we had good type completion for our bullet class here and Godot actually provides a way to do that so you'll notice that if I have like you know a certain type of node for example that's built in Godot we'll be able to tell you the specific functions for that node that don't exist on other ones and we can actually give it the same kind of knowledge about nodes that we create like our own scripts and so the way you can do that is by giving a certain script that you've created a class name and so I can come in to our bullet here and say class name bullet and now I can reference the bullet type anywhere else within our code so what I can do is come over here to our bullet manager and actually type this as a bullet you'll see it picks up on it and now remember before I didn't get that Auto completion I set Direction well if I get rid of this and start typing set Direction you'll see it's there which is awesome and then I just pass the direction here until want to forget that so this is a really nice way that you can you know if you have a type or a script that you're passing around in a lot of places and using wherever this is a really nice way to give good auto completion to help gadot figure out what you're trying to do so I believe that that's all I want to do with this right now we can come back and do more refactoring you know with that as we go I will add a vector to tight to both of these personally it's my preference to give types whenever possible I think it just makes an easier developer experience and it's easier for me to remember later on what to do you absolutely don't have to do it for a long time dynamic languages like Python which is what Gigi script is based off either didn't support or didn't encourage static typing you know I come from a not a like object-oriented but even with like something like typescript which is kind of a static type system built on top of a dynamically typed language is where a lot of my background is and so I just have a preference for it I think it scales better but you know you do what is best for you so the last thing I want to focus on today in this video before we have a killer firing and bullet spawning system is to actually add a bullet timeout timer basically or a kill timer right now if I fire a bullet it's just gonna go on forever it's never gonna end I won't show you the actual scene tree with like all the boats right now but take my word for it it's never gonna go away and so what we want to do now is just add a timer to our bullets so after you know a couple seconds they will go away and so I'll come into our bullet scene here I'll add a timer and I'll just say kill timer oops and what I'm gonna do is connect our timeout signal from our timer and then do some handling on it there and previously in our main class we connected a signal by code so there's two ways to connect a signal you can either do exactly what we did here by calling the connect method or you can actually do it through the UI um and there's there's different way like different reasons you'd want to do one to the other if you're dynamically creating instances of a scene like you're creating it through code you also have to connect signals through code so if that's what you're your use case thing you've got to do it this way but it is nice for like you know our every single bullet we ever make is gonna have this timer so it's kind of nice to do it through the UI and the way you do that is you select the kill timer come over here to the node tab make sure you're on the signals tab and then just double click timeout and it will auto fill a method name for you and you can select which node in your current scene that you want it to be connected to right now we only have a script on our bullet scene so we and that's the one we want to connect to anyway so I just hit connect and you'll see it'll auto populate a method with the same name we saw up above and all we want to do here is say Q free and what Q free does is it's basically the unity equivalent of destroy it just tells the engine hey this object whatever this script is attached to the next time it's safe to do so you can free it so there is just a free method without the Q in front you really don't want to call that ever Q free tells Godot to free it when it's safe to do so whereas free is just an instantaneous freeing of that memory so just always called Q free when you want to get rid of something and a lot of people will talk especially for something like a bullet where you're gonna have potentially hundreds or thousands of them on scene at the same time that you can use something like object pulling to make sure you're never actually destroying or freeing your bullets you're just making them invisible basically that's definitely something we might get to later on I doubt it in this series we're never gonna have enough bullets I don't think that it's gonna matter but just just as a heads up you know this is that's like that might change if that's something you want to implement you would do something different here rather than cueing it free you might set it to be invisible or just return back to the pool but anyway so what's gonna happen here is whenever our timer times out basically it's done this signal is gonna get called which will cause this method to go and we can adjust how long that takes here I don't think I want is one second actually we'll start with 0.5 just to make sure it works a one-shot timer basically means it once it runs it doesn't automatically restart and autostar means that starts right at the beginning I'm not sure if auto start works correctly I've always had issues with it I just always manually start a timers anyway just to be clear though I will set it to be a one-shot and basically what we want to do is start our timer as soon as this hour bullet is ready so as soon as it enters the scene tree and starts moving we want to set that timer to go so the first thing I'm going to do is add a reference so let's say kill timer oops I forgot the bar kill timer equals the old timer there we go sweet and what we'll do is say full timer dot start and then we'll start it so whenever our bullet enters the scene we'll start our timer and whenever our timer is done it'll keep free and I think our bullet is moving slow enough that we should actually see this happen so we should see our bullet disappear there we go so it's getting freed after a half second I want to bump this up to be two seconds I think that's a good time hopefully it should be off screen or write about so okay we might not actually be up see it but I think this is great this is kind of exactly the time I was going for where it would be just off-screen so you know basically whenever I left the screen it would free the memory so anyway guys I think that's pretty much it for this video I hope you've enjoyed absolutely let me know any suggestions or feedback or things you want to cover next in the comments below and I'll see you in the next video [Music]
Info
Channel: jmbiv
Views: 7,850
Rating: undefined out of 5
Keywords: godot, godot engine, godot 3.2, godot tutorial, godot top-down, godot 2d, top-down shooter, how to make a game in godot, game development, game development tutorial, game development for beginners, godot for beginners, how to make a top down game in godot, how to make a top down shooting game in godot, game dev, indie game dev, indie game development, how to make video games, how to godot, hobby game development, gamedev, godot game engine
Id: 9RP7Ujv0gdE
Channel Id: undefined
Length: 24min 17sec (1457 seconds)
Published: Sat May 09 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.