[Unreal Engine] Setup & Sprinting | Character Movement Component In-Depth

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all right so welcome to the third video in the series where we're going to be uncovering in depth the character movement component in unreal engine so in this video we're finally getting into the code so we're going to start by creating a new project in unreal engine and i'm running 5.03 um so yeah i'm gonna be this to this series is going to be primarily for unreal engine five but i think pretty much everything should be compatible with four if you're still using that so um it shouldn't be a problem but i always keep my unreal engine version updated to the latest so we're just going to create a default third person project and i think i'm going to name it zippy so that's going to be the name of our project we're gonna do c plus plus um [Music] target desktop and quality we're gonna go scalable again like this is probably just gonna be determined by your project you're probably not gonna actually be creating a new project with me but um just for completeness i'm going to make an entire project um so we're going to wait for that to get loaded up just wanted to interject here and say that it doesn't actually matter if your game is first person or third person because all this is going to work the same exact way and i'm probably actually in the future going to make this game compatible third person and first person and also the link to all the code we're about to write is going to be in the description on a github all right and so now we have the default third person template loaded up um i guess it's gonna update the project file and actually before i do anything else i'm not gonna close out of this now that i created the project and i'm gonna go to where that project is in my folder here's zippy and um i'm actually gonna restructure the uh code the source folder in here a little bit because i like to do it a little differently so we're going to make um a new folder public and private and then we're going to put the respective c plus files in the private and the header files in the public this just keeps things a little more organized leaving the build cs file out and i'm actually gonna go back in here and delete some of these so just the game mode actually we just don't really need a custom c plus plus game mode um and so now i'm going to open this up in my code editor so i use writer um i just find it's a lot more pleasant to look at them visual studio so we'll open this up all right so now that's loaded up we can get to work creating our character movement component so the first thing we're going to do is we're just going to create a new class called the sippy character movement component and i'm gonna be pasting in code here because it's gonna be too slow to type it all out so first i'm gonna paste in the default unreal engine class definition so i'm just creating this zippy character movement component we're deriving it from the character movement component and don't forget to add the zippy api or your project api macro and include the correct header file for the base class and then let's implement the corresponding constructor and we're just going to leave this as empty for now so in this tutorial i'm going to be setting up the character movement component but i'm also going to be implementing the sprint mechanic kind of alongside of the introduction because this will just be a helpful way to demonstrate what we've done throughout the tutorial so the first thing we're going to do is create a boolean called safe wants to sprint and this this is going to be a flag that gets set on the client whenever the client is going to press shift and then through the processes we're about to create it'll properly replicate this to the server and then the server will handle this data and this variable will also be movement safe meaning that if any um client updates get called everything will be kind of replicated and prepped properly so that we won't get any rubber band or anything like that and that's what the remainder of this video is going to be about is just setting all that up so now there's two helper classes that we also need to create and so the first one is called saved move and if you didn't watch the last video i definitely definitely recommend that you do that to get a good understanding of what we're actually doing here but so a saved move is essentially a snapshot of all of the state data in the movement component that's required to produce a move for a single frame so the character movement component is saving these saved move classes for every single frame of movement and then it's replicating a minimal version of that to the server so that the server can perform the same exact saved move and as you can guess the whether or not we want to sprint will have to be saved in our saved move because this variable is important to how the movement logic is executed so we're going to have to override the default class with our own custom and all we're going to add here is just a single variable that is going to be saved b wants to sprint and so again the difference between these this is our working variable this is the variable we'll be setting and checking and applying the logic based on the value of this this variable will automatically sort of be updated to the current value of this variable every time you create a new saved move and then every time you need to play a safe move this variable will be copied into this one and so we'll see that kind of how that plays out as we implement these functions so i'm going to go ahead and generate definitions for all these and we'll go through these one by one so can combine with is a function that basically checks um two moves uh the current move and the new move and it checks can we combine these two moves and the reason it does this is to save bandwidth so um the idea behind this method is that if all of the data in a saved move is pretty much identical based on you know whether you determine it's identical then we're going to return true and that means that we don't need to send two moves but we can just tell the server that basically run this move twice or you know however many combined moves we create um so what that means for us is something like this so first we're going to cast the new move to our custom class the zippy save move and then we're just going to check if the saved wants to sprint in this save move is not equal to the saved wants to sprint in the new move so if these aren't equal then clearly these two moves are not the exact same and they can't be combined so we're going to return false but if these two values are the same then we're going to let the super handle whether they can be combined or not because there's actually a lot more data in the saved move that's in the default class and you can peek in here and see that you know it has some parameters like force no combined it checks if the accelerations are different there's just a lot of other stuff going on here so there's a lot more reasons why you might not be able to combine to move but certainly if the sprint values are different you obviously can't combine it um so moving on to clear clear is the easiest one all it does is just kind of reset a saved move class to um or reset a save move object to just um kind of be emptied so for us we're just going to do that and just kind of reset this flag so now let's talk about compressed flags so compressed flags are actually the way that the client will replicate movement data so the save move as you can see if i hop into the um default character say move there's a ton of variables in here and it would obviously be way too expensive to replicate all of this to the server every time we perform a move which is every single frame so instead we send a very minimalistic version of this which besides a couple things that the character handles basically we just send these compressed flags and the compressed flags are you get basically eight of them because it's a eight bit integer and you can basically do anything you want with these compressed flags but um typically you know each flag kind of stands for some boolean value that would most likely probably be tied to a key press so if we actually look at the default value we say okay if uh press jump then we're gonna add we're gonna flip the jump flag if wants to crouch then we're going to flip the crouch flag and it just returns after that and then we can add what we want so ours is going to look like this and so all we're doing is we're first letting the super run and then we're going to um flip custom flag 0 if the character wants to sprint so basically the sprint channel or the is going to be assigned to custom flag 0. and if we look at this enum here what we'll see is we'll see those two flags we just saw for jumping and crouching and then there's two reserved flags that you could the engine kind of wants you to leave that for them and then we get these four flags and um if you're thinking you know geez i literally get four bits to send all my movement data um and that kind of i know when i first saw that i was like geez that's nothing like how am i going to be able to you know replicate all this complex movement but actually um you got to keep in mind that you can still send server rpcs and for most of the things that require like if you want to send a vector or anything like that any a large data you actually um want to do that in rpcs because usually those are going to be sort of one-off events like dashing or rolling or something like that and you only want to reserve the very very prevalent essential data to be compressed flags these are things that need to be sent every single frame so you want to be very sparing with your compressed flags and anytime you're sending a lot of data you want that to be in a a one-off rpc that gets called like you know every couple seconds at a maximum um so yeah that's what the compressed flags are basically this is what's actually getting sent from the client to the server um so now moving on to set move and prep move so what these two methods do is these capture the state data of the character movement component so what setmove is going to do is it's going to look through all of the safe variables or all the variables that you want in your character movement component and set their respective saved variables so it's essentially setting the saved move for the current snapshot of the character movement component and for us that's going to look like this so you can see we're first letting the super call its set move for and then we're getting a reference to the character movement component and all we're doing is setting the saved wants to sprint equal to the current value of the character movements wants to sprint and it looks like we need to include the definition for the character that just added this header file so that we can call this function now prepmove is the exact opposite so prep move is going to take the data in the saved move and apply it to the current state of the character movement component so that's gonna look like this and you can see it's literally the same thing in reverse we're just gonna set the state data on the character movement component equal to the saved value in our saved move and again always remember these saved moves there's going to be an array of them and we are potentially going to be running any one of these saved moves and the server is also going to be running essentially a saved move um and so yeah that does it for saved moves now we actually have to indicate to the character movement component that we're not going to use the default character move we're going to use our custom save move and the way we do that is through another class called the network prediction data client and just like before we're going to make our own custom version underscore zippy and really we don't need to do much here again like i said all this is doing is indicating that we want to use our saved move instead of the default one and so all we really care about in this class is just this allocate new move which will allocate a saved move zippy so let's implement these two methods and we don't actually need to do anything in the constructor other than make a call to super um and then in the let me just tighten that up and then in the allocate new move instead of calling the default we are going to allocate a new saved move zippy and if you're wondering why um you know usually you don't do any of this raw c plus plus stuff like heap allocations when you're using the unreal engine framework and the reason why we're doing it here is because these two classes the saved move and the network prediction data for the client these are not orienting from u object so you can see this this does not originate from u object it's just a generic c plus plus class and that means you can't do any of the nice casting stuff so you can't up cast and downcast at will and the reason why these aren't inheriting from you object if i had to guess it's just because we want these to just be small because we're going to be allocating a lot of them and deleting them and whatnot so we don't want to spend the time of allocating whole you objects and deleting them on the fly because we just don't need that big framework of garbage collection and everything that you object provides you so that's why we need to do this kind of workaround of adding this a second class um and so now we need to do we need to implement a couple of functions on the actual character movement component itself to indicate that we want to use this network prediction data so that's going to look like this get prediction data client and what we're going to do here is we're just going to create a prediction data class that we just made so this is a little bit confusing but what we're doing here is we are checking if the client prediction data is a null pointer and if it is we're gonna make it but if it's not we're just gonna return what we already had so this is a cached uh value of the client prediction data so we're only gonna actually create it if we haven't already created it and so this is a const function because it's a getter essentially right but um if the client prediction data is already null we're gonna have to make it which means that we need a reference to the character movement component which is non-const so it's kind of an annoying workaround that there really is no better way to do it but you're gonna have to const cast away uh the character movement component reference and then you're going to make a new network prediction data client on the heap and pass it in a reference to your current character movement component and these are just two parameters on the client prediction data because like i said before the client prediction data does a lot more than just allocate the saved move and so yeah these just alter how the networking corrections are handled but so yeah this is just one of those methods that you just um implement and then just forget about because you shouldn't really need to touch this again now going back to here we need to create one more function to get all this working which is update from compressed flags so this is the uh final sort of step in the compressed flags which is that now we are going to pass in a integer of compressed flags and we're going to up we're going to set the state of the character movement component based on those flags so for us that's going to look like this so basically all we're doing is we're just setting our safe wants to sprint equal to the value of the custom flag zero so finally that is all of the sort of infrastructure you need to now be creating and utilizing movement safe variables and so to kind of go over sort of the pipeline of what will happen you will first on tick you'll call perform move which will execute all the movement logic and it will read the it'll utilize the value of wants to sprint then it will create a safe saved move and it'll use set move for to read off the value the current value of wants to sprint and it'll save that into our saved wants to sprint then it will call can combine move with and see if there's any pending moves that are going to be sent to the server that are identical to the move we just executed and it'll combine them if necessary then it will call get compressed flags and it will reduce this saved move into this small networkable packet that it can send to the server then when the server receives the move it will call update from compressed flags and it will take those compressed flags we just sent it and update the state variables such as wants to sprint from the the client's movement data and then it will perform the move that the client performed and with all of this in place those two moves should be identical so now we have a movement safe variable that is already automatically replicated from the client to the server and it's completely movement safe so if there's any server updates and the client has to update their position everything will be in sync we shouldn't have any rubber banding or hitches or bugs in our movement code so now let's actually utilize this variable in some movement logic so the first thing we're going to do is we're going to create two functions down here called sprint pressed and sprint released and all these are going to do is simply toggle the safe move flag and if you're paying attention you might have realized wait a minute safe wants to sprint is a safe movement variable but we just created this blueprint callable sprint pressed which is obviously going to be called in a non-safe context in the movement component so how is this possible won't this break the properties of movement safety and the key here is that you can only call these functions on the client so it's kind of confusing but you can alter movement safe variables in non-safe movement functions on the client you can never utilize non-movement safe variables in a movement say function and you can't call non-movement safe functions that alter movement safe variables on the server so don't worry if that doesn't make sense because you'll build up more of an intuition when we actually start you using this stuff more but um yeah that is kind of the rule of thumb for movement safe variables so now we have a variable that is completely movement safe and the client can toggle the value of this variable by calling sprint pressed and sprint released but it doesn't do anything yet so let's create the logic for that and we're going to do that in this function on movement updated so this is a handy function that is automatically called at the end of every perform move and it allows you to write some movement logic that runs regardless of what movement mode you're in and the other thing we're going to do is create two more variables and these will be parameters that you can edit in blueprints and they're going to be called sprint max walk speed and walk max walk speed and so these will um determine how fast the character goes when they're sprinting and how fast they go when they're walking so now if we implement this and we're gonna do this so what this function does is it checks okay if we're in the walking movement mode and then if safe wants to sprint is true then set the max walk speed to sprint max walk speed otherwise set the max walk speed to walk max walk speed and this variable is going to automatically be used by the walking movement mode to basically determine how fast the player can go and yeah we're kind of piggybacking off of the walking movement mode for now so you know this certainly isn't a custom movement mode i don't think sprinting is a mechanic that requires a custom move mode but yeah this is just this is it this is how hard it is to make sprinting um and it you know after all that setup it really isn't a big deal it's really pretty easy and you're going to be reusing the setup for everything else you do in the movement mode so don't look at it like this is how long it took you to make sprinting look at it like this is how long it took you make sprinting because you're going to be reusing all this code for any other move mechanics in the game so now let's set up the character to work with our new movement component so we're going to hop into the character class and the first thing we're going to do is just add a variable that is our new movement component so that's going to be this guy and i'm making him protected because he's going to be blueprint read only that way we can call those sprint pressed and sprint release functions from blue film blueprints and then the other thing we need to do is we're going to uh change the default constructor so we're going to add this new constructor call that takes in an object initializer and this will allow us to redefine the character movement component that we're going to be using so if i hop into the c plus plus we're going to change this i mean and you don't have to exactly know how this works it's kind of just unreal engine syntax but basically we're setting the default sub object for the character movement component to be our own character move component and then lastly all we need to do here is set the we're going to cast the character movement to our new character movement class and store that in the variable we just created that way we have a reference to our new character movement component and we're going to include that definition up here all right so with this all set up in the c plus code let's hop into the editor and let's link the blueprints to call those two functions and test out if everything works alright so here in the editor the first thing i um just did real quick was i made a custom game mode because we actually deleted the one and c plus and all i did here was just set the default pawn class to be this third person character so next we're going to head over to our character here and the first thing we're going to do is we're going to make a event on the shift key being pressed and then we're gonna get our sippy character movement component we're gonna say sprint pressed and sprint released and this isn't exactly the way that i would do it in like an official game or anything but this is just a quick way to you know get the demo working and show that this is working properly and then before we compile we're also going to want to change the um variables that we created so these are our custom variables we just made so let's make the sprint max walk speed maybe a thousand and we'll keep the default at the default value 500 so this is the default max walk speed that was already set for the walking mode so let's compile and save that and now we should be good to play so first i'm gonna play it um single player so that's me walking and that's me sprinting you can see clearly faster when i'm sprinting so everything seems to be working in single player and now let's go to two players and we'll play one as the server one is the client and we'll play in a new editor window all right and so first on the server everything seems to be in order and now the true test if i go to the client you can see the client can sprint and they can walk so now but why you're really here is let's test this at like a reasonable latency so let's go uh emulate uh packet lag yes and we'll do say a latency of 200 milliseconds and that's obviously um very extreme so but this is just for a demo purpose to show you so you can see that clearly um when i move on the screen on the right it's taking a good 0.2 seconds for the screen on the left to get those updates and now when i sprint you can see everything's working in order no problems um and i'm actually also going to do uh this command p dot show sorry net show corrections and um that's going to allow me to see every time that the client gets a desync from the server so as you can see there's none obviously when i sprint nothing happens everything's in perfect order i could probably force a desync like usually collisions will sometimes cause a slight desync i'll see if i can get one here there we go so you can see that this shows the desync so this concludes the setup and the implementation of sprinting in the custom character movement component series so i hope you enjoyed the video and i hope you got a lot out of it and stay tuned for the next video in the series peace
Info
Channel: delgoodie
Views: 21,473
Rating: undefined out of 5
Keywords: gaming, fps, video game, shooter, movement, movement shooter, xbox, pc, game, multiplayer, ue4, unreal engine, ps4, playstation, steam, guns, space, zero gravity, astro, outer space, physics, momentum, devlog, developer, game dev, competitive shooter, online, network, code, programming, 3d, 3d video game, tutorial, character movement component, cmc, networking, replication, replicate, move, control, player, player controller, player movement, client side prediction, server authoritative, client prediction, server
Id: 17D4SzewYZ0
Channel Id: undefined
Length: 29min 43sec (1783 seconds)
Published: Fri Aug 12 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.