Writing a Command Parser (Godot Retro Text Adventure Tutorial #6)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video we're going to start writing our very own command processor that the game will use to process commands the user enters so you'll see if i type go it'll say go where and if i say go east we go east and if i type an invalid command like hi we get an unrecognized command you'll also learn how to add this nice starting message here that will kind of orient to your player and tell them what's available to them and kind of give them a way to play the game a little bit of instructions right when they start so let's get started alright so the first thing that we need to do in order to add a command processor to our game is come into our game script and we're actually going to add a new node that's going to be solely responsible for processing these commands but let's do a little bit of refactoring first so if we look at our game script here we'll see that we're pretty much doing all of our input handling within the on input text entered function which gets called whenever our in or our text gets entered it's a signal that we've connected to our input area so there's a couple things we need to do first i kind of want to make let's let's really lean into the single responsibility principle here and kind of modulate some of the functionality that we're doing in this function out to a different one so for example deleting our history that should really be its own function i think it can still live within this game script because it's kind of handling the game as a whole for now at least but let's move it into its own function so i'm going to create a new function here and i'll say function delete history beyond limit and you can call it whatever you want i'm just trying to give it a name that is clear i mean it clearly indicates it's not just deleting history all history you know but you can clearly see when you look at this function oh it's going to delete the history beyond our limit and so giving your function a name that just kind of makes sense and indicates exactly what it's going to do is a helpful thing to do so let's grab this i'm going to hit command x to cut and come over here and just paste it in and i'll have to shift tab that and we should be good to go okay so now we've moved out our delete history beyond limit function and then i can call it within here so delete history beyond limit so i haven't really changed anything we've just kind of made our function a little bit easier to read all this logic can just live out here and if we ever need to delete history to another part in our code we can just call this function now it's not living within this input text entered function itself so we've got that that's really good now what we need to do is actually start writing our command processor so whenever text is entered by the player this function is going to get called and we're going to get an input response instance and then we're just setting the text to be this random response here so i think at least for now it's totally fine for us to keep this input response creation within our game script but what we really want is to create a dedicated command processor node that's going to be able to do all the things that we need to to process commands internally there and then it can give us a response back to our game based on whatever the current state of the game is etc so in order to do this i'm going to come to our game in the scene tree and i'm going to hit the plus button or command a and i'm just going to add a node not a node 2d not a control a spatial just a regular node because we don't really need this to have any other properties it doesn't need to have any ui properties it's not a container and it doesn't need to have any 2d location it's just a node that's going to exist with a script and so i'm going to change this to be command processor call it that i'm going to attach a script to it and we'll just keep the default name command processor and so here we are going to need to pass in and process all of our commands now if you're familiar with text parsing and old retro text adventures there's a variety of different text parsers that have been used in games throughout the years there's the really famous infocom and some other companies like that that made text adventures that had these really amazing text parsing engines but they're pretty complicated we're not going to get to them at least not yet if ever what we can do is a very simple and it's a classic style just the two word command prompt so every command consists of two things a action word and then the object of that action so if the command is go a second word would be west or north or up or down or if it was take as the command the action would be take apple or take key something like that so it's a really simple time honored system that works and that's what we're going to start off with because it's easy to do and it'll get us pretty much everywhere we need to go in our game so what we're going to do is have a function we'll say function process command and here we're going to have two parameters one is going to be first word and this is going to be a string and this next is going to be you guessed it second word this is also going to be a string so we've got this this is our process command function it's going to take both of these things and then it's going to do something with it which we'll get to in a second first we need to actually wire this up to our game script so what i'm going to do in order to get a reference to our command processor node which i've just dragged to the top of our scene up here it doesn't really matter where it is but i'm just going to say i'll throw it up here on line 10. on ready var and we'll say command processor and this is just going to be dollar sign command processor that easy and so now we've got a reference to our command processor within our game script and we're able to use this somewhere else in our script which we will do by coming back down to our on input text entered function and here we're actually going to get our response to output to the user from our command processor so our game script here is still going to be responsible for putting all the pieces together and handling the instancing and freeing of all of our responses etc but our command processor is going to be where the logic for our game is determined or it's going to call into places that do it at least and the command processor is going to output back to our game what the actual response should be so what i'm going to say is var uh response we can change the name later but we'll say command processor dot process command and here remember this is where our process command function takes in two parameters a first word and a second word so what we could actually do is we can process i mean we've got to get that first and second word somehow so there's two options we can either do that in our on input text entered here we can get the first and second word or we could let our command processor do it itself and if you think about single responsibility we want each function in each script each node to really have one thing it's responsible for we really don't care or want our game to be responsible for parsing our input our command processor should really do that itself it's the one processing the command so we're actually going to change the parameters that we use for a command processor here i just wanted to do this first pass just to kind of show you the thought process that it's good to have as you're making your game we realize that this interface we had here where we're passing in the words would require our game to do the processing of the command or at least to parse out the first and second word in our command which we don't really want to do we'd rather save that responsibility for our command processor so in order to do that we're actually going to change our interface here and we're just going to say input string so we're only going to have one parameter it's going to be the entire input and process command in our command processor is actually where we're going to do the parsing of that and decide if there's multiple words or only one if the input is valid etc we don't want our game to have to handle it because it's just not its responsibility it doesn't make sense our game isn't the one parsing it it's our command processor so in order to do this all we're gonna do is just pass in new text to our process command remember that new text is the text that was entered by the player so we'll send that to our command processor and our command processor will return a response now eventually we'll have to not just output a text response to the player something in our game is actually going to have to change like a door will be opened or the player will go to a different room and we'll handle that in the next few videos when we actually get to implementing it but for now it's going to be enough just to start implementing our command processor so we're going to do that right now okay so one thing we're going to need to do eventually too is actually this response we get back from our command press processor we need to actually output that to the user so what we're going to do is actually replace the this is where response would go default text we had in here with response so whenever the user enters something we're going to show them both what they entered just as a validation or a confirmation so they can scroll back through the history which we're already doing and the new response they've received from our command processor so now we have everything we need to start implementing our command processor and just to make sure this is working and to show that our command processor is actually the one putting out our new input or our output that our user is going to receive we can just do return some output and now when i run our game and i type something we should see some output so i'll type hello some output and whatever i type same thing so this is a good start it shows that whatever we do in our command processor is this is this is where our response is going to come from and we're going to keep all the logic to determine that response nice and separated it's going to be modular and abstracted away from our game so our game doesn't have to know how our responses are generated it just has to know that they're getting generated and it just has to output them so just a nice little quick lesson on single responsibility principle and how to modulate your code or make your code a little bit more modular by breaking it up into blocks or nodes that each do their own function okay so let's actually start writing the code that's gonna do our command processing here i feel like i've said that three times but this time it's happening so the way i envision this at least the first pass we can always make it better later if we need to is that we can just have a match statement which if you're new to gd script is like a switch statement in any other language we can just have a match statement that will kind of match on the first word and then it'll send depending on what that first word is it'll call a specific function and each of those functions will take a set the second word as a parameter there so just as an example that what i'm kind of envisioning is let's just say var first word actually one important function you're going to need to know on a string in gd script is split and that's what we're gonna use here so i'm gonna say var words equals input dot split and there's a couple parameters that go into split the first is the delimiter so this is the character that's gonna be split on now for our game the user is going to be typing a space in between words so like go space west so our delimiter is going to be a space and then allow empty is true we want that to be false because we don't want empty words you know if they have a couple extra spaces we don't want that to be included so if you do go space space space west we just want it to be go west not go empty empty empty west and then max split we um we'll just have a zero here for now we don't need to have a max split so let's say you only wanted it to split a maximum of two times and then even if there were other spaces later on it would just be included in one word we don't really need that so what this is going to return is an array of words so we can say var first word this is going to be words of zero now we need to be careful here because some commands probably won't take a second word like let's say we add a back command later on the user doesn't need to say back west they can just say back to go back to whatever room they were just in so we're not always going to have a second word but this first word is going to be really important so we'll just start with this for now we can have something here where say like if words dot size and we can say less than one or just equal to zero so if when we split our words there's no actual words it's just one thing then we can just return an error for now we will just say return we'll just say like error no words were parse and we can edit that later on if we need but it'll work for now so we're going to get our first word here and now we need to switch on this or match on it so i'm going to say match first word and the syntax for this is pretty simple you just say match and then what you're matching on and then you just have the cases so let's start with go because we know we're going to need a go command right we want to move between rooms in our game we're going to eventually have some rooms that the player can move between sometimes the door will be locked sometimes not um hopefully items eventually may be people to talk to but for sure we're going to need rooms and somewhere to go so if the first word is go then i will just type go here as a string and then you give it a colon here to uh to say hey all the code under this block is what we're gonna do i'm gonna say go and we need to actually create this function so i'll create a function now let's say function go and this is what i meant we're going to make a specific function for every specific word and remember some words are going to require a second word go being one of them you can't just go you have to go west or go east or go north so i'll say second word and this is going to be a string and we'll handle parsing that up above but so now we see that we are um we're matching on our first word and we are going and doing now one other thing we need to be careful of is the casing of what the user types what if they capitalize go it shouldn't really matter so what we're going to do is actually use godot's built-in lower case function just to turn everything in our game lowercase so in godot the way you do that it's a function called two lower on a string so we know that words here is a string and we're gonna do two lower it doesn't take any parameters but that's how you do it and then the return value of this is going to be a lowercase word so now we can just use everything in lowercase here and we don't even have to worry about case so right now we've our parsing and if the word is go then we are going to call the go function the problem is that go is taking a second word here which we don't have and so what we can do is have a function here something that will parse that second word or print out an error if there is no second word let's do that outside of our match statement so that we can have a little bit better error handling so i'm going to say var second word and i'm actually going to set this to be just empty right now and then what i'm going to do because we don't want to say words of one if there is no second element in our words away that would throw an error so i'm going to say if words that size is greater than one so if we have at least two words then i'm going to say second word equals words of one dot two lower so what we're doing here is saying hey second word we're always going to have second word as a variable it's just gonna be empty and what that means is that we can pass in second word wherever we need it even if the user failed to enter a second word and we'll see that play out right here so if i add second word to our go command here what we can do is we can say if second word is empty so we need to say if second word equals empty then we can say we'll be able to return here a response that says hey you need to enter a second word and here it's important to remember that in our game script we said that our process command function is going to return a string so we need to write our process command function to do that and just to make sure that godot is helping us ensure we're doing that we can add a return value here that is just string and so now we'll see we're going to get an error eventually i've got a couple errors going on right now but we'll just say return go here and that means we'll return the value of go which means this go function needs to also return a string and we're going to use this pattern for pretty much every command the name of the command is the function name the second word is a parameter and a return value of a string so in our go command if we have no second word we can say something like go where to kind of indicate to the user hey you need to say you need to include a place to go um now if there is no or if there is a second word we can just return you go to and then we can just do percent s i'll explain this in just second percent second word or we'll just say you go now what i'm doing here this percent s it's it's actually a fragment that goes all the way back to c i believe it might have been the first language to do this but it's a way of printing strings so it's it's you're including a variable here so this s stands for string and percent just means variable so you're saying use a string variable and in order to actually tell godot which variable to use you use this percent sign and then you include the variable there now let's say we wanted to print multiple strings i could do percent s and we've got a problem because we're saying there's two variables here but i'm only supplying one variable here so if i wanted to interpolate or if i wanted to add in multiple string variables to a string i would use an array actually and say second word and say some other string now and you can do this indefinitely so if i had 20 strings i could just have an array of 20 here now we only have one but i just wanted to show that in the future in case you need it and just to explain what we're doing here what's going on so when we use this percent s here we're saying hey replace this percent s with the variable that we are providing after this percent operator here you can also do things besides a string so you can do percent d for example which i believe is a number or percent i i'm not sure what all the different variables are or letters you can use in godot are but there's plenty of them so anyway we've got a go function here and it is returning either go where to prompt the user to try typing the command again with an actual second word or it will say hey just a confirmation you go wherever you typed so things are looking good the only problem is now we're getting an error from godot saying hey you've got this process command function here but you're saying the return type is non-void so because we're saying it returns a string we always have to return a value that's this error here it must return a value in all possible paths well we're returning a value if go is used but if go is not used we're not returning anything and so in godot the way you can defy or provide a default case so for say you something is entered that doesn't match any of the specific or explicitly defined cases here you can have a default case and here we'll say return and we'll just say unrecognized command please try again and again we have plenty of room later on in our game to make this more intuitive but for now this is going to be a great way to start building our command processor here so right now if the user types go and they don't have a second word it'll say go where if they do have a second word it'll say you go to the place you typed and if the user tries to type anything else besides go they're just going to get an error message saying hey i didn't recognize that command try it again and so we're trying to provide something to the user that will prompt them to type in the correct things that our game will recognize and you see we've already got a pretty nice command processor here that's going to be really easy to continue building on and it really didn't require much work at all i mean this is 25 lines of code so really efficient really easy simple way to start doing some command processing okay so let's see it in action if i play our game and i type hello we should say unrecognized command which we do now if i try typing go it should say go where and if i say go east it should say you go east eventually soon we will have rooms that you can actually go into where you'll be able to output or you'll be able to see which directions are valid and so we'll have some better handling there but for now we have a command processor that is able to take user commands and do something about it so this is really cool and to end this video we're just going to do one more command to kind of give the user some options so that they're not just left out on their own trying to figure out how to use or play our game okay so towards that end let's actually add another command where we the player can type it to see what commands are available and we'll just call this help so function help and this command is actually not going to take a second word because when you type help you don't actually need a second word so we'll just have this return to string no parameter here and we'll say return okay and so we're just going to return a simple string here that's going to output to the player what commands they have available to them so we'll say you can use these commands and we'll say go and then we'll say location and we'll kind of use this bracket notation here it said to know a second word is required and then we'll just comma separate our list here so this is a very simple list it's not pulling from like a predefined list in our code so if we add more commands we're going to need to hard code them into our string which you know ideally down the road you won't have to do but we're gonna be adding commands pretty infrequently so i think having a hard-coded list here is totally okay and we can just add to it later on so the user is always able to type help in order to figure out what commands they have available to them now we need to actually add this to our match statement here so i'm going to type help and this is just going to return help no parameter needed okay so we've got the basis here for a really nice command processing system we have an example of how to do a command that takes a second word how to do a command that does not we're handling when the user doesn't um properly use a command or anything like that the one extra thing we can do just to make our game start off a little bit better and give the user some orientation about how to play is give a message to our user when the game starts off that just gives them some information so what we can do now is actually come to our game script here and then on ready we can actually create a new input uh in a new input instance that we can output just like some starting text to the player like you are here here are the commands available to you here's how to play just to give them a little bit of ways to start the game off now toward that end we want the way that our game presents information from the game itself to be consistent it's very important that your user is able to immediately identify what is a command they've previously typed versus what is information the game is giving them just by the font color and size etc so we want that to be consistent so if we go over to our input response scene that we already worked on we see we've got an input history and a response type now what i want to do for our intro text is use this response type without having an input history because the user hasn't typed anything yet it's just the start of the game so what i'm going to do is right click on a response here and i'm going to save branches scene to make this its own scene that we can reuse so i'm just going to call this response and i'm going to do the same for input history even though we're not going to reuse that right now but we might in the future and the nice thing about this is that we can keep building out response like maybe we'll change it to be a rich text label that has a bunch of potential color options or text effects or it does some formatting it's multi-line etc we're giving ourselves the space to change the internals of this a lot while making the interface that other scripts and nodes use to call into it equally the same so we're just getting a nice little bit of reusability here and now from within our game we're going to actually be able to grab our response and then have one of those so i'm going to come into our game script here and do the same thing we had before i'm just going to say cons response this is going to be pretty simple to what we did with our input response i'm going to scroll down find response.t scene and we're just going to use this when our game starts i'm going to instance a response variable and then just add some basic starting text to it and again in order to do this we can kind of refactor a little bit of code we've already written so if i come bound to input text entered we pretty much want to do the same thing we're doing here where we're doing history rows.add child input response and our input response is going to be a little bit different but we want to do the same thing so what i'm actually going to do is pull out pull these out into its own function and say function add response and we'll just say add response to game and we'll just say response and we're not actually going to type this because it could be really anything um actually we might just give it a type of control for now because it can be a input response just a regular response input history we're leaving it open-ended so that anything can be added to our history rows and so what we're going to do i'm just going to hit command x on this row to take it out and then command v to add it back in and now we're saying history rows dot add child response and what we're also going to do is add this delete history beyond limit so every time we add something to our game we're also going to delete history if we're hitting our limit and so now what i can do on our in input text entered function here is just say add oops add response to game and this is just going to be input response so this is doing the exact same thing as it was before but we've just pulled out the functionality about how we're actually adding a response to our game and getting rid of extra history to another function and that means we can reuse this at the beginning of our game and so now up here what i can do remember that we have this response variable here i'm going to say var starting message and this is going to be a response.instance so i'm going to create an instance of our starting message and then i'm going to say add response to game starting message and this is going to add our starting message to the game right off the bat and it's going to handle how like the internals of where we're actually adding it to our scene tree by default because we pulled this out into its own function and now we're getting some really nice reuse out of this function across our game script the only thing we need to do now is actually set the text that's going to appear in our starting message so in order to set this text remember that our response here is just a label and so it's just the text property that we want to access you can see the name right there it's just text so if we come to our game script i can just do startingmessage.txt and here i'm just going to hard code a string eventually you can make this an export variable or you could load it in from a json file there's a ton of options you could do but for now we're just going to hard code it and i'm just gonna write a simple message um the theme for our game is gonna be your kind of typical standard fantasy fare you know castles and dragons and all that so if you wanna make a sci-fi game or other things you're totally fine to change that in your own game as we go but just i'm gonna be doing classic fantasy stuff just so it's easy and recognizable so i'm just gonna type you find yourself in a house with no memory of how you got there you need to find your way out and then i'm also going to add you can type and i'll just say help to see your available commands and so we're going to edit this later on you know we can add more or less but we just want to have a message that appears just to kind of show the player hey this is what's going on right at the start of the game so now that we have this if i run our game we will see right at the start you find yourself in a house with no memory of how you got i don't know because it gets cut off so we got to fix that and make sure that our messages will lap or wrap around to the next line so in order to do that we need to come over to our response scene which i'll just do here and here uh you'll see that if i just start typing a lot it goes off however long but we want to hit the auto wrap button here which is basically going to say whatever width it's given by its parent container it's just going to use that amount of width so now when i start our game now that we have auto wrap enabled and let's actually make sure this is true for our input history too so if the user somehow types a massively long message this will wrap as well even though we probably don't need it so now if i try to try running our game with auto wrap turned on you'll see that we still have a problem because it's rapping but it's only giving one character width and the reason for this again just some of the things you have to learn over time by working with godot's control nodes is that if you look at our container so our background our margin container they're all taking up the full width and height same with our rows our game info panel is taking up the right height but our history rows section where we're actually adding our game info our responses doesn't take up anything by default and that's because the size flags it has for the horizontal and vertical expansion are just set to fill they don't actually expand but we want this to take up the max amount of size that our game can so now if i set this to expand horizontally and vertically it will take up as much width and height as our game is going to give it and you'll see it's now taking up the full width and height of our game and so now if i run it we will see that our starting message is available and totally readable and it's wrapping correctly because it's using the amount of width that our history rows section now has and so if i type a bunch of stuff and just start typing go you'll see that this continues to be the case and it's still scrolling correctly and it'll scroll down if i go to the top and type we're still scrolling etc so everything is still working just as we wanted it to we still have a scrollable area but we now have it wrapping correctly and we've got the start of a command processor so i hope this has been a super helpful video and that you've learned how to write your own command processor that you can expand and do what you want with if it has been a helpful video then leaving it a like and subscribe is helpful to support the channel we'd love to have you in our discord server the link to that is in the description ask any questions about the tutorial there and if you find my work helpful donating a coffee on buy me a coffee helps me continue to make this text adventure tutorial as good as possible thanks so much for watching everyone see you in the next video you
Info
Channel: jmbiv
Views: 2,240
Rating: undefined out of 5
Keywords: godot, godot engine, godot 3.2, godot tutorial, godot 2d, how to make a game in godot, game development, game development tutorial, game development for beginners, godot for beginners, game dev, indie game dev, how to make video games, how to godot, gamedev, godot game engine, godot text adventure, godot text adventure tutorial, godot text, godot input, godot user interface, godot control node, godot control, first godot game, my first godot game, godot beginner tutorial
Id: 2Cuc4i83sqs
Channel Id: undefined
Length: 31min 15sec (1875 seconds)
Published: Mon Mar 15 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.