Code-It-Yourself! Role Playing Game Part #2

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello and welcome to code it yourself role-playing game part 2 if you've not watched the first video you probably will need to this is a direct continuation from that video and if you remember at the end of that video we ended up with Jerry Oh oddly enough from the platformer game walking around a map and we can see here that Jerry OH can't overlap with anything that's in the map that we're considering static and what this video is going to be about is introducing dynamic objects I eat that's the things that move now you might be thinking well hang on we've already got a moving object surely we've already covered that well yes we have but we now need to make it more general-purpose and we're going to use object-oriented programming to do that for us in the most basic sense anything that moves we can consider a dynamic object but I'm going to extend this a little further that anything that isn't static or background is going to be a dynamic object and we'll see later on that this can involve event triggers and switches and teleporters and all sorts of things that make the game happen once we've got the very basics of dynamic objects out the way we'll look at how we can script sequences to form cutscenes we did actually describe what the dynamic objects were in the first video and in this game I'm going to restrict them to being one unit wide by 1 unit high and that equates to about 16 by 16 pixels so all of our dynamic objects are going to occupy a single tile in the game although they are free to move in a continuous domain so then we're going to keep them locked on to particular cell boundaries so let's start with the basics we know that we're going to have to have a position for our dynamic object which is going to be an x and y coordinate floating points we know we're going to need a velocity which will update the positions depending on the elapsed time so we'll call that VX and V Y we're going to have two boolean Flags determine the collision detection properties of the dynamic object is it solid against the map for example Jerry Oh can't walk through a tree or a building however things like a bird or a bat might be able to fly on top of things so we don't always want things to be solid against the map will also see later on sometimes we want to adjust these properties very briefly to help us get some certain effects we're going to check is the objects also saw it against other dynamic objects so if we've got enemies in the game again we don't want the player character to be able to walk through them but sometimes we might so we're going to keep these flags a bit separate we're also going to record is the dynamic object considered friendly or not and we'll see later on that this will determine what on interaction properties do we need to set for example if the dynamic object is friendly and it interacts with a friendly dynamic object then probably it's a conversation or something something interesting is going to happen however if one of them is friendly and one of them is not friendly it's most likely the intention was an attack so we'll want to handle this differently depending on the friendly state of our dynamic object and the way I'm going to structure the dynamic objects is to have a base class dynamic and for things that move around and perhaps have their own behaviors we're going to inherit from it and call it dynamic creature and because we may want to specify the individual behaviors of a dynamic creature this is where we're going to break it out into the different types of enemies so for example we might have a bat type enemy we may have a skeleton type enemy and we may also have friendly characters so perhaps as an NPC type for things that aren't dynamic we don't necessarily need to create an intermediate class we can still just inherit from dynamic directly so this may be for example a cell that teleports the player to a new location it may be a signpost so even though it's called dynamic what I really mean is anything that isn't static or map doesn't necessarily mean it has to move around it's just it's an entity that we can interact with at the end of the last video we ended up with three main source files we had the main application which contains the on user create and on user update functions and we also created a singleton class called RPG assets and this loaded a single instance of well basically the sprites and we're going to add more things to this so I think the maps are also going to be something that will want a global access to but we only ever want to ensure one instance of them exists so all update this class too we also created a map class which basically stored the static information of the maps and they were created via an external map editor created by this community currently moving Jerry o around doesn't involve any additional classes in fact we just store the values for position and velocity directly in the main class we're going to be chained because the player object is indeed going to be a dynamic object too so this means we'll have to make quite a lot of changes to our own user update function to accommodate a new architecture the good news is we've already got static collisions done we'll just have to refactor it a little bit to suit the new architecture but we'll also need to add dynamic collisions so this is when dynamic objects interact or collide with other dynamic objects on the whole we're going to move this class away from the idea that there is one central dynamic object in this case it's Jerry Oh instead we're going to maintain a list or a vector of many dynamic objects and they'll all be handled independently and we're going to rely on object-oriented programming to differentiate between the behaviors and interactions necessary for each object let's start by creating our base class for a dynamic object I've added two files RPG dynamics H and RPG dynamics dot CPP I'm going to include all of my dynamics in one file simply because it makes it easier for me to make the videos without having to skip between different files in practice however you probably would want to separate all of these individual classes into their own files so let's start by creating a class C dynamic I'm not going to put the method bodies into the header file I'm just going to prototype the functions so we need the constructor or put in a destructor to and I'm going to add some public properties so we know we need the position px and Y we're going to have the velocity VX and V Y and we're going to have the three billions I discussed earlier and we'll call that B solid versus map solid versus other dynamics and whether or not this dynamic object is considered friendly I'm also going to include a type of string to name the dynamic object and this is going to be very important for differentiating between dynamic objects of the same class later on and it also becomes a game design element because it will be easier for the people writing the script for the game to work with objects that have sensible names and we've done that for the sprites already string is declared yet but we'll come back to that in a minute now I'm going to add some base methods and these are going to be overridden so we're going to exploit inheritance and polymorphism a lot in order to facilitate the different behaviors of the object so we'll start by creating a virtual function and this means it is our intention that we're going to allow this function to be overridden and this one is going to be called draw self so the object is going to be responsible for deciding how it looks on the screen and so to do that I need to pass to it a pointer to an instance of the OLC console game engine and if you remember from the first video we modified it slightly to be object orientated in its own right so I'm going to just call that pointer graphics we'll need to include the console game engine at the top of our class of course now the console game engine does some naughty things one of the things it also does is include string and sets the namespace I'm sure you'll have plenty to say about that in the comments one thing that's also important for the object to draw itself is it needs to know what the current global offset is of the screen ie the camera position and this is because the object will only know where it is relative to the world coordinates it wrote know where it is relative to the screen coordinate so we need to provide this offset now this function for the base class doesn't need a body I could set it to be pure which would make this class abstract but in this case I'm just going to leave it with a blank pair of braces the second function I'm going to add again is going to be a virtual function and it's going to be update and this is where the object can update itself using the F elapsed time provided by the game engine again by default this is going to do nothing but you might be able to start to see where this is useful let's assume this dynamic object later down the line is a bat during this update function the bat will want to fly around and this will this will allow us a facility to do this I'm going to allow the dynamic object to be named a various constructor so I'll just put in those parameters so for our base class see dynamic the dynamic object or we've gotta the two methods left to fill in one is the constructor and one is the destructor right now the destructor doesn't need to do anything but the constructor does need to specify what the name is and we'll also take advantage of this opportunity just to initialize all of our variables now I'm going to assume that all dynamic objects to begin with are solid versus the map and I'm also going to assume this solid versus each other it is likely that most of the dynamic objects created are going to be things that we don't want to walk through the scenery and I'll also assume everything is friendly what a great place what you may have noticed is nowhere in this class have we provided a facility to know what the dynamic object looks like at this level of abstraction that's unimportant so let's start using the dynamic object system for the player character I'm going to inherit now a new class called C dynamic creature and I want to inherit publicly from our base class and what this means is anything that's declared public in here will remain public as usual created a new class we're going to have to create a constructor for it and this time I'm going to pass in the name but I'm also going to allow us to start using the assets so for drawing this particular creature we need to give it a sprite pack so it knows how to draw itself we also need to provide implementations for the draw self and update routines so I'm going to copy those from up here put them in down here get rid of the virtual keyword you could leave it though it doesn't make any difference really get rid of these braces but I'm going to use the override keywords so it's clear that we've overridden them but because this is an inherited class we can give it some variables of its own so I'm going to create some that are protected in particular I want to store a pointer to the sprite that we're passing and I also want some additional properties so we can assess the state of the creature and we'll assume because it's a creature that it's alive so this is where we can start thinking about some game variables so we'll have health as an integer and we'll also have max health so consider if this was the player character this would represent how much health they've got left and this represents how much health they can possibly have so if they use some item that restores health we know that there is a maximum it can be restored to of course we'll also have to give bodies now to the new constructor so here it is dynamic creature constructor is taking the name and the sprite and I'm going to use this notation to say but also please call your base constructor which will set the name for us and default the variables it's the only thing we need to record now is the sprite variable and we're going to set a default health and max health value let's say we'll have 10 to begin with Animax health of 10 - so the creature has full health at the start later on we probably want to overwrite these with different values for different creatures we also need to provide the update function and the draw self function and I'm just going to use a line of comments here so we can see that there's some separation this is why it might be useful to do things in different files so here I've got the base class and here I've got the derived class dynamic creature and this is where we can start to have a little bit of fun I have decided in advance of a format and layout of a sprite sheet for all creatures and it looks like this hey we've got individual sprites which is 16 by 16 pixels and I'm using this sprite viewer that the community created we can look at them individually but we'll see the first column is the sprite walking downwards you see the animation in the top right of the image the second one is the sprite walking towards the west and we've got north and then we've got East we've got some additional ones this is the sprite celebrating and this is the sprite dead now the artist that created this sprite pack has also included two more which is just standing facing and standing facing all way for this series we won't be using these two but what is important is that all of our creatures in the game follow the same sprite format layout because we're going to let the dynamic creature class handle which sprites need to be drawn given on the status of the creature so for example in this case we've got what we're calling said it slimes created by ER said it on the discord server it's the same thing so we've got walking towards the player walking towards the west walking away from this player walking towards the east and we've got a celebration and a dead slime and additionally again a skeleton character this time exactly the same states of animation in the same locations and this is quite important knowing that all creatures are going to be drawn with the same sprite layout means we can use some tricks so I'm going to use an enumeration here to store which direction the player character is facing and we've got to hard-code these in as a zero one two and eats east is three and i'm going to store this as facing direction now if you haven't seen this notation before it's really I've just done an enumeration in a single line and I've basically at the same time declared to this M and facing Direction variable as having the type of this enumeration so this means we can apply instead of zero one two and three we can apply southwest north and east as the facing direction and the compiler will be completely fine with that I'm going to have exactly the same four other states as a standing walking celebrating or dead I'm going to store that as graphic state so if you remember south west north and east at tie-up with which column of the sprite sheet we're going to be using and depending on what the dynamic creature is doing we'll choose whether it's standing walking celebrating or dead in the constructor I'll just default these so I'm going to say the facing direction is South and by default it's doing nothing at all it's just standing there just going back to the sprite editor again I can see a walking animation well it's probably about a fifth of a second between me pressing the opened quis so this means the dynamic creature is going to need to keep track of time to know which particular frame of animation to play so let's add that in I'm going to create a floating point variable which will just accumulate time and of course I'll also need to add that to the constructor set it to zero and this is where we'll start looking at the update function so every point two of a second I want the sprite displayed to change now fortunately the game engine provides us with how much time has elapsed so we just add that to our F timer variable and when the F timer variable gets beyond point two of a second I'm going to subtract point 2 of a second from it so this means we get a consistent period of time irrespective of what the rest of the system is doing and we'll come back to that in just a little while but let's set some of the graphic states that are fairly obvious so if the dynamic creatures health is less than or equal to zero we're going to assume it's dead that's the sprite that we want to display if the creature has any velocity components at all it's moving so we'll set it to the walking state so here I'm just looking at the absolute values of the VX and V Y components of course if it's not walking it's just standing the final state celebrating that will probably look at in the next video when we start to add some of the finishing touches to the dynamic object classes we also need to determine which direction the player is facing and we can do that based on the velocity component so if the negative is facing west or north and if they're positive its facing east or south now the draw self function for the creature needs to choose the sprite that is suitable for the state of the creature at this point in time so I'm going to create two variables sheet offset X and sheet offset Y which are indexed into this sprite location so in the top left here that sir sheet offset 0 and we go all the way over to celebrating that's a sheet offset for and of course in the x-direction and if we've got a one sheet offset in the y direction we've moved down to the next row of sprites so I'll use a switch block to determine based on the state how we set these variables if the creature is simply standing still the only property that matters is the facing direction variable on the contrary if the player is dead then we want to specifically choose a strike location to this play because there's only one option walking on the other hand is a little bit tricky because we've to to choose from depending on the state of MF timer so this implies we need an additional variable which I'm going to add an integer called MN graphic counter again in the constructor I've defaulted this to 0 I'm not going to show this each time from now on I'm going to assume that you understand that it's important to give your variables a default value in the constructor and essentially this variable that we've added is going to oscillate based on the time and we're going to use this variable to select which row of the spritesheet do we need to display to the player so every time we get this event on every sort of 0.2 of a second I'm going to increase the counter by 1 and I'm going to make sure it doesn't go out of control by modeling its own value with 2 so this will oscillate between 0 1 0 1 0 1 forever which means we've got two variables to choose our walking sprite from in the x-direction it's the direction the object is facing and in the y-direction it's that oscillating counter graphic counter you can see I've got x 16s in here because this is going to give us the pixel coordinates in the sprite sheet all that's left in the draw cell function is to actually get the creature to draw itself and because we've passed in a pointer to the console game engine we can use the draw partial sprite function to do just that but you'll see I've also included the offset variables and that's because the dynamic object exists in world space but we need to draw it in to screen space unfortunately there's a one-to-one translation because all of our cells were one unit by one unit we just need to take into account where the camera is looking in world space and the sheet offset X and offset Y variables will give us the top-left of a particular tile in a sprite sheet and we know it's always a 16 by 16 of course it's undesirable to have 16 hard-coded into the game engine you'll see it everywhere at the moment if we do want to move to higher resolution or indeed lower resolution in the future we should probably make this a constant somewhere maybe even in our RPG assets class for now though I'm going to hard-code it in because to me it helps me visualize that this offset is one full unit of pixels in our game space so let's just have a little recap of all of that we've got a spike which is carefully laid-out to be usable by the game engine if we don't obey the rules of the spritesheet none of this code will make any sense in the update function we're determining what state the dynamic creature is in at its most basic is it walking is it standing or is it dead and in the draw self function we use that state information to decide which sprite of the sprite sheet to extract in order to draw on the screen let's now go back to our main file and include the RPG dynamics files we've just created and instead of hard-coding in a dynamic object like this we're going to use our new framework but I'm not going to create a creature outright what I'm going to create is just AC dynamic and I'm going to call it MP player for now so it's a pointer to a dynamic object that's going to represent the player just for those thinking ahead we will be changing this a little bit later on I'm going to use a polymorphism now to take our C dynamic player object and from that create a new dynamic creature object which needs a name and we'll call this one player and it also needs a sprite so we'll use our singleton that's managing the assets get sprite and there's a sprite also called player with a friendly name and you'll see the compiler has no problem with this even though MP player is of type C dynamic we're basically telling it actually you have type C dynamic creature this of course now gives us lots of things we need to change in order to facilitate moving the player around the screen we're no longer handling these things directly so ever we saw position and velocity before we now need to make those relative to our new P player object so in this case it's going up its V Y and down V Y and left and right use X instead also start to see some leftover junk from the derry-o platformer we're no longer choosing the derry-o sprite based on anything that's going on here the object itself will choose its own right so we can start to clean this up a little bit and I'm also going to set this opportunity to start making this a lot more generic so where we've got variables that are specifically indicated as player position X and all they're just going to be new object position X&Y instead so for example in this case I'm going to change it to new object position X and instead of using so that the player here I'm going to just simply use object and choose the px value I'm going to create a temporary dynamic object or a pointer to one which points to RP player type and the reason for this is later on I'm going to have a big collection of dynamic objects and I want to perform the same code right now I'm hard coding it for the player so we can test all of our animation code I'm going to go now through all of the code and make these changes right I always by default want the camera to follow the player object so here we will use it explicitly and this is where we're going to always need access to a variable that represents the player object so even though the player is bundled in with a bunch of dynamic objects it's always useful that the player has some sort of priority access so here I'm going to set the camera to its position in the x-direction and here I'm going to set it to the y-direction so I've just gone through and updated all of the static collision code to handle a generic object although in this case the generic object is hard-coded to be the player the only thing left to change then is how it draws itself well we've already drawn the map so once we've drawn the map we then want to draw the object and instead of just drawing the player it's going to be drawing all of the dynamic objects later on so we'll try to keep this as generic as possible instead of drawing the object using the draw partial sprite routine we'll get the object to draw itself using its draw self function and so we just need to pass a pointer to the console game engine so I'll use the this keyword for that and we need to pass in the camera offsets which is much tidier and this is where we'll start to see some of the power of object-oriented programming is we've really quite simplified this now the object hides the nasty code with full of constants and things to make things appear on the screen and this same code will be for all of the objects that are considered dynamic when we created our player the constructor of the dynamic creature just specified zero zero as the location I'm going to card code this to a more sensible number so the player is in the map so I'll pick a number let's say is at location five five right so that's been a big lot of code let's say let's see how it goes so we can see the player character is in the map and we're moving around like Jerry OH what we don't see is any change in sprite or we've done is successfully replaced Jerry Oh for witty that's the name of this guy and that's because at no point have we called the update function on the player character so at some point we're going to need to call the update function for the dynamic object I'm going to do that before we specify any camera control code it feels like the right place it's just calling the update function with the efi elapsed time so let's take a look now well good the character is starting to walk in the right direction he's facing the right way at least and he is indeed walking but it's very fast and this is where we're gonna have to start thinking about some of the game design elements he's clearly moving too fast to his walking animation to make much sense so we were specifying the velocity 210 I'm going to set it to 4 and that's much more sensible very nice now we've got maps and we've got at least one moving objects we can start to have a little bit of fun let's start thinking about cutscenes let's start by making an assumption that the cutscene only controls the player object given that that's all we've got so far so we've got a dynamic object which represents our player but let's say we want to move the player to a certain location present some dialogue move to another location present some more dialogue wait for a minute do something there's a sequence of events that we can perform in order to facilitate plot and give the game some atmosphere so we might want the character to be able to walk to a different location so we know that that is some sort of command that we're going to need we may want the characters has simply just wait so let's add some parameters to these two so if we're going to walk to we know that we're going to have an X and a Y if we're going to wait we know we're going to have some duration in seconds let's say we want the character to say something well we probably want to show some dialogue there'll be some sort of text what we need is a program within a program and the way I'm going to do this is to create objects which represent commands and a queue of these commands is sent to what I'm calling the script processor and whenever the script processor has something to do it stops player input and surprise surprise will to use object-oriented programming to handle all of this for us so I'm going to need a basic class called command and commands are going to be temporarily in control of something until the command has been completed so I'm going to create a flag completed and I'll add two methods this command start which starts the process of the command happening and as usual we'll have update in case the command takes a certain amount of time we need that time to elapse so this will also then take F elapsed time to implement a cut sequence will queue up a sequence of commands this Q is fed into the script processor and the script processor will then go and control the game engine so if this cue is empty the script process has nothing to do it's not in control of the game engine in which case presumably the player is in control of what's happening so the script processor will also stop player input and for all of the different commands we want we're going to subclass C command for the individual implementations so for example a weight command we might want it to count the F elapsed time before we can set it as being completed for the move command we'll probably want to pass in a dynamic object for the command to take control off whilst it moves it to the location it needs to be and once it's reached that location its completed so each time the script processor determines that a command has been completed it removes it from the queue and the next command gets put in place once all of the commands are finished and the queue is empty control is given to the player again I appreciate with my simple scribbles on the screen this is quite a complicated idea to express but the code itself is quite simple so let's do that as before I've gone ahead and added RPG commands H and RPG commands dot CPP here is our basic command class it does nothing of any function all it's doing is defining the interfaces which subclasses of this will use to implement what the commands should be doing let's create another class our script processor which implements the theatrics and before we get to adding any interface functions we know that the script processor has a queue of command and I'm going to use a standard list to implement this we also know that the script processor is going to govern whether or not the user the player has any control so we'll have a very accessible flag to deny the player control if necessary I'm going to add two interface functions the first is add command which is simply going to add an instance of a command to the list and the second is process commands because commands are a temporal thing they're not just a single event it takes time for a command to walk a dynamic object from one location to another so a command doesn't just happen instantly and it's finished it takes a duration so let's add the simple ones first we've got a constructor for our script processor and we've got add command which is just going to push the pointer to a command object to the back of the list of commands the important one is the process commands function we're going to call process commands every single frame that's why we're passing elapsed time with it because some commands are going to require that duration this means we can also choose whether or not to enable player control on a frame-by-frame basis and we'll do this simply by checking is the command list empty if it is empty then the player has control there's no commands to interfere with that if there's anything in the list will deny the play control because we're going to assume some sequence of events is taking place that hasn't finished yet so if the list does contain something we need to do something if it's not empty let's check to see if the command at the front of the list has been completed yet if it's not been completed then whatever is at the front we want to call the start function on that will cause one off initialization in fact it actually won't because right now this is getting called every single frame and so that means this start function is also getting called every single frame so I'm going to modify the base command class to have a boolean flag which indicates whether the command has been started or not and this is one of those times where it's much easier to work this stuff out in code rather than with a pen and paper to begin with so along with our completed flag I'm also going to have a started flag so because I only want to call this dart function once I'm going to check the started flag before I call it so here we've got if the list is not empty that means a command is available if the command at the front of the list has not been completed check to see if it's been started if it's not been started then started and this will only occur once so this becomes an event if the command has been started and it's not completed it's a command currently in process so we want to call the update function instead if in the situation that the command at the front has been completed we want to delete the command because we're going to pass it in as a pointer and we're going to remove it from the front of the list and right now this is all our script processor needs to do it just maintains access to this list and calls the start function or the update function as necessary by the sequence so let's go and create our first command sub class and I'm going to call it move two which is going to walk a dynamic object from its current location to a target location and so I'll create a constructor for this which takes in that information so the first thing we do is take a pointer to the dynamic object and remember this can be a creature it can be anything at all because as long as it inherits from the base class all we need to do is pass that pointer in the compiler will sort the rest out for us I'm going to take a target X&Y location in world space and I'm also going to say how long should it take for that operation to occur so if we want the dynamic object to move quickly we'll set a short duration if we want it to walk there slowly we'll set a long duration we're also going to override the start and update methods of our base class now what's important to note here is that the class itself is going to take control of the object so we need a whole host of private variables for this one command to facilitate a linear interpolation from the start location to the target location so let's add some private variables the first is going to be the dynamic object itself we're also going to take the starting position X and Y we're going to set the target position x and y and we're also going to store the requested duration and the time passed so far because we're going to interpolate between the starting position the end position by calculating the time so far over the duration you'll notice we've got some squiggly red lines under the dynamics so we need to include RPG dynamics in our file let's go and add some body to this so for our command move to the constructor is going to take the target position and set the time so far to zero for the duration and just to make sure that the user doesn't enter in appropriate durations I'm going to take the maximum between the duration specified and 0.001 of a second ie one millisecond and this is important to avoid the divide by zero error later on I also of course need to store a pointer to the object that's being controlled now why haven't I updated the start positions here well simply put I don't know where the object will be when this command is called that's why we have the start function if I've got a sequence of movements I need to make sure that one movement is finished before the next so the object's location will have changed if I just specified the object's starting location as soon as I created the list of commands well it will have captured the object's position at that time I created the list which could be different to when this command is called during the execution of that list so the start command happens once and says take a snapshot of where the object is now and this could be at a point in the future and so it is in the update function that we implement the linear interpolation and this is going to be a standard linear interpolation I've done videos on linear interpolation before I'm not going to go into the details essentially we take an accumulation of the current time divided by the overall duration that gives us a normalized value and we use that normalized value in effect to choose a point on a line between the two points which I'm going to do here I'm also going to specify the velocities which is simply the amount of distance expected to be covered over that duration because speed equals distance over time once the expected duration has expired we make sure that the object is precisely where it should be because the illinit linear interpolation may be somewhere really really close but it might not be precisely so I'm going to set it exactly I'm going to set the velocities to zero and I'm going to set the completed flag to true this command has now finished and so on the next process commands update the scripts processor will identify that this flag is true reject this command and move on to the next one if there is one so let's try this out by hacking something into our main file firstly let's include at the commands file and I'm going to create a single instance of our script processor called M script to test the theory I'm going to hard code a little script in place whenever the user presses the Z key so we'll look for that event so if the Zed key is released we're going to add some commands to the script processes Q so I'll take M script add command now our command is the base class command but of course we can use inheritance and polymorphism to actually specify the command that we want adding to this q and this is where polymorphism can be really strong because all of these commands are going to be different objects but this q is quite happy to contain them all and because we've got a common interface between these different objects they can all have different behaviors now we know it takes a pointer to a command so I'm going to have to use the new keyword because we know that the script processor also deletes and cleans up the commands once it's done with them so in this case it's a move to command and the object that we're going to move is the player and for the first command I'm going to move the player to 1010 and I want that to take three seconds once it's moved there I'm going to move it to or move it fly across and keep it on ten and then I'm going to move it from 15 to 15 and we'll move it back to the start location so it doesn't really matter what it's doing it's just going to take control and move the player around for me script processing has to take priority over anything else it's going to decide whether the player has input or not so that's going to be the first thing I'm going to do in on user update and the reason we want this at the start is because the current command might disable user input so we'll check for that if it's allowing user control then we can allow the checks for the different keyboard commands let's see how this works so now I've got control over the player you can see I'm walking him around and if I press the Z key the engine is now in control I don't have any further control I can press the keys it doesn't matter but we can see it's executing the script so the character is walking out a small triangle and now I've got control again so if I press Z key the first thing the care to do is move to 10/10 in three seconds then it was 15 10 then it was 15 15 and then it goes back to 10 10 again and the nice thing is all of the walking animation and everything else is all being handled for us so even though we've added a completely different method of control the game engine itself is sorting out what's necessary behind the scenes to facilitate the type of animations we want ok so we have a single command move - and we have a single dynamic object creature let's make the code handle multiple dynamic objects I'm going to store my dynamic objects in a vector and in the on user create function I'm going to create a few more so I'm going to create a couple and we'll just call them Skelly 1 and Skelly 2 and they're not going to use the player sprite we want them to look a bit different so let's have a look in our assets file what have we got well we've got one called Skelly how convenient so let's give it the Skelly asset and I want to manually for now position these somewhere in the scene so I'll set one up being at 12:12 and another one being at five comma eight of course these aren't called mplayer anymore there's something else in fact we can temporarily create them here as AC dynamic op one and C dynamic AB two and what I'm going to do is add these to the vector and I'm going to start a convention that the player object is always element zero in this vector so I'm going to push back the player first and then I'll do the other two and this is a little design convention which we'll see is quite useful later when we're starting to implement a quest system and handling maps full of different types of dynamic objects if we always know where the player is then that can make things a lot easier later on so of course we need to handle things a little bit differently now we need to put all of our update code for objects in a loop and we'll use an auto for loop for this where each object is going to be called object and that's going to go through our vector of dynamic objects I'm using the ampersand at the start because it's chances are I'm going to be changing the properties of the object in this loop so I can get rid of that now let's follow this down to here so all of our objects get subjected to the same static collisions with the maps for now I also want all of the objects to draw themselves so I'm going to copy that and use it again down here so let's take a look to see if object-oriented programming is helping us already we could see yes it is I've got two objects on the scene now two skeleton enemies and what we could see is because the player is always the first object in the list the player is drawn behind everything this may be undesirable I always really want the player to be drawn on top of everything so the so the human player can always see the player character I'm going to fake this and this will cause some concern amongst some of you simply by drawing the player object directly as the last thing that gets drawn this may seem a little bit wasteful because we're already drawing the player once we could adjust this loop to ignore the first element of the vector for what we're starting to see form out of this is the game engine is taking shape it doesn't really matter what the objects are they'll look after themselves because they've been encapsulated in other objects the game engine just treats them all in the same way so let's add a command to move one of these other objects let's take our one so we know it's already at position 12:12 to start so in between our character walking around let's add another move command now it's a four object one and we know we don't know that it's going to be called object one so we'll use the element at index location one of our vector and we'll move it from 1212 - I don't know we'll say 1512 and we'll say two seconds for that move now you might be thinking this seems a little bit of an uncomfortable way to choose which objects we operate on you'll see later on that we can use the friendly names to decide which objects are moved around the screen making a little bit easier for the game designer so if I press the Z key again it starts our automated sequence player characters going to 10 10 then 10 15 and then object 1 has moved as well and what's really nice is because we've got an established ways to handle animation when the object moved it have the appropriate animation applied to it we've had to do no additional code even though it's a different type of object so let's wrap this video up by adding one more command and that's going to be to display dialogue on the screen and I'm not going to line by line it but I'll explain how it works so we'll create a subclass of command called show dialogue and this is going to display text to the user and the way it's going to work is each line of text is going to be specified as an element of a vector and you'll notice we're only specifying the start function to be overridden let's have a look at the body for this well the constructor just takes a local copy of that vector but the start function needs to do something a bit special because it's the game engine responsible for drawing things and this is where we're going to need to start having some communication between the commands and the game engine when we moved the objects that was a communication between the commands and the object itself in the game engine file I'm going to create a function which is for displaying dialogue show dialog and it takes again a vector of lines but we also need some additional properties to handle the showing of dialogue we're going to store the dialogue that's being shown and I'm going to create a boolean flag that says whether there is dialogue on the screen at the moment and this is quite important because dialogue is one of the only commands that we don't have any computer control over the human player may take longer to read the dialogue than you might expect and we don't want to specify a fixed duration that it's on the screen for so instead what we're going to do is show dialog until the user presses the action button which in this case is going to be the spacebar of course one option could be just to freeze everything until the dialogue has gone away but then if there are any background animations taking place they would also be frozen to so it's important that we somehow cache the dialogue information and respond to it in a real-time way so all our show dialogue function is going to do is take a snapshot of the dialogue to show and set this flag that we are indeed showing dialogue on the screen now I don't want to just render empty text to the screen I want it to look like it does in an RPG game so I'm going to create another function called display dialogue which is going to handle drawing the dialogue in a pretty way and this is probably going to be the first instance of where I'm just going to be putting code into this game engine but not talking through it in detail mostly because it's not very interesting so really all this function is going to do is draw a background rectangle of a size appropriate for the dialogue that's been given to it now because each line of dialogue is a separate element in the vector that's passed to it I can work out how many lines of dialogue are displayed on the screen by looking at the size of the vector I can then iterate through each element of the vector to work out what's the longest line of text so that gives me an x and y dimension for my box the box is going to be a blue rectangle with a white border and that's what all of this code does but it draws the white border sort of one pixel offset around and it knows the size of the font that we're using in order to draw the box to the correct dimension then it's just simply a case of using our already created draw big text function from video 1 to put the text inside that dialogue box I don't feel at this stage in the video series that it's necessary for me to go line by line talking through all of these little calculations and it's this sort of detail I'm going to be omitting going forward but I will encourage you to study the source code once I put it up at the end of this video series so dialogue in some respect is quite unique that it requires user interaction and we also don't want it to be drawn on top off so the last thing that we display if there is any to show is we display some dialogue so I'm going to call our display dialogue function with the vector that's currently caching the dialogue to be displayed now because the dialogue displays a little bit of an oddity we know that the scripting engine has denied us user control so we need to slightly break this so even though the script engine is in control there is one event that we want to look out for and this event only applies if show dialogue is true because we're showing some dialogue on the screen and we're going to respond to the spacebar being released and when the spacebar is pressed we no longer want to show any dialogue on the screen simply no longer showing the dialogue is not enough I need to inform the scripting engine that this command has been completed by the user and to do that I need to artificially tell it that its current command has been completed which means I need to add another function so the scripting engine complete command let's have a look at the script processor class and we can add in another one complete command let's go and give it a body and the complete command function will allow us to prematurely finish the command that's currently in operation so if there is anything in the list whatever is at the front of it we're going to say your completed whether it has or it hasn't it allows us to externally stop the command that's in action and this might be useful for not just dialogue but perhaps maybe you've got cut sequences where things are dragging on or whatever we can get the player to say I've had enough of this you can press the spacebar and the next command starts to execute so going back to the actual show dialogue command itself when the start function is called on that and by the scripting engine we need to call the show dialogue function of the game engine and to do this I'm going to use a static variable in the command base class which makes the game engine available to all of the commands so let's see how we implement that so here is our base command and you can see it has no functionality but I'm going to add to it a static verbal which is a pointer to our main game engine at the moment that's called one lone coda RPG now the problem is I can't just go and include one lone coda RPG because then we've got one lone coda RPG including dynamics including one lone coda RPG including dynamics etc you start to get a bit of a problem what I'm going to do is a forward Lee declare the game engine itself and all I need to do is specify class 1 along coda RPG and that tells the compiler that this symbol exists before it's reached by this line you can see it's quite happy to accept that however it doesn't exist really all we've done is shut the compiler up we do actually need to give this some sort of implementation let's go into our CPP file we know these are only compiled once and right at the top I'm going to include in here the actual definition of this variable and it's a pointer and I'm just going to initialize it to null pointer to begin with again the compiler is happy with this but it wrote me because there's still no body what we can do here now is actually include our original file and because we've included our PG main dot CPP up here what was originally just an empty symbol has now been given a bit of a body and so when we go back down to look at the subclass commands show dialog we can see that the G engine variable is now populated with all of the public functions that our game engine possesses meaning that this command can now access some of the utility from the game engine in this case to show the dialogue however at some point we need to initialize this variable to actually point to the game engine and we'll do that at the start of on user create so here's on user create we're already creating our singleton and doing static things like that so this seems like a good place to all define the G engine variable so we know it's in our base command G engine and we know it's a pointer but it's a pointer to this now I don't want to set a precedent here of creating what's called spaghetti code where we've got all of these arbitrary links between classes that is a bad design pattern and we won't see this really again now as part of the game engine but what we have allowed is all of our commands access to all of the utilities in the game engine very simply so let's go back to our scripting sequence here where we've just moved the skeleton object let's add in a new command and this command is going to be show dialog and remember it takes a vector as the input so I'm going to have it's a skeleton it might say something like go for the first line and let's do let's do a dual line one this time around I think OOP is really useful there we go let's make it appropriate for the moment so our script will now move the player then move one of the enemy objects display some dialogue on the screen and then move the player again let's take a look so here I've got control over the player I'm going to start the scripting sequence to the player moves to the starting location it's executing his first command now we're moving the enemy object the enemy objects is called the command show dialogue and we'll see we've got no user control but everything is stopped there's no more sequencing going on until I press the spacebar and we can go through the dialogue and then the sequence carries on as normal and I'm back in control so one final thing we can see when we're creating scripts we're getting a lot of code which is very similar and we don't want to have to type it out over and over again in this instance I would suggest using a macro so at the top of this file I'm going to declare a macro simply called X and it takes an argument and if you didn't know yes you can pass arguments to macros and we know that in this case the code looks like this M script dot add command new C command and then there's an underscore but then we want to use some macro syntax to concatenate the string that is n to that command wrap the whole thing up in a bracket and we're done so we go back down here and what I can do now is get rid of all of this and simply replace it with X and open brackets and you see it's quite happy ler we've created our own little scripting language inside of another program and that's all for this episode in the next episode we'll look at how we can combine scripts and sequences with quests and how we can get everything to glue together to start forming the basics of an RPG if you've enjoyed this video please give me a big thumbs up have a think about subscribing and I'll see you next time take care
Info
Channel: javidx9
Views: 42,873
Rating: undefined out of 5
Keywords: one lone coder, onelonecoder, learning, programming, tutorial, c++, beginner, olcconsolegameengine, command prompt, ascii, game, game engine, role playing game, rpg, code it yourself, code-it-yourself, object oriented programming, oop, dynamic objects, theatre, scripting, scripting language
Id: AWY_ITpldRk
Channel Id: undefined
Length: 55min 28sec (3328 seconds)
Published: Sat Mar 31 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.