Godot Top-down Shooter Tutorial - Part 8 (Enemy AI and Shooting)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

New as of this video: I've made the source code public, so you can download it and edit it rather than having to get every line from the video. You can find the link in the video description!

👍︎︎ 1 👤︎︎ u/josephmbustamante 📅︎︎ May 27 2020 🗫︎ replies
Captions
everyone and welcome to part 8 of the top down shooter tutorial in Godot my name is Joe and we have some cool stuff coming up in this video you're gonna learn how to add AI to our enemy how to make the enemies shoot at the player when the player gets close enough and then how to use a finite state machine let's get started alright so let's make our game actually feel like a game so the first thing we're gonna do is come into our enemies scene and we are going to add a node to D no to D and we're gonna call this AI and the idea here is that we want to have our AI also be something that we can compose and eventually we might have different types of AI for now we'll just have one but as our game grows you might want different types of AI so different enemies or even allies act and perform different things but um we want that to be something that you know is easy for us to just kind of drag and drop and edit and move around from the editor if possible and that lets us kind of reuse as much code as we can without having you know like a ton of just like inherited different AI classes so we're gonna try and take a stab at getting a system started towards that today so we're gonna make this AI script or note here and I'm gonna add a script and I'll just add it into actors for now and the first thing we will do is come into our enemy and we're gonna get a reference to that so I'm going to say I'm ready of our AI equals yeah great and now if I come into our AI script here the first thing we're gonna want to do is add an enum and if you've never used an enum before it's basically just like a list of predetermined values they're really just a list of numbers but you can give them names to kind of make a nice way to interact with them so we'll just have an enum here and this is just gonna contain all of our potential States so we're gonna build a finite state machine for our AI which just lists a bunch of potential States where the AI can only be in that wanted time so we're gonna have to for now just to keep it simple we'll have patrol and we will have engage and so the ID ID with this is that one the AI does not detect the player when they're not close to each other the AI will patrol around and then when the AI does detect that the player is close by they will start to engage the player and actually attack them and so we can use these States to kind of switch between as they I you know detects that a players near or not in order to actually build our engage functionality which is the state we'll start out with first we need to give our AI a way to detect that the player is actually nearby so in order to do that I'm going to add an area to D and I'm going to call this I will just call it will call it player detection zone we might expand this later to do detect things besides just the player but what I will do then is just add a collision shape to this collision shape TD and we're gonna make this be a circle shape here one thing I'm also gonna do is just reset our enemies transform so that they're just at the center of the scene and then so our collision shape for a player detection zone we want this to be big right because we want our enemy basically to say if the player enters this zone we can start attacking it and again the reason I'm putting this detection zone on the AI and not on our enemy is because ideally we want to try as best as we can to separate the functionality that our enemy needs from the functionality that is relevant to our AI so when enemy or just you know a soldier character sprite in general doesn't necessarily need an AI or doesn't necessarily need this specific type of AI it really can exist just fine on its own like it has been up until this point without an AI so we want to make that an optional feature that we can compose in to our enemy thus it's gonna all exist under our AI so I'd say that now if you got a player detection zone eventually we might have a separate kind of controller node for each of these states but for now I'll just put it on here so let's get a reference to our detection zone player detection oh there we go player technique so cool and so now what we can do is connect our on body entered here body entered to our AI script and so whenever the player enters this zone now we can actually get a reference to it and do something about it we can actually start attacking the player so within this callback we're gonna want to actually set our state so I'm also now going to one let's uh let's bump this enum up to the top and do that let's give it the proper spacing and then what I'm also gonna do is say a VAR or setup variable to contain our state so we'll just say state and then this will be we can say patrol by default and so what we can do now is then have a function so function actually it let's uh let's use a set get here to because we want to make sure that whenever our state changes we're always calling this function so we can do set Get Set state and then so here we can say set of states will say new state and so this is gonna be an integer because like I said before enews are actually integers under the hood they're just a way to interact with a predefined set of you know values in a nicer way by giving them a name so let's build this out and basically we'll only want to do is if a new state let's call this current state for now if new state equals current state so if they're the same we don't want to do anything we just want to return otherwise we want to set current state equals new state we're also gonna have a signal that we use to actually communicate to our enemy that the state has changed so let's create a new signal over the top here so oops a signal state change so I'll pass in our new state and then here we will emit signal state change with our new state one thing I did notice too is that we didn't actually named our inu here so you don't have to do this but it is kind of nice to do so you can give an enema name typically you want the name to be pascale case where your values are actually uppercase with underscores in between now we have to do state dot patrol I think I've this is an int and then we just get a little bit of better type handling around our current state so now we've got a signal that will send up with our new state and this will allow our enemy to actually listen to our state changes and what we're probably gonna wanna do you eventually let's have like some kind of a function like get new target or something so that our enemy when it realizes that our state has changed it can tell our AI to generate a new place to go to our new position but we'll get to that in a sec okay so one thing we'll need to do before we actually implement our player detection is actually save off our player somewhere so I'm gonna add a variable just called player we'll give this a type of player just for now and then we will set this to null initially and so we will this will start as null and then we'll fill it in with our actual player once we detect it so now we're ready to actually implement player detection if one thing we're gonna need to do before we write the actual code in that function though is add our player to a group so if you select our player in the scene tree and come over to node not only will there be the signals tab but there also be the groups Ted and this is where you can add any scene to a specific group so I'm gonna add this to the player group and then I'm going to add our enemies to the enemy group and the reason for doing this is because you can use groups to identify what types of things nodes are it's very similar to tags and unity and the reason we want to do this is because within our AI here we don't want to just check whether the player liked this body that we have is of type player maybe we change that later on maybe there's multiple players or maybe we want to use the same code for detecting a ai-controlled allies as well or just an AI that's on the other team so what we can do is use groups to could detect is this something that I should attack basically for our enemy here and I know that we just made a group called player but eventually we can make a group called allies or something and then we can add allied units basically to that group so this just gives us a little bit more flexibility groups are a really easy powerful way to kind of determine what how to handle a certain thing that you've detected without actually needing to know what type it is explicitly so it's just a little bit more flexible so we can say if body that is in group built-in function there and we just pass in the name which in this case was player so if our body is in the player group this condition will be true so I can come into our script again and if that's true we need to say set States whoops we'll give it our new state that engage so we found a player we need to engage and then now that we have that we can also set our player variable to be Bobby okay so now when we detect the player we will save it off and then we can use that to actually start attacking it so somehow we need to get our weapon accessible from our AI because our AI needs to tell our weapon to shoot so that leads to another question which is how do we get our AI and our weapon to talk to each other we've talked about the idea of signaling up and calling down but how do you make things communicate they're at the same level in the scene tree and so one thing you can do and there are many ways to do this and I'm not saying this is the best way one potential way you can do it is to still use signal up call down so our AI would send a signal up to our enemies saying we need to shoot and then our enemy would tell our weapon to shoot that's not bad it's kind of a kind of overkill it's adding two extra layers of stuff for something that you don't necessarily need so my favorite way when it makes sense to do to interact scenes that are in the same level of the tree is to use dependency injection and what that means is that you inject a dependency which in this case would be a scene like our weapon so we give it to our AI at one point so that it can directly access it without actually knowing where it is in the tree so if our weapon moves somewhere else in the tree or whatever it doesn't matter we've already got a reference to it in our AI and so it gives you a method or a way of accessing a certain node without actually relying on its position in the scene tree and that's what we want to avoid is relying on where something is in the scene tree so let's look at how we can do dependency injection in this case so if I come into our enemy script I can get a reference to our weapon say I'm ready var weapon which would be weapon okay there we go and then what I can do is within our ready function here say ready what we will do is say something like a I set whoops set weapon now this this function doesn't exist right now but we'll create in a sec and basically what we're doing here is saying it's okay for our enemy to know that we have a weapon or our enemy should always have a weapon and then if there's a case where it doesn't we can add some handling and for that but our AI however should not rely on there being a weapon and so our enemy can actually pass our weapon in to our AI and we will write our AI in a way so that if there is no weapon that it's given then it'll just not really do anything but it won't cause an error and that gives us some flexibility and we don't have to rely on like our scene tree being structured in a certain way so I need to create a set weapon function on our AI so I'll come into here and we will just have a function and set weapon and this will take a weapon I don't think we gave a type for that no we did not so I might go do that and for just a second so let's give this class named weapon come back to our a I now we can actually type this as a weapon and the fact that we're having a bunch of extra references in here that in general our state itself doesn't need but really just like in engage state we're attacking the enemy makes it think that it makes seemed like to me that eventually it might be worth it to break out like I had previously said to like a engaged controller or to have a specific controller for each type of state so that it can kind of handle all this in our actual state controller our AI controller just handles switching States and then reporting that back to our enemy but this is fine for now don't want to get too complicated where we don't need to so now we have this which will let us set a weapon so I'll save our weapon weapon and this will be null just like our player and then here we can do weapon equals weapon and because they're both named the same I could use self that weapon so there we go and now what we can do is that whenever we fire or going to fire we can just check if our weapons there and if we're in the engaged state but we have no weapon we can just you know print something out like hey like you can't do this and it'll just be a no op so it won't cause our game to crash okay so now that we have a weapon I think we are ready to actually start firing so let's do this in our process function so to say process and again like there's more efficient ways we could do this like instead of doing this every frame we could just listen to like maybe a weapon like weapon is ready to fire or something there's but not really that important so we can just start by doing a match statement here so match is like a switch statement and other languages it basically you give it a value to match on and then you give it's like an if statement but you're only comparing one value so we can do a match on current state and then we can say if States dot Patrol so if we're in the patrol state we just pass we'll just do nothing right now but if we are in States engage we will do something which I'll do in a minute and then godot's like default handling whereas if you somehow get a value that doesn't exist it's just this underscore right here this should never happen because we are using current state which should only be one of those but here we can just do like a print oops print err prynt there isn't there a print error method okay we can just do print I'll just say error found state for our enemy that should not exist okay so now we can actually add handling in for what happens when we're in the engage state so at this point we can assume that we have a player but we'll need a check so if we don't have if somehow we've gotten to the gauge state we don't have a player that's prom because we can't engage if there's nothing there so if player is not equal null and weapon was not equal null then we can do something I'm just gonna add this here just so we have good debugging so we can see if something like this happens I can say prints all right in the engage States but no weapon or player so that way we can see if nothing's happening it'll at least tell us why nothing's happening but now we're ready to actually fire it our player within here so now if I type in here whoops weapon and dot shoot we can run this but we're gonna notice two things one is that when we enter the sphere of detection or the radius of detection it works but our enemy there's no bullets being shot out and they aren't turning around to face us so we're gonna fix both those one at a time and we'll start with the facing the player so we're gonna solve that problem also by using dependency injection and basically from our enemy script the enemy will pass a reference to itself into our AI and in that case in this case I think it's it's okay to do that because our AI like if we're trying to follow a composition and kind of like a you know older school like how unity has worked a model of doing it to like we really don't want our AI or in this case our AI is really just like an extension of our enemies functionality so if I come into our enemy I'm actually just gonna change set weapon to be initialized on our AI and it's gonna pass in so our enemy will pass in itself and our weapon and then on our AI I'm gonna change this to be initialized that's like kind of a default name I give to dependency injection functions like this it's kind of like an unofficial constructor that I use and I'll call this just enemy or we'll call this I'll just say actor and that will be do we have actually does really matter so we can just do that and then we can say self that actor equals actor so then this way like any generic actor that needs a I can initialize this and so there is a function in Godot called get parent we could just use but I still like doing this a little bit better because it means that our AI is not necessarily tied to being a parent of the thing it should help control so I'm gonna add another one here which is if our actor too much like this to null okay now what we can do is we can say before we shoot we can actually set our actors or directors rotation to be the angle towards our player so I can do rotation oh geez I can't spell today rotation equals and we can say actor that global position dot direction too and then here we can do player that global position so we're saying get the direction from our actor to our player and then rotate our actor that way oh and I also forgot will need rotation it takes it's a float so we'll need to say that angle here to actually get the angle of that direction okay now if I run this we should see that our enemy turns and follows our player it's pretty quick right now so we definitely like it be nice to add some kind of an interpolation to that but it works it's good enough so now we can move on to actually getting the bullets to spawn and in order to get our bullets to spawn I'm actually gonna make a change to how bullets currently work so what I'm gonna do is create a new directory in our tree and I'm gonna say it Global's and this is usually what I call or where I put global signals or global I knew Mike a lot of autoload scripts and so what I want to do is actually make a global signal for firing a bullet and that way there's just one signal that can be called or emitted from anywhere when a bullet fires that our bullet manager can listen to and that way we don't have to worry about connecting to like a bullet fired signal on our player and on our allies and on our enemies and basically anything that could fire a bullet so normally I try and avoid global signals whenever possible because there's usually a better way to do things but not always and I think that this is a good example of where having a global signal for that is not a bad thing at all so I'm gonna right click on this add a new script I'll just call this global signals so it's clear what we're doing with that and let me open this up global signals and all I'm gonna do here is just have a signal which will be bullets fired and I'm gonna basically take a cue from what we're doing our player so we have the bullet position and direction okay so let's add all those in here and now what I'm gonna do in our main script is instead of connecting to our players bullet fired what I'm gonna say is just global signals dot connect so we have to connect on global signals because that is where it's that's like the namespace that our signal belongs to and I'll say bullet fire it and we'll do the same thing like we're still calling our bullet manager oh whoops and I actually need to add that as an auto load so if I go into project settings auto load what I can do is give paths of Global's this will add basically you know this global object name global signals so I can do that and now if i retype this should see that it picks it up okay so whenever this global signal gets fired our bullet manager will spawn a bullet going in that position or at that position in that direction I'll need to edit our player to do that as well so I can actually get rid of those and where's our shoot here we go and instead I can do global signals that emit signal I say fired the whoops should say bullet fired okay so this should still just work as it does for the player now that we did that so we're just using that global signal and then now for our enemy when we shoot from our AI what we can do is actually call that global signal now and actually if I think about it previously we had our weapon where even that had a weapon fired signal so I think I might actually change this so this just calls that global signal directly yeah let's try that what could go wrong feel like that's famous last words but let's try this so we'll just say bullet fired and then now I can actually just get rid of really like all this in our player so you can get rid of the shoot function in the player and just call weapon - shoot and that's all that you need we don't even need to connect the weapon fired signal on our player anymore so I'm gonna get rid of our players ready function and we should be good to go so now our weapon whenever any weapon in the game fires it is going to emit a signal for the bullet that have fired so now we can I think this should be good to go I think we can try running this looks like I can still fire which is good and if we get close enough let's go look at that so our enemy can fire now and our players getting hit this is awesome and we haven't implemented player death yet but this is great so now we've got something where it's really easy for us to implement anything really that needs to fire we've got a basic AI that all of our enemies can use and going forward there's some changes we want to make like our player or our enemy should stop firing at our player after it's a certain amount away and we can add some basic like patrol behavior to our AI so that when there's no player nearby it'll kind of just pace around but for now this is sweet guys I'm really excited about we have and even just this small thing like now it kind of feels like a game like there's enemy we can kill that fires back at us so I'm really excited to keep going and keep adding more features to us
Info
Channel: jmbiv
Views: 9,486
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: aY-B_5fYUos
Channel Id: undefined
Length: 25min 8sec (1508 seconds)
Published: Wed May 27 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.