Full Featured Controller / Mouse / Keyboard Input in GLFW | Cross Platform Game Engine Development

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's up guys welcome back to cross platform game engine development in this video what we're going to be doing is implementing input and specifically we're going to implement a system similar to what uh unreal engine godot and unity have where you're able to define actions such as strafe move forward ui accept that kind of stuff and map those actions to specific input keys which would you would then listen to those action events to process your input instead of listening to keys directly and what this allows for you to do is to provide dynamic mapping if you want to do that programmatically in code and also we're going to be able to eventually provide a mapping file that the android would load and then populate all your action mappings for you so as you can see uh below in the time bar this is a fairly long video part of the reason for that is that i'm working on a new format that i'm going to shorten next time hopefully what i'm going to do is we're first going to jump over to my whiteboard behind me and i'm going to explain the feature how input management tends to work inside these engines how i want it to work in our engine and then we go and we define a a kind of pseudo interface that we're going to aim for that will inform us as we implement the system itself so if you're interested in that you appreciate all the effort that goes into these really long lessons and the editing that's going to be happening shortly uh hit the like button down below and let me know how you feel about these if is it too long is it too short want me to break them up i put up a poll a couple days ago yesterday i think who knows uh on whether or not you prefer one long video with chapters or multiple short videos it was almost 50 50 with a 60 40 split i'll leave uh maybe i might be able to put polls up in in the i card um if i can it'll be up there if not i'll be in the description but go vote there let me know what you think what you prefer and i'll try to use that to inform my decisions in the future future so with that said let's just jump into this nonsense so to start off with our engine right now um so let's just say we'll draw a box like here and we'll call this our engine box right our engine in general we interact with our engine by um let's not put a line here we interact with our engine through our service locator right so we're look we'll um oh we'll put a box let's put it here-ish i know i ran out of room yesterday while while uh doing this we put our box here and this is our service locator right so our engine encapsulates the service locator obviously so we can bring this out like this and the service locator handles um all interactions with the engine right now so um well most interactions but with it from our game class so we have a game class that kind of jumps out we'll make that in red the game class is kind of like this and this is where our entry point is and where our application code is so this we can just say application code right and this is pretty much the only piece that kind of leaves the scope of the engine itself when you're when you're building your application layer so within our engine we have our two we have two systems we have our renderer which we'll put here renderer sorry for my handwriting and we also have our window windowing system and those are our two main systems that we currently have inside our game right now and now the colors are kind of but whatever doesn't matter now today what we're going to be adding is an input manager here now the input manager is going to be structured slightly differently than the other two because um it's not tied to any platforms you can kind of abstract it if you wanted to but in our case a lot of it's going to be tied to our windows system but it that's not necessarily the case we could detect if it's on windows and just go straight directly direct input and stuff like that but we won't work we'll stick to this this time off we'll remove this so how are we going to make this work so it's important to know that there are two types of inputs there's interrupt inputs so the interrupt inputs which is the keyboard and mouse and then there's like polling inputs which is stuff like gamepads now when something changes on an interrupt device you click a button or you press button on your mouse the mouse the device will send an interrupt signal to i believe it goes straight to your cpu which will then be caught straight by your windowing system usually and then you would forward that to your window manager or to your input manager now if you for the polling system your input manager would generally go and ask for the state of the gamepad uh getstate and then it would process any of the inputs now this is this is how we're what we need to deal with we need to provide a way to get our interrupts and polling inputs and pass those along to our application layer so we need to gather the gather the states figure out the state changes whether it was pressed released that kind of stuff and send that over to our application layer right so um i'm going to erase this and let's go into how we're going to architect this a little bit into the different pieces okay so we'll start at the application there so let's put application layer in red and we'll put it over here application just too small now you can read that that's fine application layer now the application layer um this is going to be simulating what you would normally find in like a godot or unity or unreal engine where you would register uh you would create a mapping so let's say you could say uh button a b and enter or whatever is the confirm action right so actions actions so this is what we're going to simulate here so on the application layer we need to be able to tell the input manager or tell the input system uh we need to define actions so one goal is we need to define actions and the second goal is once those actions are defined we need to be able to catch events that happen for those actions right so we do two and then uh register action callbacks so this is pretty much everything that the actual system needs to do in um on the application layer we looked into doing this a little bit inside my c plus series there's going to be probably a card showing up right about there um inside my c plus series when we did the input manager thing that's kind of what we did here is we registered the action callbacks but we're going to expand on that today and make it a lot more complicated let's look at the let's look at the input manager layer um we'll use we'll use purple all right so and this is now we're going to look at the input manager and this is where most of the most of the magic is going to happen right so what do we need for the input manager layer so um we know that we are going to need a list of actions right so we'll just say members will say we need a list of actions and that list of actions need to have associated uh inputs um associated inputs yeah you can probably read my writing it's fine so we have the list of actions and associated inputs what else does it need it needs to have callbacks so we need callbacks associated to those action actions so we say list of callbacks associated actions so these two here will handle um the application layer side of things and then we need um we need a function to process uh action events uh function to process action that's events events i love my handwriting so that will handle the application layer now now we need to look at the input layer so how do we want the input manager to work what i my goal in the way i'm designing this is to from the input manager perspective i want to um homogenize the way it gets state of devices so i don't want hardware interrupts to go directly into the input manager because that's kind of not what the input manager is for the input manager is for application layer inputs and not really hard low level inputs so we have the input manager here and what it's going to do it's going to uh and we have our game let's say we have our update loop of update loop right at the top of the update loop we want to process inputs so first thing we want to do at the top of the update loop is process inputs now what process inputs will do is it will go it will call it on the input manager the input manager is going to query its registered devices for their state so process inputs will query register devices for the states so inside here query registered devices after it gets the states of all the devices that are registered to the input manager it's going to compare compare new states to old states new states two old states now we don't know exactly how we're going to do this yet but this is just kind of how i'm thinking about it right and then once it comparison new states to old states it's going to generate action that's going to cue actions action events i should say and then it's going to fire off all those action events fire action events now you you could do them in one in in one set these last two but i'm just separating them for clarity so the reason we want to do it this way and not let interrupts go directly into our input manager is because we want to be able to control when our application is going to be handling all of the inputs right we don't want to be in the middle of a render loop and have it interrupt part of the application change the state and then cause some sort of problem right we want to at the top of the update loop do everything to do with inputs and then move on right that's what we're going to do [Music] so to enable this we need a process inputs function uh let's go back to purple so function to process inputs then as well as our so we processed and so we need to query registered devices so we need to register the devices now a device in the input manager what it's going to be a registered device let's go here so our registered devices what they're going to be is they're going to have a type which is going to be like mouse keyboard gamepad etc they're going to have a index so this is mostly for controllers so you want to be able to query the proper controller right then they're also we're also going to need a callback function or a state callback function now what the state callback function is going to do is when when your various pieces of your application of your engine are going to register the devices when they're found they're going to register a state callback function which will which will return the appropriate state to the input manager so the input manager doesn't need to keep track of how it's getting the state it just needs to know that know where to get the state from right so that's what this is for and this this will just be a list or a map probably a map but we'll see here so we'll figure out everything as we go along so we need a re registered device registered device list device list right so we got our list of registered devices that's good we need to compare old states and new states so our registered device list can also have our state or yes we'll just call this state here it's also going to hold our state so i'll cover this off so we can when we get the state we can compare it to the old state and do that so we can mark that off we can cue action events by comparing old states and new states so what we need to do and this there's going to be supporting data types like action event is probably going to be its own data type input like input type input like all that's going to have its own probably have a their own um types but we'll again we'll get there when we get there so um we need to queue action event and fire action events now act an action event is going to have a view things so an action event what is it it's going to fire an action um [Music] there's going to be it's going to have action name so uh action let's action event all right so action event so it's going to have action name it's also going to have a value so this is going to be the value of the action that has happened and it's going to say event type right and i think that might be everything we need so this can be like pressed press released um uh you know what we might not need the event type but we'll keep that there in case just as an idea [Music] and then we are going to fire action events to function the process action events yeah i think that's pretty much everything we need and that would handle these things there [Music] so i think that's pretty much everything we need for our input manager i had i know it took a while to explain all this stuff but if i just go into the code and i just dump all this code on you as i write it you might not know why we're doing everything so again the idea is for the input manager to go out to its registered devices get the state compare the state and then cue action events and fire them off within the inputs now there might be some optimizations we can do along the way but this is probably a good way to start and this is all probably already a large chunk of code [Music] alright so i know that was probably quite a bit of [Music] planning but it's important to plan these things out especially when you've got a larger feature like this and i'm hoping that this gives you a lot more context into why we do stuff instead of just dumping a bunch of code at you and having you just try to listen to me without you know something that to focus your mind on right so if again if you liked um this explanation that i did uh please leave a comment down below and let me know you did so with all that out of the way let's just jump right into the code and start implementing all of this nonsense all right so we what we have here is what we had last time um we've got our game now i've made a few changes um to the repository uh they're not huge let's look at some of them now it's mostly i just made a few tweaks to make sure it all worked within um linux right so there were a few minor fixes like i had to add a c math there and i think that's all i needed to do with add c math fix i fixed my cmake list there and i added a readme for all of the different dependencies that are needed for um x11 but other than that it's pretty much everything that needed to be done uh on this project to work in linux so let's just refresh ourselves on what we had before so we hit play now we should see our triangle from last time uh that is just has a flashing window in the background that's all we have now visually we're not going to have very much different we're going to be doing most of what we're working with inside the console itself but we know that um we should always have a triangle so let's go back and look at what we need the first thing uh we're going to need is obviously the most important thing is the input manager right so let's let's start adding our input manager so what we're going to do is we're going to add a new oops not python package new directory and we'll call this input and we will create a new c plus class called input manager now i don't want it like that i want it to be in snake case we'll do that and we'll create our input manager there now we will add the files to get now one thing that clion does that i don't like because that i don't like because um i use the glob here is that any new file i make it's being added explicitly on this line here which i can just delete and reload now we're going to move our input manager implementation into our directory down here input and we will move our cpp file down all right and with that we will name space oz here and space oz name space and there we go better so we have our input manager now we're going to have a public section a private section [Music] and uh we're gonna have another private section for functions and variables functions and members so what do we need in our input manager so the first thing we needed was a list of actions and associated inputs what we're going to be interested in doing we need to think of how we're going to access it as well is we're going to want to access the actions associated with the inputs based on the input itself so this is gonna be uh we're gonna store it in a map we're gonna do unordered map that's how we're gonna start and we're gonna make our private um declan area so we'll do here unordered map and it's going to be our the way we want to query is by input key [Music] and then the action itself is just going to be a string and we'll call this the action mapping uh we'll call it input mapping to be clear because we're mapping the inputs to actions so maybe input action mapping and we're going to include string right so now that we have that uh we need to define input key and now let's let's reload our see let's build this it's probably going to fail uh it didn't fail i don't have input key which this file does not belong to any target because this is not [Music] whatever we'll we'll we'll deal with this later we don't have input key i so it's not including input manager.h anywhere so it's not being used um let's just put it inside here just to include um no we'll put it inside our service locator because that's going to be uh where it's going to be used most likely include uh youtube engine input slash input manager we'll do that now when we build that that should throw an error now if the input key is not defined that's better so yeah so now this turns red perfect so we need to define input key now input key is going to be its own thing and we're going to create it it's in its own header file up here and what this is going to be for the most part it's just going to be enum class input key and it's going to just be like a b c d e [Music] right um why now you're going to want to eventually put all of you all of the support options in here um but for now this is all we have here now the reason we define our own is to make it more generic so that we're not relying on any platform specific uh declarations okay so now we have input key this will let me import it from here there we go now we have input key that's our input action mapping so what else do we need we need a list of callbacks in their associated actions so we want to query by action here so by action name so an order map is going to be by action name and then we are going to get the action we're going to have a vector of action callbacks and this will be action callbacks and we'll import vector here even though it looks like it was already imported from somewhere but we'll do it like that so the reason we are creating a vector of action callbacks is because we want to be able to we want to be able to map to the same action from multiple places within our application so you can imagine that the ui layer might want to register the confirm action while the another layer might want to use that same action as well and that's what we're going to do here so to support this we will need this action callback type which we will actually make inside here so this is just going to be a simple struct called action callback and what this is going to have is it's going to have a callback wrap as a string now these should probably be like hash strings something faster and easier to um to manage but we're just gonna do it like this just um because it's probably gonna be a very minor performance hit we have our callback reference so that's how we will reference this specific registering of callback and then we need our callback a callback function so action callback func and we'll [Music] say and we'll actually just call this ref an action callback function itself will be using action callback function equal std function and it's going to be similar to our uh input manager lesson and sql in our the c plus it'll be similar to how we define it in our c plus plus input manager lesson but it's going to be full it's going to return a boolean and this here is going to take a few things so let's just close this and include functional include functional all right so what do we want to do here so it returns a boolean and it's going to have a few um things it's going to have it's going to have an input source it's going to have an input index which is going to be an integer and it's going to have a value that's what we're going to do for now that's going to be our callback now input source um is not defined so here we'll go back into our input keys and we'll define another enum class input source and this will be keyboard mouse gamepad and that's all we're going to do for that [Music] all right so that is how we are going to um we're going to move this down this public section down because these are more declarations and these are going to be functions at the top put our public tag here so that's how we're going to store all of our callbacks and how we're going to um how we're going to send information back to the front end right so the next step is going to be we're going to define our functions that are going to actually um that is going to be called from our application um to register these callbacks right so we will have a void register action [Music] callback and we will give it a action name and it will take an action [Music] callback right and that's not defined yet so annoying okay well i guess this is going to go into the top anyway then we will need to be able to remove that action callback remove the action callback i know this is where that callback ref comes into play here we will just take a string with a callback rep on it and this is just so that we can we are able to search this vector for this callback reference and if it's found we can remove it from the callbacks we will need to also be able to create our input action mapping so we'll say void map input to action i'll say input key key string action void unmapped key from action on map input input action and we'll just say key key all right so this handles all of our application layer now um yeah let's let's implement these functions for now we'll then work on the other end of things where we start pulling in inputs from our window so let's generate this um let's first let's add this into here and start doing this now a couple things we want to fix before we move on uh this here um we'll default initialize it to empty just because we should it should always be default initialized and let's go inside here so register action callback this one should be fairly simple um we will just do uh let's open this on the right so we can keep an eye on what we're doing so this is action callback this is a map by action and action callbacks all right so the one thing you should know about maps is that if you as soon as you you query by something it also default initializes it if it doesn't exist so you using that knowledge what we can do is we can say um action callbacks action name so that if it doesn't exist it will default initialize that vector right so then at that point we can in place back because we will always want to put the callbacks at the back because we're making a stack um and we are going to add the callback and remove action callback is going to be uh somewhat similar but we're going to say erase if and okay let's just say this is not we don't need to wake into action callbacks dot action name [Music] oops apparently i forgot to put a action name here we'll go back and we'll add this as well to [Music] here just to be clear action.name.erase remove action callback which is going to be uh and this is actually going to be dot erase if and it's going to re uh organize that for me and then we create a function here so we'll save this action callback callback right you don't need this uh return callback dot all backrest dot breath equal uh call background and we need to pass that to our captures all right so what we did here is we we create use the erase if that will go through and it will erase any instance of this callback reference inside this vector callbacks now here we are using everything as constant as well so so we will clean that up as well and this will be a const reference as well and there we go so now so my ide c lion makes all of these recommendations to to automatically make these constant references if you're not using c lion you should remember to do this usually i do i just you know i'm doing things a little bit quickly today anyways let's move on so this will register and remove action callbacks uh mapping inputs to actions is a little bit uh simpler um we don't need any fancy erase if statements here we when we want to map it we will say input action mapping key and then here we will just say equal action and [Music] again different keys might want to map to different actions right so this um here uh it wasn't just a string what we did here is let's go back to our header file and we'll change we'll change this a little bit so here what we're going to do when we move that semicolon we're actually going to also include we're going to include an unordered set and here it's going to be on an unordered set or a set of strings right so this what this means is that an input could be mapped to multiple actions so instead of doing here what we're going to do is we're going to say uh dot in place and again this is going to complain about a const reference probably and there we go and we'll that switch it to a const reference as well now um unmapping input from action is fairly simple as well we do input action mapping [Music] and we will say key and dot erase action because it's a set um it should only be present once and again we want this to be a cost reference there and here again cost reference anything else and here also costs reference we want to make cost reference yeah because this will create a copy and place back should be we should create a copy i think so that's everything we need to do from the perspective of our game um we won't we won't go and change our main.cvp yet but there's a few things that we want to do now that we've handled um registering the callbacks and uh mapping our inputs to the actions what we're going to want to do is we're going to want to add our input manager to our service locator right so what we're going to do then is we are going to add it here so we're going to add a new static inline stood unique pointer input manager get input manager now i'm not going to fix it now let me finish this line of code um and let's add here okay so so we're we're not going to i'm not going to address it yet but i have since um grown a little bit and generally you don't want to be passing references to unique pointers around generally you you're going to want to use the unique pointer to maintain ownership and you'll actually just if you need a reference to something i'll actually i'll actually do this it would be like input manager star get input manager like that and here we would say dot get and actually we'll we'll we'll change all of this out actually well i will change it render star now what this does is in general when you're working with when you're working with smart pointers whenever you see a raw pointer you have to assume that you don't have ownership because you're never you you should never call new or delete in newer applications right in general unless you're doing something specific but here we are returning a thing and i think that will build fine i don't think that's going to affect any outside code other than let's see this pointer where this argument to member function update has typed const window oh um so we don't we want to remove the const as well because they're not const references so let's build that again and hopefully that'll work right so generally you don't you just want to assume that if you see a raw pointer that you don't have ownership you should never call delete and that way these unique pointers you can't call clear or whatever on on these unique pointers so this um is something i've been meaning to address for a little bit and i just did it now all right so now we we need to be able to provide an input manager i think inline void provide input manager now this again is probably not the greatest way of doing this either um [Music] that's how we're we're receiving a pointer and making a unique pointer from it it's okay in this scenario but it does leave room for mistakes and generally what we want to do is like um to be able to like provide a renderer class or something and then uh use use the make unique function within here to make sure that that we don't have any leaks at all um but you know we're doing it ourselves and this is what i'm gonna do uh not equal null pointer return and then we're going to say input manager equals the new pointer input manager manager and we don't have an init function so uh yeah we don't have an init function so we'll do that [Music] and then here we will shut down [Music] shutdown input manager and we will make that function static shutdown it manager if not input manager return [Music] then we will just say input manager dot reset [Music] and i believe that'll make it no point right so with this info manager.reset we are able to do the constructors and destructors so we'll put here uh got public functions here so i'll just do it here so we've got input manager and input now we are actually going to make these private constructors because we do not want to be able to initialize the input manager from anywhere in our code right so these are going to be private let's generate these definitions before i do anything else all right i'm just moving these to the top just because i like my constructors and these structures at the top even if they're private okay so we've got our input manager constructor and destructor here um what we're going to do is we're going to create a boolean here and we're just going to call it active and it will start off as false and um inside our constructor we're just going to say active equal true and in our destructor all right so now let's go in and actually provide this input manager from our game right i think it's in our game that we want to do it from so you'll notice here we have initialized services and we call our provide here we're doing this so provide input manager now what we want to do is provide our input managers we're going to service locator provide new input manager and that's going to give an error because that constructor is private right now what we want to do and this is fairly simple is we're going to make the game class a friend of our input manager so that our game class is the only one that's able to initialize the initialize the game and initialize the cert input manager so if we go back to game now and we go down to initialize services we now have an input manager provided to us right and just to make sure everything's working we'll put a stood count here input manager initialized [Music] now i don't know why utility was brought in but we'll remove that good and now we'll build that it should work and cannot access declared all right so um so inside here we couldn't get the constructor to work inside here we're not able to get the destructor to work um from i believe are yeah our unique pointer so this that might not be how we want to do it so we can try one thing which i don't think is gonna work but we can try it um here we can add another friend class service locator and we can build that and that's still not going to work so we will um change the way we're going to structure this we're going to create our input manager there we'll skip it for now um doing these friend class stuff and if if we need to we'll we'll get there later so we'll hit play and we should see successfully loaded input manager initialized so we have our input manager the next step of the process is going to be to store our input states right so the issue that i'm going to run into now as i'm looking at the code here um is that we're currently keeping all of our input keys in the same enum class which is good for simplicity of making our action mapping but is not so much for keeping state for individual devices because we don't know how we don't keep a specific list of each one so what i'm thinking is that the input state itself will just be a unordered map i guess will it be a map yeah it'll be a map of keys to state that way you we won't have to keep anything that any state that the the input provider our glfw in this case uh doesn't want to that isn't going to provide us anyway so we can only we can implement only what we're looking for right now and then we can expand as we need so i think that's what we're going to do so let's go back let's go back to the code and uh hopefully this will help and this will be good so so back to the code so we've got our input key this is all we're going to deal with for now um i guess uh mouse move uh mouse move x right all right that should give us everything we need so now we need to um keep our input state so we'll create a new header file and we'll say input devices dot h so here we're going to bring it into our input manager devices and inside here we are going to have a enum class input device type this will be keyboard mouse and gamepad and we are going to have a struct called uh input device state it's going to have a value right we're also gonna have a struct called input device and this one here is going to have an input device type type input and then it's going to have going to have an in index so we're going to int uh index and these are structs and public so i'm going to make capital and we are going to have a state callback function uh input divided state callback func using and now we'll define what this is here using equal stood function um now this is going to take a map stood unordered map of input keys input device state i believe let's do that all right so um we'll just move this truck up here um i think that's probably good we're going to include functional it's not saying i need to but i'm going to do it anyway um let's see so this will take an input device type it's going to take an index so what this is going to do is the glfw will register an input device which will have a type and index and it will provide a function that can be called to get the state of that device by index and type do i need the type because the type is in here and when i register it's going to have the function now i think i can just do this by index the type is for other purposes right i think that's good so we've got our input device stuff here let's go back to our input manager.h let's close this and look here so what we're going to want to do is we're going to want to keep a list of these devices right now we want to be able to query the devices by type yeah no no so we're just going to create a stud vector input devices devices all right and then what we're going to do here is we're going to well we're going to have a process input function here let's just scaffold this out now and then we'll we'll implement stuff later generate here so our process input function um let's put the comment up here process input well uh and inside here we're also going to need to keep our input device date current state okay so let's go back to our input manager here it's going to proc well we'll um get get new device state and compare with old state then generate action events okay so what will this do so the first thing you need to do it needs to get new device date so what it's going to do um actually it should probably do it all all in one all in one go right so process input will process all the inputs for all the current devices so auto device in devices right so we're going to say auto new state equal device dot state function [Music] uh device dot index and so actually inside our input devices we should are have the same thing here all right current state is actually going to be this unordered map here then in our input manager so we got a new state um so get used to get newspaper device compare [Music] [Music] state if device dots current state keystay first not equal key state the second okay do this okay probably implementing uh the operator overloading for um the state but we we can deal with that later so if that's not equal then we will generate device action so what we're going to do here is actually event now we're going to just um we'll just put it to do there because that will be one of our our final steps so it'll go through each device device check each of the inputs gotten from the state and then after it generates the device action it's going to overwrite that value [Music] all right um and here the generate device event action will actually be a function so we're going to uh just block it out for now we're going to say uh this will be a private because it doesn't need to be accessible anywhere else yeah might as well do it up here with the other deckler declaration so this is going to be a struck action event and it's just going to be empty for now um well you know inside and then we are going to have a uh actually here it's a private function so it should be small p and avoid generating action event event and it would be uh float old dial good all cut up good all right so we've got this and here we are going to say vector accent event events and we are going to say events dot in place back uh generate action event old owl is that enough to generate an action event um no we will also need the input key input key key first first nice and we will go here okay so um it will generate the action event for this key new val old val or this key input key that should be fine now to generate the action event itself um we will deal with this later this will be this one with the to do here and then after we do all of these we will propagate action events so our action event callback if we remember right action callback function it's got an input source input source index and float so if we go here we're not we have the key but we don't have the source we can derive the source from here we need the index which do we have the index here yes [Music] devise dot index maybe move to this [Music] all right i think that's enough uh for what we need to do in here we can revisit it again later but i think that's all we need for that area so um what we're gonna do now is we are going to we need to propagate the action events so now from here uh we would take all the action events which we're going to say for references event and events propagate action event event and we can make that here oh boy propagate action event uh action [Music] okay so here our action event itself we have determined it needs an input source so it's an action event input source source uh and source index and float value we'll we'll put value for now but we'll probably need to be something else and probably be you know the value of value sign so we'll just for now we're just going to send changes down as action events yeah that should be fine and then we can get state of the device if we need to so to propagate our action events we are going to uh we need to [Music] so i have the action name so i just need to look up the action name and run the callbacks right so uh we will just do four uh auto actions [Music] for each action in action callbacks event dot action action name we will call we will call the reference function we'll say the action not function with and here is where we will put the event dot source event dot source index event dot value and again i need i want to go here and rename this right so i'm just going to do this manually because apparently it thinks that x event dot value is used everywhere so i'm really only used there now the reason this returned to boolean is so that i can if this returns true we are going to break out of the action loop so what this is going to do is um is basically once it gets an action event it's going to propagate that event to through all of it until it reaches a blocking layer so if a ui layer is at the top of the stack um no i'm doing it uh backwards here so what i actually want to do is we're going to change this a little bit we're going to say 4 size t say int size t equal index equal action callbacks dot action name dot size minus one i bigger or equal to zero five bigger equal to zero i minus minus now we're gonna reverse go through this uh backwards because the latest one um added is on top of the stack and we want to go downwards and then we'll do the exact same thing if uh we'll just we'll say auto and get a reference to the action callback equal i and that will be if actioncallback.func event.source dot source index dot value then break so that does the exact same thing but it goes in reverse order so we are almost there now we want to generate the action index so what we're going to do here is we are going to create an action event so first we need to create get the input source um so we'll say action event new event and we'll create it here and then we'll say dot action name we don't know the action name yet we know the source index is going to be source index is going to be device index and dot value is going to be u value right we don't actually need old l here i don't think so let's go back here and we'll remove all value now all we need is the source right so uh we need the source and we need the action so first we need to get the uh actually this should be so actually this the way we have it implemented we've got a set of action mappings here so here we should return um a vector of action events and then we can move this here input manager here input that now propagate action events expected three have four all right i don't need i don't need this now i can in place back a list right yeah so this should be fine i can add a vector to the vector that's fine so what i need to do here then is i need to create a i need to get the action so auto actions equal um input map action mapping key so that'll get all the actions then i need to get the input source uh input source because the input source can be the same for all of them input source source we're going to create a new function because it's going to be a big function eventually um we create the function here get get input source from key and put input key switch key case [Music] return keyboard y x [Music] [Music] left turn [Music] [Music] i'll do that so obviously this is not the full input um we'll need to expand this later but it's a good thing to get started with so we'll go back to our input manager here equal get equal get input key from source key now we are going to pour auto action in action so the source source equal source and dot action names at the top action name equal action dot like that and then we should just actually be able to do it like this then vector action event actions and we should be able to just do action events dot place back like that right we can just align the whole thing like that okay i can do it like this so that does that and then we will return all of the action events okay so now we are uh process we process the input it it generates a bunch of events if there's been changes updates the state of the devices and then propagates the events to the various callbacks registered to the input manager so that's everything we need to do to finalize the state so now there's one last thing that we need to take care of and that is this state function right currently we're not receiving any state so i will i've got a an appointment but i will be back to finish finish this up in a little bit all right guys so it's later on in the day i've taken a nap i've done some groceries now let's jump in and try to finish this input manager and hopefully um we can do so relatively quickly so and i know we've already got like an hour of coding done so hopefully we can get this done quickly so let's just get right into it okay so where were we we had uh we had registered the callbacks we had mapped the actions we had processed all the inputs and generated action events and we are propagating those out back into our game layer so now we need to be we need to set up receiving these events registering our devices and receiving these events um getting our input state so let's see what do we how do we want to do this so the best the first thing we can do is we need a place to store our devices we got our input devices here so we need to create devices so this is where we will void register device and uh can we just use this input to buy struct uh yes yes we can so we will take an input device right and we're going to say void remove device which we're actually we're just going to regist yeah we'll say remove device and put say [Music] input source source and device that matches the type and index okay so let's go back here and let's define these functions that's one let's do this so the register device we're going to do it fairly simply we're going to did we're going to say devices dot and place back device that's simple enough and we can i guess we can make it a cost reference as well and this one will be another array shift so we'll do erase if devices and we'll say source input index input device like this and we'll say return um device dot source dot type equal source and we'll figure that out in a second and input device type i screwed up that um definitions actually from the input device type and we will pass in type device.type equal type and device dot index equal input index so it'll remove any device with that index and that device type so that that works fine and this is going to tell me to probably make a const reference just do that for me so that's um pretty much all we need for that now we need to actually register the devices now we're going to register these devices from our window management manager so if you or a window so if you look at our multi-platform window here you can see this dlfw create window after we create the window we're going to register input input devices um but we're also going to register some callbacks right and what we're actually going to do is we are going to create new input stuff inside here so we're going to create a new under input we're going to make a new c plus class and we're going to call this multi multi-platform input multi-platform input there we go so we've got this let's name space it name and put the name space inside here okay now uh let's look at the dlfw input page [Music] so this is the glfw input guide now as i mentioned in my explanation earlier there are two kinds of inputs there's the interrupting inputs which are keyboard and mouse and then there's the polling inputs which are in this case the game pads or joysticks as dlfw sees them so if you look at here if we want to get keyboard input we have to set a callback for capturing the input itself right so what we want to do is we want to create uh in this input here we're going to have a few space we're going have uh keyboard state so let's just for now we'll say um we'll say floats for now uh keyboard thing like this we'll say we're going to need a mouse thing float mouse okay like this [Music] okay so we're going to uh look at this a little bit differently we're going to say look at here um so we're going to take input keys here and when we look at our callback for input devices we're getting a unordered map of input keys and input device state right so inside here um what we're going to have is inside our multi-platform input we're going to actually have a unordered map so include unordered map and what that's going to be is it's going to say unordered map and this is going to be a unordered map and it's going to be input key and i think it's an input key input device state and input device state input like that and we can close this so let's stay this keyboard state's going to be is going to be initialized to nothing we're going to do the same thing for mouse state because there's usually only one mouse one keyboard we're going to make that assumption for now and then for our devices themselves we're actually going to make a map of maps um which are going to have the indexes here so we're going to stood unorthodox map and we'll call this and for device index and then our uh gamepad states gamepad space and let's put our under underscores here so in the end what we're going to be doing is from this window we're going to register some callbacks that's going to update too many things that's going to update the keyboard and mouse state that's the first step now do we want to register the callbacks here yeah we'll make the callbacks into our multi-platform input so we'll say void update keyboard state and update mouse state and uh gamepad states is a little bit different but and then we will also have a um do this here [Music] and we will get [Music] keyboard state and just the index and this will just return keyboard state get mouse state and get gamepad state this will be mouse day and this will be gamepad state at index so this gives us flexibility like let's say if we want to at some point uh support multiple mice and multiple keyboards this index here will will give us that flexibility to change our implementations later so um these are called back functions so we'll say update update keyboard state update mouse date and we can actually put these in here so let's look at our input guide again so we want to set a key call back that looks like this right now first thing before we do that is we are going to add a multi-platform input into our window here so we're going to say multi platform input input [Music] make that default [Music] so we have the input there and what we will do is we will create our key callback tlfw set keycallback so we'll go to our multi-platform window and we're going to register some callbacks first so glfw set key callback for this window and we need to create we're going to do this in a lambda uh actually no uh yes yes we will so um we want to capture this i believe and we want to uh follow we wanted to have this signature here put this signature here is it supposed to return anything no [Music] can i even do it this way right so what we're going to do here is since this needs to be a it cannot you can you can't pass this into here what we're actually going to do is we're going to set a user pointer on our window which will allow us to to retrieve the input and then call functions on it so we're going to do dsw set window user pointer we're going to say window and this is the void pointer so what we're going to do is we're going to say take the value of our input variable now we are going to retrieve our input in here so we're going to get get the input um so from here we will say plfw get window user pointer window and this just returns a boy pointer right yeah on window [Music] and this will be a void pointer but what we actually want it to be is we want it to be a window multi platform input star input [Music] like this and [Music] all right i just forgot about the star i think yeah there we go and we can make this auto actually auto pointer multi-platform input so that looks good we have that input and then we will call input here and that is not giving me any function so let's look inside here what do we have i didn't make a public tag which explains it so let's go back in here so this will be update keyboard state all right so what do we want to do here we want to update the keyboard state from uh we want to give it the new value so we want to set set the new value for key now there's a couple of problems here the first problem is that we are getting key as an integer and we need key as an input key right so uh what we can do is we can either pass in the integer here which yeah i guess we can we could do that or we can convert it here i think it makes sense to convert it inside input just to keep the stuff a little bit clean keep input where it belongs as much as possible so we're going to pass in the key which is an integer and we are going to pass in the new value like this so let's go back to our input and update here so ant key and did i define these yet no all right and it's not end value it's float value all right generate definition for that [Music] okay so if we go inside here we don't have value defined so we're going to say book value equals 0.8 right so what do we what is going to be the value so when we're talking about keyboard we really know one thing right we know it's either pressed or not pressed um in general that's the state so that's going to be zero for not press one for pressed now we could use we could use um an enum for press not pressed but by using a value when we get to analog input we can do ranges a lot easier so what we're going to do here is we're going to say uh first we're going to get the action right so int so if action uh let's just use a switch actually switch action tlsw press uh lfw press case lfw uh repeat and uh what are the other options we have here uh press release um uh press repeat release and unknown so here we are going to say value equal one point f and break and if it's glfw release or glfw unknown it's going to be 0.0 and actually what we can easily do here is just put a default here value [Music] and put this here and why is this okay good there we go and now what this should do is it should whenever a key is pressed that state will be updated within our input manager here yeah that state will get updated within our input manager here and then we can do stuff with it later we can do the same general idea with our mouse callback so let's go here and look at mouse input text input key didn't that wasn't put mouse button to call back yeah so we'll deal with mouse button call back for now and we'll we'll deal with mouse movement later because it's it's a little bit more complicated and i actually haven't really thought out how i'm going to do the translations on that but um we can we can figure that out um well we can i guess we can put mouse plus position x and position y as separate input keys even though they're not technically keys right because this is this describes an axis this describes a position but you're not going to be able to really call a position you're going to be able to call it movement which we'll deal with later so anyways so what we're going to do now is we are going to do the same thing for update mouse date and we're going to do this with the button callback so it's going to be symbols the thing jlfw set mouse button callback um we are going to we are going to copy this go inside here go inside our platform window and set our mouse button call back again we will create this as a lambda and we will copy the function signature from here and we will take get our multi-platform input like this now what we should also do here just for safety is if the input in case that static cast failed we don't want to cause a problem so i'll do that and then if input here we input update mouse date i'll do that all right so what does mouse state what what is male state doing so it's going to be very similar i believe it's going to be int button so we're going to put the button here and it's going to have the value as well i believe yes again it this one just has a presser release so uh action equal jfw press question mark one point f else 0.0 and that's that's a lot simpler and then we can go back into our input and update the signatures here so hint button below okay so that is our our our keyboard state here and when we are done with that uh this should the red squiggle should go away good this is actually a lot easier when we are done with these we are going to register them as input devices so for that we are going to service locator and we don't have service locator get input manager okay [Music] so auto input input manager and we're going to say input call it input manager to be clear input manager register uh so notice here register we get registering our device here isn't working right so if you look inside our input manager here we've got our register device and remove device as private functions now is there a reason to keep them private i've been i've been liking you the use of friend classes but i mean technically if people want to create add their own input libraries um at the application level you should let them so let's make these let's make these public then um no reason not to okay so inside here we are going to register device and input device all right so first thing type equal input device type keyboard dot and then index is going to be zero dot current state uh we can skip that as state function equal and now this is where we will do our binding i believe but let's just say stood fine and input multi platform input get keyboard state for input and this will probably be a pointer i believe and yeah i believe that's that's it there right does that work is that how we do it and can we build that now for some reason i have a feeling something's going to go wrong here all right so what do we what problem we've got lots of problems here uh vector now i'm pretty sure this has to do with this um i was fairly sure that you weren't going to be able to just uh not not with this one here but with this one i was fairly sure we were going to be able to in place an array into the array yeah obviously i'm pretty sure gate keyboard state does not take zero arguments really oh index right uh and then this will be zero uh that's not right either holders there's one placeholder there now how will that look now we'll go back and fix the the array later in a second [Music] what what what's it complaining about now apparently my pragma wants to not be is not playing nicely somewhere now let's a couple of things i noticed while i've been in here um one namespace all of this all right that's namespace okay come on now uh this one is not being spaced name spaces don't expect this to be much different no same thing so what's happening here is that sometimes i've inlined a lot of this stuff um it's just redefining a whole bunch of stuff because uh yeah so here we'll actually just do this and we'll copy this oops copy that delete this and we will put this inside our input manager so put this here like that and remove this and [Music] indent these returns that should uh that should do it there we go yeah so that was the issue so it's building um now let's go back to our input manager here and fix that in place uh back that so instead of in place block back we're going to do here is events i think it's just insert right and insert events dot end uh we will [Music] begin and that should fix that issue there my builds good and i click build too many times so that builds um [Music] yeah so i have uh some homework to do real quick and then i will be back to do some more of this okay so it's a new day and it's time to do this again so uh let's figure out where we were at when uh when i stopped doing this yesterday it's like uh you might have seen my post but um this took a lot longer than i expected uh so i'm having to do this over multiple days and having to figure out where i am so the last thing we did was we created a mouse callback and we registered you know we created a mouse call back updated mouse position and then we registered the keyboard that's an input device so let's register the mouse now as an input device register device and this will be input device type equal input device type mouse index is zero again and the state function will be standard bind oops behind and multi platform input get mouse state and input placeholders one oops and there we go okay so that registers our mouse and we need a semicolon now um just quickly i apologize if the sound quality is fluctuated a few times throughout this video i'm trying a few different things and i put a filter while recording on my mic to hopefully you're not hearing too much of my air conditioner right now and yeah that's that's why that's happening uh what am i missing here um oh i don't need that okay so for gamepad state we're going to need to uh register the number of game pads by index and then save the state so uh we'll do that last we'll we'll deal with these uh mouse and keyboard states for now so um if we look at input we've only got a few things uh defined right now we don't have well we might be able to do the mouse movement stuff soon but we're gonna do mouse right left middle and we're going to do a b c d e those are going to be what we support right now um actually we got a d you know what let's also just for posterity do s and w as well so that we can have full wasd controls what we're going to need to do is we're getting this as a key here so we're going to need a function that will convert our key integer into an input key so if we take uh here if we go uh input key key equal uh multi platform key to input key and we can put a key there and let's define this um call this i key like this now we can define this in here uh it could be static yeah we might as well make it static as part of our multi-platform input oh very static input key multi-platform input and like this and there we go and we can generate this and this is just returning b so this is going to be similar if you look inside here we've got a we we made a switch somewhere inside here right maybe we didn't maybe it was a different yeah so we did something like this input source from key uh we're gonna do something very similar um but converting our multi-platform to input key so we're going to put a switch on key i'm going to say glf w a and we'll put a default um return input key unknown and let's add the unknown so we're going to add it at the top actually it's weird that it started with b but we're going to add a on here as well that way i believe it should default to unknown if if uh initialize without a value so we need this glfwa so we can include glfw here include uh glfw glf w3 and i think glfw key a and we're just going to copy paste this a few times and just deal with the keys that we're concerned about so a b a b c d e w r s and w and that's all of them i believe a b c d e s w perfect and then we can just return key uh a and we don't need the break anymore do this to be see now this is you might think this is really tedious um but by doing it this way you can provide new uh input back ends uh as you want right and you just need to convert everything to input key so you do this once and you never have to do it again basically um so i'm not going to do all of the keys right now um for to hopefully keep this video shorter than 17 hours um but this gives you the good idea so this is it here and we're gonna need a a similar one for um and then we can just do here for before before we work on the similar one let's continue with here so we have input key keyboard state right so we do uh we'll we'll do uh keyboard state i key dot value equal value right and that will update the value of that keyboard that key state and what is this can be made static that's not true so that's everything we need to do for that and we're going to do very something very similar to that um we'll do input key i key equal multi platform button mouse button to input key and this will take the key as a value and we can do the same thing mouth state i key dot value equal value and this is not a button this will be a button here we'll copy this we'll create we'll create this static input key input key [Music] and button and then we will generate that right and we'll do something very similar to this now i don't know what now there's not going to be as many uh right mouse middle and maybe scroll will be in here too but we'll do this so let's open up that input document again we'll look at mouse input we'll say mouse button input for now we got the callback we've got mouse button right mouse button left and probably mouse button middle right so dlfw mouse button left dlw mouse button right dlfw mouse button middle now i believe uh if you look here there's also w mouse button one two three four five six seven eight i believe that these are uh any extra buttons you might have on um on on your your mouse like i have an extra four buttons on my mouse i don't know which one's which um but we can look into that uh separately but for now we'll just do this and we'll say mouse uh left mouse right and nice middle and we'll do the exact same thing mouse state i key value perfect so now we have the we're we're taking mouse state and we're taking keyboard state uh and we are able to get the state from it so in theory um we should have enough to at least detect a little bit of input now um so let's look at the stack so we got the input manager the input manager you we register the devices which is true we do register those devices then we we need to register and the callbacks map some inputs we'll do that in a second we process the input no we're not doing that yet uh so in our game.cpp we're providing the input manager at the top of the frame uh we update the window which is good and then we want to call service locator input manager uh if you open if here service locator get input manager manager and then we want to process input like that now um this is where i why i put it as a private function we don't want to be able to process the input anywhere we just want to do it from our game function so we're going to declare game as a friend class to our input manager so if we go here we can just say private friend class uh game right that should work um that goes away and we go into game and we are able to call process input now um i've been doing a lot of experimentation with friend classes i try i still don't know quite when you should or shouldn't use it but i think this is roughly the one of the a good example we don't want to be able to process the input on command um from anywhere so we declare the friend class to the game class and that allows us to control where it can be called from now we we might have to look at our input manager how we're uh generating action events that's what the only thing i'm really concerned about generate action event so what we're doing uh consider pre-allocating the capacity before the loop i i can yeah we can probably do that um we can move this here and we can say actions dot size like this well we'll go with that we'll we'll keep a mental note that this might be causing an issue because this creates a size it of size but it doesn't actually put anything inside them right anyways we'll see we'll see what happens so uh when we generate action events uh the new value here um that we're checking the difference between original value and second value so what we want to probably do here is the value here we'll actually set this to minus we'll just say minus 99 right now what this will do it'll be it'll basically just make sure that um that it initializes to a bad value so that when it gets to the first value that it's going to generate an action event right at the beginning so hopefully that works we'll see in a second this one i wrote that statue hit build make sure it builds before we continue and then we're gonna actually run some tests to make sure that everything we've done so far actually works because honestly i don't know okay fantastic so we've done a lot of work now now we just need to deal with these so we've got if you look inside here we've got these volunteer registration callback and map input to action so this is what we do inside our main.cpp so what we're going to do is we're going to get our input manager so again us we'll make keep it a keep a reference of the private variable uh we'll keep uh input manager uh input manager here and we'll initialize it to null pointer then here we will do uh input manager equals locator get input manager and if input manager then we will register some callbacks or we can map some stuff so first we will map uh inputs so for now let's just map um let's map striping right so let's say input manager register map map input devices what we want to do so let's we'll map our key a key right to uh strafe so this there's gonna be an issue here we also need to be able to tell give it the weight right so let's let's go in and change that i can tell that difference so here look i'll show you what i mean right so if you look here um it's kind of hard to see it's all real blurry in this picture but um let's open image a new tab and try to zoom in a little bit you're sorry if it's all blurry but you can see here they see w is and s are mapped to move forward and you're able to give them a scale or one or minus one and what that does means that when you when the w key is pressed the value is multiplied by one or minus one making it so that you can map ws to the same action uh right and um that'll work and that's actually what i intended to do i just kind of overlooked it so in map input the action uh we've got key action and then we also need a scale now how do we store this we sort this string in action so what we want to do is we actually want to um we want to create another another thing so let's create it here we're going to say struct input input action and it's going to have a string action name and it's going to have a float scale so we want to then go into our input manager and change this to [Music] use input actions and then our register map input to action here will be uh i'll say input action here it's gonna be a little bit more annoying because we're holding this in a set right so this is all this is going to be changed to a vector then and uh we can keep this as a string because we don't need to worry about the scale when we're unmapping um right so we'll copy this go to map and we will change this to here okay and now we gotta fix this so here what we can do we can then place back and that'll be fine so that'll map the the key to the action no problem um it might cause issues with duplicates we could check for duplicates but we're going to make that to do and not worry about that right now um on map input we're going to need to do this erase if stuff again so um again it's a race if and then input action mapping input action mapping and then we say action and key i don't know key action and then we take this and it's gonna be input action action um input action and then we will say input return [Music] input action equal dot action name equal action there we go and here we're going to make a construct again there we go so that will unmap the input from action and we need a semicolon and we've fixed that so now we can go into our game back into our main here and we can uh this should yeah this turned into that and we will do input action here and we'll say action name equals straight and dot scale equal minus and this is a which is going left so we'll say minus one point uh and remove that semicolon and we'll do the same thing for d to make sure this is working make this scale 1.0 all right so um that's how we map the inputs now there's going to be one more fix inside our input manager when we process our inputs and we are setting the new value um here action name equal action dot action name and new valve times action dot scale and that will that will uh implement the changes we just made and that should be good we should be able to build that now no problem all right and now finally to make sure that all of the glue works we need to create our we need to register our action callback uh register action callback so in our game uh not our game our main.cpp uh here what we're going to do is we're going to you should do this in a more structured way when you actually build your application um and we're probably going to move all of this into like an init function or something an override init function to make sure that everything initializes before getting here but um we'll do this here because i think it'll work so what we're going to do now is input manager dot uh arrow register action callback for action strafe and for here what we're going to do is we need to provide an action callback so we'll just create an empty lambda for now and inside this empty lambda what we're going to do is where's my [Music] action callback it returns a boolean and takes these as uh takes these as arguments so input source source and source index and float value and if we return true that should be okay this is why you you should remember your own api so action callback uh here uh something happened okay like this this works okay so now we have the action callback uh it should be input manager action callback like that and then we have dot reference so this is going to be let's say youtube game so the reference is so that we're able to reference it from other things and dot funk equal like this and there we go so we got a function here and we're simply going to return uh the values so spin count straping and then uh value question mark equal one dot f question mark right else left and put a new new line here that can work can i do this then there we go all right so now in theory i should be able to strafe left and right so let's see what this does i'm kind of excited and kind of scared at the same time because we might go into debugging mode in just a second here all right so we've got so we've got our window we gotta make sure oh what happened okay all right so i pressed a and something did happen it crashed but we'll see why it crashed here size two so this is so it is creating an issue here uh with in place back so um let's take this off that did i think that did cause an issue [Music] because that's what i thought it's just adding it to the back of the cue there we go striping left okay striping right strafing left okay so it's always generating my left for some reason but we're getting closer all right so device oh we're creating copy there that should fix it so we want to make device a reference here and that should solve it and should make it so it only prints when i'm holding the button down or when the button state changes right there we go right left right left and here um we go here um so it's working uh we we fixed a couple bugs along the way but here we've got we're just doing one zero so let's just make it here uh directions did string direction and we'll just call say none striping none will say and then if value equal point f then direction equal right if value equal minus one point f direction equal left and we will just put the direction in here so what this will do is it should show when i release the key too as well which is important so left right none now it's not perfect um if i hold left and i hold right and i let go right it goes to none but i'm still holding left right now so it should still it should go back to left but it's much closer to what we want to do left no right no right so progress okay so we've got uh basic input callbacks working for strafing lap we're gonna go back to um we're gonna need to to add them up together uh when we get a zero probably but we we can deal with that later so what we'll do is we'll go into our input manager.cpp and here where we generate the value probably [Music] [Music] we're generating the value here this for the input action mapping is the key so yeah we'll have to rethink this in general but here we'll put here i put to do here to do fix cases where button where conflicting mappings or something we'll just call it that for now if you want to attempt it yourself something like when we go to generate the app mapping for the input we would get the action name for that key like we are right now and then we would loop through all of them and actually do an additive process and not a a separate process so anything for that action that conflicts with each other or we we would look at all the other keys mapped to that action or something and check to see if they're still held down and if they are we would pass in that value something like that um i need to think about it more this is the but that's where i would start looking i i probably need a pen and paper maybe my whiteboard back there so anyways we've got um just as a way we've we've got it working for keys and now let's register a mouse callback just to prove that our mouse callback is working so input manager dot red um actually let's just say input manager dot map input to action input key mouse left and input action now the action name here it's going to be um what what name do we want to call this let's just say we'll just say click and scale will be one point f now we don't always want to have to give a scale here so for scale we're going to default this to one point f and our input action we don't default this to nothing just just because and yeah that's good all right so let's go back here um and we need a semicolon here and then we are going to register a callback for that action input manager register action callback click input manager action callback and what this will be the reference will again be youtube game the function uh here so i'm going to show you that we you can actually use this keyword in this one here input source source and source index float value like this and we'll return true as well and that should work and we can use this now what we're going to do here is we're going to create another function in here just to demonstrate that you can do it this way as well with this setup so let's just say boolean i'll just say void handle click and let's just say value and we'll put float value here float value and instead count and we'll do that and then here we can call handle click and click value right there we go and now if we play that um stop and play that uh we should be getting our clicks now on our left mouse button now obviously this solution is not going to be completely implemented there's just gonna be more work to be done but see click and if i let go we get pressed and released um events for our our mouse here all right so that's mouse and keyboard where i could go into more detail um yeah we'll do we'll do uh we'll do mouse movement in a bit here again this is going to be a a monster video hopefully you guys appreciate this but um so we'll do mouse movement in a bit let's work on gamepad input looking at the dlfw input guide there's a few things we need to do first so the first thing we need to do is do this glfw set joystick callback which will tell us if a joystick has been connected or disconnected right whenever a joystick can is connected or disconnected this will be called so we're going to create this callback here glfw set joystick callback um actually let's just copy this go into our multi-platform window and we've got we're registering devices here we've got callbacks here right um and again you should probably move these into it its separate function but we'll do it like this okay so um we don't need this and our joystick callback will just be int joystick id so we're going to spell this joystick id and event call that and we will do this there we go and then uh we're not going to worry about normal joysticks for now because those aren't going to be thing but if you look back at here if you look at gamepad input there's a dlfw joystick is gamepad so um here repeated branch and conditional chain that's fine so uh here we're going to do right at the beginning we're going to do if joystick dlfw joystick is gamepad joystick id so if the joystick is a gamepad all right dinner is complete and now we can continue on and hopefully finish this off this time so the first thing we are going to do is from now is going to register our input device so we can do um we've already grabbed the input manager right yeah so input [Music] not input um we want to input manager how did i do that so uh we want to get the input manager like this so inside this callback we are going to get the input manager and we are going to say input manage input manager actually we'll do this and we are going to say and input manager and we are going to say input manager register device input device now type yes i believe it's going to be type first it's going to be input device type gamepad and index equal and this will be joystick id and then state function this state function is going to be a little bit different so let's do that for now whoops we don't want that let's put this and we are going to take a multi-platform [Music] input so input key input device state it takes an integer so we need to bind it okay so um [Music] inside here we are going to make a stood bind here did find and uh multi-platform input and we're going to say uh no uh we are going to say multi-platform window [Music] get uh gamepad state and this and did placeholders and with one placeholder right now this cannot be can we do this no let's look at this input input page again do i still have it open no i just w input input guide so um configuration state changes get joystick user pointer okay so we want to do the same thing as we did up here but we want to set the user pointer glfw set joystick user pointer that's kind of annoying that any i need to give it a specific id [Music] that's annoying um so what we're going to do then is we're just going to um hack it for now while i think of a better way but we're going to start at zero we're going to say there's going to be a maximum of 10 devices let's just say 20 devices honestly and i plus and we will set the glfw joystick user pointer for this joystick id to be and [Music] input like that right and then we can take this and do this [Music] and dlfw set get joystick user pointer and joystick id and a tlfw joystick and let's see how many there are here to find 15 or 16 so [Music] let's uh [Music] set that to 16 then uh glfw joystick last right and that will give us to 15 right i'll give 0 to 15. smaller equal then because it would be 0 and all the way to 15 and this would give us 16 which would give us 15. okay that's good so that will set the user pointer to the in points here now i think for the joystick we're gonna actually want uh this as the user pointer and then multi-platform window and then we'll just do if input again just to be safe defensive defensive programming and then we will say get gamepad state for input that work let's keep this for now and we'll create our state function we'll copy this we are going to go to our multi-platform window and inside here we are gonna do this get game pad oops get gamepad date and joystick id uh what does this say expected all right and it needs another one of these good and let's define this that looks fairly solid um oh we don't want a stood function here oops we want like that and we will remove the stood function here as well this is what happens when you copy paste too quickly all right now this i believe this is going to work now good and this is just going to return one of these good all right now we'll go back up with the list red square wiggle and do get gamepad state and fantastic and now we found that to here [Music] okay now that we've got this um we can go back we know it's a gamepad because we've already done all of the work to make sure it is a gamepad um we've registered it here we want a semicolon good and then we want to unregister the device here so what we're going to do is input manager uh unread uh remove device [Music] source input device type gamepad and joystick id so that should handle joysticks being added and removed so now let's uh go into our input manager and we'll just verify that we'll do a sanity check on this so register device is we're going to say um stood count device registered [Music] of type [Music] device dot type um we'll do that um this is gonna tell me that device type doesn't work like that so i don't feel like so we'll just cast this to an inch for now we can make it we can make a no stream operator for that later to make it printable as a string but for now that's fine and now um let's play that let's make sure it builds make sure everything run a sanity check okay so we still got this we can still straight uh it's gonna yes you divide it registered to device zero and one now i'm gonna grab my controller and we're gonna plug my controller in and hopefully we're going to see a new device registered on when that happens so we are going to do plug this in is it going to work no didn't didn't register now why didn't that work so let's close this and we'll figure out why this didn't work [Music] [Music] that is not working so let's go back to the input page and see why that would not set joystick callback so the user pointer the value will be kept until the joystick is disconnected or the until library is terminated [Music] okay so let's try doing this um [Music] here as well let's we loop through all of the available joysticks and we are going to say i here if is present should count joystick i is is present and put a new line at the end of that and see what this gives us now in theory it should give me one joystick right joystick zero is present okay now why doesn't this okay okay we got joystick connected okay cool cool all right now joystick is gamepad and input manager now um [Music] say so is joystick a game pad where so it sucks that we can't put a breakpoint inside these lambdas so sometimes it seems to want to work and sometimes it doesn't want to work [Music] okay so in play mode it's working but in debug mode i'm not getting any [Music] i'm not getting any input in debug mode for some reason [Music] well that's that's odd that's real odd [Music] hmm okay well that's something that i might have to look into later but for now we'll just run in uh non-debug mode so we'll just look through see if um joystick connected okay so joystick connected is not the callback isn't called on startup so we are going to want to register connected devices here we can do that here and we can do we can basically just do this do this copy this into here and here we will do id here we will do [Music] input dot register device [Music] uh no how do we get our input manager here do that here would be end input here would be i right uh and this would actually be this there so and then here we'll do if input manager we have an input manager register the device okay so that will register the device on startup now we want to double check to make sure that this is we want to make sure that this is both okay close this so it is not a gamepad it is a gamepad okay but uh input manager is probably okay so let's go inside here and we will try this again i gotta figure out which part of this is failing so you'll see here now that we have we want to see device registered of type 2 is what we want to see when we plug it in if i hit debug again i plug it in type one okay and now it's not going into here so my guess is that input this is not being set because the user pointer is not there let's just say uh put this here we'll say disconnected um [Music] do this and this move that over we'll just make sure we have an input manager here then when it disconnects it should go inside here and uh disconnected and then at the same thing here it should say connected now we're really close we we really are very close okay so um close this disconnect it we don't get a disconnected message no plug it in don't get a connected message we unplug it so input is right so what we want to do is we want to take this put this here and we can put this inside here make sure that this has everything we need there we can bring this back one level all right so if it's a gamepad and then an input is there so what we want to do is so in theory now because remember at the beginning where we're setting the user pointer for all of these to the input right so let's just make sure callback works clicks work disconnected good now when we plug this in it's not going to say connected because our user pointer has been cleared because if you look here it says joystick user pointer the value will be kept until the joystick is disconnected or until the library is terminated so what we want to do then maybe ah this is annoying um oh i know i do here it's input right but here what i can do instead auto input equal service locator get get window right cast multi-platform window service locator get window will that work there we go that makes sense so now um we can actually uh we can move this back into here and then just input all this back into here and input manager that's fine this is one too many good um and we put an else here even though it's not really needed all right now in theory we don't even need a user pointer this is why service locator is useful so we can get rid of these user pointers let's try this now we should get connected and disconnected messages [Music] registered device 2 disconnected good good fantastic and then plug it in and what do we get connected device registered of type 2 and that is fantastic and then let's just one more let's put another sanity check inside here input manager um we'll do this on register to type two [Music] and we will also do good count device number and we'll say devices dot size and put another end line here okay so now what we should see is we should see the device number go up device number go down so we got device number one there's the total of three devices we remove our controller disconnected and see now there's only two and now if we plug it in again now there's three again and that's fantastic that's exactly what we want so um um yeah that's that's working now everything seems to be doing okay now if we stop and we start it without when we start it without a controller plugged in we should start with two devices a mouse and a keyboard and when we plug it in we get our gamepad and it's device number three now if i had another gamepad i would plug that in and i would now we need to get the gamepad state now for that what we will do is in multi-platform in window at the bottom here we created a gamepad state like this right so we're going to create a one more thing here and it's going to be update gamepad state void update gamepad state now what will this take this one is going to be different than the rest if we go back to our input guide input we are going to get a state uh glfw gamepad state and that is how we are going to do it and since it's here we're going to say glf w gamepad state state uh and we are going to want to include tlfw glf w3 all right and i think i included it here so i can just remove that g lfw gamepad state state and this is actually we're going to make this a const reference because we don't want to uh copy the state over and over again it's going to actually just go uh we'll just do this get gamepad state like this and we can actually remove this here and yeah and we can just return and here we'll actually have to implement we'll implement this as a more in-depth function like that okay and then we can go back to our multi-platform window and we're gonna return input dot get gamepad state and we're gonna say glfw get gamepad state joystick id okay so actually it's not going to be like this let's look at the documentation but it should be fairly simple we want to create a state variable we want to get the joystick id and pass in the state like that i believe and then if if this then we want to return like this else we will return um empty state like this and i one thing i don't like is if i've got a return in an if statement i like bringing it down to one level all right so that's how we will get our gamepad state we're going to return it from our input or it's going to be empty and finally we will go inside here we'll implement this now we're going to do something very similar to this here [Music] but let's look at our input here so we've got state.buttons and state.axes right so let's see what we have here so state dot buttons and glfw gamepad button so what do we have here tlfw gamepad button right now let's look at what this has zero to four zero to fourteen right and then the other ones these ones are the same as the other ones and these ones are zero to five so we can um we have a few options here we can loop through them and yeah that's what we're gonna do we're gonna loop through from zero to last and we're just going to check to see what they are and then convert them to our own input kind and then we are going to add them to our our states now input manager so this gives one device state which is fine yeah that's good so let's go inside here so what do we want to do we want to four so first we want to get get the buttons and we will actually create a map here we say state and we will return state uh gamepad okay so first thing we want to do is get the buttons so we are going to pour into i equal glf w button a we will let's just do zero i equals zero i smaller than glfw button last gamepad button last and we'll make this stuff equals i plus plus and then we want to get the axes which is going to be very similar here axis last and there we go that's this will go to zero to last now what do we want to do in here and here we want to switch on i and we will do a few things so uh what we can do is go here and we can actually just copy all of these to make it easier on ourselves all right so so [Music] [Music] so that's all the gamepad buttons we'll do the same thing with here so switch i and we will grab the axes down here and i think yeah that's pretty much all of what we need uh multi-platform input so uh how do we want to do this so the state state dot buttons let's see what this says state dot buttons [Music] i okay so first at the beginning of the loop we are going to get the state so state dot buttons i and what does this give us it gives us a uh unsigned car press or release okay so this will be can't state equal uh let's see button state equal state dot buttons i and this will be uh we'll just say int our float value equal um zero point f if button state equal to lfw press then value equal to one point f right and actually yeah that's pretty much all we need value equal i'll do this done all right so that's all we we need to do for that now we just need to fill our state so gamepad state dot dot for input key gamepad v equal value dot value equal value all right we'll do that and we're actually going to make all of our input keys in here now so we have our thumbsticks but we also let's say we also want excuse me we also want uh uh gamepad y gamepad x gamepad b gamepad a d-pad up d-pad right d-pad left keypad down we got that that axis axis uh gamepad start gamepad start gamepad select gamepad bumper r gamepad bumper l and so we've got this and then we got gamepad we'll just call it l3 gamepad r3 and then we need so i've got d-pad thumbsticks start select this bumpers uh and i need trigger um [Music] gamepad r trigger gamepad l trigger i think that's all of them and now we're going to um actually gamepad i'm gonna add some prefixes on here just because um there's some overlap so gamepad [Music] we got mouth mouse mouse mouse mouse gamepad and here we are actually going to have to he is going to be okay no no it's not going to be okay we're just going to rename it and we're going to build this and we're going to fix the errors that that created because i know how to create some errors all right so key key key key key gamepad uh input source we uh fudge me okay uh now we want to take all of these extra gamepad stuff uh put all the extra gamepad stuff here uh turn this back all right so i think that's good now i think that'll build let's see uh there's going to be an issue uh here again key add these here now we'll build this again and now i think there might only be one spot left right and main.cpp which is the last spot i expected so here when i did key a key and built that perfect cool all right so where were we we were inside here um i think yeah we did we were doing this all right so now we just have to fill all this out gamepad a gamepad x gamepad why uh bumper l bumper r call this select start uh we i don't have a guide button oh it's the xbox logo so we are going to actually move the guide button down here because i do not want to deal with it [Music] this is l3 uh r3 it's annoying i know but we have to do a gamepad state input key gamepad d-pad up value equal value and let's just paste this a few times right down left and there we go that's all the buttons and that was fun wasn't it now we can do the axis now the axis is going to be a little bit more simple this one is actually going to just give us a straight value so we can just say value equal state dot axes i and then we just straight set the axes here so we say um uh gamepad state uh gamepad input input key gamepad thumb left thumb x dot value equal value and we will just copy this a few times all right uh so why write down x right y and this is left trigger l trigger and this is our trigger and there we go uh so now we are populating the gamepad state that should be everything now with that done um this can still be made static but and this can be made static but i'm just gonna leave it for now all right so in [Music] theory we should be now able to create an action so let's let's add our strafe left and right so gamepad d-pad left actually um we can just what we can do instead of doing the d-pad is d-pad left and we can do l thumb x and that will strafe using our left left left thumb stick now let's see what that gives us will that crash or will that be successful look at this striping right strafing left oh beautiful now if we want to move forward we can do a similar thing so we can take this we can map it to y we can say move forward with a scale of one we can [Music] register the action callback which is going to basically be exact like this and we do move forward move forward move forward move forward so move forward and then we're gonna say up down [Music] moving any and there we go now we should be able to stripe up strafing none [Music] all right moving on i should be moving up though moving up moving up that's down though okay so let's uh let's get rid of some stuff here so we got move forward left thumb stick y which is good scale one move forward let's silence this one and let's instead of this let's uh i see let's if it's bigger than one uh bigger than zero and if it's smaller than zero now we should have something a little bit more in line with what we're looking for moving down moving up okay so x and y is inverted so what we want to do is so if we go here in our input so um our left thumb stick y this is actually inverted compared to what i would expect i would expect up to be plus one and down to b minus one so we are actually just going to invert our thumbstick values here and then moving up moving down moving up moving down now you're going to want to have to be able to set a dead zone right now it there is no dead zone so as soon as you start moving down a little bit and and we can do the same thing for strafing as well um if we go back to our main.cpp for strafing we can do this and we can do bigger than zero and smaller than zero strafing right moving down strafing right straightening left strafing right and there you have it that's gamepad input in a nutshell um we can now register everything so that is everything i'm going to do today what you're going to want to do if you followed along to this which if you did congratulations this is going to be a beast of a video you're going to implement the rest of your keyboard keys i'll leave that as a strategy for yourself and i left a few to do's around i've got a lot of to-do's actually but uh two to-do's so here um when we're mapping inputs to actions and technically you're able to map the same button to the same input multiple times which will probably cause bugs and here um this is to resolve the case of binary input so let's say i'm holding a which is supposed to move me left and then i also holding d which is supposed to move me right right now the latest one is going to be the latest action state um action event called and if i let one go it'll return to zero even though i'm still holding left that's something that you'd want to fix you'd want to do it in some sort of like additive fashion for doing that oh that was an adventure so after that long period i don't know how long it was three four hours at least on your end on my end i guarantee you it was longer we now have a working input manager system plus or minus a few bugs here and there i pointed out roughly where the ones i know of are how i would go about fixing them yeah so the next step is to we're going to continue on with our renderer to get it to the point where we can start using our input to make things happen on the screen and it's going to be really cool to be able to do that so with that said um thanks for sticking with me this far and leave a comment like subscribe all that crap down below and i'm gonna go get some sleep now see ya
Info
Channel: Ozzadar
Views: 330
Rating: undefined out of 5
Keywords: controller glfw, glfw input, controllers glfw, action input manager, action mapping glfw, input actions, c++, game engine input, game engine development, gamepad glfw, keyboard glfw, mouse glfw, controller input c++, cross platform controller, cross platform input c++, c++ controller, c++ gamepad, c++ keyboard, c++ mouse, ozzadar, glfw, opengl, game development, glfw input system, glfw input class, game engine input system, game engine input handling, glfw input manager
Id: Ms9metbS2lU
Channel Id: undefined
Length: 192min 41sec (11561 seconds)
Published: Sun Aug 15 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.