Placeholder Image

字幕列表 影片播放

  • Hey, guys, my name is Frank.

  • I'm starting a series on how to program a tile based platform and game called Rabbit Trap.

  • It will feature tile based collision detection in response, scrolling maps, loading levels, Sprite animation and interactive game objects.

  • In this first part of the series, I'll be talking about how to build a strong foundation for the game.

  • The concepts I'm going to explain are strongly rooted an object oriented programming and could be used in any program to improve organization of modularity.

  • This might seem boring, but if you use this approach, it can save you hours of editing and frustration and leave you more time to focus on fun stuff like level design and graphics.

  • So be sure to watch this video in this video.

  • I'm gonna talk about the model view controller approach to organizing a program and how the different components of this example work together in the main Js file.

  • Then I'm gonna talk about how this structure increases, maintain ability and modularity, as always, feel free to use the comments section and be sure to check out the links to the source code and working example in the video description And if you learn something, give this video alike.

  • So basically, what I have here is just an example of a model view controller architecture where you separate out your game logic from your display logic, and you also separate out your controller logic.

  • So I have my game logic stored in the game class display logic in the display class in the controller logic in the controller class, and each one of these handles its own business, and it's totally separate from the other.

  • So I'm not gonna run into any issues with having references to the game logic inside of the display.

  • I'm not gonna have the controller logic reference inside of the game.

  • I'm gonna have all these different components interact with each other through their public methods inside of the main Js file context.

  • And by the way, each one of these classes defined in its own individual file.

  • You don't need to have your own individual files, but it makes it a lot easier to just used these different things and other applications.

  • If I choose to, for example, the engine, I might want to use this fixed time step engine inside of another application at some point, so it's nice to just store in its own file.

  • But anyway, what all this code does for this example is basically the game Class controls color value right here.

  • That's the color variable from the game class, basically just increments the different red, green and blue channels inside of a color value.

  • So change these different colors that you're seeing on the screen.

  • And then it will talk to the display class, which governs things like the canvas and re sizing the canvas with screen re sizes and rendering.

  • And the game will hand a color value to the display in the display will render it on every frame, and the engine takes care of frames.

  • As for the controller, I didn't really have anything cool, Dude, the controller for this example.

  • So all it does is take user input from the keyboard until the user which key here?

  • She pressed.

  • So, for example, I just pressed the a key.

  • That's key 65.

  • So basically what I'm trying to say is, I want to keep things as organized as possible for this rabbit trap game, and I want you guys to kind of think about organization of code before you start developing a game.

  • Because if you just jump into things and try toe mesh all this together in one big file with references going between different components, it's gonna get very messy very fast.

  • And here's an example of that that I want to show you.

  • So I define all my components.

  • I defined my engine, is gonna run at 30 frames per second, and I'm handing in the rendering up the function down at the bottom of my main jazz file.

  • I'm gonna start my engine, and that's gonna execute the render an update function 30 times per second until I stop my game engine.

  • So the render an update functions.

  • I define up here and these air them right here.

  • So the update function is just gonna tell the game to change that color value.

  • And that's what the update function.

  • The game does just increments this color value in a random direction gradually to change the color.

  • Now, the render function is also gonna be called in every frame of my game loop and inside.

  • I'm actually gonna be communicating the game's color value to the display, and I'm gonna have the display object draw that color value to the onscreen canvas.

  • So here is a great example of why this is so good.

  • So I could just as easily right object liberals to govern my display logic and my game logic.

  • And you've seen that if you've you've washed my other tutorials, I'm kind of sloppy.

  • I just throw stuff together with object liberals here.

  • I'm not doing that.

  • If I had written object liberals, I could put a reference to game dot color directly inside of the displays.

  • Render color function.

  • Or better yet, I probably wouldn't have even had her under color function.

  • I would just have a render method that handled rendering the entire thing.

  • The reason I have to hear is because render color draws a color to a buffer canvas and then displayed out Render renders that buffer canvas to the final answer in campus that you see.

  • So the old version of how I wrote my code or just the sloppy way that I write my code would just be to put to object liberals on the main Js file and what would be displayed won't be game, and I would have a reference to game dot color inside of my render, function and display would just draw that color directly to the campus.

  • Now the reason this is bad is because, say, I want to change the name of my color variable to Hugh if I go ahead and I have a reference to game dot color inside of my display and I changed the variable color to Hugh inside of my game object.

  • Now, all of a sudden I'm editing my game class or my game object to change that value to Hugh, and I have to go into my display object and change all the references to game dot color to game dot Hugh.

  • Now that's a pain.

  • I don't wanna have to do that, and it's unnecessary, so I don't see why I do it anyway.

  • With this method, the M V C approach, you don't actually have to have any internal references to any other components.

  • The display on Lee has references to itself inside of itself.

  • The game only has references to things that it controls.

  • So how do they communicate?

  • They communicate just like this?

  • They communicate through public methods so display can only communicate with other objects like game and controller through public methods that it has.

  • So if I come into the display object here and take a look at render color, you can see that just takes a color value, and inside of it does exactly what I said.

  • It just changes the buffers Phil style to the specified color and draws that color too.

  • The buffer campus.

  • And then here's the render function, and all this is gonna do is draw the buffer canvas to the final display canvas.

  • I'm not gonna get too much into detail on that, but you get what's going on here now.

  • I could have just as easily gone like this inside of this function and said Game dot Color had I made these object liberals instead of classes.

  • But that's actually really bad practice because, like I said, if I change something inside a game, it might break my display object and prevented from working.

  • So that's twice the editing that I'm gonna have to do because I'm gonna have to change all the values in display as well as all the values in game.

  • So this is just a much cleaner way of doing things so Basically, this is how you want your different components interact and just take a look at this main Js file.

  • It's really short.

  • Everything is super clean.

  • I just have my two functions here.

  • Render an update that a hand into my game engine and here I'm defining everything.

  • This is super clean.

  • I don't have huge object liberals to take care of.

  • Just sprawling out across my screen here and my editor and to instead she ate everything.

  • It's real simple.

  • I just add my event listeners for the screen resize and the keyboard inputs.

  • And I just resize my screen initially, So my canvas fits the screen.

  • When I start up and I start my engine, everything runs the way I expected to.

  • So using the NBC approach or just separating out the different components your game until logical groups is a really great way to keep your code maintainable and modular, and I'm gonna get into why in the next part of this video.

  • So now I want to talk about how this approach makes things more modular and more easy to maintain.

  • So obviously right off the bat, it's a lot more organized.

  • You can see that clearly I have my different components reference here and in Stan Shade here in one single line each rather than having big, sprawling object liberals to find.

  • And by separating these things out into their own classes, I have the option of putting them into their own files, which makes things even more organized, because I could have all of these different classes right on top of each other in one big file.

  • Or I could separate them like I did into their own files.

  • And now, if I want to edit the game logic, I only have to deal with 42 lines of code, apparently, instead of having to search through one big file.

  • So that's obviously one way that this technique lends itself to easy maintain ability.

  • Then there's the other thing I mentioned where you don't have internal references toe one component inside of another component.

  • And like I said, then it's a lot easier to just work with public methods and have your components communicate via public methods.

  • Kind of like is happening right here in the render function Display is interacting with game through a public method called render color, rather than having an internal reference to game dot color, which would be a lot harder to maintain.

  • Because if I change game dot color in the game object, I'm gonna have to go through display and edit all of those references to game dark color.

  • So this is another way it lends itself to maintain ability.

  • Now, what about modularity?

  • Okay, well, I've got a engine class down here, which is basically a fixed time step engine.

  • Now, I can use this engine in any application that I want to.

  • And it's very easy for me to do that because this is a totally self contained class that I store in its own file.

  • Now I don't have to store in its own file, but by storing it in its own file, I can very easily just reference that single file in another application and then all of a sudden, bam, I have a perfectly good game engine for a whole another game, and I didn't even have to write code for I already had it on hand.

  • But if I were to have that game engine integrated into all my other components, it would be a lot harder for me to extract the the engine components from, say, the game logic.

  • And that just isn't very modular.

  • The wet have it set up now is very, very modular.

  • And it will save me a lot of time in the future when I need a game engine object.

  • So sometimes I write my code this way for most of my tutorials.

  • I just throw stuff together in one file because I want everything to be there for you guys to see upfront.

  • I don't wanna have to bring things in from other applications, but this is how I would go about writing a more structured application.

  • And that's what I'm trying to do.

  • I'm trying to write this game rabbit trap, And I just wanted to be very structured, and I want everything go smoothly.

  • So using the M V C approach and separating my game logic from my display logic and my controller logic is just gonna make everything go super smooth throughout this whole process.

  • So anyway, I hope this is been a good explanation of how everything is gonna be for this rabbit trap thing.

  • And I hope you guys are interested in what's gonna come because I expected to be a really cool game before I wrap things up, I want to talk about my HTML for this example.

  • So I actually am going to do a whole bunch of parts in this series.

  • And rather than having to upload the spreadsheet for this example 1000 times my get home page or have remained parts I have I don't want to upload this multiple times my get home page because I don't wanna waste memory space.

  • So I'm just gonna use the same graphics.

  • I'm gonna use the same html five and CSS file, and I'm just going to write new JavaScript files as need be so that's gonna keep the memory footprint on my get hub.

  • Paige a little bit smaller for this whole, uh, Siri's that I'm doing.

  • So anyway, the way I'm going about that is inside of my HTML.

  • I'm dynamically determining which job script files to load based on a U.

  • R l variable specifying the part numbers.

  • So when I come into my actual html page here and I look at my your l to the rabbit trap that each team will file at the end of it, there is a question mark and a one which is the part number for this example.

  • Now, if I wanted to get the part too, I would just come in and type two.

  • I haven't actually made typed Part two yet, but that's how you would access Part two.

  • So I'm accessing these different parts with a girl variable.

  • That is just the part.

  • And I have a default toe where if you don't specify anything, it just brings you part one.

  • So that's what I'm doing and this big chunk of code in the minimal.

  • The middle of my HTML file is taking care of that for me.

  • So it just it defines all the sources I'm gonna use.

  • And that's just these files.

  • Here.

  • It gets the girl variable or that part number from my girl that no one after the question mark and then it goes ahead checks to see if the viable part, if there is no part specified, it just sets it to part one, and then it just loops through an adds a script tag for every single job script file in the application.

  • So I'm always gonna have controller display engine game Maine and manifestos just my script for the video, and I'm just gonna add a new script for each one of those, and I'm gonna at it.

  • I'm gonna parts together the u R l or the source attributes to match whichever parliament.

  • So hopefully that was a good explanation of how I'm actually getting these things in.

  • And if you have any questions about why my HTML was weird like this, that's the reason I'm just trying to save memory space on my get help.

  • Paige so being cheap.

  • But I kind of figured out this cool little script to just import a job.

  • Script files based on what?

  • Parliament.

  • So anyway, that is an explanation of that.

  • This party is going to be on adding a player object to the screen and controlling him with the keyboard.

  • This is a vital component of any game.

  • So stay tuned to find out how it's done in this video, I'm gonna talk about the example program what it does and how the program has changed since part one.

  • Then I'm gonna show you the specific code that handles creating the player, controlling him and drawing everything to the screen.

  • Finally, I'm gonna show you my implementation of a fixed time step game loop and how it works if you have any comments or questions post, Um, and if it ain't point while you're watching this video, you get the sensation that you're learning.

  • Be sure to support this video with alike.

  • First thing I want to talk about is what this example actually does and how this program has changed since part one.

  • So all this example does is allow me to place a square which represents my player on the screen and use the keyboard to make him jump up and down.

  • I also added some stuff in for collision detection and to make it look nice.

  • So he changes color on every jump and he leaves a trail.

  • The reason he's leaving a trail is just because I'm filling the background.

  • I'm redrawing the background on every frame of animation with a such a slightly transparent background.

  • So that's the reason I'm getting that trail.

  • If I was drawing just a plane, Oh pack Blackground Ropeik.

  • I guess some people pronounce it that way, but I was doing that.

  • You wouldn't be able to see that trail behind him, but I thought That was pretty cool.

  • And it kind of helps you visualize the what's going on in every frame of animation because each one of squares that you see trailing behind him is drawn on one frame of animation.

  • All right, so now that you know what the program does, let's talk about what has changed since part one.

  • So in part one, I had to find my three different classes, my controller, my display and my game.

  • I also had my engine class and my main Js file.

  • So all these are updated for part two.

  • That's why the foul name has a 02 after it.

  • The ones from part one like engine.

  • I haven't changed since Part one that has a part one.

  • So that's my naming convention.

  • That amusing Really?

  • The only thing that I changed majorly besides the code that went into making the example work was I took the event listeners out of the display and the controller classes.

  • So the key down, up actual event handler function, I hand into the event adamant listener method in my main Js file.

  • I'm actually defining those inside of my main file now, so this would be one that I moved out that key, down up event listener and the resize event listener.

  • Actually, event handlers.

  • That's what these are.

  • So if I come all the way down to the bottom of my main jazz file, you could see them adding these event listeners to the window object.

  • And I'm just handing in the methods that I define inside of my main Js file into these event listeners.

  • So that's one of the changes I made.

  • I decided to make that change because I didn't think that my event handling system was really something I wanted to put inside of my individual classes in my individual components.

  • And the reason for that is a lot of times inside of these, I'm gonna have to do interactions with multiple components in my game.

  • So, for example, the resize component actually needs toe handle two different components and have them interacted.

  • Needs the display component, which handles drawing everything to screen all my graphics.

  • And it also takes information from the game component, which has information about my world's heightened with and my player, location and stuff like that.

  • So that's just a little bit about the Example Program and what it does and a few things that have changed since the last part of this series.

  • All right, so now I'm gonna talk about how I define the world and the game object, and this is basically gonna define the world that my little player square can run around it and jump in, and it's going to find the player itself.

  • That's gonna be just the rectangle that defines the player as well as the color.

  • And it's gonna handle some collision detection stuff.

  • As you can see, I can't fall through the floor and I can't jump or walk through the walls So all of this is gonna be handled inside of my game logic.

  • So I'm gonna come into the game part to file and take a look at my game class.

  • I'm gonna keep it expanded so we could actually look at the code.

  • All right, so the first thing I'm defining in here is a object literal called world.

  • So inside of my game class, I'm gonna have access to this world object.

  • And inside of this, I'm just gonna have my background color.

  • And by the way, this is that transparent color.

  • So if I come in here and I just ah, change this to something like 000000 That's gonna be the color black If I come in there and save it and then refresh my screen Now, I'm no longer gonna have that trailing graphic behind my player whenever he jumps.

  • But if I come back in here and change back control Z a bunch of times get rid of that and that and save and come back here.

  • Now, all of a sudden I get my trailing graphics behind my player whenever he jumps.

  • And it looks like he has just a cool trail of fading graphics behind him.

  • So that's pretty cool.

  • And it's really, really easy to implement.

  • All you gotta do is draw this semi transparent background color to erase your canvas on each frame of animation.

  • So anyway, to get back to the world object, I just wanna make a side note of this and how I did that color trailing feature.

  • The world is just gonna have everything to do with the world.

  • So friction and gravity you're gonna be defined here.

  • The player is gonna be defined here later on I'm going to define other objects that are gonna be in game objects.

  • They're gonna be defined here.

  • The world boundaries.

  • So the height and with of our level, are gonna be defined here.

  • If I were to change this, let's say I want to make the height something like 200 have a really tall screen.

  • If I do that and come here and refresh the screen now you can see that I have a really, really tall game world end.

  • That looks kind of stupid.

  • So we're gonna change it back, save, refresh.

  • And now I'm back to my normal size.

  • But basically, the world object just holds everything to do with the game world, including stuff like collision.

  • So this collide object function is just gonna take an object parameter.

  • And for now, that's just gonna be handled inside of our game.

  • Loop on every update and I'm gonna pass Clyde object the player object that were defined.

  • I'm gonna get to that class later.

  • And basically just gonna test see, is the object, uh, to the left of zero.

  • So that's gonna be the far left of our screen.

  • And is he beyond the right side of the screen is he above the top of the screen?

  • Is he below the bomb on the screen?

  • It's just gonna set him to where he needs to be in handle, collision with the boundaries of our rectangular world here.

  • So pretty simple stuff.

  • Everything in this example is actually really basic.

  • Take a look at the source code.

  • I have a link in the description here.

  • I have the update function.

  • Now, this is actually gonna update everything to do with our world.

  • So the player is gonna have gravity added to his Y velocity on every frame.

  • This function is gonna be called on every frame of animation inside of the game loop.

  • The player has his own update function.

  • And if I come down to the player object down here, we could take a look at it.

  • See what we got here.

  • Update.

  • It just adds his X and Y velocity to his current X and Y position.

  • Go back up, see what else we got here.

  • So we're gonna update the player.

  • We're gonna add some gravity to his Y velocity.

  • We're going to add friction, or we're gonna reduce his X and y velocities by the amount we defined in friction, which is up here 0.9 Ah, simple pseudo physics way too.

  • Employ friction in your game.

  • Then we have this stock Clyde object, this stop player, and this is referring to the world object that we're talking about.

  • So that's a really simple world object.

  • It just governs everything to do with world two dimensions the player, the colors that you're seeing in there and also in our game costs.

  • We have added Arrive added, rather the player.

  • And it's a simple class.

  • It's using dots in tax to kind of add this player class to the game class, and that's kind of just the way to keep things organized and almost like name space in there.

  • It's not real name spacing, but ah, by doing this, I'm not gonna have a global class called player.

  • Instead, I'm gonna have to reference it with game dot player like that.

  • So if I have a bunch of classes that I'm throwing into an external context, say your game is gonna run on another website and it's gonna be part of the global scope of that website, you don't want your classes and stuff to be just floating around with all the other variable names and class names that other developers have defined.

  • So it's good to try toe abstract out your own class names and variable names and do stuff like put them into anonymous functions just to keep your code separated from everybody else's code out there on different websites.

  • In a way that's besides the point.

  • We define our player class here.

  • He has a color.

  • He has a height.

  • He has a with he has X and y velocities.

  • He has a value for jumping to determine whether or not he's jumping and he's in the air if he's standing on the ground.

  • We also have his X and Y position.

  • And since I'm just being lazy and I'm not loading him in from a level map, I'm just gonna define his values.

  • All right at the start here, when the player object is in stance, see it and stand she had with the new operator.

  • So now I go into the prototype and I have a couple different functions that Governor governed.

  • How he moves out of the jump function basically just says if he's not jumping then we're gonna change the color and we're gonna make him jump down here.

  • It's a pretty simple This is the code to get a random color, and I'm not really gonna go into that.

  • But basically, it just makes his color change every time he jumps.

  • Every time I press the up arrow on my keyboard, the jump function is called on the player, and this code runs pretty simple.

  • Same thing for the move left and move right functions.

  • These functions are gonna be called in my main job script.

  • I'm going to take a look at that.

  • And second, and all it does is just add half a pixel to his ex and wired ex velocity.

  • Depending on which bunny impressed by press left, I'm going to subtract 0.5 pixels from his ex velocity.

  • If I press right on my keyboard, I'm gonna add 0.5 pixels to his ex velocity and then find that update function That's just gonna add his X and Y velocity to his X and Y locations in the canvas, respectively.

  • So, Alex, let's take a look inside of the main Js file and see where all of these air being employed, which is gonna be inside of the update function.

  • So the reason I have my update functions all over the place.

  • I have one for my player.

  • I have one for my world.

  • I have one of my main JavaScript file is because I'm doing different things inside of all of them, and I want to keep all of my objects as self contained as possible.

  • So, for instance, the player objects.

  • His update function is really simple.

  • It just handles stuff that has to do with the player my world's update function, Onley handle stuff to do with world.

  • So this that world, that update is gonna be this function right here.

  • The world's update function on Lee has to do with things inside of the world.

  • The main job scripts file Job Script file has an update function that handles the controller as well as the game.

  • So I'm mixing these two major components of my game's logic inside of this one method inside of my main jazz found.

  • That's kind of where I want everything communicate.

  • I don't wanna have those internal reference is pointing to other components inside of my game.

  • I want all of my code to be very self contained.

  • And when I have it interact with other components inside of my code.

  • I wanted to be in a safe context.

  • Kind of like the main Js file.

  • That's what I'm gonna do most.

  • My editing.

  • So anyway, getting information from the controller, I'm just saying, Is the controller being pressed?

  • Are these keys active on the keyboard?

  • And if so, I'm gonna execute a corresponding function function on my player object?

  • Finally, I'm gonna call game dot update and game.

  • The update is gonna go ahead and call World update World update is gonna call this stuff, and that's gonna give me these nice school physics and take care of all my numbers and player position and velocity and whether or not he's clotting and that's gonna take care of all of the physics logic in my game is also gonna change the player color.

  • All right, so now I just talked about what actually makes the game logic work.

  • And that is, of course, the game class itself.

  • Storing the game oh, to file.

  • So now I have to talk about how I'm getting keyboard input and relaying that to the game class so it can actually take that input and do something with it.

  • So I just showed you inside the main file here that I'm I'm conveying the keyboards input via the controller, object to the game object and telling the game object to do something with the players position, like move left to move right or jump.

  • And that's all gonna happen, depending on what the controller object is doing.

  • So let's take a look at the controller class and see how that works.

  • So the controller class just has three objects here, a left, right and up objects.

  • And those objects are gonna be controller dot button input classes.

  • So let's take a look at the button input class and see what it is.

  • It's really, really simple.

  • All it is is an object that has to boolean values inside of it.

  • One called active and one called down.

  • Now Active is gonna hold the virtual state of our bun.

  • So I'll get into that in a little bit because it has to do with jumping and down is gonna refer to the physical state of our button on the keyboard.

  • So if I physically press the up key down down is gonna be true.

  • And if I hold that key down, it's gonna remain true.

  • The active value, however, is gonna depend more on my actual game's logic.

  • So just remember, keep in mind that down refers to the physical state of the button on my keyboard and that's what that is Gonna track and active is just gonna track, uh, what that value is of my button object inside of my games code or my game logic.

  • So now that you know what the button input class is, these three objects are gonna be button inputs.

  • And this key down up event handler function is gonna be called every time I press a key on my keyboard, whether I press it down or it comes back up, this is gonna be called.

  • It's gonna check to see what the event type is key down or key up.

  • So when I press a key down on the keyboard, the types gonna be key down.

  • When are you released?

  • That key?

  • It's gonna be key up.

  • And depending on which one it is, see, they're gonna be true or false.

  • And then I'm just gonna use the get input method of my button input class and hand in that down value, which is either gonna be true or false.

  • And that's gonna look like this is just going to say, if the key tracker or the button input object if that currently is down the state is currently down.

  • Or if the state is not equal to whatever we're handing in, then we're gonna set active equal to whatever we're handing in.

  • And we're just gonna set down equal to whatever we're handing it, because that's gonna be the physical state of the bun.

  • Pretty simple.

  • Maybe not so simple.

  • Take a look at the source code and fiddle around that, because that's really the best way to get an idea.

  • I'm just here running over everything.

  • I'm trying to get it done quick.

  • Is this video?

  • Seems like it's gonna go along.

  • I don't want it to go too long, but basically this is a controller.

  • Class just takes keyboard input, and I'm adding the event listener for the controller component inside.

  • Not my engine, but inside of my main JavaScript file at the bottom.

  • If I come all the way to the bottom, you can see, I'm adding the key down and key up listener to the key down up function, which I'm defining also inside of my main file.

  • And that is just going to call the controllers key down, up method and hand in the event dot type and the event, doc key code, which we use to determine what the type is, whether it's a down presser and up press and the key code is just gonna turn which key on the keyboard impressing So 37 30 and 39 are the left up and right arrow keys on the keyboard.

  • So basically, this controller cost just gets keyboard input and then we relay that input to the game component right here in the main Js file inside of its update function, which is called on every frame of animation managed by our engine, which I'm gonna talk about later on in this video, so really simply just checks to see if the left right and up keys are active and then it goes ahead and it caused a player movement function.

  • So move left, move right or jump.

  • So where the active value comes into play here is when the player is jumping.

  • So notice that I'm actually setting the virtual state of the controller dot up button input to false.

  • I'm changing the act of value to false.

  • So that's gonna give me the ability to press my key once and hold it down and not have my player jump again.

  • If I were to come in and comment this line of code out like this and just go like this comment this guy out saved my file.

  • Come over here to my browser.

  • Refresh.

  • Now I'm just gonna jump up and down continuously.

  • Now, this might be desirable for some people's for some games.

  • This might be what you want, but for other games, you're not gonna want to keep on jumping just because you have the key press down.

  • So if that's something you want in your game, not having the player jump up and down continuously as your button is pressed in this controller class right here and these button input objects here with this duel virtual and physical, uh, button state tracker objects here these boolean values this is gonna allow you to prevent your player from just jumping up and down continuously.

  • And you're gonna get functionality that looks like this instead, you're just gonna be able to jump one time and on Lee jump when you press the key rather than jumping just continuously if you have a key held down, All right, so now that you know how to define the player, object in the world, object in our game class and you know how to get user input to actually control this guy and moving around screen, the only thing left is how to draw this guy.

  • So how were drawing the player object and the world itself?

  • To this campus element is with the display class so inside of display class I just to find a buffer in a context of my canvas of these air to ah to d rendering contexts canvas rendering contexts.

  • Ah, context is the rendering context of the onscreen canvas that you see on screen.

  • Buffer is just an offscreen canvas that is going to be sized perfectly to match our world heightened with.

  • So if we come back into our game and scroll down here, the buffer canvas is going to be sized 72 pixels high and 128 pixels wide, and that is actually gonna happen all the way at the bottom of remain Js file.

  • And the reason for that is just so everything scales the way it should.

  • So I'm setting that up right here in the initialization portion of my main Js file.

  • Let's get back in the display and talk a little bit more about it.

  • So I've added a couple of different functions.

  • I have the fill function or method.

  • All it does is take a color and draw it to the buffer, which remembers the offscreen campus, which is then drawn by the render function to our on screen campus that draws this offscreen buffer canvas to the onscreen display campus that you actually see in the browser window.

  • So the film method just fills the buffer with color.

  • Render renders that buffer to the screen draw a rectangle, just draws this rectangle here.

  • That's what I'm handing in in terms of X Y.

  • Within height and color.

  • It just draws that to the buffers.

  • Well, I'm actually rounding down the X and Y positions of the rectangle that I'm handing in this way it doesn't render it on 1/2 pixel, so Let's see what happens when I take this coat out first.

  • Let's take a look at what this guy actually looks like moving around the screen.

  • So pretty smooth.

  • Nothing weird going on.

  • All the edges look pretty straight.

  • If I come in here and I just put in X and why, though, let's take a look and see if anything changes.

  • As you can see, it looks a little bit funky.

  • I mean, it's it's hard to tell because I have this background.

  • Let's actually go in here and change the background color of my game to something else.

  • Here, let's Ah, let's make it black instead.

  • 000000 That's gonna give me black.

  • Gonna come in here, refresh my screen.

  • Take a look at the player character.

  • You see how his edges ey're kind of messed up there, kind of wavering, a little bit, kind of looks like he's just fading around instead of just sitting on the exact pixel he's meant to sit on.

  • Well, that is actually because he's sitting on half pixels from time to time, and you can fix that by rounding down the values you pass into your draw method so we can go from this which looks a little bit funky to this, which now looks really sharp and not so funky.

  • It all its basis, basically the best we can get on a HTML five campus.

  • So now let me set back my background color to the way it was before so we could have a nice fading effect that follows the character around.

  • Refresh my screen.

  • And so that's what I'm gonna use to draw my rectangle to the screen and round him to the nearest full picks a location with math thought floor so he doesn't get that weird blur around the edges.

  • Now let's take a look at the main Js file, and that's where all of these they're gonna be called.

  • Actually, I think inside of actually, no, I'm wrong.

  • Let's just take a look at the main Js file and see where these guys are called.

  • I think it's gonna be inside of the render function.

  • I don't look at this code in a little bit, so we have displayed out Phil.

  • And by the way, the render function is gonna be called by our game engine, which is our game loop on every frame of the animation.

  • So 30 times every second.

  • So we called displayed out, Phil, that's gonna fill our background color with this nice, dark gray here we're gonna call draw a rectangle and hand in the game dot world dot player, which is a rectangle we're gonna hand in all those values as well as the color value.

  • And finally, we're gonna call displayed out render, and that's gonna draw the buffer that we just filled with the background color and drew the rectangle too.

  • And we're gonna draw that that buffer to the final display canvas.

  • Now, one more thing I want to talk about here because this is pretty simple stuff is the resize functionality.

  • So the resize functionality is gonna take care of scaling my on screen canvas.

  • It's also gonna take care of keeping the aspect ratio of the game world, which she remember has the heightened with coordinates.

  • It's gonna keep that aspect ratio from the game world and the buffer to the final display canvas.

  • So we get a nice square rectangle every time.

  • So what's important about that?

  • Well, if we come down here, I told you that we're gonna set our buffers canvas heightened with to the game world heightened with.

  • Now if I were to change this and say Height times your 0.5, obviously, that would warp our game a little bit.

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

  • Oh, kind of cut off half the world.

  • But you can see that when I jump up, the player is actually a lot taller than he is wide by about twice as much.

  • So let's step back that that back to normal.

  • And go ahead and take a look at the re size function, which I'm adding to the window that handles all of that stuff.

  • So resize is right here.

  • It's gonna call display dot resize.

  • It is gonna hand in document that document element dot kind with and client hi.

  • It's going to subtract 32 pixels from both of those to give us this nice 16 pixel margin on either side of our display campus, which I have three fresh so it goes back to the way it's supposed to be.

  • So that's what that's doing.

  • Those 32 pixels then is gonna hand in an aspect ratio of our Game world, which is gonna be just the height divided by the width of our game world.

  • So now let's take a look inside of the display objects resize method and see what it's actually doing.

  • So here I am, inside of the resize method.

  • And as you can see, it's not super complicated, is actually pretty simple.

  • It's basically just going to say, if the height and with ratio that we hand it in from, ah, document that document element dot com with inclined height minus 32 pixels to give us that nice 16 pics of margin on each side is going to say if that ratio is greater than the height width ratio of the world object thegame world, then if that height with.

  • If the screen's height width ratio is greater than the height width ratio of the world, then we're gonna set the campuses height to the with times the ratio, and we're gonna set the campuses with to the width of the screen.

  • So right now we have a max with scenario.

  • Otherwise, we don't have a Max with scenario, have a max height scenario, and it's gonna set the camp's height to the max height, and it's going to set the with to the height divided by the height width ratio.

  • So this code is basically gonna allow me to do this if I come in here and I changed the scale of my window.

  • As you can see, it's always gonna keep it centered.

  • Right now, I have a max height situation, and I have a shorter within my game world.

  • But the aspect ratio remains the same.

  • And here I'm gonna go back down somewhere around here.

  • I'm gonna change over from a max height situation to a Max with situation.

  • And now you can see my display canvas gradually getting smaller in height.

  • So that's pretty cool.

  • That's the display function takes care of re sizing the canvas and maintaining that nice aspect ratio so our game can scale nicely across many different devices.

  • All right, so now I'm gonna do a brief run through of my fixed time step game engine, and this is the game.

  • Engine classes in the engine file haven't changed it since.

  • Part one of this series is exactly the same as it was.

  • Although I did add in this accumulated time variable definition because it wasn't in here.

  • It was actually being added later.

  • Right here.

  • So I just fixed that.

  • But it's basically exactly the same.

  • So what a fixed time step game loop does is it causes your game toe update and draw at a fixed rate as time passes.

  • And it does that by taking a time step value, which in this case, is gonna be 1000 divided by 30.

  • If I come over to my main Js where I actually in Stan, she ate this.

  • I am passing 1000 vibe I 30 which is roughly 33.3333 repeating forever.

  • And that is gonna be the number of milliseconds that I'm going to call Render an update on.

  • So every 33.33 milliseconds I'm gonna call render an update and the rendering update functions air just right here.

  • You've seen him throughout this video, and they're basically just gonna update my game logic and render the change game state to the screen.

  • It's hot back inside of the engine and talk about this game loop some more.

  • All right, so we've got our time step, and that's gonna be in this case about 33.33 milliseconds.

  • And so every 33.33 milliseconds, I'm gonna call the update and render function I passed in.

  • They're gonna be stored right here.

  • And I'm gonna keep track of how much time has passed in the accumulated time variable.

  • So every time 33.33 milliseconds passes.

  • Hopefully our game is updating close to that.

  • But anything that overflows is gonna be stored in accumulated times.

  • Let's say 33.33 is our minimum time step.

  • And that's exactly what it is.

  • Let's say Ah, request animation frame, which is what we're using.

  • The update our game logic fires at 40 milliseconds.

  • Now we're gonna have about what is that?

  • 76 something about six milliseconds left over.

  • And we're gonna store that and accumulated time.

  • And when accumulated time reaches our time step or 33.33 we're gonna update or call update one more time.

  • So basically, we're storing all the time that passes in accumulated time, and we're just taking bites out of that accumulated time.

  • We're taking 33 millisecond bites out of our accumulated time.

  • The time value is just gonna be the last time our run function X executes.

  • This is just gonna be the looping function.

  • This is gonna be the function that loops over and over again.

  • Ah, lot of game engines will call this cycle some call it loop.

  • I'm just calling my run because engines run.

  • So this is gonna be one run of the game, Luke.

  • So inside of the run function, we're adding the current time stamp that's handed to us by request animation frame were subtracting the current time or the last time our game executed from the current time and we're adding that to accumulated time.

  • So let's say that are the last time our game executed was at 100 milliseconds and we're just now getting in a new call to the run function from requests, animation frame, and it's gonna happen at ah, 140 milliseconds.

  • So we're gonna have 140 milliseconds minus 100 milliseconds and 40 milliseconds is gonna go into accumulated time.

  • So now we have 40 milliseconds in accumulated time.

  • We're just gonna set the the new most recent time value to the current time value, but just remember 40 milliseconds in accumulated time.

  • Here is where the fixed time step game loop really shines.

  • Because this Oh, actually, this is just a safety I'm not gonna talk about.

  • But this is where it really shines the while.

  • Luke.

  • So remember, we have 40 milliseconds of accumulated time, and we have our 33 went around that down to 33 are 33 milliseconds in our time step.

  • So 40 is greater than 33.

  • So while 40 or accumulate time is greater than our time step, we're going to reduce our accumulated time.

  • By the time step we're gonna call update, we're going to set this updated flag to true.

  • But don't worry about that too much.

  • So then we're gonna loop again and see if that's true again.

  • And we subtracted 33 from 40.

  • So that's gonna give us seven.

  • And seven is no longer greater than our time step.

  • So we're not gonna update again.

  • But let's say that accumulated time was equal to 70.

  • Okay, so now we have 70.

  • 70 is greater than 33.

  • So we're gonna call update, then we're gonna try to love again.

  • Well, 70 minus 33.

  • What is that?

  • 47?

  • Is that accurate?

  • Anyway, 47 is still gonna be greater.

  • Whatever it is, it's still gonna be greater than 33.

  • So we're gonna update again.

  • So even if we didn't have another request animation frame event fire and we didn't update the screen again, we're still gonna be updating twice on the next time, request animation from fires and gives us a new run.

  • Commander, we're gonna call run again.

  • So we're gonna be keeping up with time, no matter what happens.

  • So even if our game runs really slow and Laghi, it's still going to keep up with time.

  • It's gonna run it the same rate, basically on any device, regardless of how slowed is unless is, the device is so slow that it just can't handle the game, and then it will crash.

  • But that's that's not something you really should worry about unless you're game is crashing all devices.

  • But if your game is just crashing the you know computers from 1995 then you shouldn't worry about it.

  • But basically the fixed time step game loop just says if too much time has passed or if a certain amount of time has passed.

  • We're gonna update our game at whatever rate we wanted to update until we have caught up with, however, much time has passed.

  • And that's a really great functionality of fixed time step game lives.

  • And it's a great reason Adam in tow any game that you're writing.

  • So now down here we have the updated flag.

  • Basically, we turn that flag on, we set it to true.

  • Anytime we do an update, and then if it is true, if we have updated, then we're gonna call the render function and that's just gonna draw a game.

  • So why is this good?

  • Well, let's say ah, accumulated time on Lee has 20 milliseconds in it, but we need 33 milliseconds to justify an update.

  • Otherwise, we'd be going faster than we want to update.

  • Well, if that's the case, then this never fires.

  • We never update our game.

  • And if we don't update our game, nothing has changed.

  • And anything we draw is just gonna be a waste of CPU or GPU.

  • So we don't want to draw anything that hasn't been updated or changed.

  • So if no update has occurred, update updated stays false and we don't draw.

  • And finally, inside of the run function, we just make another call to the handle, run method and handle run method is just down.

  • Here is just a narrow function that calls, ah, the engine dot run method.

  • Now the reason I'm using an arrow function is because if you've worked with event listeners or requests animation frame before the this key word inside of whatever function you hand into your event listener as your event handler or requests animation frame as your response to a request animation frame event, firing is gonna point to the object that you request the frame or add an event listener, too.

  • So in this case is the window, so I don't want this to refer to a window.

  • I want this to refer to my actual engine object.

  • So by using this arrow function, I can actually make this refer to my engine object rather than the window.

  • So that's the only reason I have that kind of set up is an arrow functionary to make sure that this key word is referring to engine instead of window. 00:50:18.210 -

Hey, guys, my name is Frank.

字幕與單字

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

B1 中級

用JavaScript創建一個平臺遊戲-完整教程 (Create a Platformer Game with JavaScript - Full Tutorial)

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