Keyboard, Mouse and Joystick Input -- Godot 3 Tutorial Series

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everybody my carrot game from scratch welcome to the next chapter on ongoing Godot 3 tutorial series today we were going to be covering desktop input specifically keyboard mouse and joystick handling we're gonna look at how to approach each one of these from a couple different directions where you look at pulled input event-driven input and finally using action maps which is probably the way you will ultimately implement your input handling using the Godot game engine now if you're new to this series is full series built on top of the previous parts and if you're interested I'm also doing an ebook which if it is for sale by now there's a link down below otherwise I'm doing preview chapters as I create them for patron backers alright without further ado let's jump right in oh also there will be a link to a game from scratch comm with all the source code from this particular example we're about to go through so if you just need some source follow that link head on over alright so here I am creating a brand new empty project nothing too special happening here alright here we go we're gonna just go ahead create a root node and then we're gonna create a sprite node underneath it there's not gonna be a lot of flashy graphics in this cuz this is all about input alright so the first guy here is our sprite that we are going to control via a script let's save our scene that's fine we then will attach a new script to this guy so and we'll do our first run ah so we're happy and good nothing magical happening yet but we have the framework for a game to work with so the first thing we're gonna look at is handling via polling now polling is a pretty straightforward process and the choice of what you go with action amount polling or event-driven all comes down to you if you prefer to have your game respond as events come in you'd use event-driven if you want to check for events yourself you would use polling and pulling is a very simple concept basically every pass through the game loop so every time update is called we're just basically going to ask Godot hey was there any input was there any input was there any input and really that's about it so the first thing we're obviously going to need to do is implement our process function this guy right here and let's first check with keyboard handling so if input dot is key pressed really that is it you're gonna see a lot of this going on as we go forward basically input is a singleton provided by the Godot game engine that allows you access to various different input objects one of which obviously is the ability to pull the keyboard as we were seeing here and every time that Godot runs its own game loop it updates it updates the status of all of the input devices that are attached so this is the quickest and easiest way to check them and we are going to check this constant so basically if the key left is pressed this will be true now one thing to notice sometimes I use parentheses sometimes like don't completely optional in if statements in Godot so if you notice I keep switching back and forth between the two it just habit for the most part to be honest and in the event that the left key is pressed we'll move our sprite which I guess I need to define in a second to the left and we can do the similar logic for right now this constant definition is actually not in input it's in the global scope I'll show you exactly what I mean in a second so if you go here to classes search for the at and then global scope and this is basically where all of the Godot global definitions are defined including as you can see constants for mouse buttons constants for joystick buttons a bunch of error conditions etc etc but also all of your keys are currently defined they're all that they're really doing is turning it from a system code into something that makes sense in your own source code so those are all defined in the at global scope so if you want to find the reference that is where it's located all right so we check for the left we check for right and I'll also show you oops I'll show you up and a shift modifier so basically you can have multiple keys pressed at the same time as we will show here so is key pressed key up so if the up key is pressed we also want to check to see if the shift key is pressed and it's it's literally this easy so and in the event that it is will move like 10 otherwise we will just move by one pretty straightforward let me head on back over to boots made an error see there I'm actually mixing and matching myself bad me alright I assume right now I'll go back over to my editor and now we actually need to do something so we can see on screen so we'll do like we always do oops that's not what I wanted to do my note selected let's just use our icon for the texture and then we'll roughly position this guy near the middle of the screen that way our keyboard handling actually has something to handle save that out run our code and now you see I press left we moved to the left I press right we move to the right I press up we move up press shifting up we move up very fast so there is pulling for keyboard input very simple very straightforward to do you know means it zoom that in so it's a little bit easier for you guys to read yeah that's a book yeah I was also handling shift down but really that's just a bunch of copy and paste Co so now let's go ahead and implement two other things when we're just gonna handle a quitting so this is just from well for one it's a way for me to show you how you can actually quit your Godot application and to I'll show you how to handle a specific key in this case we'll just handle the Q key and to quit an application you simply call get Tree quit like so so now we've run our application you hit the Q key and your application will exit now I'm not sure the behavior of that for an each hi of target for all the other targets it should shut down the application now finally let's go ahead and take a look at handling the mouse via polling and it's almost the exact same logic so if input dot is mouse button pressed and again this defiant is in the global scope as we saw earlier on and if the mouse button is pressed we will go ahead and you might also notice that I do self dot position as opposed to just position the self is totally not required but it does give you I don't know that's actually very little reason to do it I just happen to personally prefer that look of code okay so in this case when we hit the mouse button on the left hand side we are going to jump the sprite to whatever the current position of the mouse is now you notice you pulled the mouse's position from the viewport and not from the input itself so basically this will tell you where the current mouse cursor is relative to the viewport and got to remember that the root of every single scene is a special node of type viewport so that's why we're doing getting viewport and get mouse position so if you have multiple viewports obviously you're going to have to update this logic a little bit and let's go ahead and run that so now if I left click boom we teleport to wherever we are currently handling so pretty straightforward pretty cool stuff let's modify our code just a little bit now we're gonna move on to event-driven now this works a little bit different so instead of actually pulling or saying hey what are you guys doing what's happened is anything change does anything change we instead say tell us when something changes and for that there is a special function available it is a callback function so we're just gonna go ahead and get rid of all of this you basically get rid of everything if I'm honest so let's be honest instead you override a function called input an input is passed a value called event now event is of type event why do I always forget this event input event is the base class and there's a number of different input events will actually bring this up show you input event so this is the type of class that has passed into our input function so every time an input event occurs that function will be called it will pass the appropriate event type now this is the base class and you'll see there's various different functions to find but it'll always be of a specific kind and normally ok where did they go and go back input even with modifiers yeah so here you see it could be a key event or a mouse event for example and in the event that it's a key event you'll see there's pressed scan code etc so there are specific values that go along with specific events and that's going to come handy in a second so now in our event code so this is going to call every time that you vent occur so it's going to be event-driven pushed to us as it happens so I'm going to need a variable for a little bit later I will explain why in a second but let's go ahead and create it first so all we're going to do here is handle of the mouse and it will show the simplest one handling a key event first so if the event is input event key so basically what I'm saying here is if the event type that was passed in is in the event key event this means that a keyboard was pressed then we know that it's got specific methods available to us specifically we can now see the scan code of it the scan code is the numeric code that go along that corresponds to every single keyboard now what it often makes sense to do is to convert that back to something that's human readable which is what we were about to see here so straightforward enough all we are going to do here is print out what key was pressed so print a key pressed + and then I wouldn't turn that event code into a character so we can actually see what key it was if it if it was a valid character so that's basically turning a number back into a printable char but the value being passed to us is the scan code like so that will tell us what key was he pressed now there's another key value here pana they called echo and that value will be set to true if the key was already pressed so this is a good way of knowing if a key was held down or not and we'll also check that so event dot Petco and again these values scan code and echo are only attached to this particular type of event that's why we check to see what the type was before working on it [Music] equals true we're gonna go ahead and say print key was held down otherwise we're going to say first press like so now let's go ahead and run this code and now you'll now you'll wait for it to actually run oh I think I didn't error I did an error if event dot echo equals true let's throw our colon in let's try it again and you notice here I am skipping back and forth I apologize for that it's years and years and years of habit one was equivalent to the other I'm just trying to be a little bit more gland I did it with the else too and that is put the colon in as another things Python agador about the only languages that have a good do after a conditional so it's another thing that kind of my brain screws up all the time all right so here we are in our project now and watch down here you'll see as I press keys it'll say either first press or so now I'm gonna hold down the a key and you can see was held down so handling input key event is that simple now let's move on to the mouse the mouse is also quite simple but so what we're gonna do on the mouse is have it as we move our cursor around we're going to update the position of our sprite whereas when we click we're going to what do I do I jump back to the middle of the screen the only catch is we're gonna want to make sure that when we warp the position the start position result without that just worked and then we'll move back there and add it in all right so now we go if event is input event Mouse most you Mouse motion so that the event that is fired when we move the mouse around and we'll check to see if we warp just warped that means we didn't just teleport the frame to last because basically just quickly moving the mouse screws it up and I'll show you why in a second so if not just warped we're going to just go ahead and translate so we're gonna move our stealth relative to and then if we go event dot relative and now event that relative is basically the amount that the mouse moved since the last movie event and that's why we want to do this warp thing because if we all of a sudden jump back it's going to this next relative value is gonna be really screwed up because it doesn't know that you over rolled the position like we're going to in a second so that's why we've got this if just warped St in there so we're gonna translate so this is being passed the position so if we move to the left and to down this will be a vector of negative two and two adds an example so this is just the amount that we've moved since the last movement or the last frame update all right so that is that so that will move us as we move otherwise just worked false all right we'll give that a run are noticing a trend all right let's try it again this is now when we run our code as we move our mouse we're moving relative to it now the downside is since we came in off screen we're not synched up this is part of that relative again so are we started in the central position but we came in from off screen so we're moving relative to where our cursor is on screen but that's probably not exactly what you want to do so what we're going to do is have it so that if we click the left mouse button we move the sprite to the current cursor position or if we right-click we're going to move the sprite to the center line so now we need to handle mouse clicks so if event thought is input event Mouse the masu mouse button like so all right so in the event so here we'll use a switch or a match statement event dot button index so this is the number of the button that was defined these again are defined in the global scope so in the event of button left we are now going to warp the mouse to the position of our cursor no we're gonna warp our cursor to the position of so basically we're gonna move our mouse pointer to our sprite so input dot warp mouse button which is a nice way of saying basically teleport it to and then the position of our sprite like so and we just warped so we let it know yep just warped all right well that's not what I wanted I don't know what just happened all right stop pissing me off Ariel and on the event of button right we instead are going to move our position to the center of the screen we've already seen this logic before self-taught position equals vector to get viewport dot size X div to get you pore size dot y div - alright I think that should be it for our example so go ahead and run this and now you see again we are moving relative to our mouse cursor we hit left and then we've now worked our mouse cursor to match our sprite location and again we can go off screen and then come back on and be all weird again and then left click just to sync back up to our position and then if we're over here and we want to go back to the center boom we're back in the center now do notice though that once again we are relative again so that's why that warp is in there it's because when you jump the cursor around it's not gonna know you know it's a very big movement for that next frame so you want that next relative to not show that we moved by 300 pixels otherwise it's gonna be a really offset as you saw earlier and essentially that is event driven Mouse processing again pretty straightforward stuff we're doing so far well we just handle in the input event as opposed to in the process event but yeah I was kind of about it so now that we've got that working time to delete everything we just did and move on to the gamepad now the first thing we're gonna do with the gamepad it's actually handle the is it connected or not connected one of the things about game pads is batteries die you trip over the cord you know it's quite common for a gamepad to be disconnected so this as well as once where it makes sense to check for so in our ready function oops I put the underscore the wrong spot func ready like so we are going to check to see if the if our connection is there or not we can do this I think we could do this with a node but we will wire this by hand so basically input dot connect like so connect has you can see there's two the script change and joy connection change we're going to check for joint connection change we're gonna wire it to ourself and then when we find it we are going to call the function joy con changed like so so obviously we should probably implement that function joy con changed I don't think that gets done get passed the value it's a bit it gets past a couple values device ID and is connected so the first one basically is a identifier for the device that was connected or disconnected keep in mind you can have multiple joysticks connected at the same time and the second is a boolean / if this was a connection or a disconnection and well we're not going to really do much for this we're basically gonna say if this is this actually connected in the event that it is connected we'll do some Diagnostics here so print joystick space + and it will turn that device ID back into a string space connected so in the event that we plug in a joystick we're gonna print out that it was connected and we'll now identify cos or are so many different peripherals out there we want to know if you don't get to figure out what kind of joystick it was there's pre-built definitions in there for X input devices which is your Xbox 360 controller your Xbox one controller probably the most common controller you'll find in the PC world and also the kind that other controllers try to be compatible with or there's also ps4 controllers and you've also got weird of third-party ones the steam controller or etc so we're gonna do is check to see if don't actually recognized it and this is done using the function is joy Nome and then what we're doing and you'll notice this a lot you pass in an index to all of these functions basically you're saying joystick one do a stick to joystick three joystick for that's again how you can support multiple joysticks now we're gonna we know that there's at least one joystick connected so we can get away with doing this so we'll just go so if it's here it's a record recognized roller so and then we will go print and then input dot get joy name and you can use this basically to figure out what kind of controller you're dealing with it I'll say ps4 or actually input etc as we will see in a second like so now I have a Oh what is your issue here oh look I forgot what got my : ah so in theory this code should work let's go ahead and run that now what I got to do I just reach into my backpack and grab a controller so what I'm doing is that plugging in an Xbox one controller via USB right now you'll probably hear that familiar to do that ports in use I'm not stupid hey what I'm doing is trying to plug one in yeah why aren't you going in okay there we go so controller was just plugged in and boom oh my bad yeah I forgot to put the input signal yeah single tuning here but as you could tell it did eventually fire this code so just remove my controller run our code our code is running watch down here I shall go ahead and plug it in hopefully with more success this time there you go so let it find my controller and then BOOM you can see it was connected its joystick zero it was a recognised controller and it is of type X input game put gamepad device found so that is how you check for rec controller at the same time if I unplug it we would get the same function would have called but instead this value would have been as false and we didn't really actually handle anything on that so we could probably just go ahead and say and that code will run when you unplug the controller and just take my word for that work so we won't bother testing that how so now I'm going to go ahead and actually handle the controller and we'll do this in the process function so we're pulling in this particular example so first things first let us check to see that we have a controller attached we can do that using input dot get connected joypads that will return an array of device IDs I don't really care about that what I instead care is that the size of said array is at least one so if we get to this point in time we know that there is at least one controller connected to our machine so now that we know that what can we do with it well we can see what am I gonna do here and handle the x axis x axis now it can get a little confusing confusing when you're dealing with a gamepad that the button don't always match up perfectly so there's a bunch of aliases created for us here so input dot all right get joy axis and what we do is ask for device zero and the axis where you want is axis is zero this X is zero is the right analog stick on the controller so joy axis zero now you'll notice there was a bunch of accesses five and six are triggers zero is the right analog stick one is the left analog stick and so on so you can get some that have like flight simulators where it twists or you gonna have a throttle etc so you can have a bunch of different axes to find so we're going to check the axis on one now one of the six you're gonna find with a controller is that you often want it it will register small movements left right up and down even if you're not actually pushing it so there's something called a dead zone that you normally work in and it's the first little bit of a controller where you say alright just ignore input within this wrench we're gonna make it that go with 20% so far dead zone equals 0.2 so what I'm saying is unless your value is higher than point two in any particular direction ignore it so back to our logic down here that is where we were going to use our dead zone so if abs basically just turned into an absolute so it strips the sign off x-axis is greater than dead zone so this will be if you've got to push it at at least 20% of the way accesses go from 0 to 1 or 0 to minus 1 depending on which direction they're going all right so if we are pushing it at least that much we now want to go ahead and if x-axis is less than 0 self dot all right this is very annoying scrolling spot let me just boom you up a bit self dot position dot X minus equal all right and I'm about to throw a bunch at you here 100 times Delta so we're gonna move it by a base amount of 100 pixels per second we use this logic already and it's already been explained times oh yeah I'm gonna throw a variable at you this is also our own variable here bar speed multiplier equals 3 this is where your sensitivity setting would ultimately come in you can basically create it organically to something that feels good but this is the amount that you're gonna multiply the input by so this is the amount of response you're gonna end up getting you know you don't need to have this at all but you'll find your logic your movement I'll be probably kind of sluggish if the speed modifier isn't set and a lot of users will like to have a sensitivity setting of some form so in my case I'm implementing it as a speed multiplier times and then once again the absolute value of x axis so keep in mind x-axis again is a value between 0 and 1 or 0 to negative 1 if you're pushing to the left so what we've got here is for example this could be halfway pushed to the right would be 0.5 times 3 is one point five and then say we're moving it 60 frames per second so this would be around 13 so that'll move you relative to that total amount so that is what the logic here is calculating I'm probably missing a parenthesis here I am all right now through the power of cut and paste coding if the value B is greater we instead want exact same logic we just want to use a plus so let's go ahead and run that code out and you'll now see oops so my bad actually zero is the left stick not the right stick so there you see the left stick right stick and if I do it just a little bit we go slow full-on we go fast and that is why I am multiplying the axis by the translation amount so if you just want to use this like full on or full off you just get rid of this logic part right here so everything to the right of the Astra's including the asterisk just get rid of that and you'll turn this basically back into a digital stick but that's the thing with the axises they are full analog and they've got degrees from zero to one so you can have you know amount of intensity behind your push whereas in the d-pad it is literally just on or up speaking of d-pad the d-pad is literally just a button so let's look at that now handling the d-pad is ultra simple so back here so we are in our if joystick is connected now we're gonna go ahead and check for d-pad movement I'm only gonna do one axis so if input dot is joy button pressed and they literally handle just like buttons the first thing you do again is passing the controller you want and second you pass in the button you want to test now you will notice in these definitions there are a number of predefined four different controllers such as the Xbox and the Sony controllers or you can use the generic buttons like button 0 button 1 etc and generally they will match up but this is one of those areas where device the device it can get a little freaky but what we are going to do instead once and button D look joy yeah Oh deep pad but again it's just a button and in the event of a self thought position dot why and I'm gonna be really boring about this - equals what go ahead and run our code so now if I hit the d-pad we move up by one each time like so so again buttons are literally face path D pads and buttons are all handled in exact same way using pretty much the exact same logic and there are up to 16 buttons attached to let's just show that quickly and then we will move on so you can also go through all of your buttons that are pressed so let's show so for I in range say there's I think it's commonly up to 16 buttons on an Xbox device for example so and if input is joy button press 0 comma I so if we're currently pressing a button we're just going to echo out what it is so it can be any of the 16 possible button combinations go but plus stir digital rest should be and we can turn we've got for each possible button by index we can actually turn it back into a string using input dot get joy button string and pass in the index of the button and and my print statement yes but I think I screwed something up oh that's why I screwed up all right what am I doing wrong 34 nope didn't have a close all right so now let's go ahead and run I screwed something up is joy button press press where did I do that what line joy button press yeah that was dumb alright start this all over again there we go so now you see again left stick we could just as easily convert that to a different axis if you wished or the triggers however we wished by switching the exes out d-pad up and as you can see I'm gonna start doing buttons so here is the a button look down here for the result and I will just go a and on so a button this is button 0 B button button 1x button but to Y button button 3 d-pad 12 so up is 12 right 15 down is 13 left is 14 a trigger will actually fire as a button but also as an axis so you can treat them either way left bumper is button for right bumper is button 5 button 9 is the right stick down button 8 is left stick down the home button I think it's called the menu button and that was every button I think yeah so that is all of the various different buttons and all of their defines now one of the cool things once again if we go back here to the global scope you will actually see behind each button theoretically which gamepad button is so I think if you see like the X button should be 2 3 etc I'm not really sure why you would code directly to the Xbox set or the Playstation 4 set unless for some reason you were working with those very specifically but these are basically the same definitions as the generic button versions finally we're gonna take a look at action maps now action maps or input maps or way of just tying it all together basically with an input map you create a mapping between the various kinds of inputs such as your mouse your joystick your keyboard etc and you've given an alias or an action that occurs when that happens this way you can have multiple forms of input all handled with one set of code now this is a two-step process first off we need to handle it by creating the input map to do so go to your project settings right here and switch over to the input map tag over here you'll notice there's a bunch already defined for UI management but we're gonna do instead is create our own and we're just gonna do a simple one for handling left so on left type in the name of the action and then click Add now you scroll to the bottom you'll see that left was now added as an additional option here click the plus sign to the side of it to define a new action to map to that label and we're gonna do go ahead and add a keyboard action for it it's gonna prompt us to press a key to add so go ahead and press the left key now if you want to implement Waze to key WASD key input you could also do that as well so we'll just do a plus another one add another key and in this case we'll do a so now left will fire if you press the left or the a and on top of that let's go ahead and define one more and we'll do joystick access device 0 analog axis left ok it's a lot of work it will add that in so there you can see you can define multiple different input events all mapped back to the same single individual action so now that our map is defined it's just a matter of using it in code and it's very very straightforward just head on over here to our code once again we can purge everything and in our process or input actually we'll handle this one an input so we can check if and then event dot you'll notice here why are you not firing right these action left like so and then in that case we just go self dot positioned X minus equals one so you see how all those different actions have now been just defined as a single type of action that we can test for and we can go ahead and run that at now if we press any of those input devices left we move so there's the left arrow key there is the a key and there is the left thumbstick being pressed so that is how you can go ahead and create an input matt which kind of just brings it all together chances are you're probably going to use input maps for your controls because it allows you to make your controls a bit more generic there's also programmatic ways of adding to or creating the option map so you can actually add to it using code so you can give the user the ability to confine can to your own custom actions etc and define the map that way but then your end input handling code all stays exactly the same we're not gonna actually specifically cover that today I don't want to get this video being too too long in fact actually I'm gonna wind the video down now hopefully we covered everything you're interested in seeing for input don't worry if you're into mobile devices you want to get details there I will cover that in a different chapter when we cover things like touch gestures motion sensors etc that will be in a different chapter so hopefully this is proving to be a useful series for you again there is a book based version covering the exact same content if you're interested in checking that out do check either the purpose link down below if it exists yet or the patreon link and really helps the channel out and hopefully you get a book that's useful to you that is it from now I believe I'm going to be moving on to I was planning to do sound effects next but I think I actually might do widgets and/or GUI programming and then sound effects because the ones sort of depended on the other and the way I implement in my example so do stay tuned there is more Godot coverage coming and hopefully this was useful to you if you do have any comments or suggestions do let me know in the comments down below alright that's it for now I will see you all later good bye
Info
Channel: Gamefromscratch
Views: 45,827
Rating: undefined out of 5
Keywords: Godot, GameDev, Game Development, Tutorial, Input, Keyboard, Mouse, Joystick, Gamepad, GDScript, Game Programming, Programming
Id: 7r7ZPmspDew
Channel Id: undefined
Length: 38min 35sec (2315 seconds)
Published: Tue Apr 24 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.