1 00:00:00,000 --> 00:00:01,964 [MUSIC PLAYING] 2 00:00:01,964 --> 00:00:16,612 3 00:00:16,612 --> 00:00:17,570 DAVID MALAN: All right. 4 00:00:17,570 --> 00:00:20,340 Welcome to Introduction to Game Development. 5 00:00:20,340 --> 00:00:22,820 My name is David Malan, and this is Colton Ogden. 6 00:00:22,820 --> 00:00:25,160 And this is a class that assumes only a class like CS50, 7 00:00:25,160 --> 00:00:28,160 which is the colleges and the extension schools introduction to computer 8 00:00:28,160 --> 00:00:30,050 science but more generally we just assume 9 00:00:30,050 --> 00:00:32,689 that you have prior programming experience in most any language 10 00:00:32,689 --> 00:00:34,100 and therefore have some comfort with some 11 00:00:34,100 --> 00:00:35,724 of the basic constructs of programming. 12 00:00:35,724 --> 00:00:38,875 But we assume no background in Lua or Lab 2D or any of the frameworks 13 00:00:38,875 --> 00:00:40,250 that we'll be using in the class. 14 00:00:40,250 --> 00:00:42,230 All of that lies ahead. 15 00:00:42,230 --> 00:00:46,087 So, if you're like me, you probably grew up with video games of some sort. 16 00:00:46,087 --> 00:00:48,920 And when you maybe started programming, the programming environments 17 00:00:48,920 --> 00:00:52,340 were perhaps very text based, black and white terminal window, and the like. 18 00:00:52,340 --> 00:00:55,580 And maybe you did something graphical with a language like scratch or Alice 19 00:00:55,580 --> 00:00:57,890 or beyond, or if you're in the world of the web, 20 00:00:57,890 --> 00:01:01,550 you've made more graphical applications of some sort, but still pretty static. 21 00:01:01,550 --> 00:01:04,950 The sort of content comes on the screen, then the content changes, and so forth. 22 00:01:04,950 --> 00:01:07,866 And it's a little less obvious if you're a little newer to programming 23 00:01:07,866 --> 00:01:10,340 how you go about creating some of those games 24 00:01:10,340 --> 00:01:12,410 from yesteryear with which you all grew up, 25 00:01:12,410 --> 00:01:15,350 where there's a lot more animation, there's a lot more asynchronicity, 26 00:01:15,350 --> 00:01:17,210 lots of things happening at the same time. 27 00:01:17,210 --> 00:01:19,700 A lot of events happening, and you all NOT-- 28 00:01:19,700 --> 00:01:21,740 not only want to capture this interactivity, 29 00:01:21,740 --> 00:01:23,600 but also want to respond to events that are 30 00:01:23,600 --> 00:01:25,308 happening, especially if you have players 31 00:01:25,308 --> 00:01:27,110 elsewhere next to you or online. 32 00:01:27,110 --> 00:01:29,210 And so the way the course will be structured 33 00:01:29,210 --> 00:01:32,309 is through a narrative of these various games, many of which 34 00:01:32,309 --> 00:01:33,600 you might have played yourself. 35 00:01:33,600 --> 00:01:35,450 But over the course of the semester, we dive 36 00:01:35,450 --> 00:01:37,370 into the context of each of these games and look 37 00:01:37,370 --> 00:01:39,980 at some of the underlying principles, the constructs via which 38 00:01:39,980 --> 00:01:42,604 they were built up, and really use them as a point of departure 39 00:01:42,604 --> 00:01:45,200 for talking about those various capabilities 40 00:01:45,200 --> 00:01:47,600 that you might integrate into your own games. 41 00:01:47,600 --> 00:01:49,970 And then punctuating the semester, ultimately, 42 00:01:49,970 --> 00:01:51,320 will be a number of milestones. 43 00:01:51,320 --> 00:01:54,560 Some in the form of smaller assignments that are meant to reinforce just some 44 00:01:54,560 --> 00:01:58,170 of the more recent material and sort of set you up for success when the course 45 00:01:58,170 --> 00:02:01,110 is deeper and more hands on projects. , Because indeed, 46 00:02:01,110 --> 00:02:04,401 the project is where you'll build or extend some of your own games. 47 00:02:04,401 --> 00:02:07,400 And then the class itself will culminate at the very end of the semester 48 00:02:07,400 --> 00:02:09,900 with your very own final project, an opportunity to propose, 49 00:02:09,900 --> 00:02:13,040 to design, and implement a game that somehow or other draws 50 00:02:13,040 --> 00:02:14,330 upon the course's lessons. 51 00:02:14,330 --> 00:02:16,880 So that when you walk out of here in just a few months' time, 52 00:02:16,880 --> 00:02:19,020 you've not only played your fair share of games, 53 00:02:19,020 --> 00:02:21,522 but have actually built several of your own. 54 00:02:21,522 --> 00:02:23,480 So without further ado, allow me to turn things 55 00:02:23,480 --> 00:02:27,510 over to Colton for a look and a stroll through yesteryear's Pong. 56 00:02:27,510 --> 00:02:29,050 COLTON OGDEN: Thanks, David. 57 00:02:29,050 --> 00:02:31,790 I'm very excited to begin teaching you guys this course 58 00:02:31,790 --> 00:02:34,910 because game development was actually what got me 59 00:02:34,910 --> 00:02:36,520 into programming in the first place. 60 00:02:36,520 --> 00:02:40,910 I remember back in 2006 or 2007 buying this book here, 61 00:02:40,910 --> 00:02:47,110 3D Game Programming All In One, which was a look through 3D game programming. 62 00:02:47,110 --> 00:02:51,110 And it was a monolithic text in the context of a game engine 63 00:02:51,110 --> 00:02:54,110 that was popular in the late 2000s called Torque. 64 00:02:54,110 --> 00:02:58,310 It's not as in vogue these days, but it at the time was pretty popular, 65 00:02:58,310 --> 00:03:00,200 and it used a language called TorqueScript. 66 00:03:00,200 --> 00:03:02,750 And I remember reading through this book and seeing all this code, 67 00:03:02,750 --> 00:03:05,010 and I had never seen like source code at all before, 68 00:03:05,010 --> 00:03:07,457 or had ever been introduced to programming. 69 00:03:07,457 --> 00:03:09,290 And, frankly, I found it quite intimidating, 70 00:03:09,290 --> 00:03:13,400 because I was looking at all the syntax that I didn't understand, 71 00:03:13,400 --> 00:03:15,650 and I didn't know anything about game development. 72 00:03:15,650 --> 00:03:19,857 I had always played games growing up and been fascinated by it, 73 00:03:19,857 --> 00:03:22,190 but as I started getting more comfortable with computers 74 00:03:22,190 --> 00:03:23,939 and I started to get more curious about it 75 00:03:23,939 --> 00:03:25,940 and realized that it was a major profession, 76 00:03:25,940 --> 00:03:27,770 I started to dive a little deeper. 77 00:03:27,770 --> 00:03:29,030 This was my first foray. 78 00:03:29,030 --> 00:03:32,060 And after spending a little bit of time away from it 79 00:03:32,060 --> 00:03:34,820 after looking through the source code for a TorqueScript, which 80 00:03:34,820 --> 00:03:38,240 was rather arcane, a lot of percent symbols and dollar signs 81 00:03:38,240 --> 00:03:41,720 are the weird things that I just hadn't gotten my mind around. 82 00:03:41,720 --> 00:03:45,830 I went back to it, started to really learn the basics of programming 83 00:03:45,830 --> 00:03:48,980 and other languages, like Python and c and c++, 84 00:03:48,980 --> 00:03:53,314 and I grew to really like programming and computer science a lot. 85 00:03:53,314 --> 00:03:55,980 And here's just an image of what Torque looked like at the time. 86 00:03:55,980 --> 00:03:59,330 It was really the sort of predecessor to Unity nowadays. 87 00:03:59,330 --> 00:04:02,330 Although, in my opinion, Unity does things a lot better. 88 00:04:02,330 --> 00:04:05,270 It was more accessible, it uses languages that are more in vogue 89 00:04:05,270 --> 00:04:08,270 and popular and used by other people already in other domains. 90 00:04:08,270 --> 00:04:10,489 91 00:04:10,489 --> 00:04:13,030 And so we'll be covering Unity at the tail end of the course. 92 00:04:13,030 --> 00:04:15,890 So we'll be covering predominantly 2D game development. 93 00:04:15,890 --> 00:04:18,589 But the topics that we'll be covering today 94 00:04:18,589 --> 00:04:23,087 as we get started in the context of Pong are these bullet points here. 95 00:04:23,087 --> 00:04:25,670 Lua, which will be the language that we're using predominately 96 00:04:25,670 --> 00:04:28,010 throughout the course, which is a dynamic scripting language very 97 00:04:28,010 --> 00:04:29,750 similar to Python and JavaScript. 98 00:04:29,750 --> 00:04:33,290 We'll be covering LOVE 2D as our primary game framework, which 99 00:04:33,290 --> 00:04:39,590 is a runtime and a framework which exposes all of its methods 100 00:04:39,590 --> 00:04:41,630 for drawing, audio, input, etc. 101 00:04:41,630 --> 00:04:45,530 via Lua, so that it's very easy to write code very quickly, 102 00:04:45,530 --> 00:04:46,910 but get very good results. 103 00:04:46,910 --> 00:04:50,667 And the documentation for their framework is superb, in my opinion. 104 00:04:50,667 --> 00:04:53,000 Today we'll be talking about a few just basic principles 105 00:04:53,000 --> 00:04:54,960 as we get our feet wet with game development. 106 00:04:54,960 --> 00:04:57,830 Things like drawing shapes, drawing text, 107 00:04:57,830 --> 00:05:00,440 these are both very big aspects of Pong, which 108 00:05:00,440 --> 00:05:03,500 is just a very simple game based on shapes and text 109 00:05:03,500 --> 00:05:05,120 moving around the screen. 110 00:05:05,120 --> 00:05:09,350 We'll be talking about Delta Time and Velocity, which Delta Time is probably 111 00:05:09,350 --> 00:05:11,587 arguably one of the most important variables 112 00:05:11,587 --> 00:05:14,420 that we keep track of in any game framework or engine, which is just 113 00:05:14,420 --> 00:05:17,710 the amount of time that's elapsed since the last frame of execution 114 00:05:17,710 --> 00:05:22,454 in our game, measured in LOVE 2D in terms of seconds, fractions of seconds. 115 00:05:22,454 --> 00:05:24,370 We'll be talking about game state, because you 116 00:05:24,370 --> 00:05:25,660 can have a state in your game. 117 00:05:25,660 --> 00:05:29,359 You can be at the title screen, you can be playing, you can be in a menu. 118 00:05:29,359 --> 00:05:32,650 This will, obviously, be very important because you want different update logic 119 00:05:32,650 --> 00:05:35,525 and rendering logic depending on what state you're in. 120 00:05:35,525 --> 00:05:37,900 We'll be talking about basic object oriented programming, 121 00:05:37,900 --> 00:05:41,080 for those who might be unfamiliar coming from C. 122 00:05:41,080 --> 00:05:45,640 It's basically a way of encapsulating our data, any of our game objects, 123 00:05:45,640 --> 00:05:49,150 in such a way that the variables that are relevant to them 124 00:05:49,150 --> 00:05:52,370 are put together, along with functions that will operate on that data. 125 00:05:52,370 --> 00:05:55,330 So instead of having like 20 different variables for all 126 00:05:55,330 --> 00:05:58,210 these different objects that you have to keep track of in your code, 127 00:05:58,210 --> 00:06:01,000 each individual object can keep track of all its own information, 128 00:06:01,000 --> 00:06:04,240 like its position, or anything else that's relevant to it. 129 00:06:04,240 --> 00:06:07,150 We'll be talking about hit boxes today, predominantly, 130 00:06:07,150 --> 00:06:09,280 in the context of box collision, because we'll 131 00:06:09,280 --> 00:06:11,989 be talking about Pong, which is just paddles and a ball. 132 00:06:11,989 --> 00:06:13,030 Those are all rectangles. 133 00:06:13,030 --> 00:06:16,600 And they'll be colliding with what's called axis aligned bound-- 134 00:06:16,600 --> 00:06:21,820 axis aligned bounding boxes, which makes calculating whether two boxes collided 135 00:06:21,820 --> 00:06:25,750 very simple, as opposed to calculating rotated hit boxes, 136 00:06:25,750 --> 00:06:27,190 which is a bit more complicated. 137 00:06:27,190 --> 00:06:29,439 And then, lastly, we'll polish off with sound effects, 138 00:06:29,439 --> 00:06:32,620 because adding that polished layer, in my opinion, is important 139 00:06:32,620 --> 00:06:36,670 and it ties it all together and makes it feel like a more cohesive whole. 140 00:06:36,670 --> 00:06:40,294 So two important things that we'll need to do when we're following along 141 00:06:40,294 --> 00:06:43,210 with the examples, which I'll show you a link to the repo in a moment, 142 00:06:43,210 --> 00:06:44,590 is getting LOVE 2D installed. 143 00:06:44,590 --> 00:06:46,190 It's a very simple process. 144 00:06:46,190 --> 00:06:48,290 The first link here is just a download link. 145 00:06:48,290 --> 00:06:50,920 So it's available for all major operating systems. 146 00:06:50,920 --> 00:06:53,620 So Linux, Mac, and Windows. 147 00:06:53,620 --> 00:06:56,190 And then the Getting Started link down here below 148 00:06:56,190 --> 00:06:58,300 will give you some tips as to how to get started, 149 00:06:58,300 --> 00:07:02,080 actually running it on your machine on Mac, iAlias, 150 00:07:02,080 --> 00:07:07,240 the actual runtime executable within the app that it comes with. 151 00:07:07,240 --> 00:07:11,470 So in my bash profiles that I can easily just type love space 152 00:07:11,470 --> 00:07:15,167 dot in any directory that has a main dot Lua file, 153 00:07:15,167 --> 00:07:16,750 and I can run it anywhere very simply. 154 00:07:16,750 --> 00:07:20,050 And there are similar instructions located on the page for other operating 155 00:07:20,050 --> 00:07:20,770 systems. 156 00:07:20,770 --> 00:07:23,350 And this is the repo here, which has all of the source code 157 00:07:23,350 --> 00:07:24,550 that we'll be using today. 158 00:07:24,550 --> 00:07:28,780 And I've structured it in a series of 13 different subrepos 159 00:07:28,780 --> 00:07:32,560 so that you can follow along and we can build upon Pong starting from scratch, 160 00:07:32,560 --> 00:07:35,326 going all the way to a fully implemented game. 161 00:07:35,326 --> 00:07:37,450 So the first thing we'll talk about is what Lua is. 162 00:07:37,450 --> 00:07:40,160 We'll be using Lua for about 75% of the course. 163 00:07:40,160 --> 00:07:42,370 It's a very popular dynamic scripting language. 164 00:07:42,370 --> 00:07:45,040 Portuguese for moon, and it was invented in the early 90s 165 00:07:45,040 --> 00:07:48,250 as primarily a config language and a runtime language 166 00:07:48,250 --> 00:07:54,130 for compiled code bases to save time on adding code to those code bases 167 00:07:54,130 --> 00:07:55,720 and recompiling them. 168 00:07:55,720 --> 00:07:58,480 A lot faster and a lot easier, especially in the context 169 00:07:58,480 --> 00:08:01,960 of the 90s when computers were much slower, to expose 170 00:08:01,960 --> 00:08:07,270 the core functionality of your application to Lua so that you can just 171 00:08:07,270 --> 00:08:11,500 run it dynamically and then interact with your compiled code on the fly, 172 00:08:11,500 --> 00:08:13,960 rather than having to recompile and wait minutes, 173 00:08:13,960 --> 00:08:19,150 potentially hours, just to get some new behavior. 174 00:08:19,150 --> 00:08:22,690 It's a language that's focused around the concept of a table. 175 00:08:22,690 --> 00:08:26,060 Almost everything in Lua aside from basic variables, are tables. 176 00:08:26,060 --> 00:08:30,230 A table is essentially a dictionary in Python or an object in JavaScript. 177 00:08:30,230 --> 00:08:31,790 Very similar. 178 00:08:31,790 --> 00:08:33,890 Intent, for embedded use in larger applications, 179 00:08:33,890 --> 00:08:37,150 and the very nature of Lua intended to be 180 00:08:37,150 --> 00:08:39,460 used in the context of these large applications 181 00:08:39,460 --> 00:08:42,130 meant that it was perfect for interacting with game engines. 182 00:08:42,130 --> 00:08:44,800 Because game engines are a perfect example of code bases 183 00:08:44,800 --> 00:08:48,850 that are traditionally compiled code for speed purposes. 184 00:08:48,850 --> 00:08:52,240 But it can be very cumbersome to have to add minor functionality, 185 00:08:52,240 --> 00:08:56,680 and then recompile it and potentially have your whole studio take hours. 186 00:08:56,680 --> 00:09:00,910 So we'll be using Lua and a compiled game framework, LOVE 2D, 187 00:09:00,910 --> 00:09:03,880 to allow us to rapidly develop. 188 00:09:03,880 --> 00:09:06,700 It's similar to JavaScript and Python. 189 00:09:06,700 --> 00:09:09,010 A little bit more so to JavaScript. 190 00:09:09,010 --> 00:09:11,230 And it's very excellent because it was initially 191 00:09:11,230 --> 00:09:15,070 intended as a config language, and a-- just sort of a glue layer. 192 00:09:15,070 --> 00:09:19,870 It's very good for storing data and code together, almost one in the same. 193 00:09:19,870 --> 00:09:24,700 So LOVE 2D is a fast 2D game develop-- 194 00:09:24,700 --> 00:09:25,630 development framework. 195 00:09:25,630 --> 00:09:28,790 It's compiled in C++ and it runs very efficiently. 196 00:09:28,790 --> 00:09:33,312 Because it's so simple, despite the fact that we're running it in Lua, 197 00:09:33,312 --> 00:09:35,020 and as modules for basically anything you 198 00:09:35,020 --> 00:09:37,061 would need in the context of 2D game development. 199 00:09:37,061 --> 00:09:40,690 Only 2D game development officially, although some people I know 200 00:09:40,690 --> 00:09:44,840 are working on slight little 3D experiments, but nothing official yet. 201 00:09:44,840 --> 00:09:47,872 But it has graphics, keyboard input, math, basically, anything 202 00:09:47,872 --> 00:09:50,080 you could want in the context of 2D game development. 203 00:09:50,080 --> 00:09:51,050 It's completely free. 204 00:09:51,050 --> 00:09:51,950 It's portable. 205 00:09:51,950 --> 00:09:54,310 You can even run it on mobile and also the web. 206 00:09:54,310 --> 00:09:57,400 And it's excellent for prototyping, even if you don't necessarily 207 00:09:57,400 --> 00:10:01,420 want to publish a game in LOVE 2D, it's great and easy and fast just 208 00:10:01,420 --> 00:10:03,970 to whip something up in LOVE 2D, and then port 209 00:10:03,970 --> 00:10:05,920 that over to whatever framework or engine 210 00:10:05,920 --> 00:10:08,870 you might be using in the real world. 211 00:10:08,870 --> 00:10:12,222 So before we get into looking at some actual concrete code, 212 00:10:12,222 --> 00:10:14,680 I think the most fundamental thing we should take a look at 213 00:10:14,680 --> 00:10:16,430 is what a game loop is. 214 00:10:16,430 --> 00:10:21,420 So a game, fundamentally, is just an infinite loop, like a while true or a 215 00:10:21,420 --> 00:10:22,790 while one. 216 00:10:22,790 --> 00:10:27,200 Only in this case, every iteration of that loop we're doing a set of steps 217 00:10:27,200 --> 00:10:28,850 back to back over and over again. 218 00:10:28,850 --> 00:10:31,480 We're processing input so we're seeing, has the user pressed 219 00:10:31,480 --> 00:10:34,220 a key on the keyboard, have they touched their joystick, 220 00:10:34,220 --> 00:10:36,896 have they moved the mouse, clicked the mouse. 221 00:10:36,896 --> 00:10:39,020 If they have, we need to feed that into our update. 222 00:10:39,020 --> 00:10:42,061 We need to keep track of that, and then change anything in our game state 223 00:10:42,061 --> 00:10:43,520 that relies upon that input. 224 00:10:43,520 --> 00:10:46,640 So we should move our paddles, we should detect collision, 225 00:10:46,640 --> 00:10:49,460 we should register all of this, and then whatever has updated, 226 00:10:49,460 --> 00:10:51,410 we want to rerender that. 227 00:10:51,410 --> 00:10:52,670 We want to render it-- 228 00:10:52,670 --> 00:10:55,190 render where it's changed so that we have the-- 229 00:10:55,190 --> 00:10:58,477 we see on our screen, visually, that things have actually 230 00:10:58,477 --> 00:11:00,560 changed in our game world and we interact with it, 231 00:11:00,560 --> 00:11:03,260 and we get a sense that we're using something, 232 00:11:03,260 --> 00:11:07,430 interacting with something dynamic. 233 00:11:07,430 --> 00:11:12,140 And in the context of 2D games, the most fundamental way of looking at the world 234 00:11:12,140 --> 00:11:14,720 is via the 2D coordinate system, which is just simply 235 00:11:14,720 --> 00:11:18,949 as we learned in geometry in high school, x and y-axis. 236 00:11:18,949 --> 00:11:21,740 In this case, it's slightly different than what we typically learn. 237 00:11:21,740 --> 00:11:26,360 In high school, we tend to learn that the xy origins, sort of bottom left, 238 00:11:26,360 --> 00:11:30,650 y positive goes up, negative goes down, positive x goes right, 239 00:11:30,650 --> 00:11:31,830 and negative x goes left. 240 00:11:31,830 --> 00:11:34,280 But in this case, we're actually starting in the top left, 241 00:11:34,280 --> 00:11:38,740 and then it goes y positive down, y negative up, x positive right, 242 00:11:38,740 --> 00:11:40,640 x negative left. 243 00:11:40,640 --> 00:11:42,980 And everything that we want to draw in our game 244 00:11:42,980 --> 00:11:45,350 needs to have an x and y-coordinate to draw in order 245 00:11:45,350 --> 00:11:48,170 for it to be visually seen on the screen. 246 00:11:48,170 --> 00:11:51,230 So today's goal, we're going to start a fairly low level 247 00:11:51,230 --> 00:11:55,670 and work our way up through examples today and in future classes. 248 00:11:55,670 --> 00:11:58,160 Our first game is arguably one of the simplest, 249 00:11:58,160 --> 00:12:00,920 but also, one of the most famous games of all time, Pong, 250 00:12:00,920 --> 00:12:03,500 which was released in 1972. 251 00:12:03,500 --> 00:12:05,499 And the gist of Pong is you have a paddle 252 00:12:05,499 --> 00:12:08,540 on the left side of the screen, a paddle on the right side of the screen, 253 00:12:08,540 --> 00:12:11,630 whoever scores 10 points by getting the ball past their opponent's paddle 254 00:12:11,630 --> 00:12:14,510 onto the edge of the screen, wins. 255 00:12:14,510 --> 00:12:19,220 And so today in our lecture, the scope is we want to, first and foremost, 256 00:12:19,220 --> 00:12:22,640 draw shapes to the screen, because that's how we get our ball and-- ball 257 00:12:22,640 --> 00:12:23,630 and paddles rendering. 258 00:12:23,630 --> 00:12:25,490 And those are just simply rectangles. 259 00:12:25,490 --> 00:12:28,070 We want to control the 2D position of these paddles, 260 00:12:28,070 --> 00:12:32,630 because we want them to move up and down and want the ball to also move. 261 00:12:32,630 --> 00:12:36,050 We want to detect collision between the paddles and the ball, 262 00:12:36,050 --> 00:12:38,780 because that's how we get the ball to deflect off the paddles, 263 00:12:38,780 --> 00:12:40,790 and to deflect off the ceiling and the floor. 264 00:12:40,790 --> 00:12:44,250 And, also, how we detect whether it's gone beyond the edges of the screen, 265 00:12:44,250 --> 00:12:47,270 such that one player scores a point. 266 00:12:47,270 --> 00:12:51,710 And then we want to add sound effects for sort of a feedback 267 00:12:51,710 --> 00:12:54,660 and sort of put ourselves into the game a little bit more. 268 00:12:54,660 --> 00:12:57,380 And then scorekeeping, because ultimately the purpose of the game 269 00:12:57,380 --> 00:12:59,780 is to beat your opponent, so you want a way 270 00:12:59,780 --> 00:13:02,840 to see who has scored 10 points first. 271 00:13:02,840 --> 00:13:06,590 And so we're going to look through a set of examples now in the repo. 272 00:13:06,590 --> 00:13:11,240 If we look at Pong Zero, I've set this to be called, The Day Zero Update. 273 00:13:11,240 --> 00:13:16,460 It's a trend among many games to have the games release major content 274 00:13:16,460 --> 00:13:17,870 updates as the x update. 275 00:13:17,870 --> 00:13:24,470 So just to be cute, I think we'll call each individual example here, 276 00:13:24,470 --> 00:13:26,220 The Something Update. 277 00:13:26,220 --> 00:13:29,960 And so I'm going to go into the Pong Zero 278 00:13:29,960 --> 00:13:36,680 Repo of the directory, the GitHub repo. 279 00:13:36,680 --> 00:13:40,669 And if we're looking at Pong Zero here, we can see it 280 00:13:40,669 --> 00:13:41,960 says here, The Day Zero Update. 281 00:13:41,960 --> 00:13:44,990 I've commented everything fairly heavily so that we can-- 282 00:13:44,990 --> 00:13:48,290 if you're reading the code, you can sort of get a sense of what's going on. 283 00:13:48,290 --> 00:13:52,580 At line 23, we're going to start off by just declaring 284 00:13:52,580 --> 00:13:54,039 a window width and a window height. 285 00:13:54,039 --> 00:13:55,788 And these are just constant variables that 286 00:13:55,788 --> 00:13:58,230 will be accessible throughout the rest of our application. 287 00:13:58,230 --> 00:14:01,160 So I'm just setting 1280 by 720 as an arbitrary resolution. 288 00:14:01,160 --> 00:14:03,770 It doesn't matter too much. 289 00:14:03,770 --> 00:14:07,700 An important thing that we need to look at here is that line 29, 290 00:14:07,700 --> 00:14:12,470 we're using a function called love.load, and I'm actually 291 00:14:12,470 --> 00:14:14,030 going to go back to the slides here. 292 00:14:14,030 --> 00:14:15,988 We're going to look at a few functions, and I'm 293 00:14:15,988 --> 00:14:18,560 going to go over them and just sort of tell you 294 00:14:18,560 --> 00:14:21,240 what they do before we look at the code in too much detail. 295 00:14:21,240 --> 00:14:26,330 So love.load is just a function that-- given to us by LOVE, LOVE 2D, 296 00:14:26,330 --> 00:14:28,940 and we overwrite it. 297 00:14:28,940 --> 00:14:31,080 We give it behavior, we tell it what to do. 298 00:14:31,080 --> 00:14:34,190 And LOVE 2D is going to look at it in our main.lua file. 299 00:14:34,190 --> 00:14:37,460 If we're looking at Pong Zero, you'll see it just has a main.lua file. 300 00:14:37,460 --> 00:14:42,410 LOVE 2D expects just a main.lua file, and will run the main.lua file, 301 00:14:42,410 --> 00:14:45,830 and you can reference any other file within the directory from that main.lua 302 00:14:45,830 --> 00:14:46,360 file. 303 00:14:46,360 --> 00:14:49,540 It's our bootstrap, effectively. 304 00:14:49,540 --> 00:14:52,610 We're going to override love.load with whatever 305 00:14:52,610 --> 00:14:55,250 we want to execute at the very beginning of our application. 306 00:14:55,250 --> 00:14:57,400 It's just a startup function. 307 00:14:57,400 --> 00:15:01,590 We can also define all that behavior outside of the function above it, 308 00:15:01,590 --> 00:15:03,887 but it's good practice to find it within love.load 309 00:15:03,887 --> 00:15:05,720 so that someone reading your code will know, 310 00:15:05,720 --> 00:15:08,990 OK, this is where all the startup code takes place. 311 00:15:08,990 --> 00:15:12,570 Love.update(dt) is a very important function. 312 00:15:12,570 --> 00:15:14,720 This function takes in a variable called (dt). 313 00:15:14,720 --> 00:15:17,164 Love passes it in a function. 314 00:15:17,164 --> 00:15:19,330 You're going to overwrite it with your own behavior, 315 00:15:19,330 --> 00:15:23,530 and Love is going to execute this every frame, passing it in delta time, 316 00:15:23,530 --> 00:15:27,790 and you can use delta time (dt) in that function 317 00:15:27,790 --> 00:15:32,680 to change your application based upon how much time has passed. 318 00:15:32,680 --> 00:15:36,670 (dt) will always be a fraction of a second, potentially more, 319 00:15:36,670 --> 00:15:38,320 depending on how slow your computer is. 320 00:15:38,320 --> 00:15:41,410 But, typically, one-sixtieth of a second. 321 00:15:41,410 --> 00:15:43,930 And you can scale anything in your game by that amount 322 00:15:43,930 --> 00:15:47,560 to get even behavior across all frame rates. 323 00:15:47,560 --> 00:15:52,180 Love.draw is the other big function amongst-- 324 00:15:52,180 --> 00:15:53,350 between update and draw. 325 00:15:53,350 --> 00:15:55,720 Two of the two, arguably, most important functions. 326 00:15:55,720 --> 00:15:58,120 Love.draw is the function that we're going 327 00:15:58,120 --> 00:16:02,996 to define that has all of our drawing behavior, our rendering behavior in it. 328 00:16:02,996 --> 00:16:05,620 And that's where we can draw our paddles, we can draw our ball. 329 00:16:05,620 --> 00:16:07,411 And then update is where we can like change 330 00:16:07,411 --> 00:16:09,840 the paddles position and so forth. 331 00:16:09,840 --> 00:16:13,100 Two more important functions we'll take a look at in the first example. 332 00:16:13,100 --> 00:16:19,799 Love.graphics.printf is the LOVE 2D analog of printf and C. The difference 333 00:16:19,799 --> 00:16:22,090 being that this printf lets us actually draw physically 334 00:16:22,090 --> 00:16:24,220 onto the screen versus a console. 335 00:16:24,220 --> 00:16:28,180 We give it a text as a string, and an x and a y-coordinate and, optionally, 336 00:16:28,180 --> 00:16:32,890 a width and an align, and it'll will draw the text at xy, 337 00:16:32,890 --> 00:16:35,500 but it will also take into consideration the width, 338 00:16:35,500 --> 00:16:37,720 and it'll also take in consideration the align. 339 00:16:37,720 --> 00:16:41,840 The with is how much to align it, and the align is the mode of alignment. 340 00:16:41,840 --> 00:16:47,610 So if we say x is zero width, our window width, and then we say align center, 341 00:16:47,610 --> 00:16:51,250 it's going to go between zero and our window width and center align it. 342 00:16:51,250 --> 00:16:54,460 So that'll have the effect of center aligning our text. 343 00:16:54,460 --> 00:16:57,100 But we can just as easily say, right, and it will right 344 00:16:57,100 --> 00:16:59,200 align it between those two and have the effect 345 00:16:59,200 --> 00:17:01,033 of rendering the screen-- rendering the text 346 00:17:01,033 --> 00:17:03,220 along the right edge of the screen. 347 00:17:03,220 --> 00:17:06,760 And then lastly, love.window.setmode takes a width and a height 348 00:17:06,760 --> 00:17:08,260 and some optional parameters. 349 00:17:08,260 --> 00:17:11,730 Those parameters being things like V sync and full screen, 350 00:17:11,730 --> 00:17:16,550 and will actually set up our window and get it rendering onto the screen. 351 00:17:16,550 --> 00:17:21,400 And so if we go back to our source code here, it-- at line 29, 352 00:17:21,400 --> 00:17:23,599 we're overwriting love.load. 353 00:17:23,599 --> 00:17:27,670 We're passing in love.window.setmode, window width and window 354 00:17:27,670 --> 00:17:31,570 height, which recall we defined up above as 1280 by 720. 355 00:17:31,570 --> 00:17:33,220 We're passing in a table. 356 00:17:33,220 --> 00:17:36,280 This is the syntax for a table, these curly brackets. 357 00:17:36,280 --> 00:17:39,920 And the way that we define keys and values is just with an equal sign 358 00:17:39,920 --> 00:17:40,420 therein. 359 00:17:40,420 --> 00:17:44,202 So full screen gets false, resizeable gets false, V sync gets true. 360 00:17:44,202 --> 00:17:45,910 So it's going to not be full screen, it's 361 00:17:45,910 --> 00:17:48,230 going to be a not resizeable but it is going to be 362 00:17:48,230 --> 00:17:49,840 synced to our monitor's refresh rate. 363 00:17:49,840 --> 00:17:53,410 And that's where V sync is, short for vertical sync. 364 00:17:53,410 --> 00:17:56,740 And then on line 40, we're overwriting love.draw, 365 00:17:56,740 --> 00:18:00,700 and this has the love.graphics.printf function, they're in, 366 00:18:00,700 --> 00:18:03,250 and we're saying-- we're passing in the string, hello, Pong. 367 00:18:03,250 --> 00:18:06,360 We're starting it at x zero, we're setting it 368 00:18:06,360 --> 00:18:10,090 at y window height divided by 2, minus 6. 369 00:18:10,090 --> 00:18:13,220 Because the default font size in LOVE 2D is 12 pixels tall. 370 00:18:13,220 --> 00:18:15,220 So we're shifting it up by six so it's perfectly 371 00:18:15,220 --> 00:18:18,910 centered vertically in the screen. 372 00:18:18,910 --> 00:18:22,936 And then we're setting the alignment amount, the width, to window width 373 00:18:22,936 --> 00:18:25,810 so that it's going to align it within the entire width of our window. 374 00:18:25,810 --> 00:18:27,620 And now we're setting it to center alignment. 375 00:18:27,620 --> 00:18:30,078 So it's going to be center aligned within our entire window 376 00:18:30,078 --> 00:18:31,230 starting at x zero. 377 00:18:31,230 --> 00:18:36,280 And so if we go to Pong zero, and then we actually run it, 378 00:18:36,280 --> 00:18:38,080 it has the effect of doing this. 379 00:18:38,080 --> 00:18:41,500 We're just rendering in our default font, default size, 380 00:18:41,500 --> 00:18:44,230 hello, Pong, right in the middle of the screen. 381 00:18:44,230 --> 00:18:47,290 So not a terribly exciting example, but it 382 00:18:47,290 --> 00:18:51,400 is showcasing the most important functions of LOVE 2D, 383 00:18:51,400 --> 00:18:55,060 so that we can get started with slightly more interesting examples. 384 00:18:55,060 --> 00:18:59,270 So, our first content update, is the Low-Res Update. 385 00:18:59,270 --> 00:19:03,590 So we're developing Pong and Pong is an old game. 386 00:19:03,590 --> 00:19:06,280 It doesn't look like the example that we just looked 387 00:19:06,280 --> 00:19:09,410 at where the font is fairly high res. 388 00:19:09,410 --> 00:19:11,740 We want something that looks a little more retro. 389 00:19:11,740 --> 00:19:14,890 So what we want to do is get our resolution 390 00:19:14,890 --> 00:19:18,490 looking like it's from a game released in 1972. 391 00:19:18,490 --> 00:19:22,220 So what we're going to do is look at a few more important functions here. 392 00:19:22,220 --> 00:19:25,240 So Pong One has these functions. 393 00:19:25,240 --> 00:19:28,470 So love.graphics.setDefaultFilter. 394 00:19:28,470 --> 00:19:32,710 This function, the purpose of that, is every time 395 00:19:32,710 --> 00:19:36,130 we have a font or an image in our application, 396 00:19:36,130 --> 00:19:39,010 it's going to be applied a filter by default. 397 00:19:39,010 --> 00:19:41,186 So it's going to by default a bilinear filter. 398 00:19:41,186 --> 00:19:43,060 So what's going to happen, the effect of that 399 00:19:43,060 --> 00:19:48,400 is, basically, whenever we magnify or downscale a texture, 400 00:19:48,400 --> 00:19:51,130 it's going to think that-- it's going to assume 401 00:19:51,130 --> 00:19:55,960 that we want it to be slightly blurred so as to not look too pixilated. 402 00:19:55,960 --> 00:19:57,700 Which is good in certain contexts. 403 00:19:57,700 --> 00:20:01,362 For higher res 2D game development, that's good, but as we're going to see, 404 00:20:01,362 --> 00:20:03,820 that's not particularly good in the context of retro games. 405 00:20:03,820 --> 00:20:08,180 Retro games have a very 2D, crisp, pixilated aesthetic, 406 00:20:08,180 --> 00:20:09,890 and we want to preserve that. 407 00:20:09,890 --> 00:20:11,740 And so this lets us set a default filter. 408 00:20:11,740 --> 00:20:14,030 We'll see that in usage shortly. 409 00:20:14,030 --> 00:20:17,130 Another important-- very important function which is the input phase 410 00:20:17,130 --> 00:20:21,150 of our game loop that we saw earlier, love.keypressed(key) is what's going 411 00:20:21,150 --> 00:20:24,610 to allow us to start interacting with that aspect of our game. 412 00:20:24,610 --> 00:20:28,440 So love.keypressed(key) is a callback function that LOVE expects in main.lua. 413 00:20:28,440 --> 00:20:29,940 We're going to overwrite it. 414 00:20:29,940 --> 00:20:31,980 It gets passed in a key, and this function 415 00:20:31,980 --> 00:20:35,230 gets called every time by LOVE 2D whenever we press a key. 416 00:20:35,230 --> 00:20:37,810 It'll detect a key pressed, and it will call this function. 417 00:20:37,810 --> 00:20:39,810 Whatever we've defined in here, it will call it, 418 00:20:39,810 --> 00:20:42,810 and we can set it to take in certain keys 419 00:20:42,810 --> 00:20:46,030 and perform certain operations on that input and it will get a string. 420 00:20:46,030 --> 00:20:49,320 So if we say-- if we press the escape key, 421 00:20:49,320 --> 00:20:52,390 key is going to be equal to the string escape in that function, 422 00:20:52,390 --> 00:20:53,910 and we have access to that. 423 00:20:53,910 --> 00:20:56,700 And another important function, love.event.quit. 424 00:20:56,700 --> 00:20:59,760 This has just a very simple effect of quitting the application, 425 00:20:59,760 --> 00:21:03,980 though we can call it in the code as opposed to doing it ourselves. 426 00:21:03,980 --> 00:21:07,882 And so here's an example of what texture filtering looks like. 427 00:21:07,882 --> 00:21:09,840 Point filtering is the same as nearest neighbor 428 00:21:09,840 --> 00:21:11,881 filtering, which is what we're going to be using. 429 00:21:11,881 --> 00:21:15,400 Bilinear filtering is shown on the right, where it looks pretty blurry. 430 00:21:15,400 --> 00:21:18,890 That's what LOVE 2D applies by default to both fonts and to textures. 431 00:21:18,890 --> 00:21:21,510 432 00:21:21,510 --> 00:21:23,620 And we'll see that in an example. 433 00:21:23,620 --> 00:21:25,810 I can actually run it in two different styles. 434 00:21:25,810 --> 00:21:28,890 So if you go to Pong One in the repo, and then we run it, 435 00:21:28,890 --> 00:21:32,919 we see here, hello, Pong is now blown up. 436 00:21:32,919 --> 00:21:34,710 And we'll look at some more code, actually, 437 00:21:34,710 --> 00:21:36,360 to see as to why it's blown up. 438 00:21:36,360 --> 00:21:42,870 But if we go back to our code, let me pull up Pong One. 439 00:21:42,870 --> 00:21:46,670 440 00:21:46,670 --> 00:21:52,150 Go to main.lua, and then I'm going to explain this in just a second, 441 00:21:52,150 --> 00:21:55,730 but let me comment this out and we'll see the difference here. 442 00:21:55,730 --> 00:21:58,370 You can see it looks a lot blurrier. 443 00:21:58,370 --> 00:22:00,930 And that's the default texture filtering taking place. 444 00:22:00,930 --> 00:22:04,400 It applies, like I said, not only to textures but also to fonts. 445 00:22:04,400 --> 00:22:06,150 And that's not the aesthetic we want. 446 00:22:06,150 --> 00:22:11,730 So let's look at Pong One in detail, starting at the top. 447 00:22:11,730 --> 00:22:14,250 On line 28, we're acquiring a library. 448 00:22:14,250 --> 00:22:16,850 This is how you get a library in your LOVE 2D 449 00:22:16,850 --> 00:22:18,740 application, or your LOVE application. 450 00:22:18,740 --> 00:22:21,160 Just equals require and the name of the library. 451 00:22:21,160 --> 00:22:25,490 Push is what we're going to be using to take our 1280 by 720 window 452 00:22:25,490 --> 00:22:31,490 and turn it into a virtual resolution window at 432 by 243. 453 00:22:31,490 --> 00:22:36,500 We can start to think of our game in terms of a more low res feel, 454 00:22:36,500 --> 00:22:40,220 and think about it in 432 by 243 pixels, but still render it 455 00:22:40,220 --> 00:22:42,240 in a window that's arbitrarily sized. 456 00:22:42,240 --> 00:22:47,060 In this case, we're preserving the 1280 by 720 window that we saw before. 457 00:22:47,060 --> 00:22:51,770 If you go to our love.load function, we see this being used on line 47. 458 00:22:51,770 --> 00:22:54,410 Instead of love.window.setmode, we're now 459 00:22:54,410 --> 00:22:59,000 using push setup screen, the push libraries setup screen function, 460 00:22:59,000 --> 00:23:02,570 where it takes a virtual width, a virtual height, our regular window 461 00:23:02,570 --> 00:23:05,840 width and our window height, and then the same table as before. 462 00:23:05,840 --> 00:23:09,850 And this has the effect of setting up a window that's 463 00:23:09,850 --> 00:23:12,710 got our concrete dimensions of 1280 by 720, 464 00:23:12,710 --> 00:23:16,580 but a virtual resolution of 432 by 243. 465 00:23:16,580 --> 00:23:20,480 And so now, when it renders, as we'll see shortly, as-- 466 00:23:20,480 --> 00:23:23,425 well, as we already did see, actually, it's magnified. 467 00:23:23,425 --> 00:23:28,490 It has the effect of giving us a lower resolution. 468 00:23:28,490 --> 00:23:32,930 And in line--on line 58, if we look at the love.keypressed function, 469 00:23:32,930 --> 00:23:37,400 we've put in there, if the key equals the string escape, 470 00:23:37,400 --> 00:23:39,500 then love.event.quit. 471 00:23:39,500 --> 00:23:41,030 So now we have input handling. 472 00:23:41,030 --> 00:23:45,530 We've overridden love.keypressed(key), which LOVE 2D is going to look 473 00:23:45,530 --> 00:23:47,919 for in our application, and then call as needed. 474 00:23:47,919 --> 00:23:49,460 And then we're just looking in there. 475 00:23:49,460 --> 00:23:52,250 If the key is escape, then love.event.quit. 476 00:23:52,250 --> 00:23:56,180 And if I run the application, I can now press escape on my keyboard 477 00:23:56,180 --> 00:23:59,780 and just quit it and not have to command quit or click 478 00:23:59,780 --> 00:24:03,450 the X on a Windows application. 479 00:24:03,450 --> 00:24:08,140 And we've changed one more thing, also, in the love.draw function on line 70. 480 00:24:08,140 --> 00:24:10,820 We're using the push library now. 481 00:24:10,820 --> 00:24:14,270 We need to-- it functions sort of as a state machine in that we 482 00:24:14,270 --> 00:24:17,240 set it to start rendering at a virtual resolution with push 483 00:24:17,240 --> 00:24:21,176 apply start, and then push apply end, and then anything in between, 484 00:24:21,176 --> 00:24:23,300 this is very similar actually to how Open Go works. 485 00:24:23,300 --> 00:24:25,110 We won't go into too much detail. 486 00:24:25,110 --> 00:24:30,860 But this is very similar in spirit to how much of Open Go programming works. 487 00:24:30,860 --> 00:24:32,810 Push apply start, push apply end. 488 00:24:32,810 --> 00:24:37,110 Between that, whatever we call, is going to render at this virtual resolution. 489 00:24:37,110 --> 00:24:40,250 And so we are calling the same love.graphics.print(f) function, 490 00:24:40,250 --> 00:24:43,490 hello Pong Zero, virtual high divided by two minus six. 491 00:24:43,490 --> 00:24:44,810 Same parameters. 492 00:24:44,810 --> 00:24:48,680 And it has the effect of rendering everything that's still got 493 00:24:48,680 --> 00:24:53,840 the old aliasing going on the skin-- 494 00:24:53,840 --> 00:24:58,150 texture filtering going on as the effect of giving us our magnified text. 495 00:24:58,150 --> 00:25:02,910 So same text, same size, but now our window of rendering is much smaller. 496 00:25:02,910 --> 00:25:04,550 So-- 497 00:25:04,550 --> 00:25:08,660 Any questions so far on how any of this works? 498 00:25:08,660 --> 00:25:09,160 OK. 499 00:25:09,160 --> 00:25:10,580 Awesome. 500 00:25:10,580 --> 00:25:14,660 So we've gotten text right into the screen, but we're nowhere close to Pong 501 00:25:14,660 --> 00:25:16,910 yet, so the first big thing, I think, that's 502 00:25:16,910 --> 00:25:20,210 going to get us closer in that direction is what we're 503 00:25:20,210 --> 00:25:22,040 going to call the rectangle update. 504 00:25:22,040 --> 00:25:26,080 So some important functions that we should look at. 505 00:25:26,080 --> 00:25:27,800 Love.graphics.newFont. 506 00:25:27,800 --> 00:25:30,860 The default font, I believe, is Arial. 507 00:25:30,860 --> 00:25:33,819 We don't want Arial in our application, because we want something 508 00:25:33,819 --> 00:25:35,110 that looks a little more retro. 509 00:25:35,110 --> 00:25:38,570 We want something that looks more relevant. 510 00:25:38,570 --> 00:25:43,400 Love.graphics.newFont will basically take a path to a font file 511 00:25:43,400 --> 00:25:49,050 that we have in our folder, which if you're in the Pong 2 folder, 512 00:25:49,050 --> 00:25:53,420 you'll see a font.ttf file, and a size. 513 00:25:53,420 --> 00:25:55,760 Because every font object that we instantiate 514 00:25:55,760 --> 00:25:59,240 needs to have a size, because the font objects are immutable. 515 00:25:59,240 --> 00:26:03,560 Once constructed, they cannot be changed so they need to be allocated on a size 516 00:26:03,560 --> 00:26:05,560 by size basis. 517 00:26:05,560 --> 00:26:08,540 Love.graphics.setFont will take whatever font 518 00:26:08,540 --> 00:26:13,070 object we've acquired from this function call, and we can set it here 519 00:26:13,070 --> 00:26:17,630 and it'll set the active font in LOVE 2D to be that font. 520 00:26:17,630 --> 00:26:20,240 Love is a state machine in the same sense 521 00:26:20,240 --> 00:26:25,520 as before, in that it will have an active font at any one time, 522 00:26:25,520 --> 00:26:29,630 and whatever print functions you call, will use the currently active font. 523 00:26:29,630 --> 00:26:33,590 And that also applies to whatever color you might want to render to the screen, 524 00:26:33,590 --> 00:26:34,260 whatever. 525 00:26:34,260 --> 00:26:37,070 If you have a font and you want to maybe render it in red, 526 00:26:37,070 --> 00:26:42,080 you need a set LOVE 2D's active color to red as well. 527 00:26:42,080 --> 00:26:47,330 Love.graphics.clear is a function that takes an RGBA quadruple, 528 00:26:47,330 --> 00:26:49,956 and will flush the screen in that color. 529 00:26:49,956 --> 00:26:52,580 It just has a simple effect of wiping the screen in that color. 530 00:26:52,580 --> 00:26:55,512 Useful for drawing just flat color backgrounds. 531 00:26:55,512 --> 00:26:58,220 And then the last function, probably the most important function, 532 00:26:58,220 --> 00:27:00,322 is love.graphics.rectangle. 533 00:27:00,322 --> 00:27:02,030 And this is the first function that we'll 534 00:27:02,030 --> 00:27:05,540 see that actually ends up drawing something beyond text to the screen. 535 00:27:05,540 --> 00:27:09,620 It takes it a mode, which can be fill or line, an x and a y 536 00:27:09,620 --> 00:27:13,140 and a width and a height, and it'll draw a rectangle in that mode. 537 00:27:13,140 --> 00:27:16,610 So either filled, so a filled rectangle, or a line rectangle. 538 00:27:16,610 --> 00:27:19,550 It'll take-- it'll draw it at xy with the width and the height 539 00:27:19,550 --> 00:27:21,420 that we pass in. 540 00:27:21,420 --> 00:27:24,240 So let's go ahead and take a look at Pong 2 541 00:27:24,240 --> 00:27:27,800 where we can see this actually implemented. 542 00:27:27,800 --> 00:27:31,640 So Pong 2, we have our font.ttf in there that I've included. 543 00:27:31,640 --> 00:27:33,179 And then a main.lua. 544 00:27:33,179 --> 00:27:35,220 And by the way, I forgot to mention last example, 545 00:27:35,220 --> 00:27:39,330 push the library that we required is also just in the same directory. 546 00:27:39,330 --> 00:27:43,790 And you can just do require as long as the file is there within the directory, 547 00:27:43,790 --> 00:27:45,170 it will just load it. 548 00:27:45,170 --> 00:27:46,670 You don't have to specify .lua. 549 00:27:46,670 --> 00:27:52,580 It assumes when you require some string, that it follows with the .lua suffix. 550 00:27:52,580 --> 00:27:56,780 So we're here looking at main.lua in Pong 2, the rectangle update. 551 00:27:56,780 --> 00:27:59,900 So on line 28 to 34, it's all the same stuff. 552 00:27:59,900 --> 00:28:00,800 We're acquiring push. 553 00:28:00,800 --> 00:28:04,210 We have our width and height virtually and physically. 554 00:28:04,210 --> 00:28:08,120 In our love.load function, we are on line 43 555 00:28:08,120 --> 00:28:13,100 declaring small font to be a love.graphics.newFont, 556 00:28:13,100 --> 00:28:15,590 giving it the path font.ttf because it's right there 557 00:28:15,590 --> 00:28:17,720 in the same directory at size eight. 558 00:28:17,720 --> 00:28:21,260 And this is going to create a font object, small font, that we can then 559 00:28:21,260 --> 00:28:24,600 set as the active font as needed. 560 00:28:24,600 --> 00:28:29,930 So if we go down to line 78 in the same directory, 561 00:28:29,930 --> 00:28:32,432 we see we're calling love.graphics.clear, 562 00:28:32,432 --> 00:28:33,890 and so we're passing it in a color. 563 00:28:33,890 --> 00:28:36,890 I sampled some images of Pong on Google Images 564 00:28:36,890 --> 00:28:45,050 and saw a background gray that I liked, so 40, 45, 52, RGB, and then 255 just 565 00:28:45,050 --> 00:28:49,727 means completely opaque, so no transparency, the alpha component. 566 00:28:49,727 --> 00:28:51,560 And then we're doing the same print.function 567 00:28:51,560 --> 00:28:53,210 as we did before on line 81. 568 00:28:53,210 --> 00:28:56,990 Below that on line 89 down through 95, we're actually 569 00:28:56,990 --> 00:28:59,600 calling love.graphics.rectangle. 570 00:28:59,600 --> 00:29:02,210 And these are drawing the two paddles and then the ball. 571 00:29:02,210 --> 00:29:05,720 So, note, love.graphics.rectangle fill mode, 572 00:29:05,720 --> 00:29:09,320 because we want the paddles to be completely filled, as is the ball. 573 00:29:09,320 --> 00:29:14,450 We're giving it an xy of 1030 and a width height of 520. 574 00:29:14,450 --> 00:29:19,280 And on 992, we're-- and that'll have the effect of drawing it a little bit 575 00:29:19,280 --> 00:29:23,270 shifted from the top left corner, five pixels wide, 20 pixels tall. 576 00:29:23,270 --> 00:29:26,300 On line 92, we're doing the same thing, except we're 577 00:29:26,300 --> 00:29:28,260 going virtual width minus 10. 578 00:29:28,260 --> 00:29:32,040 So it's going to go to the right edge of our screen, virtual width minus 10. 579 00:29:32,040 --> 00:29:33,660 So 432 minus 10. 580 00:29:33,660 --> 00:29:36,260 So 422. 581 00:29:36,260 --> 00:29:38,370 And then virtual height minus 50. 582 00:29:38,370 --> 00:29:41,382 So it's going to be slightly-- 583 00:29:41,382 --> 00:29:43,840 it's going to be slightly up from the bottom of the screen. 584 00:29:43,840 --> 00:29:47,540 So we have our top left paddle and our bottom right paddle. 585 00:29:47,540 --> 00:29:48,920 And then the ball is dead center. 586 00:29:48,920 --> 00:29:52,070 So we're sitting-- doing another graphics-- rectangle call. 587 00:29:52,070 --> 00:29:54,080 Virtual width minus two divided by two. 588 00:29:54,080 --> 00:29:56,300 So right in the middle, minus two, because our ball 589 00:29:56,300 --> 00:29:58,550 is going to be two pixels wide by two pixels tall. 590 00:29:58,550 --> 00:30:00,466 And same thing with virtual height, minus two. 591 00:30:00,466 --> 00:30:03,080 Our virtual height divided by two, minus two. 592 00:30:03,080 --> 00:30:08,040 So I'm going to go ahead and CD into the Pong 2 directory and run it. 593 00:30:08,040 --> 00:30:11,790 And that has the effect of we have our new font here in the middle. 594 00:30:11,790 --> 00:30:16,040 So it looks nice and retro, much more so than the Arial font as before. 595 00:30:16,040 --> 00:30:19,149 We have a rectangle here, five pixels wide by 20 pixels tall. 596 00:30:19,149 --> 00:30:21,440 A ball in the middle, which is four pixels wide by four 597 00:30:21,440 --> 00:30:23,940 pixels tall, and then a paddle on the bottom right, 598 00:30:23,940 --> 00:30:27,260 which is the same dimensions as the paddle on the left. 599 00:30:27,260 --> 00:30:29,407 So it looks very similar to Pong. 600 00:30:29,407 --> 00:30:32,240 It's not interactive at all, but we sort of getting the feel of what 601 00:30:32,240 --> 00:30:33,781 we want our application to look like. 602 00:30:33,781 --> 00:30:35,930 We have it mostly sketched out. 603 00:30:35,930 --> 00:30:41,160 So any questions so far as to how this works? 604 00:30:41,160 --> 00:30:41,660 OK. 605 00:30:41,660 --> 00:30:43,440 Awesome. 606 00:30:43,440 --> 00:30:44,340 So Pong 3. 607 00:30:44,340 --> 00:30:48,587 So currently we have no interactivity with our application, 608 00:30:48,587 --> 00:30:50,670 and we want to be able to move the paddles around. 609 00:30:50,670 --> 00:30:53,552 We don't want to just be looking at an-- at an image the whole time. 610 00:30:53,552 --> 00:30:56,010 So the paddle update is going to solve this problem for us. 611 00:30:56,010 --> 00:30:59,735 We're going to actually get our first sort of-- beyond pressing escape 612 00:30:59,735 --> 00:31:01,860 to quit the application, we're going to get a sense 613 00:31:01,860 --> 00:31:04,200 of interacting with it dynamically. 614 00:31:04,200 --> 00:31:07,620 So the important function that we're going to look at in this example 615 00:31:07,620 --> 00:31:11,310 is love.keyboard.isDownsomekey. 616 00:31:11,310 --> 00:31:13,597 And this is true-- this is a Boolean function. 617 00:31:13,597 --> 00:31:16,680 It just returns true or false depending on whether the key that we pass in 618 00:31:16,680 --> 00:31:21,420 as a string is currently pressed down on this frame. 619 00:31:21,420 --> 00:31:23,980 So it just returns true or false. 620 00:31:23,980 --> 00:31:27,030 And so let's go ahead and take a look at the demo. 621 00:31:27,030 --> 00:31:31,110 We're going to go ahead and pull up Pong 3. 622 00:31:31,110 --> 00:31:32,670 The main.lua, they're in. 623 00:31:32,670 --> 00:31:35,430 624 00:31:35,430 --> 00:31:39,640 Notice that line 37 if you're looking in Pong 3 we, have a new constant 625 00:31:39,640 --> 00:31:43,260 we've defined called paddle speed, which gets the value 200. 626 00:31:43,260 --> 00:31:46,279 And this is just an arbitrary value that I found was a good speed. 627 00:31:46,279 --> 00:31:48,320 But this is how fast our paddle is going to move. 628 00:31:48,320 --> 00:31:50,280 We're going to scale it by delta time, so we're 629 00:31:50,280 --> 00:31:53,670 going to multiply it by how many seconds have passed. 630 00:31:53,670 --> 00:31:56,100 Typically, a fraction of a second since the last frame. 631 00:31:56,100 --> 00:31:59,040 And this is going to, therefore, move the same distance 632 00:31:59,040 --> 00:32:01,050 over time depending on whether your computer is 633 00:32:01,050 --> 00:32:05,230 running at 10 frames per second or 60 frames per second. 634 00:32:05,230 --> 00:32:10,680 So if we go down here to line 63, I've also set up two new variables. 635 00:32:10,680 --> 00:32:12,630 Player one score and player two score. 636 00:32:12,630 --> 00:32:14,550 Those are both initialized at zero. 637 00:32:14,550 --> 00:32:19,920 We're going to add in this example, also, some rendering of the score. 638 00:32:19,920 --> 00:32:22,470 And notice here on line 49, I've also added 639 00:32:22,470 --> 00:32:26,130 a new font, which showcases how you need to separate fonts based on their size 640 00:32:26,130 --> 00:32:27,660 because they're immutable objects. 641 00:32:27,660 --> 00:32:30,090 Score font gets love.graphics.newFont. 642 00:32:30,090 --> 00:32:35,430 Same exact font file, but it's 32 pixels large because the font-- or the score 643 00:32:35,430 --> 00:32:38,947 when rendered in Pong is pretty large in the middle of the screen. 644 00:32:38,947 --> 00:32:41,280 And so we're creating-- we have two different fonts now. 645 00:32:41,280 --> 00:32:44,210 One for rendering our message, one for rendering our score. 646 00:32:44,210 --> 00:32:47,460 And it's just going to render these two variables, player one score and player 647 00:32:47,460 --> 00:32:48,630 two score. 648 00:32:48,630 --> 00:32:52,260 And then we've also initialized our y values 649 00:32:52,260 --> 00:32:55,290 for the rectangles, the paddles on the left and the right. 650 00:32:55,290 --> 00:32:58,410 We need to keep track of their y position, because paddles in Pong 651 00:32:58,410 --> 00:33:00,330 can only move up or down. 652 00:33:00,330 --> 00:33:02,787 So player 1Y gets the same value it did before when 653 00:33:02,787 --> 00:33:05,370 we initialized the rectangle, when we drew it onto the screen. 654 00:33:05,370 --> 00:33:07,740 It's going to start at y 30, so pretty high up. 655 00:33:07,740 --> 00:33:10,350 And player 2y is going to start pretty low, virtual height 656 00:33:10,350 --> 00:33:14,250 minus 50, which is 432 minus 50. 657 00:33:14,250 --> 00:33:17,760 And so in love.update, which is our first actual use of the update 658 00:33:17,760 --> 00:33:21,780 function on line 75 with the (dt) parameter that gets passed in. 659 00:33:21,780 --> 00:33:24,320 Note, remember that LOVE 2D will pass that in for us, 660 00:33:24,320 --> 00:33:27,570 but we need to give it-- we need to define the behavior inside of it. 661 00:33:27,570 --> 00:33:31,770 We're using love.keyboard.isDown, and we're passing in the string w 662 00:33:31,770 --> 00:33:33,590 and s for this first block. 663 00:33:33,590 --> 00:33:36,000 This first block here is player one's movement. 664 00:33:36,000 --> 00:33:40,680 So, traditionally, on computer WASD is to move-- 665 00:33:40,680 --> 00:33:43,950 and this example, we're going to allow ourselves to move both paddles, 666 00:33:43,950 --> 00:33:46,170 so we're going to use w and s for the left paddle, 667 00:33:46,170 --> 00:33:48,030 and up or down for the right paddle. 668 00:33:48,030 --> 00:33:51,050 So if love.keyboard.isDown w, which means-- 669 00:33:51,050 --> 00:33:54,660 or we've currently pressing the W key, player 1y 670 00:33:54,660 --> 00:34:00,040 is going to get itself, plus negative paddle speed times delta time. 671 00:34:00,040 --> 00:34:01,450 So it's going to move up. 672 00:34:01,450 --> 00:34:04,924 It's going to take negative paddle speed, multiply it by delta time, 673 00:34:04,924 --> 00:34:06,840 and add that onto our y value, which will have 674 00:34:06,840 --> 00:34:08,580 the effect of shifting our paddle up. 675 00:34:08,580 --> 00:34:10,469 And it's the opposite for-- 676 00:34:10,469 --> 00:34:13,480 on line 82 for-- if we're pressing the s key. 677 00:34:13,480 --> 00:34:17,870 We need to increase the y by positive paddle speed, because recall, 678 00:34:17,870 --> 00:34:19,560 y-axis movement is-- 679 00:34:19,560 --> 00:34:22,359 up is negative, down is positive. 680 00:34:22,359 --> 00:34:24,900 We're doing the exact same thing with the paddle on the right 681 00:34:24,900 --> 00:34:30,570 except we're using up and down as the strings into love.keyboard.isDown. 682 00:34:30,570 --> 00:34:36,000 And then down below here, we are rendering in addition 683 00:34:36,000 --> 00:34:38,940 to what we rendered before, also, the score now. 684 00:34:38,940 --> 00:34:44,185 So on line 125, note that we're calling love.graphics.setFont, scoreFont, 685 00:34:44,185 --> 00:34:47,310 because if we don't call this, it will use just whatever the last font was, 686 00:34:47,310 --> 00:34:49,830 which by default is the eight pixel font because we set that 687 00:34:49,830 --> 00:34:51,840 up top in our program. 688 00:34:51,840 --> 00:34:54,030 We want to set it to the score font, and then we 689 00:34:54,030 --> 00:34:55,770 want to call love.graphics.print. 690 00:34:55,770 --> 00:34:59,520 In this case, I'm just printing them in concrete places not, using printf. 691 00:34:59,520 --> 00:35:01,820 Virtual width divided by two minus 50. 692 00:35:01,820 --> 00:35:03,570 So no matter how we scale our window, it's 693 00:35:03,570 --> 00:35:08,820 always going to be 50 pixels to the left of the center of the window, 694 00:35:08,820 --> 00:35:12,040 and the 30 pixels to the right of the center of the window 695 00:35:12,040 --> 00:35:16,780 if we're rendering the player two score. 696 00:35:16,780 --> 00:35:23,150 And so if we go into Pong 3 and we run it, it looks the same as before. 697 00:35:23,150 --> 00:35:26,880 Note, that we do have a score now in the middle of the screen, zero and zero. 698 00:35:26,880 --> 00:35:30,720 But, more importantly, we can move our paddles up and down. 699 00:35:30,720 --> 00:35:34,140 But there's one problem, and that's I can move beyond the edge of the screen, 700 00:35:34,140 --> 00:35:37,320 which is not behavior that we want in our application. 701 00:35:37,320 --> 00:35:40,310 So we have some interactivity, it's moving along, 702 00:35:40,310 --> 00:35:42,570 but we still have a long way to go, unfortunately. 703 00:35:42,570 --> 00:35:43,890 Or fortunately. 704 00:35:43,890 --> 00:35:47,340 So let's go ahead and look at the ball update. 705 00:35:47,340 --> 00:35:52,710 So we have paddles, they can move, they can move beyond the edge of the screen, 706 00:35:52,710 --> 00:35:57,070 but we don't have a ball that-- it just sits in the middle of the screen. 707 00:35:57,070 --> 00:35:58,704 And that's not what we're looking for. 708 00:35:58,704 --> 00:36:01,620 We want to have a ball that we can actually bounce between the paddles 709 00:36:01,620 --> 00:36:04,314 so we can get an actual game going beyond just moving paddles. 710 00:36:04,314 --> 00:36:06,480 So a few important functions we're going to look at. 711 00:36:06,480 --> 00:36:09,250 We're going to get our first look here at random. 712 00:36:09,250 --> 00:36:13,710 So in games, random number generation is a very common thing 713 00:36:13,710 --> 00:36:17,370 so that we get unpredictability and variability between different instances 714 00:36:17,370 --> 00:36:18,982 of our game. 715 00:36:18,982 --> 00:36:20,940 An important function that just belongs to Lua. 716 00:36:20,940 --> 00:36:23,010 It's not a LOVE 2D thing, it's just a Lua thing. 717 00:36:23,010 --> 00:36:25,530 Math.random seed numb. 718 00:36:25,530 --> 00:36:28,620 So many of you have probably heard of like seed, 719 00:36:28,620 --> 00:36:30,990 like a random number generator, seed, and that just 720 00:36:30,990 --> 00:36:33,150 means a random number generator. 721 00:36:33,150 --> 00:36:35,310 Because it's pseudo random, it needs some sort 722 00:36:35,310 --> 00:36:38,850 of starting value to base all of its random numbers off of. 723 00:36:38,850 --> 00:36:42,330 It takes a starting number, it performs some mathematical operation 724 00:36:42,330 --> 00:36:45,810 on that number to derive new random values that we can then 725 00:36:45,810 --> 00:36:46,990 use in our game engine. 726 00:36:46,990 --> 00:36:49,396 But if we give it the same number every single time, 727 00:36:49,396 --> 00:36:52,270 it's just going to give us the same random numbers every single time, 728 00:36:52,270 --> 00:36:53,890 which means it's not going to be random at all. 729 00:36:53,890 --> 00:36:55,420 It's going to be very consistent. 730 00:36:55,420 --> 00:36:58,530 So we need a way to seed our random number generator, 731 00:36:58,530 --> 00:37:01,930 give it a different initial value or seed, 732 00:37:01,930 --> 00:37:06,300 and we're going to do that with the function math.randomseed somenumb. 733 00:37:06,300 --> 00:37:09,930 OS.time is an important function in the context of this, 734 00:37:09,930 --> 00:37:13,980 because a very common way of getting a different number every time 735 00:37:13,980 --> 00:37:17,550 you run your application is passing it in whatever the current time is 736 00:37:17,550 --> 00:37:20,940 in seconds, because usually it's a very large number that 737 00:37:20,940 --> 00:37:24,210 is going to be different every single time you run your game, no matter what. 738 00:37:24,210 --> 00:37:27,150 Because it's based upon, in the context of most engines, 739 00:37:27,150 --> 00:37:32,450 in the context of Lua, what's called Unix Epoch time, which is zero 740 00:37:32,450 --> 00:37:39,150 zero UTC January 1st, 1970, which is some huge number nine or 10 digits long 741 00:37:39,150 --> 00:37:41,995 that changes every single second. 742 00:37:41,995 --> 00:37:44,370 And then in order to actually take advantage of all this, 743 00:37:44,370 --> 00:37:46,119 we need a function to get a random number, 744 00:37:46,119 --> 00:37:49,427 and so we do that with math.random, which takes a min and a max, 745 00:37:49,427 --> 00:37:51,510 although you don't need to technically pass a min, 746 00:37:51,510 --> 00:37:56,670 it'll just implicitly deuse min as one if you don't pass it a min. 747 00:37:56,670 --> 00:37:59,590 And it'll return a value inclusively within that range. 748 00:37:59,590 --> 00:38:02,790 So if you say math.random one, 50, it'll give us 749 00:38:02,790 --> 00:38:04,890 a random inclusively between 1 and 50. 750 00:38:04,890 --> 00:38:08,110 And if we just say math.random 50, it'll do the same exact thing. 751 00:38:08,110 --> 00:38:12,915 It'll say-- it'll assume rmin is one and give us a value between one and 50. 752 00:38:12,915 --> 00:38:14,790 And then two important mathematical functions 753 00:38:14,790 --> 00:38:18,450 that are very basic but helpful in the context of games almost everywhere, 754 00:38:18,450 --> 00:38:21,750 it's just math.min, which returns the lesser of two values, 755 00:38:21,750 --> 00:38:24,200 and math.max which turns the greater of two values. 756 00:38:24,200 --> 00:38:29,260 And we'll see this in the context of clamping values to some range. 757 00:38:29,260 --> 00:38:31,600 So let's go ahead and take a look at a demo here. 758 00:38:31,600 --> 00:38:33,806 So I'm going to go ahead and open up Pong 4. 759 00:38:33,806 --> 00:38:37,620 760 00:38:37,620 --> 00:38:39,710 And going to look at main.lua they're in. 761 00:38:39,710 --> 00:38:42,570 762 00:38:42,570 --> 00:38:48,300 So here on line 47, we see we're calling the math.random seedfunction as before. 763 00:38:48,300 --> 00:38:52,380 And note that we're passing in OS.time, another function call, 764 00:38:52,380 --> 00:38:55,779 because OS.time is going to be different every time we run our application. 765 00:38:55,779 --> 00:38:57,570 So we're seeding our application every time 766 00:38:57,570 --> 00:39:02,197 we run it based on whatever the current second is relative to zero, zero, zero, 767 00:39:02,197 --> 00:39:04,537 zero, zero, zero, January 1st, 1970. 768 00:39:04,537 --> 00:39:06,870 Which is going to be different every single time we run. 769 00:39:06,870 --> 00:39:10,800 Assuming we don't run it within the same second. 770 00:39:10,800 --> 00:39:16,860 And then if we go down to line 71 and seven-- or, sorry, 67 and 68, 771 00:39:16,860 --> 00:39:18,420 we now have-- 772 00:39:18,420 --> 00:39:21,420 we're giving a starting value to our ball, 773 00:39:21,420 --> 00:39:23,800 because we want to actually start manipulating our ball. 774 00:39:23,800 --> 00:39:27,300 So we give it an X and a Y. So we're setting it right to the center again 775 00:39:27,300 --> 00:39:29,490 but, now, we're defining a variable for it 776 00:39:29,490 --> 00:39:33,030 instead of just rendering it statically with our love.graphics.rectangle 777 00:39:33,030 --> 00:39:35,286 function, because we want this to change over time. 778 00:39:35,286 --> 00:39:37,660 We want to start letting our ball move around the screen. 779 00:39:37,660 --> 00:39:40,830 So these x and y variables are going to start changing now, 780 00:39:40,830 --> 00:39:44,100 and they're going to change relative to its current velocity. 781 00:39:44,100 --> 00:39:48,840 And its velocity is going to be stored in ball dx and ball dy. 782 00:39:48,840 --> 00:39:53,380 dx and dy are common shorthands for delta x and delta y, 783 00:39:53,380 --> 00:39:55,500 which is how you represent velocity. 784 00:39:55,500 --> 00:39:59,100 So what we're going to do, effectively, is take whatever our delta x and delta 785 00:39:59,100 --> 00:40:01,672 y are and add them onto our ball frame by frame, 786 00:40:01,672 --> 00:40:03,630 and that's going to have the effect of updating 787 00:40:03,630 --> 00:40:05,520 our ball's position by some value. 788 00:40:05,520 --> 00:40:07,890 And separating the delta x and the delta y 789 00:40:07,890 --> 00:40:12,662 will allow us to have different angles, different trajectories for our ball. 790 00:40:12,662 --> 00:40:15,370 And then another thing that we're also doing in this application, 791 00:40:15,370 --> 00:40:18,270 we're starting with the concept of a game state. 792 00:40:18,270 --> 00:40:21,840 Because now we can have a starting state, and what we're going to have 793 00:40:21,840 --> 00:40:23,040 is a play state. 794 00:40:23,040 --> 00:40:25,320 And so all we're going to do here in this example 795 00:40:25,320 --> 00:40:28,607 and in this application is start state as a string. 796 00:40:28,607 --> 00:40:31,440 In future examples, we're going to use what's called a state machine 797 00:40:31,440 --> 00:40:34,260 and actually separate out different states into their own-- 798 00:40:34,260 --> 00:40:35,770 into their own modules. 799 00:40:35,770 --> 00:40:36,870 But in the context of this game, we're just 800 00:40:36,870 --> 00:40:38,828 going to use a simple string just to illustrate 801 00:40:38,828 --> 00:40:42,180 how it works, and we're going to say our first state, when we start the game, 802 00:40:42,180 --> 00:40:46,650 should be the start string in the start state. 803 00:40:46,650 --> 00:40:49,440 And so here on line 86, what we're going to do 804 00:40:49,440 --> 00:40:52,394 is solve a problem that we had in the last example, which was, 805 00:40:52,394 --> 00:40:55,560 the paddles could move beyond the edges of the screen, which is not behavior 806 00:40:55,560 --> 00:40:56,820 that we should permit. 807 00:40:56,820 --> 00:41:02,640 So we're going to call math.max on zero, and the same operation 808 00:41:02,640 --> 00:41:06,180 we were doing before, and that will have the effect of returning whichever 809 00:41:06,180 --> 00:41:08,260 of those two values is greater. 810 00:41:08,260 --> 00:41:10,920 So if the value is-- 811 00:41:10,920 --> 00:41:13,971 if we're adding negative paddle speed to our y value 812 00:41:13,971 --> 00:41:15,720 and it goes into the negative range, which 813 00:41:15,720 --> 00:41:18,722 means it's beyond the top edge of the screen, 814 00:41:18,722 --> 00:41:20,680 zero is going to be the greater of those values 815 00:41:20,680 --> 00:41:23,750 and so it will always be zero in that case. math.max returns 816 00:41:23,750 --> 00:41:25,000 the greater of the two values. 817 00:41:25,000 --> 00:41:27,416 So it'll have the effect of clamping it such that it never 818 00:41:27,416 --> 00:41:29,520 goes above the top edge. 819 00:41:29,520 --> 00:41:32,130 The inverse is true for line 96, where we 820 00:41:32,130 --> 00:41:35,860 call math.min on virtual height minus 20, 821 00:41:35,860 --> 00:41:39,290 and player 1.y plus paddle speed.delta-- times delta time. 822 00:41:39,290 --> 00:41:41,790 And this will have the same effect, it will return whichever 823 00:41:41,790 --> 00:41:43,560 of these two values is lesser. 824 00:41:43,560 --> 00:41:47,820 In which case, if we've gone above virtual height minus 20, which 825 00:41:47,820 --> 00:41:52,410 is down at the bottom of the screen shifted by the size of our paddle, 826 00:41:52,410 --> 00:41:55,000 it's going to set it to virtual height minus 20. 827 00:41:55,000 --> 00:41:57,390 So we never go below that point. 828 00:41:57,390 --> 00:42:01,470 And we're doing the same thing for player two, exact same logic. 829 00:42:01,470 --> 00:42:04,440 And then if we're in the play state, we're 830 00:42:04,440 --> 00:42:07,069 going to actually update our ball's position. 831 00:42:07,069 --> 00:42:10,360 So we're in the-- if we're in the start state, ball's not going to move at all. 832 00:42:10,360 --> 00:42:14,730 But if we're in the play state, we want ball x to equal ball x plus ball 833 00:42:14,730 --> 00:42:16,200 x times delta time. 834 00:42:16,200 --> 00:42:19,440 And note that there is no shorthand in Lua 835 00:42:19,440 --> 00:42:23,520 for adding the value to itself, which is why we're calling ball 836 00:42:23,520 --> 00:42:26,340 x equals ball x, plus ball x times delta time, 837 00:42:26,340 --> 00:42:30,300 instead of just saying ball x plus equals ball x times delta time. 838 00:42:30,300 --> 00:42:33,090 Just a language decision that they made. 839 00:42:33,090 --> 00:42:35,760 But if we're in the play state, this will 840 00:42:35,760 --> 00:42:39,720 have the effect of scaling whatever our current ball's velocity is and-- 841 00:42:39,720 --> 00:42:43,020 times delta time, so it stays frame rate independent. 842 00:42:43,020 --> 00:42:45,930 And then adding it to ball x and ball y, which will shift it. 843 00:42:45,930 --> 00:42:53,300 And we get this actually working down here in line 170-- 844 00:42:53,300 --> 00:42:54,690 174. 845 00:42:54,690 --> 00:42:59,370 We're now-- instead of just rendering flat numbers to the screen, 846 00:42:59,370 --> 00:43:01,740 we're actually using ball x and ball y to render. 847 00:43:01,740 --> 00:43:05,190 And if we're in the play state, those will get updated. 848 00:43:05,190 --> 00:43:13,740 But if we go back up to line 127, now, we're in the love.keypressed function, 849 00:43:13,740 --> 00:43:15,420 so we're starting on line 120. 850 00:43:15,420 --> 00:43:19,530 Before we just had the if key equals escape, then love.event.quit. 851 00:43:19,530 --> 00:43:22,620 But now on line 127, we're going to check 852 00:43:22,620 --> 00:43:25,800 to see if the key is equal to enter or return, 853 00:43:25,800 --> 00:43:29,697 and then we're going to use that as our way of just testing state changes. 854 00:43:29,697 --> 00:43:32,280 So we're going to say if the game state is equal to the start, 855 00:43:32,280 --> 00:43:35,670 once you press entered, the game state should be equal to play. 856 00:43:35,670 --> 00:43:37,960 Otherwise, set it back to start. 857 00:43:37,960 --> 00:43:41,350 And we set it back to start, we're going to re-initialize our x 858 00:43:41,350 --> 00:43:43,780 and y to be in the center, virtual width divided by two 859 00:43:43,780 --> 00:43:46,420 minus two, virtual height divided by two minus two, 860 00:43:46,420 --> 00:43:51,040 and we're going to give it an initial random starting velocity again. 861 00:43:51,040 --> 00:43:54,040 And note here, this math.random two equal-- 862 00:43:54,040 --> 00:43:57,150 is equal to one, and 100, or negative 100. 863 00:43:57,150 --> 00:43:59,990 It's just Lua's way of doing a ternary operation. 864 00:43:59,990 --> 00:44:03,400 So in C, you will often have like-- 865 00:44:03,400 --> 00:44:08,530 you would be something like math.random two equals one, 866 00:44:08,530 --> 00:44:13,030 and you would have a question mark, 100 colon, negative 100. 867 00:44:13,030 --> 00:44:14,830 It's the same exact thing, but Lua doesn't 868 00:44:14,830 --> 00:44:18,850 have that sort of shorthand for a ternary operation, so we do it with 869 00:44:18,850 --> 00:44:20,350 and and or. 870 00:44:20,350 --> 00:44:24,310 We use logical operations instead to do the same thing. 871 00:44:24,310 --> 00:44:28,660 And note here we're also showcasing that math.random can take 872 00:44:28,660 --> 00:44:30,760 either one argument or two arguments. 873 00:44:30,760 --> 00:44:32,757 In this case, we're saying math.random two, 874 00:44:32,757 --> 00:44:35,090 which means it will give us a value between one and two. 875 00:44:35,090 --> 00:44:36,370 So a 50-50. 876 00:44:36,370 --> 00:44:39,070 And then if we do negative 50-50, that means 877 00:44:39,070 --> 00:44:41,500 we'll get a value between negative 50 and 50. 878 00:44:41,500 --> 00:44:44,147 So a range of 100, effectively. 879 00:44:44,147 --> 00:44:46,980 And so what that has the effect of doing, if we run our application, 880 00:44:46,980 --> 00:44:48,100 we go into Pong 4. 881 00:44:48,100 --> 00:44:50,970 882 00:44:50,970 --> 00:44:53,570 We're in the start state so now we're rendering-- 883 00:44:53,570 --> 00:44:57,230 if we're in the start state, it's set to render that message. 884 00:44:57,230 --> 00:45:01,370 If we press Enter, the ball gets a random veloc-- it's actually 885 00:45:01,370 --> 00:45:03,710 applying the velocity frame by frame. 886 00:45:03,710 --> 00:45:05,360 It's updating in the update method. 887 00:45:05,360 --> 00:45:09,410 If we press Enter again, it gets reset and we're back in the start state. 888 00:45:09,410 --> 00:45:11,550 So we do it again, it's getting a random value. 889 00:45:11,550 --> 00:45:12,830 Do it again, random value. 890 00:45:12,830 --> 00:45:13,860 Random value. 891 00:45:13,860 --> 00:45:17,030 So every time we're getting a different random ball value. 892 00:45:17,030 --> 00:45:24,230 But what happens if we try to actually run it, or try to interact with it? 893 00:45:24,230 --> 00:45:25,140 Nothing. 894 00:45:25,140 --> 00:45:26,207 Goes straight through. 895 00:45:26,207 --> 00:45:28,040 So we're missing a key piece, even though we 896 00:45:28,040 --> 00:45:32,210 have the core components of our game engine implemented, 897 00:45:32,210 --> 00:45:35,720 we don't have any concrete game play, nothing's interacting. 898 00:45:35,720 --> 00:45:38,550 And that's a major piece that we need to look at. 899 00:45:38,550 --> 00:45:40,040 And so the next-- 900 00:45:40,040 --> 00:45:42,170 before we actually start doing that, though, we're 901 00:45:42,170 --> 00:45:46,880 going to take a look at the class update, Pong 5. 902 00:45:46,880 --> 00:45:52,640 And so in order to get into more of a-- in order 903 00:45:52,640 --> 00:45:55,130 to scale our code more effectively, we need 904 00:45:55,130 --> 00:45:58,200 to start looking in terms of classes. 905 00:45:58,200 --> 00:46:01,826 And instead of having an x and a y for our ball, an x and a y for our paddle, 906 00:46:01,826 --> 00:46:04,700 a delta x, a delta y for our ball, all these different variables that 907 00:46:04,700 --> 00:46:07,310 are sort of all over the place starting to bloat our code, 908 00:46:07,310 --> 00:46:10,154 before we get too crazy with it, we should think about 909 00:46:10,154 --> 00:46:13,070 how can we put this data altogether so that we can just think in terms 910 00:46:13,070 --> 00:46:15,530 of our paddles or our ball object. 911 00:46:15,530 --> 00:46:17,780 And so we use what's called a class. 912 00:46:17,780 --> 00:46:21,110 If unfamiliar, a class is simply a way of taking all these variables that 913 00:46:21,110 --> 00:46:25,490 we've been using thus far, but putting them together in a container such that 914 00:46:25,490 --> 00:46:29,600 we can just say, paddle.x or paddle-- 915 00:46:29,600 --> 00:46:31,970 you know, in this case, car. 916 00:46:31,970 --> 00:46:37,530 If we have a function called drive car, now we can just say, car.drive instead. 917 00:46:37,530 --> 00:46:40,520 We don't have to have functions that are separate from our values, 918 00:46:40,520 --> 00:46:42,020 that-- we can put them all together. 919 00:46:42,020 --> 00:46:45,500 We can ask what's our car's current mileage instead 920 00:46:45,500 --> 00:46:48,120 of having all these different variables all over the place. 921 00:46:48,120 --> 00:46:51,890 So the classes are effectively blueprints. 922 00:46:51,890 --> 00:46:53,390 Use it-- you define a class. 923 00:46:53,390 --> 00:46:55,970 You say, OK, my car class is going to have 924 00:46:55,970 --> 00:46:58,400 a-- it's going to have a mileage variable, 925 00:46:58,400 --> 00:47:02,081 it's going to have a paint variable, it's going to have a make and a model, 926 00:47:02,081 --> 00:47:05,330 it's going to have all these things, and it's going to maintain its own state. 927 00:47:05,330 --> 00:47:07,080 It's going to maintain all of that for us. 928 00:47:07,080 --> 00:47:09,890 929 00:47:09,890 --> 00:47:10,970 as seen here. 930 00:47:10,970 --> 00:47:14,090 And, typically, these are what are called fields. 931 00:47:14,090 --> 00:47:16,700 And then we'll have methods as well. 932 00:47:16,700 --> 00:47:20,730 Functions that, instead of being like completely separate from this data, 933 00:47:20,730 --> 00:47:23,960 a car now basically owns its own functions. 934 00:47:23,960 --> 00:47:28,337 It has its own method called drive, or turn, or honk, et cetera, 935 00:47:28,337 --> 00:47:30,170 and we don't need to have a function called, 936 00:47:30,170 --> 00:47:35,090 like, turn car, or honk car, et cetera. 937 00:47:35,090 --> 00:47:37,700 And then this class is effectively a blueprint. 938 00:47:37,700 --> 00:47:40,550 Well, we'll see shortly how to define a class, 939 00:47:40,550 --> 00:47:44,617 but in order to actually have like one paddle that has its own set of data, 940 00:47:44,617 --> 00:47:47,450 and another paddle that has its own set of data, we need to define-- 941 00:47:47,450 --> 00:47:51,170 we need to instantiate, create objects from this class. 942 00:47:51,170 --> 00:47:54,440 Basically, use this class as a blueprint, but take it to a factory 943 00:47:54,440 --> 00:47:57,470 and create concrete cars from the blueprint. 944 00:47:57,470 --> 00:47:58,685 And those are objects. 945 00:47:58,685 --> 00:48:01,200 946 00:48:01,200 --> 00:48:03,195 And so as seen here, our paddles and ball 947 00:48:03,195 --> 00:48:05,880 are perfect simple use cases for doing this. 948 00:48:05,880 --> 00:48:08,090 So let's go ahead and take a look at Pong 5. 949 00:48:08,090 --> 00:48:14,481 950 00:48:14,481 --> 00:48:17,230 So in Pong 5, immediately, if you look at the directory structure, 951 00:48:17,230 --> 00:48:21,190 you can see that we've added a ball.lua and a paddle.lua. 952 00:48:21,190 --> 00:48:25,510 And it's tradition in most languages that have object oriented programming, 953 00:48:25,510 --> 00:48:31,180 as it's called, to capitalize class names just so you can differentiate 954 00:48:31,180 --> 00:48:37,400 classes, for example, from concrete objects or variables or functions. 955 00:48:37,400 --> 00:48:43,420 So if you go to our main.lua, on line 35, 956 00:48:43,420 --> 00:48:46,700 we're requiring a library, called class, which 957 00:48:46,700 --> 00:48:50,750 is what's going to allow us to actually create these classes. 958 00:48:50,750 --> 00:48:52,400 Because classes are not native-- 959 00:48:52,400 --> 00:48:55,310 they are in a sense a native Lua feature, 960 00:48:55,310 --> 00:48:59,900 but Lua's way of doing object oriented programming is a little bit convoluted. 961 00:48:59,900 --> 00:49:04,190 Some folks have kindly put together a library that makes it a lot simpler, 962 00:49:04,190 --> 00:49:07,430 and a lot more closely related to other languages that do object oriented 963 00:49:07,430 --> 00:49:12,530 programming more predominantly, like Java or C#, or even Python, 964 00:49:12,530 --> 00:49:16,370 allow us to use the keyword class in a way that's very similar to those 965 00:49:16,370 --> 00:49:17,560 libraries. 966 00:49:17,560 --> 00:49:22,749 On line 39 and 43, we're acquiring our own code, paddle and ball, 967 00:49:22,749 --> 00:49:25,040 and we're going to take a look at those right now so we 968 00:49:25,040 --> 00:49:27,330 can see what a class looks like. 969 00:49:27,330 --> 00:49:30,314 So I'm going to go ahead and open up a-- 970 00:49:30,314 --> 00:49:33,200 971 00:49:33,200 --> 00:49:36,740 the ball file, ball.lua. 972 00:49:36,740 --> 00:49:38,630 And we can see here all we need to do just 973 00:49:38,630 --> 00:49:43,220 to create a ball class is, using our class library, ball gets class, 974 00:49:43,220 --> 00:49:45,050 and then curly brackets like that. 975 00:49:45,050 --> 00:49:47,900 And so now we have a class object, a class table, 976 00:49:47,900 --> 00:49:50,780 effectively, because everything in Lua is a table. 977 00:49:50,780 --> 00:49:53,030 But we can think about it in terms of objects. 978 00:49:53,030 --> 00:49:56,060 We have a class object called ball, and then we 979 00:49:56,060 --> 00:49:59,520 can start to define functions that belong to this class. 980 00:49:59,520 --> 00:50:03,230 So we're going to define what's called a constructor, or an init function 981 00:50:03,230 --> 00:50:04,820 in this case, an initializer. 982 00:50:04,820 --> 00:50:09,200 And it's going to allow us to initialize our ball with whatever we want. 983 00:50:09,200 --> 00:50:11,930 In this case, we want to start our ball off with an x and a y 984 00:50:11,930 --> 00:50:13,610 and a width and a height. 985 00:50:13,610 --> 00:50:16,410 And notice within here we have a word called self. 986 00:50:16,410 --> 00:50:20,330 Self and this are common words in object oriented programming languages 987 00:50:20,330 --> 00:50:25,610 that mean whatever object we're creating with this class is going to be self. 988 00:50:25,610 --> 00:50:27,010 So we'll see that-- 989 00:50:27,010 --> 00:50:28,070 we'll see that shortly. 990 00:50:28,070 --> 00:50:29,250 Self.x gets x. 991 00:50:29,250 --> 00:50:31,940 So whatever concrete object we create using this call, 992 00:50:31,940 --> 00:50:36,320 this init call, set its x to this x, set its y to the y, set its width, 993 00:50:36,320 --> 00:50:37,000 set its height. 994 00:50:37,000 --> 00:50:38,240 That specific object. 995 00:50:38,240 --> 00:50:39,980 Self. 996 00:50:39,980 --> 00:50:42,620 And then we're doing the same thing for delta y and delta x, 997 00:50:42,620 --> 00:50:46,460 only that we are setting those two random values just as we did before. 998 00:50:46,460 --> 00:50:49,910 Self.dy, self.dx. 999 00:50:49,910 --> 00:50:54,890 That belongs to whatever specific object gets instantiated using this init call 1000 00:50:54,890 --> 00:50:57,530 as we'll see in the code. 1001 00:50:57,530 --> 00:51:01,070 We're defining just a reset function here just to make it easy. 1002 00:51:01,070 --> 00:51:02,660 Before we had a-- 1003 00:51:02,660 --> 00:51:06,150 several lines of code that set our ball to the middle of the screen 1004 00:51:06,150 --> 00:51:07,580 and gave it a random velocity. 1005 00:51:07,580 --> 00:51:09,830 We're doing that now, and this is a good way of sort 1006 00:51:09,830 --> 00:51:11,810 of refactoring out groups of logic. 1007 00:51:11,810 --> 00:51:15,260 We're creating a function called reset that just does that all in one function 1008 00:51:15,260 --> 00:51:19,250 call, and we just call that within our main function, condensing our code. 1009 00:51:19,250 --> 00:51:22,130 And then notice we have an update and a render function now. 1010 00:51:22,130 --> 00:51:24,440 And we are going to call these from our own update 1011 00:51:24,440 --> 00:51:27,560 and our own draw function such that every object 1012 00:51:27,560 --> 00:51:31,940 that we want in our game, every entity, and we'll build upon this game 1013 00:51:31,940 --> 00:51:33,680 by game in the future. 1014 00:51:33,680 --> 00:51:39,170 We'll just call update and render on everything from our main.lua, 1015 00:51:39,170 --> 00:51:43,160 and defer all of that to each individual class and objects 1016 00:51:43,160 --> 00:51:46,670 so we don't have to have a main.lua that's like 800 lines of code. 1017 00:51:46,670 --> 00:51:50,480 We just break out all of the updates that are pertinent to the ball here, 1018 00:51:50,480 --> 00:51:53,720 and all the render code that's pertinent to the ball here, 1019 00:51:53,720 --> 00:51:56,090 call each individual balls update and render, 1020 00:51:56,090 --> 00:51:59,630 and save ourselves a lot of time in refactoring. 1021 00:51:59,630 --> 00:52:01,655 We're doing the same thing if we look at paddle. 1022 00:52:01,655 --> 00:52:04,400 1023 00:52:04,400 --> 00:52:06,410 Paddle's a class, as well. 1024 00:52:06,410 --> 00:52:10,310 It gets the reason the class library. 1025 00:52:10,310 --> 00:52:13,910 Same exact sort of thing here, xy with height, and a dy. 1026 00:52:13,910 --> 00:52:17,960 In this case, we're just initialising that to zero so that we're not moving. 1027 00:52:17,960 --> 00:52:20,640 And then we're calling the update function here. 1028 00:52:20,640 --> 00:52:25,250 So if our dy is less than zero, we're using the math.max function as before, 1029 00:52:25,250 --> 00:52:29,630 with the top edge of the screen, and then whatever our y plus our current dy 1030 00:52:29,630 --> 00:52:31,820 is, so delta y. 1031 00:52:31,820 --> 00:52:34,880 And then here, self.y gets math.min, virtual height 1032 00:52:34,880 --> 00:52:39,220 minus self.height, self.y plus self.dy times delta time. 1033 00:52:39,220 --> 00:52:42,470 So that's the clamping behavior that we saw before with the paddles, only now, 1034 00:52:42,470 --> 00:52:45,660 we took it from main and we put it in our update function 1035 00:52:45,660 --> 00:52:48,110 so each paddle calls its update, and we take some lines 1036 00:52:48,110 --> 00:52:49,700 of code out of our main file. 1037 00:52:49,700 --> 00:52:52,622 And then it has its own render function here, same as the paddle. 1038 00:52:52,622 --> 00:52:55,580 The render function for the paddle and the render function for the ball 1039 00:52:55,580 --> 00:52:57,380 are effectively the same. 1040 00:52:57,380 --> 00:53:04,300 And so if we go to our main, we-- we're acquiring the paddle 1041 00:53:04,300 --> 00:53:07,000 and we're acquiring ball so that we can use them. 1042 00:53:07,000 --> 00:53:13,810 So if we go down to line 79, instead of initializing our ball dx or ball dy, 1043 00:53:13,810 --> 00:53:18,520 ball x, ball y, paddle y, player 1y, player 2y, 1044 00:53:18,520 --> 00:53:22,990 now, we have player one is simply paddle 10, 35, 20. 1045 00:53:22,990 --> 00:53:25,180 And player two is a paddle, virtual width minus 10, 1046 00:53:25,180 --> 00:53:28,090 virtual height minus 30, 520. 1047 00:53:28,090 --> 00:53:30,990 Ball is a ball, virtual width divided by two minus two, 1048 00:53:30,990 --> 00:53:33,385 virtual height minus two, minus two, [? five by ?] two 1049 00:53:33,385 --> 00:53:35,170 minus two, four and four. 1050 00:53:35,170 --> 00:53:39,070 So those paddles now have control over their own x and y, their own width 1051 00:53:39,070 --> 00:53:45,010 and height, and the battle-- or the ball has its own control over the xy width 1052 00:53:45,010 --> 00:53:45,960 and height. 1053 00:53:45,960 --> 00:53:48,650 And the self applies to this object. 1054 00:53:48,650 --> 00:53:53,300 This is whatever self was in our constructor that we saw before. 1055 00:53:53,300 --> 00:53:55,630 So even now we can just call-- so we can simply 1056 00:53:55,630 --> 00:54:01,550 say player 1.x player 1.width, player 1.y, and everything is contained. 1057 00:54:01,550 --> 00:54:04,055 We don't need a million variables to keep track 1058 00:54:04,055 --> 00:54:05,680 of all the things going on in our game. 1059 00:54:05,680 --> 00:54:08,890 And this is going to be especially important as we scale, and we have-- 1060 00:54:08,890 --> 00:54:11,500 maybe we have 100 things on the screen at one time. 1061 00:54:11,500 --> 00:54:14,560 We don't want 100 times x variables where 1062 00:54:14,560 --> 00:54:20,690 x is, however many properties that thing has that we need to keep track of. 1063 00:54:20,690 --> 00:54:23,470 It's all the same logic except, now, we're 1064 00:54:23,470 --> 00:54:28,420 calling player one update and player two update in our update function, 1065 00:54:28,420 --> 00:54:30,670 instead of having all that logic therein, 1066 00:54:30,670 --> 00:54:33,342 where they're moving and then keeping track of whether 1067 00:54:33,342 --> 00:54:36,050 or not they're going past the top and bottom edges of the screen. 1068 00:54:36,050 --> 00:54:39,370 And then if game state is play, we're now just calling ball update. 1069 00:54:39,370 --> 00:54:43,210 And these are all getting passed in delta time. 1070 00:54:43,210 --> 00:54:45,120 And then same thing here. 1071 00:54:45,120 --> 00:54:47,890 Instead of having all that logic for restating the ball 1072 00:54:47,890 --> 00:54:50,327 as one block of code, we took it out, we refactored it, 1073 00:54:50,327 --> 00:54:52,660 we put it into our ball class, and now all we have to do 1074 00:54:52,660 --> 00:54:56,470 is just one line of code, ball or reset. 1075 00:54:56,470 --> 00:55:00,490 And then here down on line 169 in our draw function, 1076 00:55:00,490 --> 00:55:04,060 we just have player one render, player two render, ball render. 1077 00:55:04,060 --> 00:55:06,925 And later on as we scale and we make games 1078 00:55:06,925 --> 00:55:09,550 that have a lot more things on the screen, a lot more entities, 1079 00:55:09,550 --> 00:55:11,890 we can just do these renders in a loop. 1080 00:55:11,890 --> 00:55:15,700 We can just say for each entity in our screen, just render it. 1081 00:55:15,700 --> 00:55:18,340 For each entity in our screen, just update it. 1082 00:55:18,340 --> 00:55:21,190 We can condense thousands-- hundreds of lines of code 1083 00:55:21,190 --> 00:55:25,030 into just a few lines of code by deferring update logic 1084 00:55:25,030 --> 00:55:27,820 and rendering logic to each individual entity, 1085 00:55:27,820 --> 00:55:30,070 thanks to object oriented programming. 1086 00:55:30,070 --> 00:55:33,370 And so that's how we're going to refactor using classes. 1087 00:55:33,370 --> 00:55:37,930 So any questions on how any of that works so far? 1088 00:55:37,930 --> 00:55:40,640 1089 00:55:40,640 --> 00:55:41,290 Cool. 1090 00:55:41,290 --> 00:55:44,170 This is a good point, I think, to take a five minute break. 1091 00:55:44,170 --> 00:55:49,676 And once we come back, we'll talk about how to look at frames per second. 1092 00:55:49,676 --> 00:55:52,180 All right. 1093 00:55:52,180 --> 00:55:56,800 So we're going to take a minute just to look at something kind of small, 1094 00:55:56,800 --> 00:56:00,940 but often it's the case where in games if we 1095 00:56:00,940 --> 00:56:06,400 want to make sure that we are performing like our applications performing well, 1096 00:56:06,400 --> 00:56:08,940 we want to-- some way to monitor our frames per second. 1097 00:56:08,940 --> 00:56:10,731 And so I figured I would just take a second 1098 00:56:10,731 --> 00:56:15,160 to illustrate this quickly so that we can use this in the future. 1099 00:56:15,160 --> 00:56:18,830 The two functions that are going to be important for us here-- 1100 00:56:18,830 --> 00:56:21,959 well, the first of these is just a little small cosmetic addition 1101 00:56:21,959 --> 00:56:22,750 to the application. 1102 00:56:22,750 --> 00:56:25,720 It's just love.window.setTitle. 1103 00:56:25,720 --> 00:56:31,530 Title, so far our application, I'm not entirely sure what the-- 1104 00:56:31,530 --> 00:56:35,110 it says by default, I think it says-- 1105 00:56:35,110 --> 00:56:36,650 what does it say-- untitled. 1106 00:56:36,650 --> 00:56:37,150 Yeah. 1107 00:56:37,150 --> 00:56:41,410 So that's not-- it's a layer of lack of polish, more or less. 1108 00:56:41,410 --> 00:56:43,820 And it'd be nice just to solve that problem quickly. 1109 00:56:43,820 --> 00:56:47,937 So we're going to call a function called love.window.setTitle, some string, 1110 00:56:47,937 --> 00:56:49,520 which will solve that problem quickly. 1111 00:56:49,520 --> 00:56:52,900 We can make it look as if we have that detail down. 1112 00:56:52,900 --> 00:56:57,160 And then the thing that's actually going to let us determine whether or not 1113 00:56:57,160 --> 00:56:59,500 we are running well or we're running very poorly 1114 00:56:59,500 --> 00:57:04,180 is a function called love.timer.getframespersecond.getFPS, 1115 00:57:04,180 --> 00:57:07,700 which is something that LOVE graciously gives us for free 1116 00:57:07,700 --> 00:57:09,950 and allows us to very easily slap it wherever we want. 1117 00:57:09,950 --> 00:57:11,866 We can print it to the console, or we can just 1118 00:57:11,866 --> 00:57:13,420 draw it straight to our application. 1119 00:57:13,420 --> 00:57:15,640 In this case, we're going to do the latter. 1120 00:57:15,640 --> 00:57:19,800 So I'm going to go ahead and go into LOVE-- 1121 00:57:19,800 --> 00:57:24,390 or Pong 6 in our main. 1122 00:57:24,390 --> 00:57:27,830 If we go ahead and look at line-- 1123 00:57:27,830 --> 00:57:32,190 1124 00:57:32,190 --> 00:57:34,460 where is it-- line 64. 1125 00:57:34,460 --> 00:57:37,600 love.window.setTitlePong, just quick and easy. 1126 00:57:37,600 --> 00:57:39,770 Now, our window header is set appropriately, 1127 00:57:39,770 --> 00:57:49,810 and if we go down to line 198, here I've decided 1128 00:57:49,810 --> 00:57:54,850 to sort of split out this in a separate function, 1129 00:57:54,850 --> 00:57:57,640 called display FPS on line 198. 1130 00:57:57,640 --> 00:58:02,320 And the function is defined on line 207, so a function display FPS, 1131 00:58:02,320 --> 00:58:03,570 takes no parameters. 1132 00:58:03,570 --> 00:58:07,160 Its only goal is to just draw our current FPS to the screen. 1133 00:58:07,160 --> 00:58:10,000 So we're going to set our current font to a small font. 1134 00:58:10,000 --> 00:58:13,990 We're going to set our color-- so this is what I alluded to before in that we 1135 00:58:13,990 --> 00:58:19,090 can set LOVE's rendering color to some RGBA quadruple, 1136 00:58:19,090 --> 00:58:24,580 and anything that we draw beyond that point will then be drawn at-- 1137 00:58:24,580 --> 00:58:27,140 it'll be drawn into whatever that color is. 1138 00:58:27,140 --> 00:58:32,485 So, in this case, we're giving it red of zero, 255 on the green, zero blue, 255 1139 00:58:32,485 --> 00:58:35,110 fully opaque, which has the effect of setting our color to just 1140 00:58:35,110 --> 00:58:36,640 completely green. 1141 00:58:36,640 --> 00:58:40,360 And then love.graphics.print, our current FPS-- 1142 00:58:40,360 --> 00:58:45,700 string, and then our current FPS here, which is love.timer.getFPS. 1143 00:58:45,700 --> 00:58:49,590 But it's going to return that as a number, and by default, 1144 00:58:49,590 --> 00:58:52,480 Lua does not allow you to concatenate strings and numbers, 1145 00:58:52,480 --> 00:58:55,790 so we're going to concatenate here with this ..operator, 1146 00:58:55,790 --> 00:58:58,180 which is the way of doing string concatenation in Lua. 1147 00:58:58,180 --> 00:59:00,013 We're going to call the two string function. 1148 00:59:00,013 --> 00:59:01,792 So we're going to take in love.timer.FPS, 1149 00:59:01,792 --> 00:59:03,500 we're going to make it a string, and then 1150 00:59:03,500 --> 00:59:05,110 we're going to concatenate it here. 1151 00:59:05,110 --> 00:59:08,832 And then we're going to call love.graphics.print on that value, 1152 00:59:08,832 --> 00:59:10,540 and then we're going to put it at 10, 10. 1153 00:59:10,540 --> 00:59:14,360 So shift it just a little bit from the top left edge of the screen. 1154 00:59:14,360 --> 00:59:18,310 So that's going to have the effect of the go to Pong 6, and we run it. 1155 00:59:18,310 --> 00:59:20,587 We can see now it starts at zero and 52 because it 1156 00:59:20,587 --> 00:59:22,420 has to gather a few frames of data before it 1157 00:59:22,420 --> 00:59:25,090 has a number we can actually use. 1158 00:59:25,090 --> 00:59:32,011 But we see there FPS at 60, and so our game runs, otherwise, just the same. 1159 00:59:32,011 --> 00:59:32,760 Completely random. 1160 00:59:32,760 --> 00:59:36,820 A little bit broken, but that's OK, we'll fix it up. 1161 00:59:36,820 --> 00:59:39,850 But currently we have a problem, and that's 1162 00:59:39,850 --> 00:59:43,210 that our ball is just going straight through our paddles. 1163 00:59:43,210 --> 00:59:45,274 So how can we fix this problem? 1164 00:59:45,274 --> 00:59:49,070 1165 00:59:49,070 --> 00:59:51,640 We need some way of detecting collision. 1166 00:59:51,640 --> 00:59:59,350 So in 2D games, generally, there's a concept of aa bb collision detection. 1167 00:59:59,350 --> 01:00:04,750 And what this is is axis aligned bounding box collision detection, which 1168 01:00:04,750 --> 01:00:09,124 means that we have bounding boxes, just rectangles, quads, 1169 01:00:09,124 --> 01:00:12,040 which have an x and a y and a width and a height which are nonrotated. 1170 01:00:12,040 --> 01:00:14,470 So they're completely aligned with our axes. 1171 01:00:14,470 --> 01:00:16,810 They're completely parallel perpendicular. 1172 01:00:16,810 --> 01:00:22,660 So the only way that we can get this easy math, the aa bb collision 1173 01:00:22,660 --> 01:00:25,982 detection working is if we have no rotation of our boxes. 1174 01:00:25,982 --> 01:00:27,440 They have to be completely aligned. 1175 01:00:27,440 --> 01:00:30,860 But if they are, we have a very simple algorithm, 1176 01:00:30,860 --> 01:00:34,600 which is we're just making sure that no edges of our boxes 1177 01:00:34,600 --> 01:00:39,290 are outside the opposite edges of our-- of the other rectangle. 1178 01:00:39,290 --> 01:00:45,070 So if we have one rectangle-- and I'll illustrate this on the screen here. 1179 01:00:45,070 --> 01:00:47,290 We have two rectangles. 1180 01:00:47,290 --> 01:00:53,380 If this top edge is below this edge, we know 1181 01:00:53,380 --> 01:00:56,530 no matter what, they're not going to inter-- they're not intersecting. 1182 01:00:56,530 --> 01:00:58,720 There is no way it can because it's below here. 1183 01:00:58,720 --> 01:01:02,080 So no matter where it is on the x and the y, if it's below here, 1184 01:01:02,080 --> 01:01:04,120 it's not a collision. 1185 01:01:04,120 --> 01:01:08,450 If this edge is on this side of this rectangle, 1186 01:01:08,450 --> 01:01:11,950 we know, as well, there's no way those two boxes can overlap. 1187 01:01:11,950 --> 01:01:15,340 And it applies to every edge as long as it is the opposite edge. 1188 01:01:15,340 --> 01:01:19,450 So if this edge is below this one, if this edge is above this one, 1189 01:01:19,450 --> 01:01:22,240 if this edge is on the right, and this edge is on the left, 1190 01:01:22,240 --> 01:01:25,790 it means that no matter what, those boxes aren't colliding. 1191 01:01:25,790 --> 01:01:28,600 So we can simply do four conditions. 1192 01:01:28,600 --> 01:01:38,500 We can say, if rec1.x is not greater than rec 2.x, plus rec2.width, 1193 01:01:38,500 --> 01:01:43,900 and rec1.x plus rec1.width is not less than rec2.x, 1194 01:01:43,900 --> 01:01:49,870 so if the two edges are not beyond their opposite edges, same thing with the y, 1195 01:01:49,870 --> 01:01:55,090 and the y plus rec1.height, we know that we have a collision. 1196 01:01:55,090 --> 01:01:59,830 We know that because we haven't fulfilled any of those criteria. 1197 01:01:59,830 --> 01:02:05,680 But we know that if that's not true, if the-- 1198 01:02:05,680 --> 01:02:09,300 one of the edges is not beyond the opposite edge, then it's-- 1199 01:02:09,300 --> 01:02:10,820 we do have a collision. 1200 01:02:10,820 --> 01:02:12,380 So it is going to be true. 1201 01:02:12,380 --> 01:02:14,660 So we'll see that here in our code. 1202 01:02:14,660 --> 01:02:26,200 The go to Pongs 7, at line 113, we have a function 1203 01:02:26,200 --> 01:02:29,300 that we're calling called ball collides. 1204 01:02:29,300 --> 01:02:31,684 Our ball class has a function called collides. 1205 01:02:31,684 --> 01:02:33,850 So let's go ahead and take a look at our ball class. 1206 01:02:33,850 --> 01:02:40,070 1207 01:02:40,070 --> 01:02:42,740 And in this case, we've defined our function such 1208 01:02:42,740 --> 01:02:45,020 that it takes in a paddle parameter, so it's 1209 01:02:45,020 --> 01:02:47,210 going to compare against another rectangle that 1210 01:02:47,210 --> 01:02:50,410 has an xy and a width and a height. 1211 01:02:50,410 --> 01:02:57,470 And we're saying that if rx is greater than the paddle x, plus the paddle 1212 01:02:57,470 --> 01:03:01,850 width, which means if rx is greater than the right edge. 1213 01:03:01,850 --> 01:03:04,880 So if our top left is greater than the-- 1214 01:03:04,880 --> 01:03:08,250 or just our left-- is greater than the right edge, 1215 01:03:08,250 --> 01:03:10,190 we know that we can't collide. 1216 01:03:10,190 --> 01:03:16,130 Same thing if it's greater than the other rectangles, 1217 01:03:16,130 --> 01:03:20,250 self.x plus self.width. 1218 01:03:20,250 --> 01:03:21,350 No, sorry. 1219 01:03:21,350 --> 01:03:24,040 In that case, if the paddle's x is-- 1220 01:03:24,040 --> 01:03:26,840 it basically the same operation but from the paddle's perspective. 1221 01:03:26,840 --> 01:03:31,130 If the paddle is greater than the rectangle on the right side, 1222 01:03:31,130 --> 01:03:33,525 if it's farther along the right side past the right edge, 1223 01:03:33,525 --> 01:03:35,150 we know that there can be no collision. 1224 01:03:35,150 --> 01:03:36,470 It's just impossible. 1225 01:03:36,470 --> 01:03:38,270 Same thing with y. 1226 01:03:38,270 --> 01:03:42,050 If the y-- self.y, so this ball is y-- 1227 01:03:42,050 --> 01:03:45,060 is greater than the paddle's y, plus the paddle height. 1228 01:03:45,060 --> 01:03:47,420 So if it's below the edge of the paddle, because we're 1229 01:03:47,420 --> 01:03:51,980 taking the height into consideration, or if the paddle's y is greater 1230 01:03:51,980 --> 01:03:57,140 than this ball's y plus self.height, then we 1231 01:03:57,140 --> 01:04:00,440 know that that also can't be a collision. 1232 01:04:00,440 --> 01:04:03,206 But if that's not true, then we need to return true. 1233 01:04:03,206 --> 01:04:06,170 1234 01:04:06,170 --> 01:04:11,050 And so if we go back to our main-- no, that's the wrong main. 1235 01:04:11,050 --> 01:04:16,840 We go back to main.lua here. 1236 01:04:16,840 --> 01:04:19,590 We're calling ball.collides. 1237 01:04:19,590 --> 01:04:22,030 So if we're in our game state, if we're in our-- sorry, 1238 01:04:22,030 --> 01:04:25,810 if we're in our play state, if game state is equal to play, 1239 01:04:25,810 --> 01:04:29,650 if the ball collides with player one, so player one is the left paddle. 1240 01:04:29,650 --> 01:04:36,400 So if there's a collision detected, the ball.dx and dx is our x velocity. 1241 01:04:36,400 --> 01:04:39,500 So it's whatever direction it's moving on the x-axis. 1242 01:04:39,500 --> 01:04:44,230 So it's going to be moving to the left if it's gone-- 1243 01:04:44,230 --> 01:04:46,067 if we detected a collision. 1244 01:04:46,067 --> 01:04:48,400 And it doesn't matter whether it's moving left or right, 1245 01:04:48,400 --> 01:04:52,842 but we needed-- what we need to do is set it to its negative value. 1246 01:04:52,842 --> 01:04:54,550 Because if it's moving left and we said-- 1247 01:04:54,550 --> 01:04:59,380 let's say it's moving left at its negative 20 pixels and we set to 20, 1248 01:04:59,380 --> 01:05:01,840 the dx is now 20, it's going to start moving to the right. 1249 01:05:01,840 --> 01:05:06,880 It's going to have the effect of inverting its x velocity 1250 01:05:06,880 --> 01:05:09,170 and, therefore, reversing its direction. 1251 01:05:09,170 --> 01:05:12,280 But what we're also doing here with times 1.03 1252 01:05:12,280 --> 01:05:15,070 is we're multiplying a little bit just to speed up the game. 1253 01:05:15,070 --> 01:05:18,460 Because we don't want the game to into perpetuity just have the same velocity. 1254 01:05:18,460 --> 01:05:20,230 It's not going to ramp up the excitement. 1255 01:05:20,230 --> 01:05:23,200 We want to keep things going, we want to get some momentum going, 1256 01:05:23,200 --> 01:05:25,660 so what we're going to do is call ball.dx 1257 01:05:25,660 --> 01:05:30,707 equals its negative value times a scaler that we've determined arbitrarily. 1258 01:05:30,707 --> 01:05:32,665 In this case, I've decided it should be point-- 1259 01:05:32,665 --> 01:05:37,060 1.03 so it'll increase it by 3% every time. 1260 01:05:37,060 --> 01:05:41,515 And then in the event that we have a-- 1261 01:05:41,515 --> 01:05:45,760 our ball-- because it's getting added, its x velocity is getting 1262 01:05:45,760 --> 01:05:48,550 added each frame to its position, we want 1263 01:05:48,550 --> 01:05:52,620 to make sure that it's not like inside of our paddle. 1264 01:05:52,620 --> 01:05:55,600 Because it is possible that it could shift a certain number of pixels 1265 01:05:55,600 --> 01:05:59,260 to the left, or to the right, because the same operation applies. 1266 01:05:59,260 --> 01:06:01,750 Such that the two are sort of like on top of each other. 1267 01:06:01,750 --> 01:06:04,161 We want to re-- we want to shift it, we want to reset it. 1268 01:06:04,161 --> 01:06:06,910 So what we're going to do-- because it'll detect another collision 1269 01:06:06,910 --> 01:06:08,040 immediately if that's the case. 1270 01:06:08,040 --> 01:06:10,270 If it, on the next frame, it's within that paddle, 1271 01:06:10,270 --> 01:06:13,860 it's going to say that it's still colliding with that paddle 1272 01:06:13,860 --> 01:06:15,610 so it's going to shift its velocity again. 1273 01:06:15,610 --> 01:06:18,430 And it's going to have the effect of it infinitely sort of bouncing 1274 01:06:18,430 --> 01:06:19,804 back and forth within the paddle. 1275 01:06:19,804 --> 01:06:21,160 We don't want that to happen. 1276 01:06:21,160 --> 01:06:23,466 So if we detect a collision, we want to shift it. 1277 01:06:23,466 --> 01:06:26,590 We want to make sure it's completely outside of the paddle's collision box. 1278 01:06:26,590 --> 01:06:31,390 So we're saying ball.x gets player one.x, plus five. 1279 01:06:31,390 --> 01:06:34,210 Plus five because that's the width of the paddle. 1280 01:06:34,210 --> 01:06:38,401 So that has the effect of just once you detect a collision, negative set-- 1281 01:06:38,401 --> 01:06:40,150 x velocity to negative, and then instantly 1282 01:06:40,150 --> 01:06:44,830 shift it right on the right edge of the left paddle. 1283 01:06:44,830 --> 01:06:47,170 And we're doing the same thing here. 1284 01:06:47,170 --> 01:06:51,670 If ball collides a play or two, we're doing the-- we're negating or inverting 1285 01:06:51,670 --> 01:06:54,340 its x velocity. 1286 01:06:54,340 --> 01:06:57,520 And then-- this is the same exact operation, 1287 01:06:57,520 --> 01:07:01,360 but since it's based on the-- the left top left corner, 1288 01:07:01,360 --> 01:07:04,540 we can't minus it by five, that wouldn't make sense. 1289 01:07:04,540 --> 01:07:07,840 We're going to minus it by four because that's the width of the ball. 1290 01:07:07,840 --> 01:07:11,310 So if we minused it by five, we would have one pixel of space. 1291 01:07:11,310 --> 01:07:15,010 We plussed it by five on this example, because we're 1292 01:07:15,010 --> 01:07:16,667 coming in from the right side. 1293 01:07:16,667 --> 01:07:19,750 We want to just make sure that it's right on the right edge of the paddle, 1294 01:07:19,750 --> 01:07:22,240 so we're setting it to player one.x. 1295 01:07:22,240 --> 01:07:25,960 And in this case, we're using the minus four 1296 01:07:25,960 --> 01:07:28,210 because that's the width of the ball. 1297 01:07:28,210 --> 01:07:30,995 So we want to shift it to the left, the width of the ball, 1298 01:07:30,995 --> 01:07:34,120 and that will have the effect of the right paddle, if there is a collision, 1299 01:07:34,120 --> 01:07:36,520 it'll just get shifted over, and the right-- the ball 1300 01:07:36,520 --> 01:07:40,150 will be touching the paddle right on their two edges. 1301 01:07:40,150 --> 01:07:45,700 Here on line 118, we're solving the problem we had before of what 1302 01:07:45,700 --> 01:07:48,580 happens when the-- 1303 01:07:48,580 --> 01:07:50,830 oh, sorry, that's actually not what I was thinking of. 1304 01:07:50,830 --> 01:07:57,880 This is the-- if there's a collision, then we want the ball's y velocity 1305 01:07:57,880 --> 01:07:59,350 to randomize every time. 1306 01:07:59,350 --> 01:08:02,470 So this has the effect of when we're playing the game 1307 01:08:02,470 --> 01:08:05,320 and we've detected a collision between the two paddles, 1308 01:08:05,320 --> 01:08:08,320 we don't want the same angle back and forth every time 1309 01:08:08,320 --> 01:08:11,710 because then the game will just infinitely take place the exact same-- 1310 01:08:11,710 --> 01:08:14,291 the same angle will just keep happening over and over again. 1311 01:08:14,291 --> 01:08:15,290 We don't that to happen. 1312 01:08:15,290 --> 01:08:19,689 We want some variability in terms of how the ball bounces off the paddle. 1313 01:08:19,689 --> 01:08:22,130 So what this does is, still within the condition, 1314 01:08:22,130 --> 01:08:25,210 if the ball collides with player one, we're 1315 01:08:25,210 --> 01:08:31,010 going to, say, if the y velocity of the ball is negative, 1316 01:08:31,010 --> 01:08:32,740 then we want to keep it going negative. 1317 01:08:32,740 --> 01:08:34,823 We still want the ball-- like if the ball's coming 1318 01:08:34,823 --> 01:08:37,420 at a sort of an upward angle and it bounces off the paddle, 1319 01:08:37,420 --> 01:08:39,290 we want the x velocity to shift. 1320 01:08:39,290 --> 01:08:42,819 We want it to go to opposite direction, but we want the ball to keep going up. 1321 01:08:42,819 --> 01:08:45,069 We don't want the ball to like bounce back down, which 1322 01:08:45,069 --> 01:08:45,950 wouldn't make any sense. 1323 01:08:45,950 --> 01:08:47,680 We don't want to negate the y velocity. 1324 01:08:47,680 --> 01:08:49,638 So we're going to keep the y velocity negative, 1325 01:08:49,638 --> 01:08:53,050 we're going to set it to a negative value between 10 and 150. 1326 01:08:53,050 --> 01:08:54,050 And it's just arbitrary. 1327 01:08:54,050 --> 01:08:55,700 You can set that to whatever you want. 1328 01:08:55,700 --> 01:08:58,840 And then we're going to do the same thing if the y velocity is positive. 1329 01:08:58,840 --> 01:09:02,229 We want the ball to-- 1330 01:09:02,229 --> 01:09:07,510 we want the ball to go in the positive direction if it's already coming down. 1331 01:09:07,510 --> 01:09:10,569 So we're doing the exact same thing here. 1332 01:09:10,569 --> 01:09:13,403 It's the same logic in the player two instance. 1333 01:09:13,403 --> 01:09:16,319 And then this was what I thought I was looking at before for a second, 1334 01:09:16,319 --> 01:09:20,649 but this is how we fix the issue of the upper and lower boundary of the screen. 1335 01:09:20,649 --> 01:09:21,149 Right. 1336 01:09:21,149 --> 01:09:24,750 Because it's one thing to solve the fact that we have the paddles 1337 01:09:24,750 --> 01:09:27,720 now deflecting the ball, but we don't want the ball 1338 01:09:27,720 --> 01:09:30,270 to infinitely go above the top edge of the screen, 1339 01:09:30,270 --> 01:09:32,080 or the bottom edge of the screen. 1340 01:09:32,080 --> 01:09:34,842 So this is just a simple if condition. 1341 01:09:34,842 --> 01:09:37,050 We're just saying if the ball's less than or equal to 1342 01:09:37,050 --> 01:09:40,407 zero, which means if the ball's at the top edge of the screen, 1343 01:09:40,407 --> 01:09:42,240 just set it to zero, so make sure it doesn't 1344 01:09:42,240 --> 01:09:46,319 go above the edge of the screen, and then negate its wide velocity, 1345 01:09:46,319 --> 01:09:48,490 so it's instantly going to start going downwards. 1346 01:09:48,490 --> 01:09:50,273 Yes. 1347 01:09:50,273 --> 01:09:53,880 AUDIENCE: This question is about Pong 7, line 113. 1348 01:09:53,880 --> 01:09:57,270 Couldn't the shifting of the balls dx and y 1349 01:09:57,270 --> 01:10:01,660 be done in the ball collides function, if there is a collision? 1350 01:10:01,660 --> 01:10:06,330 COLTON OGDEN: The shifting of the ball's function if ball collide-- no, 1351 01:10:06,330 --> 01:10:08,580 collides the ball-- the collides function 1352 01:10:08,580 --> 01:10:11,470 is a-- it just returns true or false. 1353 01:10:11,470 --> 01:10:14,520 So it would be-- 1354 01:10:14,520 --> 01:10:19,530 I mean, I think you probably could refactor it out that way, 1355 01:10:19,530 --> 01:10:21,540 but the purpose of collides isn't to have 1356 01:10:21,540 --> 01:10:23,819 any sort of side effects like that. 1357 01:10:23,819 --> 01:10:25,860 Its only purpose is just to return true or false. 1358 01:10:25,860 --> 01:10:28,651 Because we can do any-- we could have any sort of behavior we want. 1359 01:10:28,651 --> 01:10:31,560 In a collides function, we may not necessarily want to shift the ball 1360 01:10:31,560 --> 01:10:33,601 or do anything, we might just want it return true 1361 01:10:33,601 --> 01:10:35,280 and print something to the console. 1362 01:10:35,280 --> 01:10:37,920 So, in terms of, I think, in an engineering perspective, 1363 01:10:37,920 --> 01:10:40,847 it makes more sense just to have a simple true or false function, 1364 01:10:40,847 --> 01:10:43,680 and then determine how you want that to actually influence your game 1365 01:10:43,680 --> 01:10:48,310 state inside your main function, or inside some other function. 1366 01:10:48,310 --> 01:10:48,810 OK. 1367 01:10:48,810 --> 01:10:51,420 1368 01:10:51,420 --> 01:10:53,570 And so, Yeah, we went down here. 1369 01:10:53,570 --> 01:10:57,660 The top edge of the screen, and then bottom edge of the screen. 1370 01:10:57,660 --> 01:11:00,840 If the ball.y, it's same exact thing, just the bottom edge of the screen. 1371 01:11:00,840 --> 01:11:04,770 If the ball.y is greater than or equal to virtual height minus four, 1372 01:11:04,770 --> 01:11:06,840 and we're doing virtual high minus four, why? 1373 01:11:06,840 --> 01:11:08,190 AUDIENCE: Could get stuck at the bottom 1374 01:11:08,190 --> 01:11:09,450 COLTON OGDEN: Exactly. 1375 01:11:09,450 --> 01:11:12,180 So we want to make sure that we write-- as soon as we-- 1376 01:11:12,180 --> 01:11:15,310 the bottom edge of the ball touches the bottom of the screen, 1377 01:11:15,310 --> 01:11:18,720 we want to detect a collision, then we want to say ball.y, the-- 1378 01:11:18,720 --> 01:11:22,290 gets virtual height minus four in case it overshot the bottom edge 1379 01:11:22,290 --> 01:11:25,600 based on how much time has elapsed and how much the velocity is, 1380 01:11:25,600 --> 01:11:28,320 you want to instantly put it right up so that it's at the edge 1381 01:11:28,320 --> 01:11:29,910 so it's a clean bounce. 1382 01:11:29,910 --> 01:11:32,340 And then we want to negate the y velocity just the same 1383 01:11:32,340 --> 01:11:35,500 as we did up above. 1384 01:11:35,500 --> 01:11:46,160 And so if we run our program here, Pong 7, looks the same, 1385 01:11:46,160 --> 01:11:50,200 but now the ball's bouncing. 1386 01:11:50,200 --> 01:11:52,810 And note that it got a neg-- it got a random-- it 1387 01:11:52,810 --> 01:11:54,601 looks like it's going below the bottom edge 1388 01:11:54,601 --> 01:11:57,760 because the monitor is currently at 720 and that's the window resolution, 1389 01:11:57,760 --> 01:11:59,740 but it is bouncing off the bottom edge as well. 1390 01:11:59,740 --> 01:12:03,250 And the angle, if you'll note, is a little bit different every time, 1391 01:12:03,250 --> 01:12:10,180 because we are giving it a random y velocity, a y-- 1392 01:12:10,180 --> 01:12:10,680 yeah. 1393 01:12:10,680 --> 01:12:14,770 And then that's influencing-- oh, I messed up. 1394 01:12:14,770 --> 01:12:17,784 I wanted to illustrate the speed increase. 1395 01:12:17,784 --> 01:12:19,450 It's going to take a little bit of time. 1396 01:12:19,450 --> 01:12:25,180 But every time it detects a collision, it is going to be scaling its-- 1397 01:12:25,180 --> 01:12:28,346 the x velocity by 1.03. 1398 01:12:28,346 --> 01:12:30,220 So it's going to make it a little bit faster. 1399 01:12:30,220 --> 01:12:31,840 Now, currently, the y angle's a bit steep, 1400 01:12:31,840 --> 01:12:33,950 so it's going to take forever to illustrate that. 1401 01:12:33,950 --> 01:12:37,190 But we'll see that in a later example. 1402 01:12:37,190 --> 01:12:42,290 So we have the basics of our game. 1403 01:12:42,290 --> 01:12:43,960 But how are we keeping score? 1404 01:12:43,960 --> 01:12:47,518 What's the determining factor for how we keep score in Pong? 1405 01:12:47,518 --> 01:12:48,579 Left or right. 1406 01:12:48,579 --> 01:12:51,120 As long as it goes past the left or right edge of the screen. 1407 01:12:51,120 --> 01:12:53,635 So what do we need to thereby do? 1408 01:12:53,635 --> 01:12:55,054 AUDIENCE: [INAUDIBLE] 1409 01:12:55,054 --> 01:12:57,000 COLTON OGDEN: We do need a counter, and we 1410 01:12:57,000 --> 01:12:59,700 need to also monitor whether the ball has 1411 01:12:59,700 --> 01:13:03,720 collided with the left or the right boundary of the screen. 1412 01:13:03,720 --> 01:13:06,150 And then have that increment that counter. 1413 01:13:06,150 --> 01:13:08,970 So we're going to go ahead and take a look at Pong 8 1414 01:13:08,970 --> 01:13:13,330 to see how this is implemented. 1415 01:13:13,330 --> 01:13:18,352 We have here on line 88 and 89 some counter variables, player one score, 1416 01:13:18,352 --> 01:13:19,060 player two score. 1417 01:13:19,060 --> 01:13:21,640 We've had those for a long time, but we haven't used them. 1418 01:13:21,640 --> 01:13:23,440 We've only used them to draw to the screen. 1419 01:13:23,440 --> 01:13:27,940 We're actually going to now increment them, and show them as scorekeeping 1420 01:13:27,940 --> 01:13:31,390 variables in our code here. 1421 01:13:31,390 --> 01:13:33,290 I thought I had implemented it in Pong 8, 1422 01:13:33,290 --> 01:13:36,760 but I think I might have left out the actual incrementing of the score. 1423 01:13:36,760 --> 01:13:39,220 But this is the logic that's pertinent to that example. 1424 01:13:39,220 --> 01:13:43,030 So if ball.x is less than zero, which just 1425 01:13:43,030 --> 01:13:48,430 means if we've gone past the left edge of the screen, 1426 01:13:48,430 --> 01:13:49,840 ignore serving player for now. 1427 01:13:49,840 --> 01:13:52,640 The important thing is now we are doing player two score, 1428 01:13:52,640 --> 01:13:54,580 gets player twp score, plus one. 1429 01:13:54,580 --> 01:13:56,140 Just a simple increment. 1430 01:13:56,140 --> 01:13:57,880 And then we're resetting the ball. 1431 01:13:57,880 --> 01:13:59,740 Same thing for here. 1432 01:13:59,740 --> 01:14:01,990 If the ball.x is greater than virtual width, 1433 01:14:01,990 --> 01:14:08,860 so pass the right edge of the screen, and actually it could be ver-- 1434 01:14:08,860 --> 01:14:12,120 if ball x plus four is greater than virtual width, 1435 01:14:12,120 --> 01:14:14,000 then and it will have the same effect. 1436 01:14:14,000 --> 01:14:16,947 But, actually, no, because we want to make sure 1437 01:14:16,947 --> 01:14:19,030 that we don't see the ball at all when they score. 1438 01:14:19,030 --> 01:14:20,488 So, yeah, this is actually correct. 1439 01:14:20,488 --> 01:14:23,920 If ball.x is greater than virtual width, then serving player gets two, 1440 01:14:23,920 --> 01:14:25,895 player one score is player one score plus one. 1441 01:14:25,895 --> 01:14:27,520 And then we're going to reset the ball. 1442 01:14:27,520 --> 01:14:29,050 Serving player. 1443 01:14:29,050 --> 01:14:33,370 So now what we need to talk about is the idea of serving. 1444 01:14:33,370 --> 01:14:35,680 So when we start up the game-- 1445 01:14:35,680 --> 01:14:37,328 so let's go ahead and take a look at-- 1446 01:14:37,328 --> 01:14:42,004 1447 01:14:42,004 --> 01:14:44,920 we're going to go to Pong now so we're going to go straight to Pong 9, 1448 01:14:44,920 --> 01:14:47,700 and then we need to take a look at what a state machine is. 1449 01:14:47,700 --> 01:14:50,670 So currently in the game, we've talked about state a little bit. 1450 01:14:50,670 --> 01:14:53,700 We've had the start state, which means the game is ready for us 1451 01:14:53,700 --> 01:14:56,857 to just press Enter and then the ball will go off in a random direction. 1452 01:14:56,857 --> 01:14:58,190 And then we have the play state. 1453 01:14:58,190 --> 01:15:02,490 And the play state is set to our paddles interacting with the ball, 1454 01:15:02,490 --> 01:15:06,000 and then keeping track of score, basically. 1455 01:15:06,000 --> 01:15:08,010 A state machine is very important. 1456 01:15:08,010 --> 01:15:11,400 It's a ubiquitous concept in game development. 1457 01:15:11,400 --> 01:15:14,730 It just means, how can we monitor what state we're in 1458 01:15:14,730 --> 01:15:19,050 and what transitions take place between those states to bring out new states. 1459 01:15:19,050 --> 01:15:22,230 And each individual state has its own logic. 1460 01:15:22,230 --> 01:15:26,220 And by breaking out the logic of these states separately, 1461 01:15:26,220 --> 01:15:32,820 we can scale our code much bigger and not have monolithic code for-- 1462 01:15:32,820 --> 01:15:36,270 this particular diagram is an example of what you might have as a state 1463 01:15:36,270 --> 01:15:40,290 machine for a character like Mario where you have a ducking state, a release 1464 01:15:40,290 --> 01:15:43,320 state which takes the down-- the in-- like the input of down. 1465 01:15:43,320 --> 01:15:45,750 So if we're releasing down, it'll become standing. 1466 01:15:45,750 --> 01:15:49,130 So ducking state, the transition is release the down key, 1467 01:15:49,130 --> 01:15:50,270 he becomes standing. 1468 01:15:50,270 --> 01:15:52,140 Standing key, press the down key. 1469 01:15:52,140 --> 01:15:56,350 He becomes ducking, these are states and transitions. 1470 01:15:56,350 --> 01:16:01,770 These individual states are the overall representation of his behavior 1471 01:16:01,770 --> 01:16:03,080 at large, basically. 1472 01:16:03,080 --> 01:16:06,240 And the same logic applies to our game. 1473 01:16:06,240 --> 01:16:09,090 We have a play state, we have a serve state. 1474 01:16:09,090 --> 01:16:11,010 We want to have maybe a game over state. 1475 01:16:11,010 --> 01:16:16,300 If someone scores 10 points, then it should say, oh, the winner is x. 1476 01:16:16,300 --> 01:16:19,290 And you can define any arbitrary number of states, it-- 1477 01:16:19,290 --> 01:16:22,540 which depends upon your model, whatever game you want to develop. 1478 01:16:22,540 --> 01:16:24,690 For example, like Super Mario has a title screen, 1479 01:16:24,690 --> 01:16:27,864 maybe your game has like a high score state. 1480 01:16:27,864 --> 01:16:30,030 You want to display all the high scores in your game 1481 01:16:30,030 --> 01:16:35,470 and we'll actually show that in a lecture next week. 1482 01:16:35,470 --> 01:16:37,050 But this is what a state machine is. 1483 01:16:37,050 --> 01:16:40,860 It's just a-- it can be in any one particular state at one time, 1484 01:16:40,860 --> 01:16:45,270 and the transitions are what allow you to go in between your states. 1485 01:16:45,270 --> 01:16:49,150 And each state does have transitions in and out of other states. 1486 01:16:49,150 --> 01:16:51,830 And we're going to use this in Pong 9. 1487 01:16:51,830 --> 01:16:54,870 So beyond illustrating the score, we're going 1488 01:16:54,870 --> 01:16:59,370 to start keeping track of more than just the start and the play state. 1489 01:16:59,370 --> 01:17:02,560 We're actually going to start modeling the serve state. 1490 01:17:02,560 --> 01:17:05,018 And so let me go ahead and illustrate what this looks like. 1491 01:17:05,018 --> 01:17:08,370 1492 01:17:08,370 --> 01:17:12,630 So if we're here, I just pressed Enter. 1493 01:17:12,630 --> 01:17:16,140 We started at the start state as normal, but I pressed Enter 1494 01:17:16,140 --> 01:17:18,180 and now it says player one serve. 1495 01:17:18,180 --> 01:17:19,970 So we're actually serving. 1496 01:17:19,970 --> 01:17:22,530 And so if I press Enter again as it instructs me, 1497 01:17:22,530 --> 01:17:25,290 player one is on the left, the ball should move to the right. 1498 01:17:25,290 --> 01:17:27,640 Which it does. 1499 01:17:27,640 --> 01:17:33,010 So I'm going to go ahead and lose on purpose as player two. 1500 01:17:33,010 --> 01:17:34,590 And now it's player two's serve. 1501 01:17:34,590 --> 01:17:38,370 So whichever character, whichever player loses should get to serve again. 1502 01:17:38,370 --> 01:17:40,560 And so now if I press Enter, note when we 1503 01:17:40,560 --> 01:17:42,960 were player one, the ball moved to the right. 1504 01:17:42,960 --> 01:17:46,000 So for player two, the ball moves to the left. 1505 01:17:46,000 --> 01:17:49,260 So we have now a little bit more interaction. 1506 01:17:49,260 --> 01:17:51,150 We have different states. 1507 01:17:51,150 --> 01:17:53,940 We start off the game, and then we serve, and we play. 1508 01:17:53,940 --> 01:17:57,140 So when the ball's live, when we're actually doing this, 1509 01:17:57,140 --> 01:17:58,140 we're in the play state. 1510 01:17:58,140 --> 01:17:59,800 Now we're in the serve state. 1511 01:17:59,800 --> 01:18:03,720 So what's the transition between the-- 1512 01:18:03,720 --> 01:18:07,400 sort of the play state and the serve state? 1513 01:18:07,400 --> 01:18:08,804 What's the transition there? 1514 01:18:08,804 --> 01:18:12,550 1515 01:18:12,550 --> 01:18:13,950 We score a point. 1516 01:18:13,950 --> 01:18:17,800 So if we are looking at our state diagram and we're in the play state, 1517 01:18:17,800 --> 01:18:23,284 the transition to the serve state is x player scores a point. 1518 01:18:23,284 --> 01:18:25,450 And then if we're in the serve state, the transition 1519 01:18:25,450 --> 01:18:29,180 is someone presses enter. 1520 01:18:29,180 --> 01:18:30,200 Enter key gets pressed. 1521 01:18:30,200 --> 01:18:33,110 And so that's how we want to think about our games 1522 01:18:33,110 --> 01:18:37,820 if we have a bunch of different sets of, sort of logic, 1523 01:18:37,820 --> 01:18:42,500 that we can sort of take out of our game and think about conceptually, 1524 01:18:42,500 --> 01:18:46,190 it allows us to break our game up into a bunch of different modes and states, 1525 01:18:46,190 --> 01:18:49,370 and not really get overburdened by all these variables 1526 01:18:49,370 --> 01:18:51,860 that maybe need to keep track of-- or what state are we in? 1527 01:18:51,860 --> 01:18:53,090 Like what are all these variables doing? 1528 01:18:53,090 --> 01:18:55,460 And we'll see how we can break this out in a more 1529 01:18:55,460 --> 01:18:57,469 modular fashion in future weeks. 1530 01:18:57,469 --> 01:18:59,510 Note that right now, currently all we're doing is 1531 01:18:59,510 --> 01:19:04,010 we're setting a state variable to some string here, 1532 01:19:04,010 --> 01:19:05,990 and just doing if conditions on it, which 1533 01:19:05,990 --> 01:19:08,960 works fantastically for small examples. 1534 01:19:08,960 --> 01:19:14,660 So if, like, for example, if we're in the update function and game state-- 1535 01:19:14,660 --> 01:19:17,750 see, over here we're saying if the game state is set to serve, 1536 01:19:17,750 --> 01:19:24,430 then we're initializing all of the variables. 1537 01:19:24,430 --> 01:19:27,650 And if the game state is play, then we need 1538 01:19:27,650 --> 01:19:31,200 to actually perform our logic here. 1539 01:19:31,200 --> 01:19:34,040 So if the-- if we're in play, this is going to get called, 1540 01:19:34,040 --> 01:19:36,980 each frame, and we're going to say, if ball collides with player one, 1541 01:19:36,980 --> 01:19:37,730 do all this stuff. 1542 01:19:37,730 --> 01:19:42,560 And then this allows us to sort of think of our game. 1543 01:19:42,560 --> 01:19:46,160 It's almost like having separate update functions within our update function. 1544 01:19:46,160 --> 01:19:49,220 And we'll actually see how we can take these out of one update 1545 01:19:49,220 --> 01:19:53,310 function in future weeks with a actual state machine class 1546 01:19:53,310 --> 01:19:56,090 and implement things a little bit more abstractly. 1547 01:19:56,090 --> 01:20:01,260 But suffice to say, now, whenever we want to like, for example, 1548 01:20:01,260 --> 01:20:04,280 make a transition, if someone scores here, 1549 01:20:04,280 --> 01:20:06,760 like if we're going to the left side of the screen, 1550 01:20:06,760 --> 01:20:09,320 it's ball x is less than zero. 1551 01:20:09,320 --> 01:20:12,350 All we have to do is just set this state to serve 1552 01:20:12,350 --> 01:20:16,040 and our update function is then going to update appropriately. 1553 01:20:16,040 --> 01:20:20,150 So any questions on how sort of state machines or the state 1554 01:20:20,150 --> 01:20:23,196 works here in the context of Pong? 1555 01:20:23,196 --> 01:20:23,696 Yes. 1556 01:20:23,696 --> 01:20:29,600 AUDIENCE: So the state machine is the relationship to the state 1557 01:20:29,600 --> 01:20:34,520 or is the container of the states? 1558 01:20:34,520 --> 01:20:37,550 COLTON OGDEN: The state machine is sort of a-- 1559 01:20:37,550 --> 01:20:40,580 the overall conceptual look at what your different states 1560 01:20:40,580 --> 01:20:42,680 are and their transitions, yes. 1561 01:20:42,680 --> 01:20:45,560 And in future weeks we're not implementing a state machine 1562 01:20:45,560 --> 01:20:47,150 object or a class here. 1563 01:20:47,150 --> 01:20:50,120 But in future weeks, we will see the state machine class 1564 01:20:50,120 --> 01:20:53,240 that manages transitions between different states in a more 1565 01:20:53,240 --> 01:20:54,910 modular and clean fashion. 1566 01:20:54,910 --> 01:20:57,140 All we're doing here is our state machine 1567 01:20:57,140 --> 01:21:01,050 is just if statements and saying if the state is equal to this, then do this. 1568 01:21:01,050 --> 01:21:03,716 And then change the state to some value. 1569 01:21:03,716 --> 01:21:05,911 AUDIENCE: So the state machine is a concept? 1570 01:21:05,911 --> 01:21:07,160 COLTON OGDEN: It is a concept. 1571 01:21:07,160 --> 01:21:07,940 Yeah. 1572 01:21:07,940 --> 01:21:13,880 But we will see an implementation of a state machine as an object next week. 1573 01:21:13,880 --> 01:21:17,010 Any more questions? 1574 01:21:17,010 --> 01:21:17,746 OK. 1575 01:21:17,746 --> 01:21:19,730 Cool. 1576 01:21:19,730 --> 01:21:22,850 So currently we have scoring. 1577 01:21:22,850 --> 01:21:26,180 As we saw, the player one score and player two score are getting 1578 01:21:26,180 --> 01:21:28,820 incremented now and, therefore, getting rendered to the screen 1579 01:21:28,820 --> 01:21:31,940 whenever we go to the left or the right edge. 1580 01:21:31,940 --> 01:21:33,840 So we're keeping track of score. 1581 01:21:33,840 --> 01:21:38,183 But what do we need now in order for someone to win? 1582 01:21:38,183 --> 01:21:39,937 AUDIENCE: [INAUDIBLE]. 1583 01:21:39,937 --> 01:21:40,770 COLTON OGDEN: Sorry? 1584 01:21:40,770 --> 01:21:42,021 AUDIENCE: [INAUDIBLE]. 1585 01:21:42,021 --> 01:21:43,820 COLTON OGDEN: Yes it does. 1586 01:21:43,820 --> 01:21:44,630 Exactly. 1587 01:21:44,630 --> 01:21:46,827 So it's actually quite simple. 1588 01:21:46,827 --> 01:21:49,160 All we need to really do is just an if statement, right? 1589 01:21:49,160 --> 01:21:54,260 If someone's score is equal to some value, 10, then some player has won. 1590 01:21:54,260 --> 01:22:00,290 So if we look at player-- if you look at Pong 10, we go to main, 1591 01:22:00,290 --> 01:22:14,080 and go to here, line 174, and also line 160, 1592 01:22:14,080 --> 01:22:19,114 we can see that all it literally is is in our logic 1593 01:22:19,114 --> 01:22:21,780 from before, where we're just testing to see whether the ball is 1594 01:22:21,780 --> 01:22:23,370 gone beyond the left or the right edge. 1595 01:22:23,370 --> 01:22:26,120 Because this is effectively where you need to do your check anyway 1596 01:22:26,120 --> 01:22:28,350 to see whether someone scored a point. 1597 01:22:28,350 --> 01:22:31,140 So all we're doing is adding logic to that part of the program 1598 01:22:31,140 --> 01:22:34,600 and saying after we increment their score. 1599 01:22:34,600 --> 01:22:38,182 If it's equal to 10 we're setting a value called the winning player. 1600 01:22:38,182 --> 01:22:39,390 We're going to set it to two. 1601 01:22:39,390 --> 01:22:44,130 So if the ball.x is less than zero, then that means that player one got 1602 01:22:44,130 --> 01:22:46,960 scored on because it went pass the left edge of the screen. 1603 01:22:46,960 --> 01:22:50,640 Therefore, player two score should go up. 1604 01:22:50,640 --> 01:22:54,150 And, therefore, the winning player should be, too, if the player two or-- 1605 01:22:54,150 --> 01:22:56,430 two score is equal to 10. 1606 01:22:56,430 --> 01:23:00,180 And in this case, see, we're here, we're setting a new state done, 1607 01:23:00,180 --> 01:23:04,680 and then if that's not the case, or if their score is still less than 10, 1608 01:23:04,680 --> 01:23:09,180 we should still-- we should set it back to serve, and then reset the ball. 1609 01:23:09,180 --> 01:23:13,450 And so if we go to our update function, here-- 1610 01:23:13,450 --> 01:23:18,090 actually, we're doing it in our update phase. 1611 01:23:18,090 --> 01:23:22,320 So, currently, if it's the done state, the ball gets reset 1612 01:23:22,320 --> 01:23:24,780 but no update is being applied to the ball in that case. 1613 01:23:24,780 --> 01:23:26,950 We still have scores 10. 1614 01:23:26,950 --> 01:23:30,090 It'll still render the score, so score whoever's got 10 1615 01:23:30,090 --> 01:23:32,100 and it'll show the other player's score. 1616 01:23:32,100 --> 01:23:35,400 And the actual logic that applies here, whenever 1617 01:23:35,400 --> 01:23:39,510 we want to break out of that state, is in our love.keypressed key function. 1618 01:23:39,510 --> 01:23:41,520 We see you're on line 227. 1619 01:23:41,520 --> 01:23:46,140 If game state is equal to done, which we set it to before, 1620 01:23:46,140 --> 01:23:48,237 and this will only execute if they've pressed 1621 01:23:48,237 --> 01:23:50,070 Enter or return, so it's effectively waiting 1622 01:23:50,070 --> 01:23:52,770 for them to press Enter or Return. 1623 01:23:52,770 --> 01:23:54,840 You want to set game save back to serve. 1624 01:23:54,840 --> 01:23:56,470 We want to reset the ball. 1625 01:23:56,470 --> 01:23:58,540 We want to initialize those scores back to zero. 1626 01:23:58,540 --> 01:24:02,430 So we're setting up a brand new game, effectively. 1627 01:24:02,430 --> 01:24:06,650 If the winning player is one, then we'll give serving player to two 1628 01:24:06,650 --> 01:24:09,570 so that they have the advantage on the next game, 1629 01:24:09,570 --> 01:24:11,830 and then otherwise set it to one. 1630 01:24:11,830 --> 01:24:17,160 And if we go down to our render function, so down to line 275, 1631 01:24:17,160 --> 01:24:21,789 if we're in the done state, then we should render to the screen player 1632 01:24:21,789 --> 01:24:23,580 and then winning player, because, remember, 1633 01:24:23,580 --> 01:24:26,230 we set winning player to one or two, depending on whether-- 1634 01:24:26,230 --> 01:24:29,370 depending on who won and who scored the tenth point. 1635 01:24:29,370 --> 01:24:35,880 We'll say player one or two wins, and we'll just render that and then 1636 01:24:35,880 --> 01:24:37,560 press Enter to restart after that. 1637 01:24:37,560 --> 01:24:39,150 And that's the logic for that. 1638 01:24:39,150 --> 01:24:41,700 And we can see this in playoffs. 1639 01:24:41,700 --> 01:24:45,930 If it's too slow, we might not have to go through an entire run. 1640 01:24:45,930 --> 01:24:47,930 But I sped up the-- 1641 01:24:47,930 --> 01:24:50,520 whoops-- I want to actually get the ball back. 1642 01:24:50,520 --> 01:24:53,360 I set up the speed so it's-- we're in the serve state, 1643 01:24:53,360 --> 01:24:57,570 we're in the play state, it's up-- the ball bounced back. 1644 01:24:57,570 --> 01:25:02,390 It's going to be a bit tedious, but suffice to say, it's a big payoff. 1645 01:25:02,390 --> 01:25:02,890 Don't worry. 1646 01:25:02,890 --> 01:25:06,050 1647 01:25:06,050 --> 01:25:08,870 Should have set the speed a little faster. 1648 01:25:08,870 --> 01:25:09,550 Almost there. 1649 01:25:09,550 --> 01:25:12,140 1650 01:25:12,140 --> 01:25:14,520 It's getting tense. 1651 01:25:14,520 --> 01:25:15,750 And player one wins. 1652 01:25:15,750 --> 01:25:19,590 So there we-- we're also setting the font to a larger size, and in the code 1653 01:25:19,590 --> 01:25:21,960 I create a new font object that's basically 1654 01:25:21,960 --> 01:25:26,430 between the small font and the score font, which is a large font. 1655 01:25:26,430 --> 01:25:28,590 So 16 size font. 1656 01:25:28,590 --> 01:25:31,890 And so player one wins, and that's really all it boils down to. 1657 01:25:31,890 --> 01:25:35,910 Just keeping track of your counter and just making sure that when you do 1658 01:25:35,910 --> 01:25:39,390 hit 10 in your logic for detecting the screen collisions, 1659 01:25:39,390 --> 01:25:41,094 that you set the state to done. 1660 01:25:41,094 --> 01:25:44,010 And if the state is done, then you just need to monitor keyboard input 1661 01:25:44,010 --> 01:25:46,830 and see whenever someone presses enter. 1662 01:25:46,830 --> 01:25:48,740 Someone press Enter in our love.keypressed, 1663 01:25:48,740 --> 01:25:50,580 it does the-- it has the effect of setting 1664 01:25:50,580 --> 01:25:53,742 player-- it's player two serve because player one won so that's only fair. 1665 01:25:53,742 --> 01:25:56,700 We're going to press Enter to serve and then we begin a brand new game. 1666 01:25:56,700 --> 01:25:57,449 And that's simple. 1667 01:25:57,449 --> 01:26:03,120 So now we have an infinitely playable game with a bunch of simple states. 1668 01:26:03,120 --> 01:26:05,670 We're missing a very important detail, though, in my opinion, 1669 01:26:05,670 --> 01:26:06,390 and that's sound. 1670 01:26:06,390 --> 01:26:08,430 Currently, our game is just very-- 1671 01:26:08,430 --> 01:26:10,990 it's great, the gameplay all works. 1672 01:26:10,990 --> 01:26:13,980 Everything is working fine, but just missing a little polish. 1673 01:26:13,980 --> 01:26:14,970 And so what we're going to do is we're going 1674 01:26:14,970 --> 01:26:17,429 to start adding audio to the game, which is, in my opinion, 1675 01:26:17,429 --> 01:26:20,011 one of the more fun things to add because it also means you're 1676 01:26:20,011 --> 01:26:21,390 close to the end of your project. 1677 01:26:21,390 --> 01:26:25,150 Love.audio.newsource is a function we're going to look at here. 1678 01:26:25,150 --> 01:26:29,930 All this is going to do is take a path and then optionally a type. 1679 01:26:29,930 --> 01:26:31,980 And this path is going to be to a sound file, 1680 01:26:31,980 --> 01:26:35,430 and it's going to create an audio object that you can play back 1681 01:26:35,430 --> 01:26:37,590 at any point in your application. 1682 01:26:37,590 --> 01:26:41,860 So what we're going to effectively do is just whenever a collision happens, 1683 01:26:41,860 --> 01:26:46,020 depending on what type of collision it is, we'll just play a particular sound. 1684 01:26:46,020 --> 01:26:49,300 And a program that I really like to use for all of this, 1685 01:26:49,300 --> 01:26:51,660 and I-- what I encourage you guys to download 1686 01:26:51,660 --> 01:26:54,570 if you want to start tinkering with your own sounds for your project, 1687 01:26:54,570 --> 01:26:57,300 is a program called bfxr. 1688 01:26:57,300 --> 01:26:58,885 It's free on Windows and Mac. 1689 01:26:58,885 --> 01:27:00,510 I'm not sure if they have a Linux port. 1690 01:27:00,510 --> 01:27:05,160 They might have a Linux port of a similar program called sfxr 1691 01:27:05,160 --> 01:27:07,219 which is what this is based off of. 1692 01:27:07,219 --> 01:27:10,260 But what this allows you to do is just generate a bunch of random sounds. 1693 01:27:10,260 --> 01:27:13,770 And I can illustrate that shortly for you. 1694 01:27:13,770 --> 01:27:16,740 If you would like to grab it, it's on bfxr.net. 1695 01:27:16,740 --> 01:27:19,310 It's a super quick download and-- 1696 01:27:19,310 --> 01:27:26,890 here, I'll actually-- I'll demonstrate it just so we can see how it plays out. 1697 01:27:26,890 --> 01:27:28,950 So this is the interface. 1698 01:27:28,950 --> 01:27:30,510 Make sure I have some audio. 1699 01:27:30,510 --> 01:27:34,050 And then there is a lot of different presets here. 1700 01:27:34,050 --> 01:27:37,170 So there's pickup slash coin, laser slash shoot. 1701 01:27:37,170 --> 01:27:39,750 It's meant for sort of like small games like this, 1702 01:27:39,750 --> 01:27:45,229 like implementing on the fly prototype audio type stuff. 1703 01:27:45,229 --> 01:27:46,770 But you can see, it just implements-- 1704 01:27:46,770 --> 01:27:49,140 I'll turn that down, it's a little loud. 1705 01:27:49,140 --> 01:27:50,697 It-- we have power-ups, for example. 1706 01:27:50,697 --> 01:27:53,780 So every time I click on this, it's going to give us a random power-up so. 1707 01:27:53,780 --> 01:27:55,704 [COMPUTER SOUND EFFECTS] 1708 01:27:55,704 --> 01:28:00,520 1709 01:28:00,520 --> 01:28:05,340 And then randomized, you get all sorts of weird nasty stuff. 1710 01:28:05,340 --> 01:28:08,740 And then-- the stuff that we'll use is blip slash select. 1711 01:28:08,740 --> 01:28:13,060 Most of the things in our in like interfaces and games like Pong you 1712 01:28:13,060 --> 01:28:17,120 just want simple sounds like this. 1713 01:28:17,120 --> 01:28:20,620 So I've already done the work of generating a few sounds 1714 01:28:20,620 --> 01:28:23,770 that I thought fit pretty well. 1715 01:28:23,770 --> 01:28:28,785 I'll go ahead and show you the code first in Pong 11. 1716 01:28:28,785 --> 01:28:31,660 If we go to-- if you'll see-- if you look at the directory structure, 1717 01:28:31,660 --> 01:28:33,220 you'll see we have a sounds folder. 1718 01:28:33,220 --> 01:28:36,730 In the sounds folder, I've created three sounds, paddle hit, which 1719 01:28:36,730 --> 01:28:39,160 is anytime the paddle hits the ball. 1720 01:28:39,160 --> 01:28:41,740 Score, which is when any-- anytime the ball 1721 01:28:41,740 --> 01:28:45,190 goes past the left or the right boundary of the screen. 1722 01:28:45,190 --> 01:28:47,680 And then wall hit, so any time the ball touches 1723 01:28:47,680 --> 01:28:49,600 the top or the bottom of the screen. 1724 01:28:49,600 --> 01:28:52,580 And so the logic of this is extremely simple. 1725 01:28:52,580 --> 01:28:54,531 All we need to do is whenever-- 1726 01:28:54,531 --> 01:28:56,905 we already have it implemented, so all we need to do is-- 1727 01:28:56,905 --> 01:29:01,570 1728 01:29:01,570 --> 01:29:07,002 oh, first thing, I should say, and this is a good illustration of a table. 1729 01:29:07,002 --> 01:29:09,210 And we'll start to see this a lot more in the future. 1730 01:29:09,210 --> 01:29:11,950 We didn't really use tables much in this lecture, 1731 01:29:11,950 --> 01:29:16,390 but the table is Lua's like sort of be all, end all, data structure. 1732 01:29:16,390 --> 01:29:19,810 It's the dictionary-- Python dictionary, JavaScript object. 1733 01:29:19,810 --> 01:29:23,020 It's an array, it's everything that you need for anything 1734 01:29:23,020 --> 01:29:24,830 beyond simple variables in Lua. 1735 01:29:24,830 --> 01:29:28,360 It's what everything, even like classes in other libraries are made out of. 1736 01:29:28,360 --> 01:29:32,140 In this case, we're just initializing a table here called sounds, 1737 01:29:32,140 --> 01:29:35,410 and we're passing in three keys so it takes-- it can take in key value pairs, 1738 01:29:35,410 --> 01:29:40,330 or you can just give it a list of values and it will create indices for them 1739 01:29:40,330 --> 01:29:42,280 implicitly. 1740 01:29:42,280 --> 01:29:46,460 Here, we're just passing in like you would do in Python or JavaScript. 1741 01:29:46,460 --> 01:29:50,740 Paddle hit, and note that it does need these square brackets in order 1742 01:29:50,740 --> 01:29:55,870 to initialize key value pairs in a table like-- in this format here. 1743 01:29:55,870 --> 01:29:58,740 Paddle hit gets love.audio.newsource. 1744 01:29:58,740 --> 01:30:00,490 And in this case, it just takes in a path, 1745 01:30:00,490 --> 01:30:02,930 so sounds slash paddle head dot wave. 1746 01:30:02,930 --> 01:30:05,530 And we're giving it the key word-- or the string static, 1747 01:30:05,530 --> 01:30:08,690 which is the type of asset it is-- 1748 01:30:08,690 --> 01:30:09,610 it's stored as. 1749 01:30:09,610 --> 01:30:12,734 So you can have either static or stream audio assets. 1750 01:30:12,734 --> 01:30:14,650 So if they're static, they're loaded in memory 1751 01:30:14,650 --> 01:30:17,191 and they're kept in memory for the execution of your program. 1752 01:30:17,191 --> 01:30:20,560 If they're stream, then they're loaded on the fly as needed 1753 01:30:20,560 --> 01:30:21,670 by your game engine. 1754 01:30:21,670 --> 01:30:24,100 And streamed audio assets can be helpful if you 1755 01:30:24,100 --> 01:30:28,037 have a huge game with a ton of sounds and a long like large audio files. 1756 01:30:28,037 --> 01:30:30,370 You don't want to keep all those in memory, necessarily, 1757 01:30:30,370 --> 01:30:33,670 because that could take up many, many, many megs or gigs of audio. 1758 01:30:33,670 --> 01:30:36,882 And if you're sort of loading assets on the fly, 1759 01:30:36,882 --> 01:30:38,590 if you have dynamic loading in your game, 1760 01:30:38,590 --> 01:30:41,320 then that's another thing you should take into consideration. 1761 01:30:41,320 --> 01:30:43,810 In this case, these are very tiny sound files, 1762 01:30:43,810 --> 01:30:46,409 because they're like-- like a fraction of a second long. 1763 01:30:46,409 --> 01:30:49,450 So we're just setting them all to static so they get preserved in memory, 1764 01:30:49,450 --> 01:30:52,160 and we're just loading all three of them into this table. 1765 01:30:52,160 --> 01:30:56,530 And if we want to refer to these later on, all we need to do is sounds-- 1766 01:30:56,530 --> 01:31:01,510 we can either reference sounds.paddlehit, like that, 1767 01:31:01,510 --> 01:31:02,500 if we wanted to. 1768 01:31:02,500 --> 01:31:08,050 Because by default, Lua just gives you a dot keyword, sort of the way JavaScript 1769 01:31:08,050 --> 01:31:11,830 does its objects, with the same name as the key that you passed in, 1770 01:31:11,830 --> 01:31:18,550 or you can do it the Pythonic way, which is, without the dot, sorry-- 1771 01:31:18,550 --> 01:31:23,060 with just square brackets and now have the same of-- those two are equivalent. 1772 01:31:23,060 --> 01:31:27,280 It won't work, though, if you decided to put a space in your key, 1773 01:31:27,280 --> 01:31:30,640 it will, I believe, it will-- 1774 01:31:30,640 --> 01:31:33,600 just won't work at all, but it may inject an underscore. 1775 01:31:33,600 --> 01:31:35,870 I'll have to test it out and find out. 1776 01:31:35,870 --> 01:31:40,330 But, generally, it's not best practice to use dot 1777 01:31:40,330 --> 01:31:43,450 when you already have the keys lined up like this, anyway. 1778 01:31:43,450 --> 01:31:45,970 And what you can do with strings that you 1779 01:31:45,970 --> 01:31:49,600 can't do with dot using the dot notation, 1780 01:31:49,600 --> 01:31:53,530 is dynamically generate a lookup of your table with you-- 1781 01:31:53,530 --> 01:31:56,170 which you can do with strings, which you-- yeah, 1782 01:31:56,170 --> 01:32:00,460 because you can't in a four loop do four something in table, 1783 01:32:00,460 --> 01:32:02,620 and then table dot something, that just won't work. 1784 01:32:02,620 --> 01:32:06,310 But you can do for everything in your table 1785 01:32:06,310 --> 01:32:12,626 and then look up the key as that value, that iterated value in your table. 1786 01:32:12,626 --> 01:32:14,921 We'll see examples of that in future lectures. 1787 01:32:14,921 --> 01:32:16,670 But that's just something to keep in mind. 1788 01:32:16,670 --> 01:32:19,210 So we have our table here, a sounds table. 1789 01:32:19,210 --> 01:32:22,490 Oh, and-- and we have our sounds ready, they're loaded in memory. 1790 01:32:22,490 --> 01:32:24,550 All we need to do now is wherever we have 1791 01:32:24,550 --> 01:32:28,370 anything that, like any collision in our code, we just do this. 1792 01:32:28,370 --> 01:32:30,970 It's as simple as the table. 1793 01:32:30,970 --> 01:32:33,590 At the key that we want, colon, which is Lua's 1794 01:32:33,590 --> 01:32:38,560 way of calling a function of a class or a table. 1795 01:32:38,560 --> 01:32:43,840 Colon play, and the play function is part of the new source audio 1796 01:32:43,840 --> 01:32:46,512 object in LOVE that we created in the table, 1797 01:32:46,512 --> 01:32:48,970 and that will just have the effect of just playing it once. 1798 01:32:48,970 --> 01:32:50,380 You can set it to looping. 1799 01:32:50,380 --> 01:32:54,662 You can say the sounds paddle hit, set looping to true, 1800 01:32:54,662 --> 01:32:56,870 and it will just infinitely play over and over again, 1801 01:32:56,870 --> 01:32:58,210 which we wouldn't want for a sound like this. 1802 01:32:58,210 --> 01:32:59,251 It would sound obnoxious. 1803 01:32:59,251 --> 01:33:01,226 But if you have a music track, for example, 1804 01:33:01,226 --> 01:33:03,100 in a level, or something like that, you would 1805 01:33:03,100 --> 01:33:06,790 want set looping to be true so that when it finally ends, 1806 01:33:06,790 --> 01:33:11,500 your user isn't just playing a game in silence. 1807 01:33:11,500 --> 01:33:14,810 So we're doing it with paddle hit, we're doing it with wall hit as well. 1808 01:33:14,810 --> 01:33:19,300 So I've named them appropriately so that it's easy to infer where 1809 01:33:19,300 --> 01:33:21,730 and for what purpose the sound files are used. 1810 01:33:21,730 --> 01:33:24,650 So whenever they're at the upper or lower boundaries, play the wall 1811 01:33:24,650 --> 01:33:29,560 hit sound, and then whenever the ball reaches 1812 01:33:29,560 --> 01:33:31,450 the left or the right edge of the screen, 1813 01:33:31,450 --> 01:33:34,250 just play the score sound effect. 1814 01:33:34,250 --> 01:33:39,490 And so if we play our game, and this is always one of my more favorite parts 1815 01:33:39,490 --> 01:33:43,150 is playing the game with audio because it just makes 1816 01:33:43,150 --> 01:33:45,640 such a difference, in my opinion. 1817 01:33:45,640 --> 01:33:47,755 We get sound effects. 1818 01:33:47,755 --> 01:33:52,510 It's a little thing, and it's very easy, but it adds-- it adds so much flavor. 1819 01:33:52,510 --> 01:33:54,520 And then (explosion), and there we go. 1820 01:33:54,520 --> 01:33:58,060 And then our game is practically implemented at this point. 1821 01:33:58,060 --> 01:33:59,890 There's just one more example that I would 1822 01:33:59,890 --> 01:34:02,681 like to show you guys, a small example, because all of the examples 1823 01:34:02,681 --> 01:34:08,020 thus far have had the resize equal-- resizable equals false. 1824 01:34:08,020 --> 01:34:11,870 Sort of key in the push setup screen initializer, 1825 01:34:11,870 --> 01:34:15,700 and in case you want to have a game where you can resize your window, 1826 01:34:15,700 --> 01:34:18,310 all we need to do is call a function called love.resize, 1827 01:34:18,310 --> 01:34:20,370 which takes a width and a height. 1828 01:34:20,370 --> 01:34:23,140 And what we're going to end up doing with that, 1829 01:34:23,140 --> 01:34:25,960 specifically, for our use case, because we're using push, 1830 01:34:25,960 --> 01:34:30,730 we're going to go to Pong 12. 1831 01:34:30,730 --> 01:34:40,170 And then if we go to main.lua we see here on line 85, 1832 01:34:40,170 --> 01:34:43,852 I've changed resizable to equal true now so that it will actually 1833 01:34:43,852 --> 01:34:45,310 allow us to resize the application. 1834 01:34:45,310 --> 01:34:47,684 If that's false, you won't even be able to click and drag 1835 01:34:47,684 --> 01:34:50,500 the bottom corner of the screen, it just won't let you do it. 1836 01:34:50,500 --> 01:34:56,400 And then all you have to do is call love.resizewidthheight, 1837 01:34:56,400 --> 01:34:59,160 and then pass in push resize with height. 1838 01:34:59,160 --> 01:35:03,810 Because push underneath the hood takes a texture and renders to it, 1839 01:35:03,810 --> 01:35:05,700 and then upscaled it to fill your window, 1840 01:35:05,700 --> 01:35:08,826 and so it needs to know what your current window dimensions are so 1841 01:35:08,826 --> 01:35:10,950 that it can upscale it to fit the right dimensions. 1842 01:35:10,950 --> 01:35:12,783 And push also adds things like letterboxing, 1843 01:35:12,783 --> 01:35:16,440 which is convenient if you want to maintain the exact same aspect ratio. 1844 01:35:16,440 --> 01:35:19,050 And in a game where maybe you have the UI that's 1845 01:35:19,050 --> 01:35:21,480 driven by the size of your application, this function 1846 01:35:21,480 --> 01:35:23,646 will be important because then you can resize your-- 1847 01:35:23,646 --> 01:35:27,150 you can resize and reposition your UI elements appropriately. 1848 01:35:27,150 --> 01:35:30,990 Because if your game is small, maybe you want certain parts of UI 1849 01:35:30,990 --> 01:35:33,030 to be invisible, or in a different location 1850 01:35:33,030 --> 01:35:36,210 altogether, just so that you don't take up a ton of screen space, 1851 01:35:36,210 --> 01:35:39,190 and just to accommodate all possible users of your application. 1852 01:35:39,190 --> 01:35:43,610 But that has the effect now of-- if we go into Pong 12 1853 01:35:43,610 --> 01:35:48,510 and then run it, actually, might not even be able to use the but-- yeah, 1854 01:35:48,510 --> 01:35:51,390 I can just do this now. 1855 01:35:51,390 --> 01:35:56,100 I can resize it, and it'll maintain the virtual width and height 1856 01:35:56,100 --> 01:36:01,240 that we set it to before, because that's like first and foremost 1857 01:36:01,240 --> 01:36:03,420 what push will do and it'll letterbox no matter 1858 01:36:03,420 --> 01:36:07,391 what size your application is to make sure that it maintains that aspect 1859 01:36:07,391 --> 01:36:07,890 ratio. 1860 01:36:07,890 --> 01:36:10,380 So if you're beyond that aspect ratio vertically or horizontally, 1861 01:36:10,380 --> 01:36:12,270 you'll get the appropriate letterboxing for it. 1862 01:36:12,270 --> 01:36:13,311 So it's super convenient. 1863 01:36:13,311 --> 01:36:19,020 You don't have to worry about your users getting super distorted aspect ratios, 1864 01:36:19,020 --> 01:36:22,380 because they are using some sort of unforeseen resolution. 1865 01:36:22,380 --> 01:36:25,110 That will always maintain it even if it's super tiny 1866 01:36:25,110 --> 01:36:27,080 because their monitor is super thin. 1867 01:36:27,080 --> 01:36:29,030 It will always maintain the aspect ratio. 1868 01:36:29,030 --> 01:36:33,460 But that's pretty much it for Pong, actually. 1869 01:36:33,460 --> 01:36:37,414 We have a complete game, start to finish, and if you have any questions, 1870 01:36:37,414 --> 01:36:38,580 I'd be happy to answer them. 1871 01:36:38,580 --> 01:36:41,550 1872 01:36:41,550 --> 01:36:43,350 Any questions? 1873 01:36:43,350 --> 01:36:43,850 Cool. 1874 01:36:43,850 --> 01:36:44,100 All right. 1875 01:36:44,100 --> 01:36:46,400 Well, I'm excited to teach the rest of this course to you guys. 1876 01:36:46,400 --> 01:36:47,620 We've only scratched the surface. 1877 01:36:47,620 --> 01:36:48,786 We have a lot more to cover. 1878 01:36:48,786 --> 01:36:51,900 Next week, we'll actually be covering Flappy Bird 1879 01:36:51,900 --> 01:36:54,710 so we'll get some nice colorful graphics, which 1880 01:36:54,710 --> 01:36:58,620 is a stark difference to our black and white aesthetics today. 1881 01:36:58,620 --> 01:37:00,000 But that's it for Pong. 1882 01:37:00,000 --> 01:37:02,297 So thanks for coming. 1883 01:37:02,297 --> 01:37:02,797