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

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello and welcome to part three of my code at yourselff role-playing game series if you've not seen the previous episodes of this series then I suggest you go and watch them because I'm just going to carry on directly from where I left off and indeed where had we left off well we'd implemented some dynamic objects and we'd implemented the ability to script sequences and watch them play out on the screen so here the character is now under control of the scripting engine and we can see we've got other objects also in control of the scripting engine and we've also got some dialogue being displayed um one of the things that we got from the last video is that by using object-oriented programming that adding new objects to the game was very simple and they behaved appropriately without any additional coding from me and so ultimately we've got the very basic scaffolding for a role-playing game and this video is going to be about how do we start bringing it together to implement the game so we are going to be adding a few more dynamic objects and there's quite a lot of those so I'm going to add them as and when we need them and we're all going to look at the ability to change from one location to another and hopefully by the end of the video we'll have a very simple system for the implementation of quests but before we get started I want to address an oddity of the last video which was this line here and I did apologize for it in the video I needed to include it in order to make the video work on the limited time budget that I had to film it and this is a rather unusual statement so some of you may not have seen the inclusion of a CPP file before but it works at the following way firstly you can include any file here all include does is cut and paste text into that location in the compilation stream and in this instance we needed to include it to get access to this token here and fulfill it with some implementation and unusually because of the way I do things that we'd created a RPG main which included our override class of the console game engine let me just find it here in a way similar to how my other videos work I tend to do everything in one file and so it looks a bit odd and when we're working with object-oriented programming in C++ it is a little bit bad form to do everything in a single file so I'm going to start this video by quickly whizzing through all of this and extracting it into a header file and a CPP file I'm going to call those engine dot H and PG engine CPP and we'll launch the game from our main file appropriately using the engine so one moment please whilst I whiz through this at high speed you perfect I've now encapsulated everything that we've created in a class called RPG engine and given it the dot H and dot CPP files the functional code hasn't changed at all so let's start by implementing the process that allows us to control the main character witty from one map to another when he goes through this door we wanted to load the inside of this building knowing that I'm going to be using more than one map I want to store maps as an asset on the whole maps won't really change so I want to make sure I've only ever got one instance of them in which the same way that we handled sprites so I'm going to modify the RPG assets to also handle the loading of maps maps function in exactly the same way we handle the sprites I'm going to handle the maps I'm going to create a map of maps I'm also going to add a getter function for this too so we can get a map by passing along a string name to identify which map are we interested in returning in a very similar fashion to sprites we're going to need to implement a load Maps method too so I'm going to create a little Auto lambda function here to automatically load the map and name it for me but I need to tell my singleton what is a map so I'm going to include that up here let's just remind ourselves what the map class did well it simply stored information that represented the tiles and information that represented whether a tile was solid or not and it created itself it was a self-contained fully encapsulated object and if we look at the implementation of maps we can see that for anything that derives from the C map class we override the constructor and that's where we populate information about what makes that map unique and also if we remember from I think the first video we created to village maps well I'm not going to create village - I'm going to create that as being home 1 and village 1 and the idea being that we can enter the building that exists within the home one map so in the constructor we call the create function which will load the home dot level data created by the external map editor I don't want to use the village sprite sheet for this time guys because have a special one called hi-tech it's kind of like a laboratory and I'm going to call this map home going back to our load Maps function in the ass class we just now want to call load and pass in a newly created instance of each map that we're going to use so we've got a map village one and home one in the main engines on user create function we specified the map directly we set a verbal MP current map to that map now instead of doing that we're going to grab it from the assets just to make sure this works and we're going to use the singleton directly RPG now sits let's get an instance of it and we'll call the get map function and we want the first map to be coder town we'll have to tell the singleton to also load the Maps so let's just see if that works perfect it's loaded our map from the singleton we need a way to transition from one map to the other and the easiest way to do this within our current framework is to create a dynamic object which teleports the player from one Maps position to another map and position so let's take a map like I've drawn here which is an empty map except it's got a building in it and we've specified some of these cells are solid so the player can't walk through them I'm highlighting those in red what we won't do is specify this cell that contains a door to be solid because we want the player to be able to step on top of that door what we will do instead is specify that here we have a dynamic object and we'll call this one a teleport and so if the player interacts with this teleport dynamic object in any way either by talking to it are using the action button or by stepping over it we're going to have an on interact function for this dynamic object and that will allow the dynamic object to then go and coordinate the game engine to load the map that's required and relocate the player there so along with this dynamic object for the teleport we want to store a map address or name which represents which map are we teleporting to and we also want some coordinates that represent the location so let's just take a quick look at a secondary map so let's say we've got some solid blocks here to slightly a zoomed out scale we leave some here now this represents the doorway of the inside of that building so in a similar sense we want a dynamic object that represents a teleport here so when the player interacts with this dynamic object the player is going to be teleported to this location and when the player interacts with this dynamic object the player is going to be teleported to this location and this way we can transition between maps but this is also now introduced a new feature that we haven't covered yet in a previous video and that is this ability to interact with things in the game now because this is a big project I'm not going to type out all of the code line by line so instead I'm going to cut and paste and try and describe the bits that are relevant I hope you'll forgive me in this sense just too much code to start doing this and we'll be creating videos until Christmas and I really want to wrap this up in in one more video after this so here's the teleport dynamic object it inherits from dynamic and it overrides the draw self and update functions that we created in the previous video the constructor of the teleporter takes the position of where this teleporter exists in the current map it takes a string that represents the map to teleport to and then it takes a target X&Y which is the coordinates of where to place the player once the teleportation has happened and of course all we're going to store in this class are those coordinates where the X and y of the target map and the map name let's go and implement the body of this now so the constructor all that's going to do is cache a local copy of the data for that object and because we want the teleporter to be able to be walked on top of we make it solid against nothing so the player which is a dynamic object himself can actually walk over this object dynamic objects are also responsible for drawing themselves so in this case we don't really want to see anything but I'm going to leave it in that it draws a circle and so we can see that we've put the object in the right place when we come to release the game will comment this out and the teleporter object does nothing on its update function it's just a static thing that fires an event so we don't need to give it any behavior but how do we associate now the teleporter with the map we can see in our own user Creed function we've been doing this by hand and we can no longer do this anymore it's grown beyond this in the initial debugging stage I think it's now time that we allow the maps to assemble their own collection of dynamic objects so I'm going to add to the main map class an additional function called populate dynamics and that takes a reference to our vector of dynamic objects that the game engine has and of course I'm going to need to override that for both our village and our home derived classes and this function means that whenever we load a new map we can add the dynamic objects that belong to that map to the main dynamic objects container for the engine to process so let's just move home out of the way for now and focus on village 1 the fact that it returned something right now he's a bit irrelevant so I'm just going to make that return true by default but what I do want to add to this vector is the teleporter so I take the vector that's passed in and I push to it a new set of dynamic objects and in this case it's the teleport object I'm setting it to be position 12 and 6 and I want the target map to be called home which will have noticed is the name of our other map and the location within that map I want to go to is 5 and 12 we can work out the coordinate where we want to place the teleporter by using the map editor and we can see here it's 12 and 6 so this is the location we're going to put our new dynamic object likewise going back to the home I'm going to add to teleporter objects they're next to each other so the locations on the map are 5 by 13 and 4 by 13 so they're next to each other in the x-axis but they both teleports to coder town which was the name of the original village and they teleport to the same location looking back at our own user crate function again we can see that where we were hand crafting this construction before we're now going to leave that to the maps so it's time to remove all of this and create a change to map function so in the header file for the game engine I'm going to add the function change map which will load up a new map and position the player at the required location but there's a process involved when changing maps the first thing we'll want to do when we change your maps is destroy all of the dynamic objects in our vector of dynamic objects so I'm going to clear it and we'll improve this later by deleting the objects that we've created on the heap but since we've just cleared the vector I also want to add the player back to it because remember the player is always going to be the first object in our vector of dynamic objects the name of the map has been passed into the function so I'm going to use the name as an index into our asset singleton to get the new map and we'll set our current map variable accordingly I then want to update the players location to wherever was requested I want then populate the map with the dynamic objects so the current map which is the new map the player has just been teleported to I'm going to call the populate dynamics function which will then append to the vector of dynamic objects our teleporters in this instance back in the on user create function I no longer need to create my two skeletons I'm going to get rid of them for now and I now know that my change to map function is responsible for populating my vector of dynamic objects so I don't even need to do this I still need to create the player as a dynamic object so I'm going to leave that bit in and the current map is also no longer relevant so we'll get rid of that and we'll use our new change map function to initialize everything for us to begin with I want the player to be in coder town and I know looking above that the location is 5 by 5 I can remove this as well let's just see if that does indeed load the map it has and even better we can see it's drawn the teleported object with a circle if you remember we told it to draw itself by drawing a circle were perfect but how do we check if two dynamic objects interact with each other well we need to go back to the core of our physics engine right now it's set up to check whether the objects interfere with the map and if it does it positions them and handles the response accordingly but all of that code only worked for static map collisions I'm going to add some new code now to handle collisions between dynamic objects and we're going to assume that they're also one unit wide by 1 unit high like everything else in the game but the main difference here is that dynamic objects are not always going to be bound to an integer boundary within the game space so in this loop where I'm looping through every dynamic objects I'm going to have a second loop where I'm also going to iterate through every dynamic object on the whole we know there's really not going to be that many dynamic objects within a given scene but the reality is in most cases it's going to be perhaps five or ten dynamic objects per map we want to make sure that the collision isn't being tested between the dynamic objects to be tested and the original dynamic objects there's no point in checking does an object collide with itself once we have determined that a valid collision can occur we're going to use the solid states of the two objects to work out whether we should carry on checking if they're allowed to interact or not so if both of the objects are considered solid against dynamic objects then they must not be allowed to overlap but if either one of the objects is basically transparent to dynamic objects then they can overlap we'll come back to solid versus solid dynamic objects later but for now we want to overlap with our teleporter I don't care about things like enemy creatures and other things in the game overlapping with most of the dynamic objects in fact I really only care what the player is interacting with so I'm going to make sure that the object under test is indeed the player object which we know is the first element of our vector of dynamic objects and given that all of our objects are unitary in width and height I can do a very simple rectangle rectangle collision to check half the indeed overlapped in this instance I'm just taking the coordinates of my base dynamic object and I'm comparing it to the coordinates of the target dynamic object and if this returns true then they have overlapped just throw a quick comment in here so we can keep track of things so the object is a player and it's allowed to interact with things so if the player has overlapped a dynamic object I want it to do something I'm going to first of all check is this something that is map related which means we're going to need a new function in our map class I'm going to add in a function called on interaction which again takes the vector of dynamic objects it also takes in a pointer to a dynamic object which in this case would be the player but it could be something else that's been triggered by the interaction testing and I'm also going to pass in what is the nature of this interaction and to keep the code a little bit readable instead of just specifying a number there I'm going to throw in a little public enum talk or walk and we can use that to differentiate between the player interacting physically with an object or just walking on top of an object this means we also need to implement our own interaction in our sub classes I'm going to start a convention that if there is a successful interaction that this function returns true otherwise it returns false and right now even though we've just added the nature of the interaction it doesn't matter for a teleporter in any way that the player interacts with it I'm keen to do something when we defined the C dynamic teleport subclass we gave it a name we called it teleport which means we can identify what dynamic object was interacted with so in this instance if the targets name is teleport then I want to somehow call the change map function in the main game engine fortunately we already have a process to do this but we need to modify something ever-so-slightly we could script it in as a scripted sequence we only have two commands at the moment command move two and command show dialogue let's create another one which is change map and in this instance it looks very similar to our dynamic teleport function it takes the same properties but if we remember the start function is what we're going to be overriding when this command gets called by the script processor let's give this new command some body so the constructor is just going to store the name and the position where we're going to teleport the player to and the start function the bit that does the business is going to call the main game engine change map function which we created earlier then it's going to mark it as completed so the script processor will reject it so this command will happen almost instantaneously and it's quite useful to have a command like this because you could imagine a cut sequence which takes place over several maps you'll need to change them somehow so going back to our map where we're handling this interaction with the teleport because the teleporter contains its own information about how to perform the teleportation we don't need to look for specific names of teleports it's just a teleport we just whenever it happens we want to add a command to the script processor queue and so the command in this case looks a little bulky because we've got some casting going on in here but you can see we take the global pointer to this script engine and we add to it our new command with the parameters but and because adding a feature like this is a never-ending rabbit hole it makes a very embarrassing video making but we also now need to expose the script processor to our maps so let's make a change to the base map class that allows it to be aware that the script processor exists and in the base map class I'm going to add a static pointer to the script processor I'll need to give that some definition in the maps CPP file I'm just going to remember to set this to public so we can actually access it externally now in much the same way that we set the game engine to be accessible by the commands we need to do the same for the script engine so I'll find our public script variable and we'll set it to the address of our scripting engine there's only one last thing to add before we continue and that is the on interaction function for the home one map derived class which you can see is exactly the same if there is a teleporter it just deals with itself and sorts itself out going back to the main game engine now we can actually implement what happens when the two dynamic objects interact with each other and we want to check the map to see if the map is interested in that interaction so here I've got witty walking around the map we're going to go and interact with the teleporter you might see that the collision detection there is a little bit sticky wait adjust that in a minute but let's put witty into the dough hang on something very strange has happened here well we can see that witties location changed but the map isn't being rendered correctly but two more teleport circles have appeared in fact if I walk back into those we're back into the normal map so it looks like it might be working however it's not loaded the map correctly let's investigate and the problem is here when we entered home one it loaded the level correctly but it couldn't find the sprite high-tech we don't really have any error checking on on this project that's just to keep the code as clear as possible but this is the kind of thing that we might want to actually throw an exception for when the game starts needless to say adding high-tech to our list of low despite its various simple indeed dull let's try again so I'm in control of witty we walk him over to the teleport and we've loaded a new map with a new tileset and what wit see back great and go back and forth as much as we like and we'll try the other teleporter in this room and there we go so we've handled a local map to local map navigation now I just mentioned that the collision detection was getting a bit sticky and that's because we don't handle witi like we handled cheerio cheerio relied on acceleration which meant on a reasonable frame rate the actual amount that cheerio moved per frame was very small and if you remember we didn't quite test a whole cell we would test depending on Dario's origin point was going to say about here we would only test about not 0.9 in this direction and the whole 1.0 in that direction and this made sure that the object that represented Jo was slightly smaller than the gap it was trying to squeeze into and this worked very well but for witty it doesn't work because we don't really use acceleration which means witties movements are a lot more discreet around the map and that's why it takes a bit of effort to find the sweet spot that allows Witte to enter the constrained solid map tiles now because precision platforming isn't the name of the game when it comes to a top-down RPG what I'm going to do is instead of representing a contracted border like this all dynamic objects are going to be represented instead of one by one they're going to be pulled in a little bit so if we say that this was our original dynamic object boundary which is 1.0 and one point no in reality what we're going to be checking for is some inner boundary that is smaller than a unit square so in that case all objects can fit within adjacently spaced solid tiles which means I'm going to have a fixed border in from the object and so I'm going to change our collision detection code to take into account of this border I'm going to hard-code the border I'm going to say no point 1 to begin with and if we test that we can now see that the collision system is a lot more relaxed about allowing Witte to enter single square gaps since we have a populate dynamics function now as part of our maps we don't just have to add boring things like teleporters we can also add enemies which would be common to this region and indeed right now they would respawn each time that the map was loaded we'll come back to persistence in a later video but let's have some fun let's add some skeleton characters that we created in the first video to this map you'll see I haven't created the object yet and that's because in the first video we created a skeleton just by implementing a dynamic creature object directly I think now it's time that for different types of enemies and creatures we want them to have their own objects because we'll be able to script their own behavior then that way let's start with the very basics we'll create the object and we'll give it a constructor I'm going to give our dynamic creature scale the object and implementation for its constructor and this is where we'll take the opportunity to give it a name we'll call it Skelly and we'll also grab a sprite sheet as we did before that represents what this creature should look like we'll also set the health and max health properties and I don't think skeletons are very friendly so we'll set that to false now all of the other functions about how to draw the creature were contained within the base class so we don't need to reprogram those if we go back to the maps where we're populating the dynamics we can see well the map is happy with that - and if we take a look we can see it has randomly placed three skeleton objects within the map they don't do anything yet that's because they're stupid they don't have a brain they have no way of implementing behavior we know we can command them with the script processor to walk around but they don't have any choice but to stand still right now so how do we give these creatures a little bit of a eye well looking at the dynamic base class we know they have an update function so we know there is a facility to actually implement behavior specifically though creatures may have an AI type of behavior most dynamic objects might just change what we look like but for creatures we want them to do something a bit more sophisticated so in the base class Kreacher I'm going to add another function which needs to be overridden by subclasses called behavior which means in our Skelly class we also need to have the function behavior for the base dynamic creature class its behavior does nothing we're not interested but we do want this behavior function to get called and the only thing that we know is called on every single frame is the update class so let's add to our update class at the end a call to behavior and we want to pass in the elapsed time and our player pointer now the eagle-eyed amongst you will have noticed hang on what's this player pointer where is it appeared that wasn't in the first video and you're quite right when I went away to think about what dynamic objects might require I thought actually most of them will respond to the player in some way or another so I wanted to include a pointer to the players dynamic object in case that interaction needed to occur and this is a good example of why this might be a useful thing to do we want the skeletons in this case to attack the player so they're going to have to walk towards the player they need to know where the player is in the game engine going back to the dynamic creature update function it will call a behavior function but it will call the behavior function of whichever object is the subclass so in this case even though this function is fulfilled by dynamic creature it will call the behavior function of the skeleton if the object is a skeleton and this is polymorphism and this is why object-oriented programming is very powerful so I'm reusing code all over the place and you'll notice that each time I add new objects all I'm really doing is adding a constructor and overriding one or two methods so let's have a think about our skeletons behavior because we've passed a player object in we can do some interrogation of what is the relationship between the skeleton and the player and the relationship that I'm most interested in right now is the distance between the two so I'm just going to use Pythagoras theorem to calculate what is the distance between this skeleton object and the player and I can check that if the distance is less than a certain amount then I can set the skeletons velocity parameters to make this garden walk towards the player in this case I'm taking the difference that I use as part of the Pythagoras calculation and normalizing it so I've got a a unit vector for velocity and I'm setting the velocity speed to two if the player is further away than six then I just want the skeleton to stand still a very simple behavior let's see what that works like well I'm in control now we can see there's three skeletons I'm going to edge towards this one now we've entered the radius and you can see it's following me around and it's completely animated on its own accord and it's doing the directional changes of its own accord and that's because we're using polymorphism in this instance to handle all of that fiddly stuff for us we can also see that because we've set the dynamic objects to be in this case solid against the map that it can't pass through them which is also a desirable property even though we've not explicitly coded that in the game engine has handled it for us we do see some undesirable things that the dynamic objects are going on top of each other so it now looks like we've really just got one skeleton where we should have three and also it seems to get quite skittish the objects are jumping around a fair bit and that's because it's making decisions too frequently effectively our skeletons are making a decision every single frame and unless you into speed running it's very difficult for normal people to do a decision at 60 frames per second for example it's very possible that all of our dynamic creatures are going to need some sort of AI so I'm going to alter the dynamic creature base class to include some utility variables to help us implement an AI and in this instance I'm going to create a variable floating point called State tic and the idea of state tic is it's going to deplete every frame and when it becomes less than or equal to zero the AI is going to make a decision about what it's going to do and once it's made that decision it will also then reset this state tic variable so we're in this way we can have the AI make a decision every one second or every two seconds or every half a second instead of being every frame so going back to our skeleton implementation let's set that variable to something sensible so to begin with let's say we only want these skeleton to make a decision about anything every two seconds in the behavior function itself we're going to reduce the static variable by F elapsed time and only if the static is less than or equal to zero do we actually want to do anything that changes the current operating state of that dynamic object we must also then reset the static variable in this case I'm going to set it by adding one more second so to begin with the skeletons won't do anything for two seconds but then after that every one second they're going to make some sort of decision and see how that works so the skeletons rung do anything to start with but now we can see that once they've made a decision they stick with it they're still overlapping though let's see if we can sort that out and the reason they're overlapping is because in that event of two solid dynamic objects we don't do anything we just allow it to happen I'm going to resolve the collisions between dynamic objects in exactly the same way I'm resolving them between the dynamic objects and the map the only difference is here is when there is a collision instead of using an integer rounding to locate the objects in the right place we need to look at the dynamic objects properties and decide what is one unit to the left or one unit above all on unit to the right of the dynamic objects that have collided again I'm going to check if the bounding rectangles overlap and if you remember with cheerio checking horizontally before checking vertically is very important else things get stuck at the diagonals so we'll first check to see what's the horizontal situation and resolve it and so if there is a collision we can see that the dynamic objects position where it's going to become is set to the object that it's collided with at plus or minus 1 depending on which direction the base object is traveling in in the first place once we've done the collision detection for horizontal we then do collision detection for vertical so let's see how that looks well if we attempt to skeleton out of his hiding hole here we go we can see firstly witzy and the skeleton can't overlap each other that's very nice so the collision detection is working on a dynamic object versus a dynamic object let's pull some more skeletons into the arena now they still got to obey the rules of the map as well but nicely this time they're not overlapping they might be competing for space but they certainly don't interfere with each other and will probably end up a situation where the player becomes boxed in so far the only form of interaction we have is when the player overlaps a dynamic object but what if the player actually intended to interact with something I uses the action button we'll call it the spacebar in this to perform an action either attack or read a signpost open a chest activate a switch well let's get rid of our little script testing code and do something when the user presses the spacebar in this instance we want to interact with the object that's in proximity to the player here we've got two dynamic objects once the player and one represents a signpost so I don't know we're not implemented that dynamic object yeah yep let's say it presents some information to the screen when the user interacts with it to check for interaction we want to see what's adjacent to the player and the player will only face one of four directions north south east or west so in this instance we can set out four probes to see if there is a dynamic object that lies beneath the probe so if the player is facing north or upwards in this case we'll set a probe to be here likewise South will be down here west and east and what we'll assume is that these probes are positioned one unit away from the center of the dynamic object we already store facing direction as part of a dynamic creature objects we just don't expose it in the class so I'm going to add that function in this one so simple I'm just going to make it immediately return MN facing direction so now when the user presses the spacebar we can use the direction that the player is facing in to determine this position of a probe so I'll call our test point test X and test Y and depending on the facing direction we want to offset test X and test Y accordingly there's a little error here and I think it's worth discussing it says that get facing Direction is some sort of a problem if we look at the error class see dynamic has no member get facing direction and that's true that's because we've added it to the C dynamic creature class we don't really want to be putting casting in at this point and we know that the P player object is always going to be of type dynamic so in our RPG engine dot H file I'm going to change our P player object from type C dynamic to C dynamic creature once we've got the location of the probe we then have to test it against all dynamic objects to see if it has hit one of them I'll use a little iterator to go through the list and this is just a case of checking to see is the test coordinates within the bounds of the dynamic objects rectangle we'll assume for now that the player can really only interact with friendly things so I want to check that the target is friendly because if it's not a friendly then the interaction would be an attack and we'll worry about attacks in the next video in the same way that we checked for two overlapping rectangles for dynamic objects and call to the walk into action in this case we're just going to call the talk into action on the map so how can we test this well in the home map I'm going to add another object something that we can talk to and I'm going to call this creature Bob and we'll just make Bob look like a skeleton for now I'm going to set Bob to be positioned at twelve across and fall down and we'll add Bob to the vector of dynamic objects because bob hasn't been given a special class of his own and he's just a dynamic creature his animations will work but he has no behavior there was nothing specified but we want to check if the player can interact with Bob and we'll let the map handle this into action so in this case if the targets name is Bob then we want to do something will display some dialogue and in the last video we showed what we can wrap up commands in a macro to make them a little easier to read so let's see what happens when we go and interact with Bob firstly let's get to Bob I have to draw these skeletons out of the way first right let's go back in so we've entered there's Bob Bob's a skeleton but I don't fear him he's not going to attack me I can't walk in or over Bob but I'm going to interact with him by pressing the spacebar hello I'm Bob so we've now created a framework which very simple to add interactable events what's interesting in this case is it is the map that handles the event but a map should be a fairly static thing what if bob was part of a quest and needed a complex chain of events to decide his actions and his dialogue well this implies we need an interaction layer one up from maps to handle quests so let's look at the chain of events that needs to happen and in which priority when an interaction occurs so we'll know we've got the player he's going to interact with Bob the first thing we might want to check is the dynamic creature that represents Bob we'll give that a non interaction function like we have with the maps if the dynamic creature is not interested in that interaction then the next thing that might be interested in is the quest we don't have a quest object yet but let's assume that we do have something then we can also call the quests on interaction function in fact what we may find is we've got numerous quests on the go at any one time if none of the quests are bothered about the interaction then the only thing left is the map and so we'll make sure that our own interaction functions return a boolean true or false to say whether they have processed this into action and it should be or should not be continued further down the chain so if the dynamic creature says false I'm not interested in that interaction it then moves on to the first of the quests if none of the quests are interested it then moves on to see if the map is interested so what's going on with these quests well I'm going to create a quest object and I'm going to create a list of quest objects and to this list I'm going to add one quest which is going to be my base quest this is the overall game quest completion of this quest signifies that the game has finished all of the other quests are granted to the player as they proceed through the game and typically the most recent quest added to the list will on the whole be the one the player is most interested in solving not always but mostly and so as the player accumulates quest to solve the list grows and as they slowly tick off the quests one by one we end up with just the base quest left and it's important to all have a bass quest because when the game starts that's all they'll be is a bass quest it's just the condition for finishing that quest won't be possible by encapsulating quests in a single object in this way we can store the game's state we can save to file as the only variable information in the game will exist within the quests we'll make sure that maps are just static yes they contain some rudimentary dynamic things in order to help the player throughout the game such as signposts and teleporters and other bits and pieces but fundamentally it is the quests that contain the dynamic information that really represents the current state of the game and by maintaining a list of quests in this way saving and loading the game is quite trivial we can just serialize the quests the disk and then read them back again and repopulate this list the second benefit to having a quest encapsulated is we can add lots of interesting functionality to a quest so decisions that get made to decide how the quest pans out can be stored as local variables as can quests such as please collect ten dead skeletons it's in the quest itself we can store the value that records how many skeletons we have collected as quests are quite fundamental I'm going to create two new files RPG quests dot H and RPG quests dot CPP the definition of the quest is quite simple it is of course going to be just a base class and it shares lots of properties with things we've seen already we've got a non interaction function we've got a populate dynamics function now that's quite important because we might want certain dynamics to only appear when a quest is available to solve and this this way we can implement puzzles and locked doors and that kind of thing quests also need access to the dynamic objects and they need access to the script processor in fact they also need access to the RPG engine itself so they can issue commands or we're beginning to see here is a bit of a pattern of similarity we're seeing lots of objects with the same properties and this is always an indication that our object-oriented design could be abstracted even further and indeed it seems to be the case that most of the significant objects in the game have a non interaction populate dynamics a name and links to the script and the game engine I'll think about encapsulating that for the next video so the base quest there's nothing interesting at all it just has blank implementations of the two functions that get overridden in the quest start h file I'm going to define our first quest and just because we're testing I'm going to call it test quest quests have an additional boolean variable stored in them to say whether or not they are completed and we're going to maintain our quests in a list I'm going to add quests to the main game engine and I just simply need a standard list two pointers of quests in on user create where we're creating the initial conditions for the game I need to now create an instance of this quest and add it to the list and I'm going to do that just before I create the player a new C quest test quest now we've just decided that quests can also populate maps with dynamic objects so we'll also update our change map function and once we've added the dynamics from the map we're going to add all of the dynamics from all of the quests that are currently in the list of active quests and the populate dynamics routine for a quest also takes in the map's name so the quest itself can filter out which objects do I need to add to this map so let's create now a little quest where we've got a character outside of the house that you need to talk to before you can talk to a character inside of the house we go to our Maps we're currently using the map to place the object Bob I'm going to disable that for now and also I don't need to add the creature Bob anymore because we're going to make Bob owned by a quest so we've only got the one quest test quest if the map that we're populating is equal to coder town then we want to populate it with Bob Bob's going to sit outside and will populate Bob at being at six comma four hopefully that's not going to be in the middle of a building I'm going to add a private variable to our test quest which is unique to this quest and it's going to allow us to keep control of what's going on in this quest and I'm going to call it phase so we know where we're up to so if the map coded town and the face is zero then we're going to display Bob however if the math is home and the phase is one then we're going to display Bob in his home so let's see what happens now when the user interacts with Bob we can check for this by looking at what the target is if the target that's being interacted with is Bob and this quest is interested in that we can check for it and we can also check during this quest what is the current phase well if it's zero we'll get Bob to say something hello you are in phase zero of this quest that's going to be the first dialog box the second dialog box will be you will need to make sure we don't go too long speak to me inside and once this event has occurred we want to return true because we're done with it we've processed it this this particular instance has now happened we don't want the interaction to propagate any further down any of the quests because there may be other quests that respond to Bob later on we'll also set the phase to one so now we'll do something similar phase one we know Bob will be inside this time so now we'll get Bob to say you are in phase one of this quest and that just returns true it is possible at this point we could say the quest has been completed well let's see how this works out now back in the game engine where the user has chosen to interact with something we need to check if any of the dynamic objects related to the quests are being interacted with so I'm going to iterate through them using little or list and we check them quest by quest and if any of them return true I'm going to set a boolean flag to true and break so the first quest that is happy to accept the interaction and deal with it will stop that event from propagating forward so let's see how Bob moves around depending on where the quest is currently up to so there's Bob currently in the village map we'll go into the house and we can see Bob isn't there because we're only in phase zero of the quest let's move back outside and we'll go and interact with Bob spacebar hello you are in phase zero of this quest you will need to speak to me inside fair enough Bob let's go and have a look inside ah there you are you've appeared and just before we go and speak to him if we go back out now you see bob has disappeared he's gone inside oh we can speak to Bob you are in phase 1 of this quest very nice we need a way to give the player new quests and I'm going to make that scriptable because that's the most common way you get quests in a role-playing game for example let's say we talk to Bob and Bob says please get me my widget of wisdom for Mount Doom or some other poetic RPG nonsense then at that point we want to add a new quest to the list of quests that the player is embarked upon this means we need a command call add quest and the implementation of an quest is also very simple it calls a function that we've not created yet called add quest on the game engine so let's add to the game engine another function and quest that takes a pointer to a quest and all this add quest function does is push the quest to the list of currently active quests we will at times need to expunge quests that have been completed from this list and so I'm going to use a little bit of standard library loveliness to do just that we use the remove if function which is part of algorithm to go through the list and check for any quests that have the be completed flag set to true from that point we erase those quests now we can start to play with layers of quests and I'm going to get rid of test quest now and change that to main quest so this is going to be the one that always exists throughout the game let's add a character to our main quest so the main quest is now placing the character Sarah here in purple in village map and it places Skelly or Bob in the home map I don't have any interactions defined yet with Sarah but in the main quest on interaction function I am going to add a server response and it's going to be show the dialogue and I'm going to start putting the characters names in so it's a little bit clearer what's going on I will do that by saying Sarah as the first line you have no additional quests I'm now going to add an additional quest which is going to get given to the player when they interrogate Bob so we'll call this one Bob's quest for now Bob's quest isn't going to implement any dynamics at all it's just going to handle interactions so when Bob's quest is active when you interact with Sura we're going to get Sara to say you are doing Bob's quest but we need to get Bob to issue the original quest and this can happen in the main quest so if we interact with Bob as part of the main quest then we can call our command add quest which takes a pointer to the new quest which in this case it's going to be Bob's quest we'll do some dialog first just to give it some flavor so the character speaking is Bob I need you to do something for me I'll have a second line cuz I've got to fit it in somewhere predictably the rats in my basement the obligatory first quest of any self-respecting role-playing game so we can start to see where game design becomes a little bit complicated the game designer needs to think about all of this and how it all interacts with each other but fortunately because we've encapsulated a lot of it it becomes a much simpler task than having a whole smattering of variables across the whole program let's now take a look at the quest priority system in action we can talk to Sara which is the main quest and she says you have no additional quests let's go on into the building and speak to Bob hi Bob Bob I need you to do something for me predictably there are rats in my basement well Bob has now issued us with a new quest a sir is Stiller but now her response is you are doing Bob's quest it's no longer the default message supplied by the main quest so the quest has taken control it's taken priority of what events and what are the nature of the interactions going on admittedly this video has been quite complex but it's got some very important issues out the way we're handling quests and were handling navigation of the player around the world and we've created new dynamic objects to help us do this in the next video we'll be looking at combat and items preview here I can select I need to heal myself to get back up to full health and that should enable me to have the beam sword there we go and we can kill things and interact with other things on the map let me just pick up a health that way I can get my beam sword back perfect I don't like killing the green things they're too nice anyway if you've enjoyed this video big thumbs up please have a think about subscribing obviously make sure you've watched the other parts of it first come and hang out on discord we're still looking for artists and plot designers and everything to work on the very final build of all of this and I'll see you next time take care
Info
Channel: javidx9
Views: 25,836
Rating: undefined out of 5
Keywords: one lone coder, onelonecoder, learning, programming, tutorial, c++, beginner, olcconsolegameengine, command prompt, ascii, game, game engine, rpg, code-it-yourself, code it yourself, role playing game, ai, navigation, assets, wittybit
Id: UcNSb-m4YQU
Channel Id: undefined
Length: 51min 46sec (3106 seconds)
Published: Sun Apr 29 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.