字幕列表 影片播放 列印英文字幕 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.
B1 中級 口袋妖怪編碼教程--CS50的遊戲開發入門教程 (Pokémon Coding Tutorial - CS50's Intro to Game Development) 8 0 林宜悉 發佈於 2021 年 01 月 14 日 更多分享 分享 收藏 回報 影片單字