Placeholder Image

字幕列表 影片播放

  • SPEAKER 1: All right, welcome to GD 50, lecture 7.

  • This week we'll be talking about one of my favorite franchises of all time,

  • a core part of my childhood--

  • Pokemon as shown by the Poke Ball on the screen there.

  • So back in 1997 I think it was, the first Pokemon

  • game was released, Red and Blue.

  • I believe it was released a year earlier in Japan where it

  • was released as Red, Blue, and Green.

  • And the overall goal of the game was fairly straightforward.

  • You were a Pokemon trainer.

  • Your goal was to go out into the world and try and capture

  • any number of 151 different types of these creatures called

  • Pokemon that were based on a whole bunch of different types of creatures.

  • Shown in the screenshot here, there's a Weepinbell fighting and Geodude.

  • A Geodude was a rock type, Weepinbeel was a grass type.

  • You had different types of Pokemon.

  • When they fight each other, some types were better than other types,

  • like this sort of very large rock, paper, scissors relationship.

  • And it was just a very addicting formula.

  • You'd have a team of these creatures that you had caught and raised

  • and battled, and you'd fight other trainers.

  • And the awesome part of this was you could go

  • and you could actually fight your friends,

  • or trade Pokemon with your friends that they had caught.

  • And you would often share stories back and forth

  • about the different rare creatures that you would have encountered,

  • and all sorts of things.

  • You'd have a customized party that was sort of a part of you.

  • And so this is Pokemon Red.

  • The series has evolved over time.

  • This is a screenshot of Gold and Silver, which was released a couple of years

  • afterwards for the Gameboy Color.

  • Again, this was released for the regular Gameboy.

  • Gold and Silver introduced a bunch of new features including breeding,

  • and a day, night cycle, and a lot of other things

  • that became part of the core series.

  • Here is Ruby and Sapphire, which was for the Gameboy Advance

  • and got a significant graphical update, but the core formula

  • stayed much the same.

  • Here is Diamond and Pearl, which is for the DS, which

  • it made use of two screens, as seen on the top and bottom there.

  • Here is Black and White, which was another step forward

  • in that it introduced three dimensional graphics for the over world,

  • so you could actually see some sort of 3D for the first time in the franchise.

  • And then more recently, we've seen for the 3DS, games like X and Y,

  • which is shown here, and Omega Sapphire, Alpha Ruby--

  • Alpha Sapphire and Omega Ruby, and Moon and Sun.

  • And so this is a great illustration of why

  • the RPG genre of video games, role playing game,

  • even though it's sort of its own unique take on the formula.

  • But it allows us to, we can sort of dissect this and take a look

  • at what makes an RPG and what makes a Pokemon game altogether

  • for a nice cool demonstration.

  • So today, we'll be talking about a few new things.

  • We'll be doing things a lot differently in this lecture example

  • relative to other examples, because we're transitioning away from the state

  • machine and talking about a construct called the state stack, which

  • is effectively a more advanced version of the state machine.

  • Whereas before, we had a state machine that was in one state at a time,

  • whereby we could be in the play state or the start state or what

  • not, we can now actually have multiple states

  • that exist in parallel that are on a stack of data structure, which you've

  • seen in CS 50 if you've taken, where we can have,

  • for example, the field state, the play state at the very bottom,

  • which is always there, and then we can push states onto the stack

  • as we need to for example, a dialog state

  • so that we can actually display some dialog,

  • some text to the screen without getting rid of the play state

  • that we had there before.

  • It allows us to render multiple things at the same time,

  • and then also return back to prior states,

  • rather than completely create new states every time

  • we want to make a transition.

  • We'll be talking about turn based systems.

  • So an RPGs like Pokemon and others, there

  • are often battle systems that are usually

  • turn based in this particular genre where you're fighting-- you have

  • one team or one character fighting against against one other team or one

  • other character, and you take turns fighting each other.

  • And you have an indefinite amount of time to make your decision

  • and then form some sort of strategy as to how

  • you want to approach the problem.

  • We'll be taking a look at a very primitive turn based system,

  • but a fully functional one today.

  • Another huge aspect of this genre is graphical user interfaces or GUIs

  • as they're shortened.

  • Things like panels, and scroll bars, and text boxes, and menus, all

  • sorts of these things that allow us to get a more visual sort of look

  • at our data, and allow us to navigate a much more complex game ecosystem more

  • efficiently.

  • And to tie it all together, RPG mechanics at large,

  • we'll be looking at things like leveling up and experience

  • and how to calculate the damage that one party does to the other party

  • throughout the course of a battle.

  • And so it will be a fairly complicated set of examples,

  • but fairly illustrative of the genre as a whole.

  • So I'd like to demonstrate sort of the example that I put together.

  • If I could get a volunteer from the audience to come up and take a look.

  • Tony that'd be awesome, thank you so much.

  • So this is my simple but fully featured, more or less, demonstration of Pokemon.

  • So if you want to enter or return.

  • So this is a--

  • so here we have a--

  • we can see right off the bat, we have a text box and a play state

  • like we did before.

  • So this text box is actually a state that's layered above the place.

  • So you can see it has some instructions about, if you want to press P,

  • you can heal your Pokemon.

  • You can press Enter to dismiss.

  • So if you go ahead and press Enter, you'll

  • be able to actually move around now.

  • And so something to note is before, input was actually

  • halted while the dialogue was on the top of the screen for the play state.

  • You're actually not allowed to access or update this bottom state,

  • because the state stack is only allowing input to the top state.

  • And so I have limited the play here just to this box,

  • but if we walk in the tall grass down here, in Pokemon,

  • in order to actually initiate an encounter

  • with another Pokemon or another wild Pokemon, you walk in the grass.

  • So here we've walked in the grass.

  • There's a random chance for this to happen, there's a 1 in 10 chance

  • basically.

  • So it's saying that a wild Bamboon appeared.

  • So a wild creature appeared.

  • He's level 5, we're level 5, should be a fairly even battle.

  • So you can press Enter, and it will say go

  • the name of your Pokemon, which is an Aardart in this case,

  • and it's randomly allocated at the moment.

  • So go ahead and press Enter one more time.

  • Now we can see on the bottom right, we have a menu.

  • So we can fight or we can run, so those two choices.

  • So we can go ahead and fight.

  • So we fight, whichever Pokemon has the higher

  • speed will go first and do damage.

  • We obviously, do a lot more damage, but he's a little bit faster,

  • so he's going to go first.

  • So we fight one more time.

  • We should be able to knock him out.

  • So as soon as we do, we get a victory message, we get a victory song.

  • If we press Enter, we'll actually get some experience

  • for defeating that enemy.

  • So we've got quite a bit of experience, we got 65 XP.

  • In that bottom bar, we can see we have all these GUI elements,

  • we've got a panel here, we have text boxes, we have progress bars,

  • all these pieces are coming together to give us sort of this turn based system.

  • And so after this, we may level up just to demonstrate

  • leveling, which is part of the RPG mechanic side of this game.

  • So we have to press this one more time.

  • We did, perfectly.

  • So they leveled up.

  • And so now we're level 6, so we can see the 6 changed above our progress bars.

  • So now will be a little bit stronger every time.

  • And the stats aren't shown here, it's actually a part of the assignment

  • is to create a menu that will actually show you how you leveled up,

  • what stats actually increased.

  • But underneath the hood, behind the scenes,

  • you actually are getting stat increases.

  • And so here we can see that if we our HP goes all the way down to zero,

  • we faint.

  • And when we faint, the screen instead of fading to white

  • will actually fade to black to illustrate the difference

  • between the two transitions.

  • And so that we can keep playing indefinitely,

  • the game will restore your Pokemon back to full health.

  • And so this will go on forever.

  • This is effectively what the simulator is, it's just

  • a simple series of infinite battles.

  • There are random Pokemon in the grass.

  • There's five total that you can fight.

  • But all the core pieces of what make the game are here.

  • So actually, let's illustrate running too

  • if you wouldn't mind, just so that we can see if there is a difference.

  • So we can actually flee, and then battle we'll get cut short there.

  • We won't get any XP, we won't faint, but we still

  • get taken back to the play state.

  • So that's all for the Pokemon demo.

  • Thanks so much Tony for coming up to demo it.

  • Well, there's a lot of pieces involved here, but as we will see,

  • once we have a lot of these sort of foundational pieces implemented,

  • it's not too difficult to start layering more and more of these

  • onto the game to make it even more rich.

  • In fact, the assignment is to--

  • and we'll see this at the end, I'll recap this at the end--

  • but the assignments goal is for you to implement a menu--

  • similar to the menu that we saw where fight and run were shown--

  • that will show you what your current stat is for each of your stats,

  • there's attack, defense, speed, and your HP.

  • It will show you what your stat is before leveling, the amount

  • that it will increase by, and then the final amount,

  • which is sort of similar to how the actual Pokemon games work,

  • so you can see rather than just seeing, oh I increased my level from 5 to 6,

  • 6 to 7, you can see, oh, my strength increased from 12 to 14.

  • I'm a little bit stronger, I'm going to do more damage on the next play

  • through.

  • So our goal here, we're going to take a look at the field state, the play

  • state, and the battle state.

  • And there's a common dichotomy in most of these sorts of games,

  • be it Final Fantasy, or Dragon Quest, or Pokemon

  • where there is a field, where you are walking around,

  • you're character interacting with a game world with NPCs, going through towns,

  • and what have you.

  • And then a battle mode, sort of a battle state

  • where you're actually fighting against some sort of enemy,

  • or a series of enemies, a party or a single creature.

  • And so we've implemented simple versions of both of these to illustrate

  • and also the transitions between them.

  • Before we start, I want to make another sort of plug

  • for this howtomakeanrpg.com, this book, I actually learned a lot from this

  • and about using LUA in the context of game development.

  • And I pitched this I think in one of the earlier lectures,

  • but if you want a deeper dive into a lot of these constructs,

  • and to sort of get a sense for how you might do something like cut scenes,

  • or more complicated battle layouts, and a lot more like--

  • it goes into a lot of detail about a lot of awesome things,

  • definitely check it out.

  • It's not free, but if you're interested in this genre, which I am,

  • it's definitely worthwhile.

  • Here's what the sprite sheets look like that we'll be

  • using for this sort of demonstration.

  • The Pokemon aside, which are individual textures.

  • Here we're using a simple sprite sheet, which

  • just has a bunch of tiles, most of which we did not use.

  • Note that the bush, the tall grass is not on any sort of background.

  • And therefore, we need to layer, basically

  • have two separate tile maps as opposed to one, which we didn't do last time.

  • And we were reusing the sprite sheet that we used in the Zelda lecture.

  • Before, we used it for all the enemies, the skeletons, ghosts,

  • and slimes, et cetera.

  • But now we're actually using it for the PCs that it contains.

  • Specifically, just the male NPC here, which is our main character.

  • The foundational class that we're using in this lecture

  • that sort of everything else revolves around and makes this work

  • is the state stack.

  • And so before, what we had was a state machine, right,

  • where we were in one state at a time.

  • So you can almost think of it like, we have a box here,

  • and it just has one socket.

  • And then we're always looking at this one socket,

  • whether it's the play state, or the battle state,

  • or a transition of some kind.

  • And now we're transitioning into the idea of, instead,

  • of just one state that we can only see at once, we'll make it a stack.

  • And so what we can do with this is, rather than just having one,

  • we can therefore render multiple states at a time, right?

  • So let's say this is like the field, right, or the play state.

  • And then maybe this is like a dialogue, or something, right?

  • Like we saw before in the field, we had a text box.

  • We can actually layer things on top of each other.

  • And then maybe this is like a fade out, right, or a fade in.

  • So we start with the play state maybe, and we're walking around,

  • and we interact with in NPC.

  • Rather than transition the play state to a dialog state,

  • which would, in our previous model, completely eliminate the play state,

  • because there's only one state that could be active at a time.

  • Now we just render, however many states we have in our stack,

  • we just render them sequentially based on the order that they were popped in.

  • We would rendered this first, we basically render from the bottom up.

  • Render the play state, then the dialogue, then the fade in.

  • And this will have the effect of doing a whole bunch of different things, which

  • we'll see in the distro.

  • But we only really ever need to update one state at a time,

  • right, because if we have the play state active and the dialog state active

  • and the fade in state active, in this order, as a stack,

  • right, we were pushing the operation for pushing for getting something

  • onto the stack is called a push, and getting it off

  • is called a pop if unfamiliar.

  • If we're pushing all of these states on, then usually,

  • we only need to update whatever's on top, right?

  • If there's no fade in for example, and we only

  • have a dialog state active, or a dialogue and a place

  • state in that order top to bottom, then we usually

  • don't want him update what's going on in the play state.

  • We're only concerned with the dialogue that's taking place.

  • We only want that to take input.

  • And when we press Spacebar, Enter, or whatever

  • button clears that dialog state, we pop it off, right,

  • and then we're back to the play state.

  • Then we're just updating the play state.

  • And so being able to update just what's on top while

  • being able to render everything that's on bottom.

  • And this doesn't hold true for all game formulas,

  • there's certainly some games where you can have a dialogue and a play state

  • both get updated, but that's still using a state stack of sorts,

  • you're just then updating things in a top down way.

  • But this allows us to do all kinds of things like transitions,

  • and preserving-- like for example, the fact that we have a play state

  • and we can pop a battle state on top of that,

  • where we don't see the play state underneath it,

  • we only see the battle state, and that's all updating.

  • But it is we pop the battle state off, we're

  • right back where we just were in the play state.

  • It's preserved its state, for lack of a better word, from before.

  • And this is something that this model affords us comfortably.

  • And so that's sort of the foundational class that's

  • implemented in this distro, which will allow

  • us to do all kinds of awesome things.

  • So let's go ahead and take a look at what that looks like.

  • I have state stack open here.

  • So state stack has just a set of states here.

  • And then whenever we want to insert a state,

  • it's going to be at the end of the state stack.

  • So it's going to be whatever the last--

  • so if we're looking at it as a series of states in a table,

  • it'll be whatever the last index is in the table.

  • That will be our, the top of our stack.

  • And you can implement this either way in reverse if you wanted to.

  • It's just easier, because you can just do a simple table.remove to get rid

  • of the--

  • table.remove on that table to get rid of the last state

  • without having to shift back everything.

  • So if we did it starting at index one, you'd have to shift everything back.

  • And it would also be a little bit weird, because you would start at one,

  • and then things would go left.

  • But basically, in order to update whatever our end state is,

  • we just do at #self.states, which will be however large our state stack is,

  • we just call update on that state.

  • And then process AI is here, although we're not using it.

  • But if you had AI, it would be the same thing

  • as basically an update for artificial intelligence for your state.

  • Rather than rendering just one state at a time,

  • we iterate through all of our states here

  • using ipairs, which will iterate through them numerically

  • starting at one going to the end.

  • So we call for istate, and ipairs of self.state, render it,

  • so that will render everything back to front, or bottom to top,

  • and allow us to get this layered look where

  • we have a play state going on underneath for example,

  • and then a dialogue on top.

  • Or we have a battle state going at the very top,

  • and maybe that battle state itself pushes a dialogue

  • state to the top of the stack, or a bunch of other states,

  • a transition, whatever you would like.

  • To clear it, we just reset the table to an empty table.

  • To push a state, we just do an insert on that state, and then

  • we call enter on that state.

  • As we did before, sort of similarly with state machine, when we changed a state,

  • we would call enter on it and exit, but now we're

  • just calling enter when we push.

  • And then to pop, all we do is call exit on whatever

  • the last state is in our state stack, and then just

  • call table.remove on self.states.

  • And by default, when you call table.remove on a table, just a table

  • with no other arguments, it'll remove whatever

  • the last index is of that table.

  • Does that makes sense?

  • Anybody have any questions as to how the state stack works?

  • All right, awesome.

  • So let's take a look then at the start state.

  • So this is the start date.

  • Fairly simple, we just have a couple of text labels,

  • and then we have just a randomly assigned sprite going left to right.

  • How do we think we're achieving the movement?

  • Yep, timer.tween, and then we're just drawing

  • an ellipse, right, pretty simple.

  • And then when we press Enter, note the transition.

  • Notice that there's a fade to white, and then fade to transparent.

  • And so if we recall from when we looked at match three, how do we do this,

  • do we remember?

  • AUDIENCE: [INAUDIBLE]

  • SPEAKER 1: Exactly, and the rectangle was stored where?

  • Sorry, to repeat for the camera, we had a rectangle

  • that filled the entire screen, and we just tween the transparency for it,

  • which is true.

  • The rectangle there before though was stored

  • in whatever state was active at the time, which was like the start date,

  • or the I think begin game state was the name.

  • The actual state that wasn't necessarily relevant at the transition.

  • But using a state stack, we can actually decouple this idea.

  • We can take the concept of a transition, and because imagine

  • if we wanted to make a transition between every single state

  • that existed in our game, right?

  • If we wanted to transition from the battle to the field,

  • or the field to the battle, or whatever else we might want, like the start

  • to the field, we would need a rectangle in every single one

  • of those that has an opacity that we're keeping track of.

  • And that's not necessarily germane to the purpose of that state, right?

  • So because we now have a state stack, we can actually

  • abstract out this idea of a transition, and turn it into its own state.

  • We can have a transition state.

  • And recall that since we're just layering everything

  • [? off ?] all these states, and we're rendering them sequentially,

  • having a state that possesses it's own for example, opacity rectangle,

  • we can just layer that, push that onto the stack, and render that,

  • and it'll give us the illusion of having this transition.

  • But we don't need to actually have it be part of the state

  • that we're trying to transition out of and into.

  • Does that make sense?

  • So let's take a look, for example, at the--

  • let's take a look at the start state first,

  • just so we can see where that actually gets kicked off.

  • So the start state, we kick off some music, we have a sprite,

  • and a sprite x and y.

  • These are values that are relevant to the sprite that's moving, actually,

  • we only have one sprite ever moving left to right.

  • It just gets teleported to the right edge of the screen

  • as soon as it gets taken to one edge.

  • So we only have one sprite, one x and y.

  • And then every three seconds as we see here,

  • we have a callback function that will tween the sprites x to negative 64

  • over 0.2 seconds, so really quickly.

  • And then on finish, teleport it to the right.

  • And then do the same exact thing, but tween it

  • to the center over 0.2 seconds.

  • And then as soon as we press Enter or Return, note this here,

  • we have gStateStack, not a gStateMachine anymore,

  • and we're pushing on to it a fade in state,

  • which takes an RGB, a duration, and a callback function.

  • Now if you look at main.lua, this is relevant,

  • because now we no longer have a state machine, right?

  • We previously had a global state machine, gStateMachine.

  • We would give it a list of indexes into functions, anonymous functions.

  • Those would return the instantiation of a state.

  • And then when we called change, the state machine will

  • index into its list of states, and call that anonymous function,

  • which would have the result of changing the state to some state

  • that we've implemented as a class, right?

  • Now we just create a state stack and we just

  • push a new start state onto the class.

  • And so what this will do is effectively the same thing, only now

  • we can layer things onto the start state, right, or play state,

  • or whatever we want to, and we're not going to ever get rid of it.

  • I mean, we can get rid of it, but we don't have to.

  • For the play state, especially, we want that to pretty much never get

  • popped off the stack, because that's going

  • to preserve all of our information.

  • We're going to default back to that to store all of our character information,

  • our Pokemon information, whatever else we might

  • want to add onto this shell of a game in inventory,

  • et cetera, a world state at large.

  • We want to preserve that and keep that consistent across all of our battle

  • states and so forth.

  • And the battle states will just pull information from that world,

  • from that play state, and construct a battle as needed.

  • Does that makes sense?

  • OK, so here we're effectively, the only real changes we've made to main

  • are no longer a state machine, now we have a state stack,

  • going to push start state, that start state

  • has some behavior just like any other state that we've implemented before.

  • And what I was about to get into before was here on line 36 of the start state,

  • we're pushing another state onto the stack.

  • So there's already a start state, and it's from within the start state itself

  • actually.

  • We're going to take that stack, which is just one level deep,

  • and then we're going to make it two levels deep now.

  • So now we're going to add a fade in state.

  • And the fade in state as we can see, takes in an RGB.

  • Does anybody have a guess as to what the RGB is relevant for?

  • AUDIENCE: [INAUDIBLE]

  • SPEAKER 1: To whether you want to fade in black or white?

  • Yes, any color.

  • We can make this, we can make it a fade to red if we wanted to,

  • or fade to blue.

  • But we don't have to create two separate classes for a fade in white,

  • fade in black, fade out black, fade out white.

  • We can just give it a color.

  • And then I mean, we could even go a level further with this,

  • and make it take in an opacity as well, so that we don't need a fade in state,

  • or a fade out state, we just need a fade state, right?

  • And the fade state will determine, based on whatever

  • last opacity parameter we give it, the right way to fade in and out.

  • But in this case, the difference between the fade in state

  • and the fade out state is one knows to go to 0, one knows to go to 255.

  • That's really the only key difference.

  • And then this 1, the duration, right, we need to tell it how long to fade.

  • And then this last bit here is a function.

  • We're giving it an anonymous function, this is a callback function,

  • because the fade in state by nature is an asynchronous state.

  • It does its behavior over time.

  • So we need a way, we need to tell it, OK,

  • when you finished doing what you're doing, call this bit of code

  • here, so that we can do something not immediately,

  • we can sort of defer its execution till later.

  • And this is something that we'll see common throughout this lecture,

  • because we have this implemented also in like dialogue for example,

  • because we don't know when the user is going to press Spacebar on the dialog

  • state and clear the window.

  • But what if we want that window, the clearing of that dialog

  • to trigger some sort of event, right?

  • For example, if they press Enter when they're in the battle,

  • we want it to go to the next action.

  • We don't necessarily know when it's going to happen,

  • so we'll just pass in an anonymous function to that dialogue state

  • that the class will call whenever the close function is called on that dialog

  • state.

  • It says, when closed, execute this anonymous function.

  • And then that anonymous function can do whatever you want to do.

  • It could pop another other several states onto the stack.

  • But this is what allows us to chain asynchronous behavior.

  • That's the key here.

  • So this anonymous function-- so we'll take a look now actually

  • at the fade in state, just so we can see what this looks like.

  • So we see here, fade in state, right, takes in the color.

  • We saw before, that will be the color we fade to.

  • The length of time that it'll take us to actually perform the transition.

  • And what are we using for the transition do we think, timer.tween, right?

  • So most everything that we'll do actually

  • throughout the course of this lecture that has asynchronous behavior,

  • we can implement it with timer, which is nice.

  • It allows us to fairly succinctly and declaratively tell

  • right out what exactly we want to have happen over time.

  • In this case, we're going to tween over the course of time

  • the opacity of our self to 255.

  • So the fade in is going to fade into the full color of whatever we

  • have given it.

  • So it's going to go from 0, which is shown here by default, to 255.

  • And then as soon as we finish that tween,

  • that is when we pop the fade in state.

  • We're going to pop ourselves off the state effectively, off the stack

  • effectively.

  • And then here, we're calling on fade complete.

  • And that's where the anonymous function is.

  • On fade complete is passed in here.

  • So by putting that function into the finish function of the tween operation,

  • we've allowed ourselves to defer that function that we've written up in the--

  • it's in the start state.

  • We defer the execution of this function until after that tween operation

  • takes place.

  • Does that make sense?

  • OK, awesome.

  • And that's effectively what it is.

  • And that's a common theme that we'll see if you're looking through the distro,

  • you'll see it in a lot of places.

  • Anonymous functions or callback functions

  • rather being passed into things like the dialogs, and the fades,

  • and a few other places.

  • In the take turns state for example, there's

  • a function that takes in at a callback function as well.

  • And that's effectively how you can chain asynchronous behavior that

  • executes over time, rather than it being blocking.

  • Does anybody have any questions so far as to how this works, at all?

  • All right, so when the fade is done--

  • we're still the start state here--

  • at this point, the fade is done, we're executing this anonymous function.

  • We're going to pop the start state off of the stack in this case.

  • And then we're going to push a--

  • we're going to do two pushes here.

  • One is to push a play state, which recall is where the NPC [INAUDIBLE]

  • character walking around.

  • And another one is to push a dialogue state.

  • And so what this will have the effect of doing is rather than us

  • immediately going into the play state and being able to walk around,

  • we're actually put right into a world where

  • there is a message waiting for us that we have

  • to press Enter on in order to continue.

  • And when we press Enter, because we're pushing the play state first,

  • and then the dialogue state, the dialogue state

  • is at the top of the stack, right, because things

  • get pushed onto like a stack of plates.

  • You put a play state plate on the bottom and then another plate on top,

  • and that plate is the dialogue state in this case.

  • And you can only interact with the top--

  • we're only updating the top plate at once in this model.

  • We could obviously make a more complicated state

  • stack that allows us to have several layers of states being updated at once,

  • but for simplicity, we only opted to allow the top layer to be updated.

  • The dialog state is going to be the active state,

  • it's going to be receiving input.

  • All of them are going to be rendered, so we're

  • going to render things from the bottom up.

  • We're going to render the play state, then

  • we're going to render the dialog state, but the dialog state's

  • going to be active.

  • We're only going to be able to press anything on that state.

  • And then lastly, actually, even beyond the dialogue state,

  • we're pushing another state, we're pushing a fade out state.

  • And in this case, it's the opposite of the fade in state,

  • it just takes in an RGB, and we'll go from 255 opacity

  • to zero opacity in that case.

  • And so what that allows us to do for playing right,

  • we're here in the start state, pressing Enter.

  • That's our fade in state was there.

  • And then we pushed to the play state and the dialogue state and the fade

  • out state at once, so you would almost think

  • that we push a fade in and then the fade out,

  • but we have to lay that foundation before we

  • put the fade out state on top of the stack,

  • right, because the top layer gets updated.

  • So we have to push the fade out state on top of all of those.

  • That will get updated, that will fade out,

  • and then we're back to the two states that we push

  • before we pushed the fadeout state.

  • Does that make since?

  • OK.

  • Does anybody have any questions as to how that sort of flow works?

  • Cool.

  • All right, so that's the gist behind, I mean,

  • that's essentially the core of what we're doing today

  • is the state stack pushing multiple states.

  • And then just figuring out the right order

  • the need to push them in to get the desired appearance that you want,

  • right?

  • We push the fade out state while we're in the start state,

  • or fade in state rather.

  • That will take us to white, and then like sort

  • of, almost like underneath the-- behind the curtain, we're popping everything,

  • and then we're adding the play, dialogue, and then another fade out

  • state.

  • And so you sort of have to balance the order that you put things in in order

  • to achieve the desired results.

  • It may not necessarily be exactly as you intuitively

  • think until you think about just how we're updating and rendering

  • things on a stack.

  • And so that's the ultimate hurdle I think in really getting comfortable

  • with the distro, but once you've gotten that, everything else sort of falls

  • into place.

  • That and the sort of abundance of asynchronous functions, as we'll

  • see pretty shortly when we look at GUIs, and how we've implemented

  • a lot of basic GUI functionality.

  • A lot of that is very, very call back driven,

  • just because of the nature of it being based on user input, right?

  • You don't know when the user's going to do any input,

  • so defer whatever happens with that GUI code

  • with the triggers involved when the user presses Spacebar,

  • Enter, and then call that function that you've passed into that GUI widget.

  • All right, so we've taken a look at the state stack.

  • We've taken a look at the start state, the fade in state,

  • let's take a look now at the play state.

  • So the play state--

  • a lot of this is actually very similar to what

  • we did back with Zelda, which is a very similar type of game top down.

  • View, the only difference really with that was RPGs of this nature--

  • Final Fantasy, Pokemon, Dragon Quest, they're

  • tile based to the degree of even your movement is tile based.

  • And so we've striven to implement that with this lecture.

  • So when we move our player, our character,

  • it doesn't have free motion like we did with Zelda for example.

  • So I'll demonstrate this.

  • So I can go to the field state here, the play state, sorry.

  • And then when I move, if I press right, he

  • moves in that direction at a perfect grid interval.

  • So if I move up, I'm taking my hand instantly away,

  • he's going to keep moving, and he's going to stick hard set to this grid.

  • And that's just a sort of trend that these games have implemented.

  • It allows you to stay perfectly aligned with the grid, and helps you

  • I guess certain game--

  • I don't think it's strictly necessary for probably

  • most of the games that choose to implement this.

  • I think it was a symptom of tile based games from the NES and Gameboy

  • era being easier to design and implement,

  • because they're very tile based systems.

  • But I mean, even as an aesthetic choice, I suppose it makes sense,

  • because everything aligns very perfectly.

  • So that's the core difference really with the field state in this game.

  • So how can we go about implementing a grid aligned movement system like this

  • with our player relative to how we did it in Zelda for example?

  • How do we think-- sure yeah?

  • AUDIENCE: So we don't x and y's, we just have the tile positions.

  • SPEAKER 1: So you don't have x and y's, we just have the tile positions.

  • Close, I would say it's more focused on the tile positions,

  • but you still do need an x and a y, because you still

  • need to draw that sprite at that exact position.

  • Right, yes Tony?

  • AUDIENCE: Well, when you need to move the sprite, instead

  • of moving at every update, you tween it between the two tile locations.

  • SPEAKER 1: Exactly, so rather than moving the sprite

  • at exact pixel positions per update, you tween the sprite when you receive input

  • to a specific location.

  • And then we actually stop input at that point as well.

  • There's no use for us having any input when we're not exactly at a given tile,

  • so we disable input while he's walking effectively.

  • And so this is implemented, if we're looking at the distro in the entity

  • class, there is a--

  • I believe it's in here--

  • maybe player, hold on.

  • Oh sorry, no it's entity walk state, not the entity.

  • Entity is just a container for the information that's relevant.

  • So here in the entity walk state, we have attempt move.

  • And so what attempt move does is essentially

  • it looks to make sure that we're within the bounds of the map, right?

  • And then if we are--

  • every entity in this game now has a map y and x, and a regular y and x.

  • And so the regular y and x, we still need in order

  • to draw our sprite at a specific location on the map.

  • We still need to draw it going between 240 something and 230 something, right?

  • But we need a map x and a map y to basically say, OK,

  • the sprite should be at this position on the map.

  • And then we'll just tween it between that position times 16,

  • and it's the position plus or minus x or y

  • times 16, which will give us the exact x and y value that we

  • need to draw it onto the map.

  • And so that's what we're doing here.

  • So were going to call attempt move on input.

  • So anytime we do any input--

  • and this is done in the player like idle, or player, yeah, player

  • idle class--

  • player idle state.

  • We change the animation to write animation.

  • And then we get it's current map x and y.

  • And then based on whatever direction the player is looking,

  • or the entity is looking, we could use this for an NPC class, or the like.

  • We just modify our 2x and 2y.

  • So to 2x and 2y is going to be the value that we're tweening towards times 16,

  • right?

  • And so if we're trying to go outside the map boundaries,

  • just changing us back to idle won't let us do that.

  • Otherwise, set our map y and map x to that position immediately,

  • right, because that's just a minus or plus one operation.

  • And then over the course of 0.5 seconds, actually tween to that value.

  • And we can see here, we're tweening to the tile size,

  • and actually to the tile size minus self.entity.height divided by 2.

  • Do we know why that is?

  • We do that, because if we're looking at the field, we can see here,

  • notice that we're not perfectly lined up with the grass, right?

  • It's kind of like we're halfway above it,

  • because it looks just a little bit more natural this way,

  • this is how most sort of games look.

  • And if you're in a game like this and you're

  • like walking up against a wall for example,

  • this will allow you to sort of look as if you're

  • up against the wall rather than sort of being

  • at the edge of where the bottom of the wall is, and kind of looks unnatural.

  • Hence why we minus 1/2 our height right there.

  • And then when we're finished, we actually

  • test to see whether we're still pressing a key, and if we are,

  • then change our state to walk again, which

  • we'll just repeat this process depending on which direction we're looking at.

  • And that's effectively it.

  • And that's what allows us to get this grid based movement.

  • Any questions as to how this works?

  • Cool.

  • Let's take a look then at the play state, let's go back to it.

  • So we have a level, the level contains our entity,

  • and it can contain all of our entities, and whatever

  • objects you want it to contain.

  • In this case, when we're in the play state as well,

  • we're going to check to see if we press P,

  • because that's recall, where we can heal our Pokemon,

  • just a little game hack just to make demoing it a little bit easier.

  • But if we press P, we play the heal sound, we take our--

  • and we'll look a little bit more detail as to this, all this in a little bit.

  • But self.level.player.pa rty.pokemon@index1.currenthp

  • equals self.level.player.party.pokemon@1.hp.

  • So the difference is current HP is whatever you currently have,

  • you could have taken damage.

  • HP is whatever your max HP is.

  • And this is like in a nutshell how you get

  • like stat changes in games and RPGs, and health and mp differences.

  • You've got to keep track of a max and a current value for all of those things,

  • and then depending on whether you're buffed or debugged,

  • or whether you have taken damage or not, or used spells or not,

  • you can have an accurate reflection of where

  • your character is and then always return back to that state

  • whenever you need to.

  • The interesting thing here, the slightly more complicated thing

  • is when we press P, we want to show a dialog that says,

  • and I'll demonstrate this, we want to show a dialog just

  • like this one that says, we press P, your Pokemon has been healed, right?

  • Now I can't move.

  • I'm pressing the arrow keys.

  • I can't move my character at all, because this dialog state--

  • we're in a new state, well, we've pushed a new state onto the state stack.

  • And that's the dialog state here, which has taken a value.

  • And because it's the top layer of the stack, it can't get updated,

  • or it's being updated, and we can't update the play state, right,

  • based on how we've modeled our state stacks operation, or how it works.

  • And then as soon as I press Enter, it gets popped off,

  • we've just popped it off.

  • Now the place states at the top, I can move again.

  • So that's what's going on.

  • So the dialog state then is actually very similar in a sense, to the fade

  • in and fade out state in that, notice that it takes an anonymous function.

  • When does this anonymous function get called?

  • Do we know?

  • At the end of what?

  • AUDIENCE: [INAUDIBLE]

  • SPEAKER 1: Yeah, well, when the user closes the dialog box, correct.

  • So let's take a look at the dialog state then.

  • And we can see, it's actually pretty simple, it's pretty small.

  • We have a text that it takes and a callback, right?

  • The text is used here.

  • We instantiate, and this we'll see in detail when we start looking at GUIs,

  • and all the widgets they've implemented here.

  • This text box gets put at a hard coded position, and it receives this text.

  • And then we set our self.callback to that callback function.

  • If we have closed the text box, meaning, we're

  • looking to see at self.textbox.isClosed, which

  • is a function of the text box class.

  • If it's closed, then execute self.callback, and then pop

  • this dialog state of the stack, right?

  • So it's similar in a sense, to the fade in and fade out,

  • and then it takes anonymous function.

  • The only difference is in how it gets executed.

  • With the fade in state, the anonymous function

  • was called at the end of the finish function, which

  • is part of the tween object.

  • In this case, we're executing the callback function explicitly when

  • we've closed the text box.

  • So we're waiting for user input versus waiting for some asynchronous operation

  • to finish.

  • And then of course, we call text box render,

  • and then we'll see all of these methods shortly as part of these widgets,

  • but at a glance, this is all that's really happening with the dialog state.

  • Very simple, using the same pattern that we've

  • seen of deferring future behavior to anonymous functions.

  • Any questions as to how this works, or anything so far?

  • Cool.

  • All right, let's take a look back at the play state,

  • I believe we're getting close to being finished with the play state.

  • Yes, so everything, that's basically what the play state is in this game.

  • And then a lot of what's going on takes place in a level as well.

  • So in a nutshell, we have two maps, two layers, right,

  • because the grass in the tile sheet is its own sort of [? alphaed ?]

  • out object, it's got transparency around it.

  • We keep a layer of the base, a layer of the grass underneath,

  • and then a separate layer for the tall grass.

  • And then we can just look and to see when

  • we're walking in the player walk state when we've walked over tall grass.

  • And then what do we need to do to start a random encounter?

  • Yes?

  • AUDIENCE: [INAUDIBLE]

  • SPEAKER 1: Yes, how do we initiated though?

  • That what are we looking for?

  • We do push a battle state as soon as we've triggered one,

  • but how do we trigger one?

  • What are we looking for?

  • AUDIENCE: [INAUDIBLE] player is in the grass.

  • I don't know if it's on moving to a new grass,

  • or if it's time spent in the grass.

  • Yeah, we do a random chance whenever the players on grass.

  • And it's whenever they start to walk and there on grass in this case.

  • But you can do it either way, you can do it

  • when they're leaving the grass, walking into the grass.

  • In this case, it's whenever you press the button,

  • and they happen to be on grass, it'll do a random chance, one in 10.

  • And if it's equal to 1, 10% chance it'll trigger an encounter.

  • So that's the gist behind triggering a random encounter,

  • and a lot of these games really--

  • some games do it differently.

  • They'll sometimes make it more likely the more steps you've taken,

  • they'll like sort of keep a counter to say, oh, I've taken 100 steps,

  • it should be a lot more likely now.

  • Some games will just be completely random, 1 in 10, 1 in 5,

  • depending on how the developers decided to implement their game.

  • The former is a bit more robust.

  • But for simplicity, we just chose, math.random10 equals 1.

  • So yeah, we create the tile maps here, pretty straightforward.

  • And then the actual random encountering takes place in the player walk state.

  • So here we have check for encounter.

  • And so what this does is whenever we enter the walk state, which

  • is we press the button to enter, or to walk,

  • this entire function gets called, because we

  • do the transition to the player walk state in the state machine.

  • All of the entities are still using just a regular state machine

  • not a state stack.

  • Wasn't necessary for this demonstration, though I'm sure

  • there are some used cases for using a state stack for an entity.

  • In this case, we're just using a regular state machine.

  • So when we change to the walk state, we are calling enter as we've seen.

  • And then we call self, checkForEncounter.

  • And so self, checkForEncounter will set a flag

  • if we have not started an encounter basically and will allow us to move.

  • And if we have checked for an encounter, it will,

  • or if we have triggered an encounter, it will push in checkForEncounter,

  • it'll actually push a battle state onto the stack.

  • So checkForEncounter just basically does what we said before.

  • If the grass layer, because we have two layers,

  • right, we have the base layer and the grass layer.

  • So if the layer at yx where yx is are entities map x and map y.

  • If the ID of that is equal to tall grass,

  • and we have just a global constant table called

  • Tile IDs, which has all these IDs.

  • And math.random10 is equal to 1, OK, change the entity state to idle,

  • so don't let them keep walking.

  • Pause the field music rather than stopping,

  • so that way when we come back to the field later and we press play,

  • it will be at the exact point that it was before.

  • Triggered the battle music, and then, we've seen this already, fade in state,

  • push to the stack, right?

  • So over one second, we're going to fade to white.

  • So this will have the effect of the music starting,

  • but we're fading to white right away, which

  • is very sort of similar to how most RPGs do it.

  • And then we have our callback function, which

  • will execute as soon as the fade in state's done, right?

  • In this case, push to battle state.

  • Battle state takes an entity, and the entity

  • has all of our Pokemon information, that's

  • why we're passing that into the battle state.

  • So the battle state can say, oh, what Pokemon do you have?

  • OK, I'll be able to look at your party and say, OK, your first Pokemon

  • is this, send him out to battle, et cetera, right?

  • And then lastly, push a fade out state, right,

  • because now we've got the battle state on top of the play state,

  • but we want to fade into it, right?

  • So we're going to fade, we're going to put the battle state first,

  • and then because we're using a stack, we're

  • going to put the fate out state on top of that, and then fade out to that,

  • pop that off the stack.

  • And then we have our battle state that we just pushed, right?

  • And then self.encounterFound get's set here.

  • And that's creating an encounter, checking randomly,

  • pushing the right things under the stack, battle state,

  • fade state, fade in, fade out.

  • And then you're set to go.

  • So that's effectively what the--

  • it's known in RPGs as the field versus the battle or encounter state.

  • Even though we're calling it play state here, we've left the field,

  • we've gone into the battle at this point.

  • And so now we've seen basically everything

  • that the field has to offer us.

  • And we've covered everything that's relevant there.

  • So we're going to take a break now for five to 10 minutes,

  • and then when we get back from the break,

  • we'll talk about GUI elements, panels, text boxes,

  • and then we'll dive into the sort of mechanics of the battle state.

  • All right, welcome back to lecture 7, Pokemon.

  • So before the break, we talked about the play state,

  • we talked about the states stack more importantly,

  • and then we talked about how a anonymous functions are

  • sort of the backbone to how we get a lot of this asynchronous

  • and deferred behavior for our game, which is very common in RPGs,

  • and I mean, a lot of genres, a lot of complicated genres of this sort.

  • Another big key part of games like this are the graphical user

  • interfaces, or GUIs as they're shortened to.

  • Things like panels on the screen, things like labels--

  • text labels that move around, things like lists, text boxes, scroll bars,

  • and you can get a lot crazier with it.

  • In this particular lecture, we'll be talking mostly about panels, labels,

  • text boxes, and scroll bars--

  • progress bars rather, not scroll bars.

  • But the sort of the first I think corner--

  • or the first sort of like keystone GUI widget

  • that we should take into consideration is the panel.

  • So a panel is [INAUDIBLE].

  • So if we look at this in a game--

  • just pretend this is a panel I guess.

  • So this is effectively all a panel is, right?

  • It's just sort of a rectangle.

  • It allows us to-- if you're looking at most user interfaces,

  • like text boxes on your screen, or if you're on Facebook

  • and you're looking at almost anything, like your little message

  • window, a lot of those things at the very core, the very bottom,

  • the foundational part is just a panel.

  • So any guesses to how in Love2D, we can make a simple panel?

  • AUDIENCE: Two rectangles of different colors.

  • SPEAKER 1: Two rectangles of different colors,

  • that's exactly what we end up doing.

  • So that's effectively how we can make a panel.

  • There's another way of making a panel, which we won't do in this lecture,

  • but it's called--

  • we use as a construct called a nine patch.

  • So a nine patch is--

  • imagine taking this little image here, and it's of some arbitrary size,

  • but it's very small.

  • And this is very similar to how a lot of games

  • implemented their panels or their graphical user

  • interfaces back in the 80s and 90s, I mean, to a lot of games till this day.

  • But back when hardware was fundamentally tile based,

  • you could take a image like this, split it up into nine pieces--

  • nine patch is where the terminology comes from.

  • And sort of similar to how we actually constructed the Zelda dungeon, recall,

  • where you have corner pieces, and then a top, bottom, right, and left side.

  • You just layer this, one of each of these,

  • first off, right, of the corner pieces.

  • And then however many you need of these on the sides

  • to create this rectangle, right?

  • So imagine we've created--

  • these are all, if we can visualize these as being a bunch of tiles, right?

  • So just imagine that we've taken these corner pieces,

  • these are the corner pieces, we've taken one of each of those.

  • And then we take these side pieces, and we just like draw a bunch of them

  • like that.

  • And then we take this centerpiece, and then we

  • can either layer it, or tile it a bunch of times, or just stretch it.

  • And stretching it has a bunch of nice bonuses associated with it depending

  • on how you've set your filter mode, love.graphics.setdefaultfilter,

  • if you set it to bilinear versus nearest,

  • you can actually get a nice gradient.

  • And if you set it to nearest, you get a nice pixelated look.

  • But you'll see this often, and Unity has nice support for this.

  • Take an image that has maybe more complicated than you could

  • get with just two rectangles, right?

  • Something that actually has a design and maybe a gradient color, and actually

  • layer--

  • I mean, create a arbitrarily sized text box to fit your needs.

  • And if these aren't even increments or whatever your tile size

  • is on your 9 patch, you could just scale the top, bottom, left, and right side

  • as well just to keep it scaled, the centerpiece.

  • So does that makes sense.

  • So this is common, we won't be using that in our lecture,

  • but it's a very, very common piece to a lot of graphical user interface design.

  • In a lot of games, you'll see it a lot if you get more into game development,

  • so it's definitely worth talking about.

  • Another piece that we'll be talking about today is the text box.

  • So I mean, what's a guess as to what the text box,

  • how we can implement a text box, and how we will implement a text box?

  • So what foundational piece can we start with?

  • We already have-- yeah?

  • AUDIENCE: You just put use the love print

  • to the screen over one of those boxes.

  • SPEAKER 1: So use the love print to the screen over one of the boxes, exactly.

  • Yep.

  • So maintain a list of text items, right, text.

  • And then just draw them inside a panel, and there's a text box.

  • You've taken two ideas, and sort of mix them together.

  • A selection is kind of the same thing.

  • It's a the only difference being that with a selection--

  • so a selection is another thing if we think about,

  • for example a menu where we have fight, and like run,

  • and it may be in a more fleshed out game, we have like an item thing,

  • right?

  • So that's a menu effectively.

  • It is very similar to what we get with a text box,

  • but it's got a set of ingredients here, fight, item, run,

  • which they aren't set to wrap, they're not one like contiguous set of text.

  • It's just a bunch of items.

  • And then nice thing about a selection is that you can

  • have a cursor on your selection, right?

  • And then what do we need to associate with like, for example,

  • if we want this to actually do something,

  • and if we think about what we've been doing so far,

  • how do we go about implementing functionality

  • with a selection like this?

  • Like what needs to get associated with each of those entries in our selection?

  • Callback function, right?

  • Just as we've done with everything else.

  • If you have a fight item here, each of these, if we think of the selection

  • as being just this part of what we're looking at,

  • right, because this background part is just a panel, we don't care about that.

  • We care about the selection at the moment.

  • The selection is the items and the arrow, right?

  • When as we'll see in the assignment, your goal will actually

  • be to take selection and get rid of the arrow functionality,

  • because for the assignment, you don't need

  • or want to have a selection active, a cursor active.

  • You just want a list of things.

  • But based on what the cursor is pointing at and when we press Enter or whatnot,

  • we should index into the selection, and then

  • execute a callback that's associated with each of these items.

  • And that's how we can get behavior out of the selection,

  • rather than just being a list of things that we render to the screen.

  • If we have fight, and we click Enter, a callback

  • is set to maybe push a state onto the stack that

  • will trigger an interaction between the two entities on the screen, right?

  • The first one will attack the second one,

  • the second one will attack the first one.

  • And that's sort of its own asynchronous set of states that do its own thing,

  • but it's kicked off via an anonymous function

  • that we've associated with each of these things, right?

  • An item pushes another state, which is like an item mini state, where then you

  • open up a brand new set of menus that you can look through all your items,

  • and each of those items has a callback associated with it, right?

  • Your potion has a callback associated with it

  • that says, when I click on this, either by default,

  • just restore the HP of my active Pokemon,

  • or let me choose who to restore.

  • So therefore, push another state, which is like a select Pokemon

  • screen with its own set of callbacks associated with each of those.

  • It's just in order to get all of this sort of complicated behavior

  • that you need to, it's really ultimately just pushing states

  • and adding callback functions to all of these different options

  • that you can select.

  • And then run, push a fade state, and then pop this state,

  • and then push a fade out state.

  • And that's really all we're doing.

  • And so this look at all of these GUI widgets

  • here is just sort of a conceptual look, but we'll take a look very shortly

  • at some actual implementation.

  • The last one that I want to look at is the progress bar.

  • So a progress bar for example, the HP that we've

  • seen in the actual battle where when we take damage, it goes from right to

  • left.

  • Any guesses as to how we've implemented a progress bar?

  • Yes, Tony?

  • AUDIENCE: Once again, two rectangles.

  • SPEAKER 1: Two rectangles, yes, exactly.

  • One, and then the nice thing about rectangles in Love2D

  • is you can set the edges on them to be rounded or not

  • via an optional parameter.

  • So without anything more complicated than a rectangle

  • we can just create these sort of almost ellipsoid progress

  • bars, very simple progress bars.

  • Ones the red, right, the red that's the background.

  • And then ones the outline, the black.

  • And one is set to fill with the first parameter,

  • one's set to line with the first parameter.

  • Now how do we go about animating whether or not,

  • how do we animate the decreasing amount of health when we take damage?

  • Yes?

  • AUDIENCE: Between the width.

  • SPEAKER 1: Between the width, exactly.

  • And what are we tweening it by?

  • How are we tweening it?

  • How would we calculate how much we need to tween it?

  • AUDIENCE: Well, you could just have your width equal your health remaining.

  • SPEAKER 1: If your width is set to equal your health remaining,

  • then your health is maybe 10.

  • And you want your health bar to be like 100 pixels

  • long, how is that going to work though?

  • AUDIENCE: Multiply it.

  • SPEAKER 1: You could multiply it, but if you

  • know the width that you want your progress bar to be,

  • you can just multiply the width by the ratio of the max

  • value of your HP, or sorry, the ratio of your current HP over your max HP,

  • right?

  • So if you're missing-- if you have 50 HP, and you're missing 5 HP,

  • your ratio is 45 over 50.

  • And if you multiply that by your width, you get the exact amount of width

  • that you need regardless of how wide you want the bar to be,

  • if you want to be 1,000 pixels, if you want it to be 50 pixels,

  • as long as you multiply current health over max health times the width,

  • you'll get that ratio no matter what.

  • Does that makes sense?

  • Cool.

  • So that's a look at all the GUI widgets that we're looking at, how they sort

  • relate to what we're doing.

  • We'll take a look at their implementation here.

  • So I'm going to go ahead and open up the panel.

  • And I'm going to move a little bit quickly

  • so we can get into sort of the meat of the battle here.

  • The panel is as we've said before just two rectangles, right?

  • It takes in an xy within a height.

  • And then we would just draw two rectangles.

  • One is larger than the other.

  • The bottom rectangle is slightly larger than the top rectangle.

  • So the first rectangle gets drawn and it's whitish.

  • And then-- oh, I'm sorry, sorry about that.

  • We have a xy within a height.

  • And then we're drawing two rectangles to the screen.

  • We have the background rectangle, which is drawn first,

  • which is going to be the full xy width and height of the panel.

  • And then we're going to draw that at a white color, and then draw--

  • in the context of this game-- we're drawing everything at the same color,

  • but we can change the color.

  • If we wanted to parameterize it, we could do that.

  • We could set, we could have a color option here in the constructor.

  • We're not doing that, we're just drawing everything the same color.

  • But that's how you would get like customized menus,

  • some RPGs let you do that.

  • And then what we're doing here is we're just within a small slightly smaller

  • boundary.

  • So just two pixels smaller on the x and y.

  • Where you are going to draw the second rectangle,

  • which is a kind of dark shade of gray.

  • And that is a panel, that is all panel is.

  • And then we could just have a function called

  • toggle, which sets it to visible or not visible.

  • And if it's visible, get rid of it, or if it's visible, sorry, draw it.

  • Otherwise, don't draw anything when it gets rendered.

  • So that's a panel in a nutshell.

  • Any questions?

  • Cool.

  • So the next thing that we should look at is the text box.

  • So a text box--

  • so the text box is a little bit more complicated than a panel.

  • A text box in a nutshell needs to take in some arbitrary body of text,

  • and it needs to chop it up based on how wide your text box is.

  • And if it surpasses the height of your text box,

  • right, ideally, you should page your text so that you can press Space bar,

  • Enter and go through pages of text until you've exhausted all of your text.

  • And you press Enter one last time, and you get rid of that text box.

  • And so we have a panel here, which we have an xy width and height

  • in our constructor for the text box.

  • And we have our text as well.

  • And then we have a font if we want to explicitly decide

  • what font we want to use.

  • In this case, or at large, we're going to say

  • that we instantiate a panel at xy width and height, nothing too fancy.

  • And then the fancyish part, the slightly more complicated part

  • is here on line 20 where we say, underscore

  • self.textChucks gets self.font, getWrap, self.text, self.width minus 12.

  • So anybody know what this function does or want to take a guess?

  • Yes?

  • AUDIENCE: Is that the page thing you were talking about?

  • SPEAKER 1: Exactly, it's the paging of the text.

  • Is the chunking of the text rather.

  • Not the paging of the text, so much as is the chunking of the text, which

  • we will use to page the text.

  • So we take some you know arbitrarily large body of text,

  • it can be as large as we want it to be, and given--

  • this is actually a function of to Love2D font object.

  • So this is given to us from Love2D.

  • Get wrap will return two values, the second of which

  • is all of the pieces of text that the main big body is divided into

  • based on the width.

  • So this self.width of minus 12, that's how wide

  • it's going to divide our text into chunks of up to,

  • it could be slightly smaller than, because it divides it

  • based on the word.

  • But no piece of text will ever exceed self.width minus 12 width.

  • And this will allow us to then render several lines of text within our text

  • box, and they will never exceed the boundary, right?

  • And so the paging functionality is actually in next chunks.

  • So we call self next here at the end of [? knitt ?] function.

  • And then self next basically checks to see, OK, are we at the end of the text?

  • If we are, then we're not going to display any text,

  • and we're going to close the window.

  • We're going to close the panel.

  • But if we are not at the end of the text, like we still get text left,

  • what we want to do is new table.

  • And then we're going to, up to three iterations,

  • we keep track of where we are in our chunks, right?

  • We get self.text chunks equal to all of those chunks, right.

  • And that could be an arbitrary number.

  • It can be only one chunk, there could be like 30 chunks, right?

  • We need a counter to keep track of where we

  • are in terms of like based on what page we're on, right,

  • and however many lines we rendered to the screen thus far.

  • So starting at I, and I get's chunk counter,

  • and chunk counter will get incremented by three

  • every time we call next chunks, which is every page.

  • We could have easily just called this next page as well.

  • It's going to insert into that chunks table

  • that we just created, self.textChucks at i.

  • And once we've reached the number of chunks total

  • that we returned from get wrap, we're going to flag end of text

  • as being true, and then we're going to return it.

  • And so what this will do is, eventually, we're

  • going to be equal to the number of chunks that we got from font get wrap,

  • right?

  • And once we are, that will signal with next

  • that it's time to close the text box, because end of text

  • will have been set to true at the end of that last chunking process.

  • And then we can see here, when we update text box,

  • and that whenever it's on the top of the stack,

  • remember, we're looking for a Space or an Enter press,

  • and then we just call self next.

  • And that will have the effect of eventually closing our text box.

  • And then is closed recall, we looked at that earlier, we checked

  • to see it is the text box closed?

  • And that's just a flag that we set here.

  • And then for rendering purposes, we render the panel first.

  • And then for each of our displaying chunks, so we only have up to three

  • displaying chunks at one time, which gets set by the next chunks function.

  • We just print that to the screen using i as a multiplier on our y.

  • And so that will render up two or three lines, i, i plus 1, i plus 2.

  • Any questions as to how the text box works?

  • It's a little more work than the panel for sure,

  • but it's fairly straightforward.

  • We're just keeping a list of a bunch of text things,

  • and then we're just chunking them based on how wide the text

  • box is, the dimensions thereof.

  • And then let's take one look at this selection.

  • So a selection is basically, a list of text items with a cursor, right?

  • And as I said before when we were looking at the screen over there,

  • each of those text items has a text value and a callback function.

  • And the callback function is what allows us

  • to assign behavior to this selection object beyond just displaying things,

  • right?

  • Because when you have a menu, when you have a selection

  • and you select something, you want behavior to happen, right?

  • So each of these items indef.items will expect to have a callback function.

  • And then here, when we update the selection, what we're doing

  • is we're updating whatever our current selection is, which is just

  • a number between 1 and the number of items in that selection making sure

  • that if we're at one and we go minus one, that we go back to the bottom.

  • And if we're at the bottom when we press, and we go up,

  • we go back to the top.

  • And we play sounds, cutesy, things like that.

  • And then for each--

  • and for our selection here, from one to number of items,

  • we calculate how much padding that we need.

  • And we draw the cursor at our current selection,

  • and then we draw each item based on i and whatever our gap width is

  • of our panel, which we assign it to.

  • So we divide our panel up, and then basically just

  • keep track of where current y is and draw

  • the actual selection and the cursor if that's

  • the current selection to the screen.

  • Any questions as to how a selection sort of works?

  • Notice here, if we press Return, if our selection is being updated,

  • self.items at self.currentSelection.onSelect.

  • So it's expected that that item will have an onSelect function, which

  • is that callback function.

  • OK, and lastly, we'll take a look at the menu.

  • And then we'll finally take a look at the battle,

  • which is where sort of everything kind of comes together with all of this.

  • And that'll be it.

  • And then we'll talk about the assignment.

  • So the menu is a panel and a selection together.

  • That's the gist behind what a menu is in this game.

  • You can define a menu to be a lot of things,

  • and you can get a lot more complicated with a menu, but in this example,

  • in this implementation, we're just saying a menu is a selection

  • and a panel put together as one item.

  • And we've seen it in the game, [? if we're ?] going to run it.

  • That's just a text box.

  • Going to look for a battle.

  • OK, so here's a battle.

  • That's just an empty panel at the bottom, regular panel,

  • but now it's a text box.

  • We push the text box onto the stack.

  • Push another text box onto the stack.

  • And so this is a menu right here.

  • Notice that there is a cursor and there's

  • a selection embedded within a panel.

  • And each of those items, the fight and the run,

  • those have a callback associated with them.

  • The purpose of the fight callback is to trigger a new state

  • where the two Pokemon asynchronously attack each other, in chain behavior

  • one after the other.

  • And then run pushes a dialogue, then pushes a fade state,

  • then pops both of them, and then pushes a fade out state

  • and puts us back to the play state.

  • So that's effectively what's going on and that's

  • an example of what the menu looked like.

  • And so a menu, just a selection with a panel put together.

  • When we draw the menu, we draw the panel and then the selection.

  • And then when we update the menu, we only update the selection,

  • because that's all we care about.

  • And that's basically it.

  • And so the menu itself will get a def, that def

  • should have items, that items will get passed to the selection.

  • That's pretty much, that's it for the--

  • oh, progress bar as well.

  • We'll look at progress bars when we get to the actual battle state.

  • So now, let's take a look at a few of the classes and data structures

  • that are pertinent to the Pokemon themselves.

  • So if you look at party as are first class, very

  • simple class, literally just this--

  • self.pokemon is def.pokemon is just a container at this point.

  • You can take this--

  • I mean, even in I think a fully fleshed game,

  • you wouldn't really need much more than just this.

  • But if you needed to expand upon this idea at all

  • and you know preserve metadata that exists for the party,

  • this would be a perfect way to do it.

  • The actual pokemon class itself is not a whole lot more than effectively

  • a bunch of stats.

  • And that's a lot of what an RPG is.

  • This genre is-- it's mostly just numbers.

  • You're just comparing numbers against numbers

  • and then adding a roll of the dice.

  • That's effectively, that's what Dungeons & Dragons, a lot of it is.

  • And that's-- yes?

  • AUDIENCE: Would it make more sense to store

  • just delta per level and your initial one, so you can have fewer variables?

  • SPEAKER 1: Say it one more time.

  • AUDIENCE: Wouldn't it make more sense, instead

  • of storing your HP and everything for each level

  • to store your initial stats in each area,

  • and how much you would go up per level.

  • SPEAKER 1: Would it make more sense to store the amount that you go up

  • per level for your Pokemon?

  • Yes, that is what we're doing.

  • So we have a base--

  • so here's how the split works for the stats in this case, right?

  • We have base HP, base attack, base defense, and base speed.

  • A level 1 Pokemon has-- a level 0 Pokemon

  • has these stats of this species, right?

  • Every Bamboon or whatever Pokemon that we choose

  • will have whatever we've allocated it to be it's base HP,

  • base, attack, base defense, base speed.

  • And then the thing about Pokemon and I mean, a lot of RPGs

  • will sort of do this thing, but we need some way

  • of leveling up the Pokemon in an necessarily non-deterministic way.

  • Like two Piggies that level up may not have the same stats, right?

  • One might have slightly higher attack than the other,

  • one might have slightly higher defense than the other.

  • We do this using what's called an IV, and that's what Pokemon itself does.

  • And it's short for individual value, this is sort of

  • like the DNA of your Pokemon, right?

  • So this HP IV is separate from your base attack, base speed, base et cetera.

  • And this basically, it gets compared against a dice roll every time

  • you level up three times.

  • And this is how I've programmed it, it's not necessarily

  • how Pokemon itself does it, but you will roll a dice six times,

  • or three times, one through six like a normal die.

  • And you'll look to see if that roll is greater than your IV, right?

  • Or it'll check to see whether your IV is less than or equal to that dice roll.

  • And if it is--

  • or sorry, if it's greater than or equal to that dice roll.

  • And if it is, it will increment that stat by 1 for those three dice rolls.

  • So you can get up to three more, or you can increase the stat

  • by up to three times per level.

  • But you can only have an IV up to five.

  • So you're rolling against a six, and you will occasionally not roll a 6.

  • It checks to see whether or not the IV is

  • greater than or equal to the dice roll.

  • And if it's not greater than or equal to the dice roll in the event

  • that it is a six, or if the IV is up to a four

  • for example, which means a five or six will go against it,

  • then it will not get a stat increase.

  • And this is a sort of simple way of implementing this DNA based system.

  • It's randomized, but it's a weighted, right?

  • If you have a higher IV, you have a higher likelihood

  • of being greater than or equal to the dice roll.

  • And so that's how we implement stat increases.

  • And then we need a way of keeping track of what our stats are,

  • like our actual stats.

  • So our actual HP, our actual attack, our actual defense,

  • and actual speed that's been calculated level by level,

  • we need a way to keep track of that.

  • We need level, we need our current XP, and then

  • we need our-- and the amount of XP to gain

  • a level, which will get higher and higher per level, as you can see here,

  • because it takes in the self.level times self.level.

  • And then it multiplies that by five times 0.75.

  • And then your current HP.

  • So we're really not storing our value level by level,

  • we need the base because we need to know what our base was.

  • I mean, we could effectively globally reference these variables,

  • but it's minor efficiency gains at that point.

  • But we need the IVs and we need the--

  • I mean, we need a reference to the IVs, we need a reference to the base HP,

  • and we need to keep track of whatever our actual stats are,

  • and then our current HP always, because our current HP

  • can differ from our actual HP.

  • And in the actual game, you can have your attack, defense,

  • and speed also vary match by match, because you

  • have moves that lower your speed, lower your attack,

  • lower your defense, et cetera.

  • In this case, we haven't implemented that,

  • so we don't have a current attack, current defense.

  • But in a more complete implementation, you would have that sort of thing.

  • Does that sort of answer your question?

  • Is that in the right vain?

  • OK.

  • And so here's the level up code.

  • So like I said, three dice rolls, one to three.

  • If six is less than or equal to our IV, so it could be a six, in which case,

  • it would be greater than what are max IV could possibly be.

  • IVs range from one to five, but if it's less than or equal to that IV,

  • then we're going to consider that a stat increase.

  • It's a weighted odd to determine whether or not we get a stat boost.

  • And it does this for every stat, and then it returns all of the increases.

  • And this is relevant, this line 95 for a return HP increase,

  • return attack increase, defense increase.

  • This will be relevant for assignment 7, because your goal is

  • to take these increases and actually display them to this user in the battle

  • state when he gets a victory, or he or she gets a victory

  • and has gained a level.

  • You will display a menu with a selection that has all of these things,

  • and you'll need this value.

  • So it returns these values here, and you'll be calling this function any way

  • from your battle state stats level up.

  • Or we'll be calling level up rather, which returns self stats level up.

  • And that's all a Pokemon is.

  • It's effectively mostly a data structure.

  • And we use this in our battles to throw dice effectively back and forth,

  • and have a victor and a loser, and then gain XP and gain levels that way.

  • So any questions as to how a Pokemon object class works?

  • Cool.

  • We'll take a quick look at what the actual definitions look like,

  • which you can probably take a guess.

  • It's very simple, just key names.

  • And then we have the actual name, we have the sprite names,

  • we have the HP, attack, defense--

  • all the things that get put into the actual object,

  • they need a reference to in the definitions.

  • And so Pokemon ultimately are just this, they're

  • just data, right, which is what we talked about in a prior lecture, data

  • driven design.

  • The more you can take all of your Pokemon

  • and make them into, or anything, Pokemon or any object,

  • and turn it into an easy to write data structure like this,

  • the easier it is for you to add more.

  • We could easily add, it wouldn't take too long to create 150 of these.

  • I mean, they wouldn't be all that interesting,

  • because we don't have moves implemented yet.

  • But in an ideal world, we'd find a way to also model moves as data,

  • and therefore, you can just link moves to your data structure,

  • to your Pokemon object like this.

  • Yes Tony, did you have a question?

  • AUDIENCE: Well, I just wanted to mention that the paradox

  • games are very good about that.

  • [INAUDIBLE]

  • SPEAKER 1: Oh, like Crusader Kings?

  • AUDIENCE: Yeah.

  • SPEAKER 1: The comment was Paradox Games are very good about data driven design.

  • I'm assuming you've dug through their files?

  • AUDIENCE: To some extent, and also it's just

  • if you play their games for awhile, it's everywhere, like to the extent

  • that sometimes on the Wiki, they put the source code up.

  • SPEAKER 1: Oh, yeah.

  • Yeah, no, it's just good game design.

  • Ultimately, if you want to--

  • and their games are large, they have a lot of content.

  • If you want to have a lot of content in your game,

  • you need to find a way to take the burden off the programmer

  • and put it onto the designer, or at least make

  • it easier for the programmer, because making source code

  • and debugging source code all day long, especially for very complicated things

  • is not easy.

  • And it's ultimately not a desired thing to do, right?

  • It's a lot easier for me to whip up a new creature in 10 lines of code

  • here and feel good about it than hard coding a lot of these sort of things,

  • right?

  • So shifting as much of it to data as you possibly can should be your end goal.

  • So that's what Pokemon defs look like.

  • Before we get into the actual battle, we want

  • to take a look at what a battle sprite is.

  • So a battle sprite is what was rendering onto the screen, right?

  • So we take a look here.

  • That's not a battle sprite, but almost a battle sprite.

  • That was just a texture.

  • So if we get into a battle, slowly but surely.

  • All right, so these are battle sprites, and they don't look much different

  • than a regular sprite, and they're not that much

  • different than a regular sprite.

  • But they have some functionality that's important, mainly that functionality

  • where one is flashing, and then one was being opaque, right?

  • So in order to do both of those things, we need to store some sort of data

  • within our sprite, right?

  • Yes?

  • AUDIENCE: Zelda for the invulnerability flashing.

  • SPEAKER 1: Yes, exactly.

  • For what we used in Zelda for the invulnerability flashing.

  • For the enemy, or I should say, for whoever is getting attacked, yes.

  • They are getting an opacity flag stored.

  • They have an opacity flag stored in their object that we can tween, right,

  • we can tween on and off over the course of time.

  • That's what we did with the entity in Zelda when it took damage.

  • And we set it to invulnerable, and while it was invulnerable,

  • it was flashing on and off.

  • But we can't necessarily do that with the sprite that's blinking white,

  • because there's not really a like white flag, right?

  • We can't make something completely white with just a flag.

  • That's something that we actually need to use a shader for.

  • And so a shader, and we're not going to get into too much detail about this,

  • shaders are pretty complex, a little arcane at first.

  • But what they are is effectively a little program that

  • runs on your graphics card, and that looks at when you're drawing something,

  • it looks at every pixel depending on what kind of shader you're doing.

  • But for the sake of this demonstration, we'll look at every pixel

  • that you're drawing to the screen, and perform some sort of function

  • on that pixel, and produce a new value, right?

  • And this is how you get a lot of really crazy awesome things to happen,

  • but it can be pretty insane.

  • Shader Toy, I think is the website that has a ton of really cool--

  • I'm not going to pull it up now, just 'cause I don't remember the name,

  • I believe it's shader toy.

  • There's a website where people post all the shaders that they've written,

  • and you can see a lot of really crazy stuff, things

  • that you would never imagined were possible with just code like this

  • effectively, looking at positions of pixels

  • and [? pictures ?] on the screen and whatnot.

  • But effectively what this does, this is a white shader, the goal of this shader

  • is to just turn a sprite completely white.

  • That's all the goal of this shader is.

  • So it gets a float called white factor, which [? you'd ?] say here.

  • And then white factor effectively is just

  • going to be summed onto whatever the RGB is

  • of that pixel, whatever pixel that we're drawing when the shader is active.

  • What that has the effect of doing is, white factor, if it's equal to 1.

  • Here's the thing about shaders and a lot of this stuff,

  • a lot of the data structures within shaders

  • are based on floats that are from zero to one.

  • So if we assign the RGB of something to a vec 3 that's 1, which is 111,

  • that's going to be 255, 255, 255.

  • Therefore, that pixels RGB is white, pure white, right?

  • And so what we're doing here is on our battle sprite, self.blinking

  • and one or zero, remember, that's the LUA [? turnerri ?] operations.

  • So we're saying, if self.blinking is true, one else zero.

  • So send our shader white factor based on whatever value self.blinking is.

  • And so that will have the effect of the shader getting a one or a zero,

  • and adding a one or a zero to the RGB of that sprite.

  • And if blinking is set to true, the sprite's

  • going to basically be drawn every pixel at 255, 255, 255.

  • Otherwise, it'll get drawn with whatever that image's pixel value is

  • at that position.

  • Does that makes sense?

  • OK.

  • The syntax is a little bit weird, but that's

  • what's happening here in this shader.

  • And there's a link here where I found the shader,

  • but it's a very simple, very simple shader,

  • probably like one of the simplest shaders you could write.

  • But it's a great example of what you can do with a shader, and pretty simply.

  • And it's nice, because you can take like texture coordinates and do math

  • based on that, or pixel coordinates and do math based on that.

  • You can pass in like a sine function for example, in your file,

  • and have that sine function perform work on like RG or B value of your sprite

  • and do all kinds of cool stuff.

  • It's really neat, like the possibilities are limitless with shaders.

  • But that's how we get it to blink, because you

  • can't do that outside of this--

  • I mean, there's probably some weird way could get it to work as well,

  • but this is probably the simplest way we can get our sprites blinking white.

  • And so self.blinking just gets a timer.every0.1 or whatever.

  • We'll actually see that in the attack state.

  • But that'll flick to self.blinking between true and false.

  • It'll negate itself over and over again.

  • All right, so that's the battle sprite.

  • Last thing we'll look at is another extremely simple class, opponent.

  • All the opponent is is it has a party, that's it.

  • But in a fully fleshed game, your opponent

  • might have a like trainer sprite.

  • A message that it says, like a full party

  • of Pokemon, a gold value that will give you when you defeat it,

  • all kinds of things.

  • But it's here just as a simple illustration.

  • Yeah?

  • AUDIENCE: [INAUDIBLE] put a method for on defeat

  • if you want to maybe have it kind of collapse

  • the room or something like that.

  • That would be another thing that you could do.

  • SPEAKER 1: Oh, a method?

  • Yeah, we can associate a method with an opponent called on defeat,

  • or whatnot that will do arbitrary things, collapse the room,

  • or otherwise.

  • Yes, absolutely.

  • Or even push a new state, like to like teleport

  • us to a new location in the world map.

  • Maybe we like cleared the elite four and we

  • want to get teleported to like the end credits, exactly.

  • Limitless possibility.

  • So let's go ahead and take a look now while we have just like 20 more minutes

  • or so left.

  • We'll take a look at the battle state, because the battle

  • state and the states that they're in are probably the more complicated side

  • of how this works.

  • So a battle state, we have a player, we have a bottom panel, the bottom panel

  • for when we start the state just for that part,

  • but otherwise, we're always pushing things onto it.

  • Whether we've started the battle or not, because when we are fading in--

  • sorry, yeah.

  • Because when we initialize this state, we also push a fadeout state onto it.

  • But we don't want to trigger the tween of the Pokemon sliding

  • from left to right until after that state gets popped.

  • So we have a flag here, which will get set to true on the very first update

  • iteration.

  • And then when that gets set to true, we'll

  • actually tween the Pokemon going left to right,

  • and kick off all the other sort of asynchronous processes

  • that exist thereafter.

  • But let's look at the battle one more time just to see what's going on.

  • So I'm going to walk until I get into a battle.

  • OK, we got a battle.

  • So notice here, the fade in happens as soon as the--

  • the slide in happens as soon as the fade starts, right,

  • as soon as the fade finishes, I should say.

  • We get a message popped onto the screen, right?

  • It says a wild X appears.

  • Right, that's the enemy Pokemon.

  • We hit Enter.

  • Turn this down a little bit.

  • We hit Enter, and then we pop another--

  • push another state onto the stack, another battle message, which

  • is very similar to a dialog state.

  • Says go our Pokemon.

  • And then we push a menu onto the screen, right?

  • We've got a menu that says, fight or run, a selection.

  • It's a menu, which has a selection.

  • And then now, this is the top of the stack, right?

  • So it's the only thing getting input.

  • Everything else is rendering beneath it, but nothing's getting input.

  • So we have the option to either fight or run.

  • Let's say we fight.

  • We fight, we got a new state now, we're in an attack state.

  • Several things just happened.

  • So what happens as soon as we kick off the attack state?

  • Yeah?

  • AUDIENCE: You get a text box saying, x attacked y.

  • SPEAKER 1: Yep, so the first thing we have happened

  • is, a text box that says, x attacked y, where

  • it could be either us or the opponent, because it's

  • based on whoever has the higher speed.

  • And then what happens next?

  • AUDIENCE: [INAUDIBLE].

  • SPEAKER 1: Well, it does.

  • So let's take a look at it right now and tell

  • me what exactly happens as soon as the text box pops up.

  • So what were the pieces that happened there?

  • AUDIENCE: Flash.

  • SPEAKER 1: OK, so the attacker flashes white,

  • right, which is the shader that we looked at.

  • That's the shader blinking on and off.

  • There's some timer that says, every 0.1 seconds, blink on or off.

  • And then what happens?

  • AUDIENCE: Then the damage is dealt. [INAUDIBLE]..

  • SPEAKER 1: Well, damage is dealt, yes, but what happens visually as soon

  • as the white blinks?

  • AUDIENCE: The other one blinks.

  • SPEAKER 1: The other one blinks.

  • What's the other one blinking?

  • AUDIENCE: I'm not sure.

  • SPEAKER 1: So it's opacity, right?

  • So remember, we're doing the exact same thing we just did with that white,

  • with the blinking, but we're tweening every 0.1 seconds

  • the opacity of the defending Pokemon.

  • And then we take damage.

  • Then what happens when we take damage?

  • AUDIENCE: The reverse basically.

  • SPEAKER 1: Well, what gets animated when the thing takes damage?

  • We've animated the blinking, we've animated the opacity.

  • AUDIENCE: [INAUDIBLE].

  • SPEAKER 1: The health bar drops, right?

  • So we're chaining several things together.

  • We're chaining-- first, we're doing them every 0.1 seconds for six times,

  • blink white.

  • Then blink the other thing opacity, right?

  • And we're playing sound effects at the same time too,

  • we're playing a sound effect for the attack, sound effect for the hit.

  • And then once that's finished, tween the health bar, right?

  • So we've modified the health of the defending Pokemon.

  • And then what happens after the first one, after that process is finished.

  • AUDIENCE: Repeat for the other side?

  • SPEAKER 1: Exactly, repeat the exact same thing, but for the other side.

  • But what are we doing in between each of those?

  • We have to do something.

  • AUDIENCE: Checking if somebody dies.

  • SPEAKER 1: Checking if somebody dies, exactly.

  • And if somebody dies--

  • let's say we die, what happens?

  • AUDIENCE: [INAUDIBLE].

  • SPEAKER 1: Well, we yeah, we go back to the play state.

  • We fade out to black, and then we go back to the play state.

  • What happens if we knock out the enemy?

  • AUDIENCE: Go to this screen.

  • SPEAKER 1: Exactly, and what happens on this screen?

  • So what's the first thing that happens?

  • Well, so recall, what happened when the Pokemon died?

  • What happened?

  • AUDIENCE: It fell off its platform thing.

  • SPEAKER 1: Exactly, so that's a tween probably, right, on his y value.

  • Then what happens?

  • AUDIENCE: [INAUDIBLE].

  • SPEAKER 1: Exactly, we've pushed a battle message state onto the screen.

  • And then what happens when we press Enter?

  • AUDIENCE: [INAUDIBLE].

  • SPEAKER 1: What just happened right there?

  • AUDIENCE: [INAUDIBLE] text box that says,

  • you earned whatever experience points.

  • Then you get your XP goes up.

  • And presumably, it checks if you leveled up.

  • SPEAKER 1: Yes, correct.

  • AUDIENCE: [INAUDIBLE] to level up.

  • SPEAKER 1: Exactly, so when push a dialogue

  • to the screen that says you've earned x experience points, the XP bar tweens,

  • right?

  • We've gone up to however our ratio of current XP to next level XP is.

  • We animate our text bar that way, or progress bar that way.

  • Then we push a fade in state, right, to white.

  • And then we have to pop everything off the stack,

  • and then push a fade out state to the top of the stack,

  • and then we're back to the play state.

  • But if we do level up, we need to play the right music, play the right sound,

  • and then part of the assignment will be actually, in that exact function,

  • you're going to need to add some behavior that will do what?

  • AUDIENCE: [INAUDIBLE] display the change basically,

  • and what the new one will be.

  • SPEAKER 1: Yes, and what are we going to need to do.

  • What will we need to do in order to?

  • AUDIENCE: [? Explain, ?] what was it called?

  • The selection box, but without the selection part basically.

  • SPEAKER 1: Yes, so once we've taken-- once we've leveled up

  • and we're in that victory state of the battle state, right,

  • we need to push a new state, a new menu state, which

  • has all of those stats and the amount that they've increased.

  • And then when we press Enter, presumably, we

  • should pop that off, and then pop everything else back to the play state,

  • and then do the fade in as normal.

  • And that is the battle state in a nutshell,

  • a lot of pieces that sort of are waiting on each other and input and stuff

  • like that.

  • But fairly easy to understand, just because a lot of it is very simple

  • things that are just chained together over and over

  • again to produce this sort of interesting behavior.

  • So here we have sprites, recall the sprites are what

  • we're going to need to animate those.

  • We have health bars, which are progress bars, which

  • are just two rectangles that are--

  • ones a line, a black line, and ones a fill that fills beneath the line,

  • so that we get a sense of how much is missing, right?

  • We get the width, the height, a color.

  • We can give our progress bar any color we want to,

  • which is how we get the difference between, say, a health bar and an XP

  • bar.

  • We just make one red and one blue, and we draw them in different spots, right,

  • but they're both equally progress bars.

  • And then they get a value.

  • Their value is whatever sort of determines how much of the rectangle

  • is scaled.

  • And the max is how much that should be divided by in order

  • to produce a ratio for the total width--

  • a scaler for the total width, which will allow us

  • to get the sense of an amount missing.

  • And then a player circle x, opponent circle

  • x for the ellipses, just the graphical details for the actual Pokemon,

  • so that we can get their stats, so that we can actually do dice rolls, or not

  • really dice rolls in this case, but so that we can add or subtract

  • HP based on attack and defense.

  • And so here was the update, so trigger slide in.

  • So what trigger slide in does, is a one second tween,

  • which you talked about, right?

  • The Pokemon going left to right, or left to right, right to left.

  • There x values, just tweening in over one second.

  • As soon as that's finished, we're going to trigger starting dialogue.

  • So the starting dialogue is push a battle message state onto the stack.

  • The battle message state is just like a dialogue state in that it gets a string

  • here, so a wild something appears.

  • It gets a callback function for once we press Enter on that.

  • And the callback function is itself another push of a battle message state

  • that says, go, and then our Pokemon.

  • So notice that we're referencing the self.opponent.party.pokemon there,

  • and self.player.party.pokemon there to get the actual name.

  • And then once we've popped that off, then we push a battle menu state here,

  • right?

  • So let's take a look at the battle menu state.

  • So this is interesting, because this is where we actually define

  • the behavior for our menu works, right?

  • Recall, we need something to tells us what happens when we press Fight,

  • and what happens when we click Run.

  • So when we click Fight, notice here items,

  • right, self.battlemenu gets menu, and menu expects items.

  • This items key, this table gets fed right into the selection, right?

  • And the selection, it expects remember, a text,

  • because it has to know what to render at that index.

  • And then an on select function.

  • And that on select function is the callback

  • that gets executed when you press Enter at that particular location

  • in the menu.

  • In this case, fight, what that does is it pops this battle menu state where

  • we no longer need the menu, so pop it.

  • And then push a new take turn state.

  • And then take turn state in this game is the Pokemon fighting each other, that's

  • what the take turn state is.

  • And it could have been called fight state, for example,

  • but take turn state is a little more versatile.

  • If we wanted to maybe make, maybe one Pokemon wants to run,

  • the enemy wants to run and we want to fight, right?

  • But you can't always run, so they should try to run,

  • and then we can still fight them.

  • Or they can use an item, or we can use an item, right?

  • There's a lot of different things you can do.

  • Or we want to throw a Poke ball at them, and if we fail,

  • then they should fight us, right?

  • Take turn is just a general purpose state

  • that we could repurpose for whatever use we

  • want to with any interaction between us and the opponent,

  • whether it's fighting, running away, or using items, catching them, any

  • of these combinations of things.

  • But in this case, for the sake of this example, for simplicity,

  • we've only implemented fighting.

  • The we and the opponent fight each other during this state, which

  • is, one attacks the other, and then we check for deaths

  • in between both of those.

  • And then go to victory or feinting depending on which of those holds true,

  • if either.

  • Running is slightly different.

  • So if we run, I've programmed it to be 100%, it will 100% of the time work.

  • In Pokemon, you actually have a chance to run

  • based on what the delta is between you and your enemy.

  • So if they're stronger than you, you actually aren't guaranteed to run away.

  • So what we do here in my implementation is, we just pop the battle menu,

  • so it's gone.

  • And then we push, you fled successfully to the screen, this battle message.

  • But there's a difference here versus the other battle messages that we've shown.

  • I mean, it's not really different, but it's something to keep in mind.

  • So I'm going to get into a battle.

  • And so first of all, with that message that you just saw on the screen,

  • I had to actually press Enter, right?

  • I discarded it explicitly by pressing Enter.

  • And that holds true also for these messages.

  • It won't do anything until I press Enter.

  • So I press Enter, and then I press Enter, and it does it's thing.

  • But notice the difference between when I hit Run.

  • I'm going to hit Run, I fled, and it does it on its own.

  • It's not waiting for input, right?

  • So how have we implemented that?

  • Yeah?

  • AUDIENCE: Using timer, you'd automatically

  • do it the same way you would afterwards, instead of waiting for you input,

  • you just wait for the timer to end.

  • SPEAKER 1: Exactly, so we use a timer, and then when the timer is finished,

  • we pop the battle message just like we would have popped it by pressing Enter.

  • This false flag is what allows us to do that.

  • We press false and false is, can we input or not?

  • And we can't.

  • So actually, if we didn't do any timer thing after this,

  • and we just did that false flag, the battle message would be there forever,

  • and we could never get rid of it, ever.

  • It would get stuck forever.

  • So we got to be responsible and say, OK, we're going to put a timer,

  • we're going to call timer.after 0.5 seconds immediately after that.

  • We're going to push a fade in state.

  • And then we're going to do these two pop operations here as soon as that fade in

  • happens.

  • This first pop will pop the message, right, this message here

  • that we didn't pop through input.

  • So this is actually garbage collecting, in a sense, for us.

  • It's discarding the message that we couldn't discard automatically.

  • And then we're going to pop the battle state, right?

  • So running will push the battle message, trigger a timer tween

  • for our timer.after five seconds, sorry, push a fade in state.

  • And then after the fade in states done, then pop both of those states.

  • The message and the battle state take us back to the play state.

  • And that's where we'll be as soon as that's all done.

  • And that's all that's in the battle menu state.

  • Any questions as to how the battle menu works, the difference between fight

  • and run and sort of how those operate?

  • OK.

  • So let's take a look then at the take turn

  • state, which is the last piece and the largest piece I would say.

  • This is the most relevant to the assignment.

  • So we maintain a reference to which Pokemon is first or second to go,

  • which sprite is first or second to go, and which progress bar is

  • first or second to go up here.

  • And we do that, like I said, based on speed.

  • So whichever Pokemon is faster, and we could have also

  • made this a little bit shorter, just by keeping the sprites and the progress

  • bars as members of the Pokemon object, or the class,

  • but since they're kind of separated, like we don't necessarily

  • want a Pokemon to have a reference to it's progress bar at all times,

  • or I mean, you could.

  • There's nothing preventing you from doing it.

  • It would only serve the purpose of shortening this code here.

  • But we need to keep a reference to this so that we can call attack here,

  • which is this large bit of code twice, without needing

  • to duplicate all of that code twice.

  • Does that makes sense?

  • So Tony, did you have a question?

  • AUDIENCE: Well, I was just thinking, you could probably

  • put that into a helper function where you just

  • change the order you pass it in.

  • SPEAKER 1: Sorry?

  • Say it again.

  • AUDIENCE: I just kind of feel like, I guess you could take the code,

  • and you could avoid duplicating that I guess.

  • 'Cause it's just reversed, so what you could do is you could--

  • if you passed into a helper function, which you would just,

  • instead of passing it first--

  • instead of passing it, opponent Pokemon, player Pokemon,

  • you would pass it, player Pokemon, opponent Pokemon.

  • And that would probably work I think.

  • SPEAKER 1: Well, you also have to take into consideration--

  • so the comment was, you could pass in the player Pokemon and the opponent

  • Pokemon into a function, and then you reverse them in that function,

  • I'm assuming, have reverence them and reverse them.

  • But the sprites are decoupled from the Pokemon,

  • and the progress bars are also decoupled from the Pokemon.

  • So we could shorten this by making these four things here fields

  • of the Pokemon objects, but they're not strictly

  • pertinent to the operation of the Pokemon object.

  • And it sort of kind of makes the Pokemon objects a little

  • too, not basically abstract or lightweight enough,

  • and it only serves the purpose of this point,

  • of just shortening this bit of code.

  • There's probably a more elegant way to do it, but it's hard to say.

  • If this code were to get larger, maybe.

  • But the gist of this is basically to have

  • a pointer to whatever Pokemon, progress bars, and sprites should operate first

  • in the attack versus what should operate second.

  • And then the two will trade blows in order based on who's first and who

  • second.

  • So when we enter the take turn state, we're

  • going to trigger that attack, here this function attack, which we'll take in

  • first, second, first, second, first, second for the Pokemon

  • sprite and progress bars.

  • And then anonymous function, which get's executed as

  • soon as the attack is finished, right?

  • So this is a code that will pop a message that gets pushed in attack,

  • and then this is where we actually check deaths, right?

  • And it will determine whether we go to victory or faint screen or not.

  • If not, and we return if so.

  • If not, we're going to do another attack, but see,

  • everything is reversed now.

  • Now it's second, first, second, first, second, first.

  • So we have the same function, self attack,

  • which just takes in the attacker.

  • And it's effectively, attacker, defender, attacker, defender, attacker,

  • defender for the Pokemon sprites and progress bars.

  • And so the attack function here first pushes a--

  • well, OK.

  • What does the attack--

  • let's go over it one more time.

  • What do we think the attack function does in order.

  • We covered them just a moment ago, but what

  • was the order that happens when something attacks another thing?

  • Yeah?

  • AUDIENCE: The attacker blinks white.

  • SPEAKER 1: Attacker blinks white.

  • AUDIENCE: Then the defender blinks opacity.

  • SPEAKER 1: The defender toggles it's opacity.

  • AUDIENCE: And the health bar shrinks.

  • SPEAKER 1: Health bar shrinks.

  • Exactly, and then that's basically it for attack, right?

  • Blink, play a sound, blink, play a sound, shrink the bar,

  • and also we're doing damage in that function as well.

  • We actually have to change the status of the Pokemon.

  • So this is effectively where it starts, right?

  • We place a battle message state onto the stack that says,

  • the attacker name attacks the defender name.

  • Notice that it gets false just like the run message

  • did, because we're not accepting input here.

  • But it's up to us actually, it done up here at line 42 of the enter state.

  • But we're going to after 0.5 seconds, play the attack animation.

  • So power up sound every 0.1 second.

  • We're going to member the blinking flag on the sprite,

  • we're going to toggle it by setting it not to itself.

  • So if something is not itself, if it's a truthy value, it becomes falsy,

  • if it's falsy, it becomes truthy.

  • So basically, toggling between true and false.

  • Limit of six, right, because remember, every will

  • do something every amount of time indefinitely,

  • unless you pass in a limit of some value, in this case, a limit of six.

  • So we're saying, only execute this code six times, only blink six times, right,

  • only toggle six times, blink three times, right,

  • because it has to go on and off.

  • And then as soon as those six iterations are completed,

  • we call the finished function on that timer object,

  • which takes an honest function.

  • As soon as that happens, we do the opacity bit, right?

  • We blinked the attackers, so now we've got to blink the defender.

  • So we play the hit sound.

  • We do the exact same thing that we just did for the blinking, only now,

  • every 0.1 second, we are setting its opacity to either 64 or 255,

  • depending on what the value of its opacity is, right?

  • So we are toggling between 64 and 255.

  • Limit of six, take a function, calculate damage,

  • which we've just very simply done it, attack minus defense, right, up to 1

  • though.

  • So if the defense is actually higher than the attack,

  • which will still do at least one damage.

  • And then over 0.5 seconds, we take the defenders bar,

  • and we tween the value equal to their current HP minus damage, right?

  • And then that will set in the bar, in the progress bar, it'll set its value.

  • And even though the progress bar is behind state wise,

  • right, it's on the bottom of the stack, because it's on the battle state.

  • And we're in currently the take turn state,

  • but because we're still manipulating the values of that state,

  • and we're rendering every state, we're actually still manipulating

  • that state regardless of the fact that it's not on the top of the stack.

  • So that allows us to shrink that Pokemon's progress bar regardless of it

  • being on the top of the stock or not.

  • Then once that's finished, once the tween is finished,

  • actually set the current HP to that amount,

  • because we're only tweening the progress bar's value, which is

  • independent from the Pokemon's value.

  • And then that's the end of the attack.

  • The attack is completely finished at that point.

  • So any questions as to how the attack works?

  • Just a chain of tweens basically.

  • So we do an attack, then check deaths is the next function.

  • And we're almost finished, I'm going to kind of go quickly here, it's at 7:30.

  • Check deaths is the player Pokemon current HP less and equal to 0,

  • or is the opponent Pokemon current HP less and equal to zero.

  • If the former's true, we need to faint, and if the latter is true,

  • we need to go to victory.

  • So faint is effectively a battle state, right, when it says, you fainted.

  • And then what?

  • Remember what happens when we faint?

  • AUDIENCE: [INAUDIBLE] text box, and then it leaves.

  • SPEAKER 1: It leaves, do you remember how

  • it leaves as it differs from like running away, for example?

  • AUDIENCE: [INAUDIBLE].

  • SPEAKER 1: Well, beyond that, aesthetically,

  • do you remember how it's different?

  • AUDIENCE: [INAUDIBLE] differently to black, I think.

  • SPEAKER 1: It does.

  • It fades to black instead.

  • So that's how we can differentiate when we're fainting

  • versus when we're running away.

  • And so that's what we're doing here.

  • Notice that the fade in state RGB is zero, all of those.

  • So it's going to fade in to 000255, as opposed to 255, 255, 255, 255.

  • So it's going to be a black fade in versus a white fade in.

  • And then once we've--

  • this it just sort of a thing that I implemented so

  • that we can keep playing indefinitely.

  • But once that's finished, restore the player Pokemon to full health,

  • resume all the field music stuff.

  • And then once we've pushed a fade out state, 000,

  • and then we've gone back to the field, let's push.

  • Notice that here it takes a function, right, after the fade out state's done.

  • Once the fade out is finished-- so as soon as we're back to the play state,

  • push a dialogue state that says, your Pokemon

  • has been fully restored, try again.

  • Which will take the context, and we'll [INAUDIBLE]

  • to press Enter to get past it.

  • That's fainting.

  • Victory is a little bit more robust.

  • So victory is-- do you remember what happen when we get a victory?

  • AUDIENCE: Well, it has to check leveling up as well.

  • It says, you've defeated your opponent, then your XP bar increases.

  • Then if you've leveled up, it tells you that you leveled up,

  • and then it leaves.

  • SPEAKER 1: So it tells you you defeated your opponent, XP bar increases,

  • checks for a level up, and then leaves.

  • After displaying the level up message or not, it leaves.

  • It pops everything back to the play state, exactly.

  • So remember, the very first thing that happens

  • though, the opponent sprite gets tweened over the course of 0.2 seconds,

  • it's y value to virtual height, which means,

  • all the way to the bottom of the screen, right?

  • The typical defeated your opponent from Pokemon sort of animation.

  • Once that's finished, play victory music,

  • push a battle message state that says, victory, right?

  • Once that's popped of the stack, calculate the XP,

  • which is, I just chose arbitrarily sum all the IVs of that Pokemon times

  • it's level, and that's the XP you got.

  • Push a state that says, you earned x XP, right?

  • It's false, so that means it doesn't take input.

  • So that means it's up to us in order to pop that off the stack.

  • So after 1.5 seconds, we play a sound, and then we tween that XP bar going up,

  • right?

  • So that's what's going on here, self.battleState.playerxpbar,

  • we're tweening of the math.men, of the XP plus XP,

  • or XP to level, because if we don't, it could go past the edge of the XP bar,

  • because we could go over our XP to level, right?

  • Let's say we have 10 XP till we gain a level, we could gain 20 XP.

  • We'd be 10 XP overboard.

  • So we don't want to tween our XP bar past the edge of the XP bar,

  • it would be a graphical glitch.

  • So a math.men our XP plus XP, and our XP to level,

  • which will take the lesser of the two values.

  • Once that's done, it's tweened, we're going to pop the message off,

  • and then we're going to actually add the XP, level up.

  • So this is where we level up if the XP is greater than XP to level.

  • Play a sound, set the XP to the current XP minus our 2 level XP,

  • which will mean that we'll have some carry over, right?

  • And then actually call the level up function.

  • Now here is where--

  • oh, and also after that, congratulations, you've leveled up.

  • Fadeout white, which is just a white fade out here.

  • I used it twice, so I made a function for it.

  • Just pushes a fade in state.

  • Stop the victory music, play the field music, pop, push a fade out state.

  • So either way, when we've got a victory, we're going to push a fade out white,

  • or we're going to call fade out white, correct?

  • So push a battle message state, and then as soon

  • as we press Enter, because we leveled up, fade out to white.

  • And if we didn't level up, but we still got to victory,

  • we still need to fade out white.

  • And so this is where your assignment is, assignment 7.

  • Assignments 7 is, notice that we have self.playerPokemon level up.

  • The key thing that we are going to need to do here

  • is add a menu that shows us how we leveled up.

  • And if you recall, playerPokemon level up returns all the stats

  • that you've increased this level.

  • So you can show a menu that just says, your HP plus that amount, right?

  • You're going to get all four values.

  • It's going to explode to all four of those values.

  • And then you're going to create a new battle--

  • or not new battle menu, but a new menu of whatever you want,

  • but probably on the right side of some vertical height for items.

  • The only difference here, the only key thing

  • that you're going to take into consideration

  • is, and I'll go back to the slides, because we're actually

  • done at this point going over the code.

  • But the selection items, you won't be able to actually select anything,

  • it's just going to be purely visual.

  • So you're going to need to edit selection to have the option

  • to not have a cursor.

  • And this is detailed in the spec, which was actually

  • released before lecture today.

  • So you can take a look at that.

  • But you'll need to make a change to selection.

  • But all the pieces are there.

  • It should be a fairly easy assignment as long

  • as you understand how the states work, how the menu works,

  • and how to create a menu based on those values,

  • and how to actually get the values from level up.

  • So some missing features that we didn't talk about, which we didn't implement

  • are, for example, the detailed level of screen,

  • which is your assignment, monster catching, right?

  • We only have a party of one Pokemon throughout this whole entire thing,

  • but one of the arguably main appeals of the game is to be able to catch more.

  • So that would be something to add, to prioritize probably adding to the game.

  • A field menu so can actually look at all the Pokemon you've caught.

  • That would be nice, so you can actually see how much HP they have.

  • In item inventory, because the game, the regular games have items.

  • You can use potions, you can find gold nuggets

  • that you sell for a ton of money.

  • Different abilities, currently we only have basically one fight operation,

  • which is like a tackle.

  • And the game itself, the regular game has

  • like over 100 different moves that have elemental attributes,

  • and do different things, and cause status effects,

  • buff you or your opponent.

  • So adding those is appealing, and maybe being

  • able to represent them as data is nice to.

  • Trainers that you can encounter in the game that have their own preset

  • or randomized Pokemon for to fight.

  • Monster evolution, because that's like one of the funnest things

  • is taking a really weak Pokemon, and like raising

  • it to become really strong, and evolving it at a certain level.

  • Towns, routes, other levels beyond just our basic square area.

  • Monster breeding, which is introduced in the second series,

  • so that you can take two Pokemon and have a chance

  • to get an egg with really good stats or a really rare Pokemon from it.

  • And then like a day night cycle maybe where different Pokemon come out

  • at different times of the day.

  • So you are incentivize to play at different times

  • of the day for that purpose.

  • But that was it for Pokemon.

  • Next week we'll actually be diving into Unity.

  • So we're actually done with LOVE 2D, which is a lot of fun,

  • but now we'll be going into how to make 3D games.

  • So this is a screenshot from the game we'll be making next week,

  • which is a 3D sort of side scrolling Flappy Bird esque helicopter

  • game based on a famous web game called Helicopter Game.

  • And it was sort of one of the early ancestors to Flappy Bird.

  • On the Wikipedia page, it actually says that too.

  • I remember playing, it was back in like 2007, or 2006, or something like that.

  • But your goal in this game-- this is a modified version of that--

  • your goal is your-- everything is 3D, but it's a side scrolling perspective.

  • So this is called 2.5D for that reason.

  • You're controlling a helicopter, you're the purple helicopter.

  • And your goal is to in an infinitely scrolling world.

  • So we'll revisit infinite scrolling, but in 3D, avoid skyscrapers.

  • So you can see there is a green skyscraper, crudely modeled.

  • Collect coins, so you can see there's a coin there, it's a 3D coin,

  • it will always be spinning.

  • Your coins are up at the top right.

  • You'll see a background that's infinitely scrolling.

  • And then you'll have jets that will randomly

  • fly above you to sort of give you another sort of layer or dimension

  • of obstacles to watch out for.

  • And this will teach us a lot of the basics of how unity works,

  • so we can start getting into even more interesting things like a first person

  • like sort of core exploration game.

  • And then lastly, when we end the semester with Portal,

  • we'll look at a couple of fancy things there.

  • But that was it for Pokemon.

  • Thanks for coming, and I'll see you guys next

  • time.

SPEAKER 1: All right, welcome to GD 50, lecture 7.

字幕與單字

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

B1 中級

口袋妖怪編碼教程--CS50的遊戲開發入門教程 (Pokémon Coding Tutorial - CS50's Intro to Game Development)

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