Placeholder Image

字幕列表 影片播放

  • hello and welcome to code it yourself role playing game Part two.

  • If you've no watch the first video, you probably will need to.

  • This is a direct continuation from that video on.

  • If remember, at the end of the video, we ended up with Jerry.

  • Oh, oddly enough, from the platform game walking around a map on, we can see here that Dario can't overlap with anything that's in the map that we're considering static on 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 no 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.

  • Andi switches on tele porters on all sorts of things that make the game happen once we've got the very basics of dynamic objects out, the way we look at how we can script sequences to form cut scenes.

  • We did actually describe what the dynamics objects were in the first video on in this game.

  • I'm going to restrict them to being one unit wide by one unit high on that.

  • 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 they're not going to keep them locked on to particular cell boundaries.

  • So it's start with the basics.

  • We know that we're going to have to have a position for our dynamic objects, which is going to be an X and Y coordinates 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 V X and V why we're going to have to.

  • Boolean flags determined the collision detection properties off the dynamic object.

  • Is it solid against the map?

  • For example, Dario 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.

  • We'll also see later on that sometimes we want to adjust these properties very briefly to help us get some certain effects we're going to check.

  • Is the object also solid against other dynamic objects?

  • So if we've got enemies in the game again, we don't want the play character to be able to walk through them.

  • But sometimes we might.

  • So we're gonna keep these flags a bit separate.

  • We're also going to record.

  • Is the dynamic object considered friendly or not on?

  • 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 well wantto handle this differently, depending on the friendly state of our dynamic object on the way I'm going to strip to the dynamic object is to have a base class dynamic on for things that move around and perhaps have their own behaviors were going to inherit from it on.

  • Call it dynamic creature on 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, is 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 sign post, 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 the three main source files.

  • We had the main application, which contains the on user creator.

  • Nonusers update functions.

  • And we also created a Singleton class called RPG assets, and this loaded a single instance off.

  • Well, basically the sprites on.

  • We're going to add more things to this.

  • So I think the maps are also going to be something that will want global access to, but we only ever want to ensure one instance off them exists.

  • It's all update this class, too.

  • We also created a map class which basically stored the static information off the maps, and they were created very an external map editor created by this community.

  • Currently, moving Jerry around doesn't involve any additional classes.

  • In fact, we just store the values for position of velocity directly in the main class.

  • We're going to be changing this because the player object is indeed going to be a dynamic object to.

  • So this means we'll have to make quite a lot of changes to our our news update function to accommodate a new architecture.

  • The good news is, we've already got static collisions.

  • Don't we'll just have to re factor 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 off.

  • Many dynamic objects on the law 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 dot h an RPG dynamic start c p p.

  • 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 the 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 head of file.

  • I'm just going to prototype the functions, so we need the constructor we'll put in a D structure to, and I'm going to add some public properties, so we know we need the position PX and why went after Velocity VX on V Y.

  • I'm going to have the three billions I discussed earlier when we'll call that be solid versus map solid versus other dynamics on whether or not this dynamic object is considered friendly, I'm also going to include a type of string to name the dynamic object.

  • This is going to be very important for differentiating between dynamic objects off 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 isn't declared yet, but we'll come back to that in a minute.

  • Now I'm going to add some bass methods on 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 overwritten on.

  • 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 Council game engine on.

  • 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 point graphics.

  • We'll need to include the council game engine at the top of our class.

  • Of course, now the council game engine does some naughty things.

  • One of the things it also does is include string and sets the name space.

  • I'm sure you'll have plenty to say about that in the comment.

  • One thing that's also important for the object to draw itself is it needs to know what the current global offset is of this screen.

  • I e.

  • 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 coordinates, so we need to provide this offset now dysfunction 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 on.

  • 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 they said 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 is a facility.

  • To do this.

  • I'm going to allow the dynamic object to be named Devere.

  • It's constructor, so I'll just put in those parameters.

  • So for our base Class C dynamic, the dynamic object, all we've got to the two methods left to fill in one is the constructor, and one is the destructive.

  • Right now, the destructor doesn't need to do anything, but the constructor does need to specify what the name is.

  • 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.

  • 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 ashamed.

  • 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.

  • Ah, 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 gonna have to create a constructive 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 drawer self on update routines.

  • So I'm going to copy those from up here, put them in down here, get rid of the virtual keyword.

  • He could leave it, though it doesn't make any difference, Really, get rid of these braces, but I'm going to use the override key words, 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 pointed to the sprite that were passing through, and I also want some additional properties s so we can assess the state of the creature on will 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 interview, 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 on.

  • 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 on.

  • I'm going to use this notation to say But also, please call your based constructor which will set the name for us.

  • Andi felt the variables.

  • 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, most say, will have 10 to begin with an IMAX health of 10 to So the creature has full health at the start later on, we'll probably want to overwrite these with different values for different creatures.

  • We also need to provide the update function on the drawer cell function.

  • 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 I've got the base class on 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 off a format and layout of a sprite sheet for all creatures, and it looks like this here.

  • We've got individual sprites, which is 16 by 16 pixels on.

  • I'm using this strike 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 2nd 1 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 this point celebrating on this is the Sprite dead.

  • Now the artist that created this spy packers also included two more, which is just standing facing on standing facing away for this Siri's.

  • 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.

  • Slim's created by it said it on the discord server is the same thing.

  • So we've got walking towards the player, walking towards the west, walking away from this player, walking towards the east on we've got a celebration on a dead slime.

  • Additionally, again, a skeleton character.

  • This time exactly the same states of animation in the same locations on this is quite important.

  • Knowing that all creatures are going to be drawn with same Sprite layout means we can use some tricks.

  • So I'm going to use a new Marais Shin here to store which direction the player character is facing.

  • I'm just gonna hard code these in as a zero one two and eats east is three.

  • I'm going to store this as facing direction.

  • If you haven't seen this notation before, it's really I've just done a new Marais shin in a single line on dhe.

  • I've basically at the same time declared to this M and facing direction variable as having the type of this enumeration.

  • This means we can apply instead of 012 and three.

  • We can apply southwest, north and east as the facing direction on the compiler will be completely fine.

  • With that, I'm going to have exactly the same for other states.

  • That's a standing, walking, celebrating or dead.

  • I'm going to store that as graphic state.

  • So if you remember South, West, north and east, a tie up with which column of the Sprite sheets we're going to be using on depending on what the dynamic creature is doing well, choose whether it's standing, walking, celebrating or dead in the constructor.

  • I'll just default these somewhere to say the facing direction is south on dhe.

  • By default, it's doing nothing at all.

  • It's just standing there, just going back to the spite editor again.

  • I can see a walking animation.

  • Well, it's probably about 1/5 of a second between me pressing the open down keys.

  • So this means the dynamic creature is going to need to keep track of time to know which particular frame of animation to play.

  • Sleds aren't that in.

  • We want 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 a zero, and this is where we'll start looking at the update function.

  • So every point to off a second I want the Sprite displayed to change now.

  • Fortunately, the game engine providers with how much time has elapsed.

  • So we just add that to our F time of variable.

  • And when the F time a variable gets beyond 0.0.2 of a second, I'm going to subtract 0.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 will come back to that in just a little while.

  • But let's set some of the graphic states that a fairly obvious.

  • So if the dynamic creatures health is less than or equal to zero, we're going to assume it dead.

  • That's the sprite that we want to display.

  • If the creature has any velocity components, it all, it's moving, so we'll set it to the walking states are here.

  • I'm just looking at the absolute values of the V X and B Y components.

  • Of course, if it's not walking, it's just standing.

  • The final state celebrating it 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 could do that based on the velocity components.

  • If they're negative, it's facing west or north, and if they're positive, it's facing east or south.

  • Now the drawer cell 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.

  • Sheets, offset acts and sheet offset.

  • Why which are indexed into this Sprite location.

  • So in the top left here, that sir sheet offset zero on we go all the way over to celebrating that's sheet offset four.

  • And, of course, in the ex direction on if we've got ah, one sheet offset in the UAE direction with moved down to the next row of Sprite, so I'll use a switch block to determine based on the state how we set that these variables.

  • If the creature is simply standing still, the only property that matters is thief facing direction variable on the country.

  • If the player is dead, then we want to specifically choose s right location to display because there's only one option.

  • Walking, on the other hand, is a little bit tricky because we've two to choose from, depending on the state of M F timer.

  • So this implies we need an additional variable, which I'm going to add an interview called MN graphic counter again in the constructor.

  • I've defaulted this to zero.

  • 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 Ossa late based on the time.

  • And we're going to use this mobile to select which row of Sprite sheet 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 one.

  • I'm going to make sure it doesn't go out of control by Modern, its own value with two.

  • So there's lots of late between 010101 forever, which means we've got two variables to choose our walking spite from in the ex direction.

  • It's the direction the object is facing, and in the wind direction, it's that's oscillating Counter graphic counter you can see I've got multiplied by sixteens in here because this is going to give us the pixel coordinates in the Sprite sheet.

  • All that's left in the drawer cell function is to actually get the creature to draw itself on because we've passed in, appointed to the council game engine.

  • We can use the drawer 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 screen.

  • Space, unfortunately, is a 1 to 1 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 on the sheet offset X and offset why variables will give us the top left of a particular tile in our spite sheet on.

  • We know it's always 16 by 16.

  • Of course, it's undesirable to have 16 hard coded into the game engine.

  • We'll see it everywhere at the moment.

  • If we do want to move to higher resolution or indeed low 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 in Visualize that this offset is one full unit off pixels in our game space.

  • So let's just have a little recap of all of that.

  • We've got a sprite sheet, which is carefully laid out to be usable by the game engine.

  • If we don't obey the rules of the Sprite sheet, 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 drawer cell function, We use that state information to decide which spite of the sprite sheet to extract in order to draw on the screen.

  • Let's now go back to our main file on 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 a sea dynamic, and I'm going to call it MP player For now, 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 the 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 R.

  • Singleton that's managing the assets.

  • Get Sprite on dhe.

  • The despite 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 were 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 no need to make those relative to our new P player object.

  • So in this case is going up.

  • It's V Y down B Y, and left and right.

  • Use X instead will also start to see some leftover junk from the Jerry Oh, platformer.

  • We're no longer choosing the Jerry Oh, Sprite based on anything was going on here.

  • The object itself will choose its own sprite so we can start to clean this up a little bit.

  • And I'm also going to say this opportunity to start making this a lot more generic.

  • So where we've got variables that are specifically indicated as player position accident, they're just going to be new object Position x and y instead.

  • So, for example, in this case, I'm going to change its two new object position X on dhe.

  • Instead of using sort of the player here, I'm going to just simply use, object and choose the P X value.

  • I'm going to create a temporary dynamic object or pointed to one which points to r P.

  • Play a 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 coating it for the player so we can test all of our animation code.

  • I'm gonna 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 will use it explicitly.

  • And this is where we're going.

  • Toe always need access to a variable that represents the player object.

  • So even though the players 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 ex direction on here.

  • I'm going to set it to the white direction.

  • So I've just gone through an updated, all of the static collision code to handle a generic objects.

  • Although in this case the generic project is hard coded to be the player, the only thing left to change that is how it draws itself well.

  • We've already drawn the map so once have drawn the mat with them, want to draw the object on instead of just drawing the player, it's going to be drawing all of the dynamic objects later on, so we'll try and keep.

  • This is generic is possible.

  • Instead of drawing the object using the drawer, partial Sprite routine will get the object to draw itself using its drill cell function and so we just need to pass a pointer to the council game engine.

  • So I used the this key word 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 Constance and things to make things appear on the screen.

  • On 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 00 as the location I'm going to card code.

  • This two more sensible numbers to the player is in the map.

  • I'll pick a number.

  • Let's say, is it location?

  • 55?

  • Right, So that's been a big lot of code.

  • Let's let's see how it goes so we could see the player Character is in the map and we're moving around like Dario.

  • What we don't see is any change in Sprite or we've done it successfully replaced Dario 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 F 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 on.

  • 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 for his walking animation to make much sense.

  • So we were specifying the velocity to 10.

  • I'm going to set it to four, and that's much more sensible, Very nice.

  • Now we've got maps, and we've got at least one moving object.

  • We can start to have a little bit of fun.

  • Let's start thinking about cut scenes.

  • Let's start by making an assumption that the cut scene 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, presents a 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 is simply, just wait.

  • So let's have some parameters to these two.

  • So if we're going to walk, too, we know that we're going to have an X and A Y.

  • We're goingto 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 be some sort of text.

  • What we need is a program within a program on the way I'm going to do this is to create objects which represent commands on a queue of these commands is sent to what's on calling the script process er on.

  • Whenever the script process ER has something to do.

  • It stops player input and surprise.

  • Surprise.

  • We're going to use object oriented programming toe 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 on allowed to methods.

  • This command start, which starts the process of the command happening and, as usual, will have update in case the command takes a certain amount of time.

  • We need that time to elapse.

  • This will also then take F elapsed.

  • Time to implement a cook sequence will queue up a sequence of commands.

  • This Q is fed into the script process, sir, On the script process.

  • Er will then go on control the game engine.

  • So if this Q is empty, the script process has nothing to do is 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 sub Class C Command for the individual implementations.

  • So, for example, a weight command we might want to count the F elapsed time before we can set it is being completed for the move.

  • Command will probably want to pass in a dynamic object for the command to take control of whilst it moves it to the location it needs to be.

  • Once it's reached that location, it's completed.

  • So each time the script process ER determines that a command has been completed.

  • It removes it from the Q.

  • On 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, not H.

  • An RPG commands dot c p.

  • B.

  • Here is our basic command class.

  • It does nothing of any function.

  • All it's doing is defining the interfaces which sub classes of this will use to implement what the command should be doing.

  • Let's create another class, our script process, sir, which implements the theatrics.

  • And before we get to adding any interface functions, we know that the script processor has a Q off commands, and I'm going to use a standard list to implement this.

  • We also know that the script process 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 to interface functions.

  • The first is at 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.

  • On it's finished, it takes a duration.

  • So let's have the simple ones first.

  • We've got a constructor for our script process, sir, and we've got add command, which is just going to push the pointer to a command object to the back off 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 on.

  • We'll do this simply by checking.

  • Is the command list empty?

  • If it is empty than the playhouse 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 wrote, 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 tohave 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 with 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 start 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 commander 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 on, 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 you're in the situation that the command of the front has been completed, we want to delete the command because we're going to pass it in as a pointer on.

  • We're going to remove it from the front of the list, and right now this is all our script process.

  • Er 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 subclass.

  • I'm going to call it move to which is going to walk a dynamic object from its current location to a target location and so create a constructive for this which takes in that information.

  • So the first thing we do is take a pointer to the dynamic object.

  • And remember, this could be a creature.

  • It can be anything at all, because as long as it inherits from the base class or 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 and 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, will set a short duration who wanted to walk there slowly will set a long duration.

  • We're also going to override the start an 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 ah, whole host of private variables for this one command to facilitate a linear interpellation from the start location to the target location.

  • So let's out some private variables.

  • The first is going to be the dynamic object itself.

  • We're also going to take the starting position X and why we're going to set the target position x and Y on.

  • We're also going to store the requested duration on the time passed so far, because we're going to interpret late 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 moved 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 inappropriate durations, I'm going to take the maximum between the duration specified on 0.1 of a 2nd 1 millisecond, and this is important to avoid a divide by zero error later on.

  • I also of course, need to store appointed 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 this 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 objects location will have changed If I just specified the objects starting location as soon as I created the list of commands, well, it will have captured the objects 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 star command happens once and says, Take a snapshot of where the object is now on.

  • This could be at a point in the future.

  • And so it is in the update function the week implement the linear interpellation.

  • And this is going to be a standard Lee new interpellation.

  • 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 on.

  • We use that normalized value in effect, to chew the point on the 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 linear interpellation, maybe 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 on.

  • I'm going to set the completed flag to truth.

  • This command has now finished and so on.

  • The next process commands update the script's process.

  • Sir 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 the commands file on.

  • I'm going to create a single instance of our script processor called M Script to test the theory I'm going too hard code a little script in place whenever the user presses Zed 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.

  • It's all taken script at 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 could be really strong because all of these commands are going to be different objects.

  • But this cure 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 pointed to a command, so I'm going to have to use the new key word because we know the script process also deletes and cleans up the commands once it's done with them.

  • So in this case, it's a move to command on the object that we're going to move is the player on for the first command.

  • I'm going to move the player to 10 10 and I want that to take three seconds.

  • Once it's moved there, I'm going to move it to two or move it five across and keep it on 10 and then I'm going to move it from 15 2 15 and then 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 it's going to be the first thing I'm going to do in on user update on.

  • The reason we want this at the start is because the current command might disable user input, so we'll check for that.

  • 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 zed 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 its executing the script so the character is walking out a small triangle, and now I've got control again.

  • So by press Zed key first thing the character do is moved 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.

  • Okay, so we have a single command move, too, and we have a single dynamic object creature.

  • Let's make the code handle multiple dynamic objects.

  • I'm going to stall my dynamic objects in a vector and in the unused create function, I'm going to create a few more.

  • So I'm going to create a couple on.

  • We'll just call them Skelly one on Skelly, too, and they're not going to use the player Sprite.

  • We want them to look at the difference.

  • 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 hurt somewhere in the scene.

  • So I set one at being at 12 12 and another one being at's five comma eight.

  • Of course, these are called and player anymore.

  • There's something else, in fact, weaken.

  • Temporarily create them here as a C dynamic up one NC dynamic Bob, too.

  • What I'm going to do is add thes to the vector, and I'm going to start a convention that the player object is always element zero in this factor.

  • So I'm going to push back the player first and then I'll do the other two.

  • And this is little design Convention, which will see is quite useful later when we're starting to implement a quest system on dhe handling maps full of different types of dynamic objects.

  • If we always know where the player is, then that could 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 on will use an auto for Luke for this, where each object is going to be called objects that's going to go through our vector off dynamic objects.

  • I'm using the ampersand at the start because its chances are going to be changing the properties of the object in this loop.

  • Just 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, also want all of the objects to draw themselves.

  • So it was a copy that I'm used to getting 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 to skeleton enemies, and what we could see is because the player is always the first object in the list.

  • The players drew on behind everything.

  • This may be undesirable.

  • I always really wanted the player to be drawn on top of everything, so they said the human player can always see the player character.

  • I'm going to fake this, and this will cause some concern among 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 already drawing the player once, we could've just this loop to ignore the first element of the vector.

  • But 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 have a command to move one of these other objects.

  • Let's take ob wants.

  • We know it's already at position 12 12 to start.

  • So in between our character walking around, let's have another move.

  • Command for object one.

  • 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 12 12.

  • Two.

  • I don't know.

  • We'll say 15 12.

  • I will say two seconds for that move.

  • Now you may be thinking this seems a little bit of an uncomfortable way.

  • Thio.

  • Choose which object to operate on.

  • You'll see later on that we can use the friendly names Thio decide which objects are moved around the screen, making a little bit easier for the game designer.

  • So if I press the Zed key again, it starts.

  • Are automated sequence play characters going to 10 10 than 10 15 on an object one has moved as well on what's really nice is because we've got an established way.

  • The handle animation.

  • When the object moved it had the appropriate animation applied to it.

  • We had to do no additional code, even though it's a different type of object.

  • So let's wrap this video it 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 create a subclass off command called show dialogue, and this is going to display text to the user on the way it's going to work.

  • Is each line of text is going to be specified as an element of the vector, and you'll notice we're only specifying the start function to be overwritten.

  • 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 that's what we're going to need to start having some communication between the commands onto the game engine.

  • When we moved the objects, that was a communication between the commands on the object itself.

  • In the game engine file, I'm going to create a function which is for displaying dialogue, show dialogue, and it takes again the vector of lines.

  • But we also need some additional properties to handle the showing of dialogue we're going to store.

  • The dialogue was 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 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 fixed duration that it's on the screen for so instead, what we're going to do is show dialogue until the user presses the action button, which in this case is going to be the space bar.

  • 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 too.

  • So it's important that we somehow cash the dialogue information on respond to it in a real time way.

  • So all our show dialogue function is going to do is take a snapshot off 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 wanted to look like it does in an RPG Games, 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 factor that's passed to it.

  • I can work out how many lines of dialogue a displayed on the screen by looking at the size of the vector I can.

  • Then it's a rate through each element of the vector toe workout.

  • What's the longest line of text? 00:47:28.250

hello and welcome to code it yourself role playing game Part two.

字幕與單字

單字即點即查 點擊單字可以查詢單字解釋

B1 中級

編碼-自己動手!角色扮演遊戲第二部分 (Code-It-Yourself! Role Playing Game Part #2)

  • 4 0
    林宜悉 發佈於 2021 年 01 月 14 日
影片單字