1 00:00:00,000 --> 00:00:00,500 2 00:00:00,500 --> 00:00:04,110 SPEAKER: So this is Mari0, the tiles update, and so the goal of this update 3 00:00:04,110 --> 00:00:07,270 is a little bit simpler, a little bit more distilled in 4 00:00:07,270 --> 00:00:11,340 that we're just going to have a clear background and then a tile floor 5 00:00:11,340 --> 00:00:14,070 that we can then eventually start adding on to, 6 00:00:14,070 --> 00:00:18,180 including our avatar and other bits of the world, like pillars and clouds 7 00:00:18,180 --> 00:00:19,090 and so forth. 8 00:00:19,090 --> 00:00:23,200 But the very first thing we should get on the screen is just some tiles drawn. 9 00:00:23,200 --> 00:00:25,560 So this is the sprite sheet we're going to use 10 00:00:25,560 --> 00:00:27,330 to end up creating our game world. 11 00:00:27,330 --> 00:00:29,300 So you'll notice that it's pretty simple. 12 00:00:29,300 --> 00:00:30,550 It just has a few blocks. 13 00:00:30,550 --> 00:00:33,300 It's got the bushes we looked at-- the bush and the cloud, rather. 14 00:00:33,300 --> 00:00:36,000 It has a flagpole, which you won't actually get to in the track 15 00:00:36,000 --> 00:00:39,000 but is going to be part of the assignment, including some flag images 16 00:00:39,000 --> 00:00:39,270 there. 17 00:00:39,270 --> 00:00:41,880 You don't have to necessarily use all of the images, just the flagpole and one 18 00:00:41,880 --> 00:00:43,218 of the flag images. 19 00:00:43,218 --> 00:00:44,760 We have the two pieces of the pillar. 20 00:00:44,760 --> 00:00:48,940 Notice that it's actually split into two chunks of this texture. 21 00:00:48,940 --> 00:00:54,450 So this is actually uniform in that these are all 16 by 16 tiles just 22 00:00:54,450 --> 00:00:56,635 put together, packed together uniformly in a grid. 23 00:00:56,635 --> 00:00:59,010 And we're going to look at how to actually split this out 24 00:00:59,010 --> 00:01:02,523 very soon in this exact section, even. 25 00:01:02,523 --> 00:01:03,690 But all the pieces are here. 26 00:01:03,690 --> 00:01:07,440 And this is only a four by four tile, so everything is very compact. 27 00:01:07,440 --> 00:01:11,040 The first thing that we need to look at in order to get our world up 28 00:01:11,040 --> 00:01:13,890 and running is the idea of a tile map. 29 00:01:13,890 --> 00:01:17,340 Thus far, we've really only taken a look at drawing rectangles to the screen, 30 00:01:17,340 --> 00:01:19,590 but we want to start drawing textures instead. 31 00:01:19,590 --> 00:01:23,220 And we're going to look at love.graphics.draw to do that. 32 00:01:23,220 --> 00:01:27,600 But we, of course, need to have some way to store our world for it to be-- 33 00:01:27,600 --> 00:01:30,790 so we can render it to the screen, we need to have a way of representing it. 34 00:01:30,790 --> 00:01:33,790 And to do that, the way that I like to represent, it is through a 1 to 1 35 00:01:33,790 --> 00:01:36,600 mapping, especially for this kind of simple illustration, 36 00:01:36,600 --> 00:01:39,100 as numbers, something that looks very similar to this. 37 00:01:39,100 --> 00:01:41,983 So you can even look at this and visually see sort of how it maps. 38 00:01:41,983 --> 00:01:42,900 Now, this is a mockup. 39 00:01:42,900 --> 00:01:44,817 This isn't really how it's going to be stored. 40 00:01:44,817 --> 00:01:48,720 But you can see, if you imagine that the zeros here are empty space 41 00:01:48,720 --> 00:01:51,720 and the ones are bricks, well, it's actually laid out identically 42 00:01:51,720 --> 00:01:54,780 to what you just saw in the screenshot before, where the zeros really 43 00:01:54,780 --> 00:01:57,120 are the sky, the ones really are the bricks. 44 00:01:57,120 --> 00:02:00,510 And now the numbers here aren't going to match up evenly, and especially 45 00:02:00,510 --> 00:02:03,390 as you get into more sophisticated game where the tiles might have 46 00:02:03,390 --> 00:02:06,000 other metadata associated with them, this model 47 00:02:06,000 --> 00:02:09,600 is a little bit too simple for maybe a real world use case. 48 00:02:09,600 --> 00:02:12,600 But for this example, it actually works perfectly well. 49 00:02:12,600 --> 00:02:15,810 So that's it in terms of just the getting started, 50 00:02:15,810 --> 00:02:20,170 the sort of mental framework we need to lay to get started with the code 51 00:02:20,170 --> 00:02:20,670 itself. 52 00:02:20,670 --> 00:02:22,300 Let's actually dive into the code. 53 00:02:22,300 --> 00:02:26,040 I'm going to go over here to my Games folder, create a new folder altogether. 54 00:02:26,040 --> 00:02:28,380 It's going to be called Mario, in which I'm 55 00:02:28,380 --> 00:02:33,270 going to create Mari0 when I drag that over to VS Code, 56 00:02:33,270 --> 00:02:34,977 and then I'm going to maximize this. 57 00:02:34,977 --> 00:02:37,560 Now we're going to put a few pieces together, so a lot of this 58 00:02:37,560 --> 00:02:38,890 is going to look probably pretty similar. 59 00:02:38,890 --> 00:02:40,182 I'm going to create a new file. 60 00:02:40,182 --> 00:02:42,430 It's going to be main.lua Inside of here, 61 00:02:42,430 --> 00:02:45,690 I'm going to have just a few functions that were important to us before-- 62 00:02:45,690 --> 00:02:49,950 love.load, love,update, which we actually aren't going to use in this 63 00:02:49,950 --> 00:02:52,680 iteration, and love.draw. 64 00:02:52,680 --> 00:02:55,920 No love.keypress just yet. 65 00:02:55,920 --> 00:02:59,350 What I want to have happen, again, we want WINDOW_WIDTH, probably, 66 00:02:59,350 --> 00:03:02,730 for 1280, WINDOW_HEIGHT for 720. 67 00:03:02,730 --> 00:03:04,840 We're going to go right to using push this time, 68 00:03:04,840 --> 00:03:06,540 instead of using the normal resolution. 69 00:03:06,540 --> 00:03:10,560 So I'm going to upfront say virtual width should be equal to 432, 70 00:03:10,560 --> 00:03:14,210 and the virtual height should be equal to 243, just like that. 71 00:03:14,210 --> 00:03:16,460 And I know I'm going to use push, so I'm going to say, 72 00:03:16,460 --> 00:03:18,780 push is equal to require push. 73 00:03:18,780 --> 00:03:22,050 I'm going to actually go back out here to Pong, 74 00:03:22,050 --> 00:03:26,970 and I'm just going to copy both the class and the push.lua 75 00:03:26,970 --> 00:03:30,957 from that last example, go back over to Main, go to my Mari0, 76 00:03:30,957 --> 00:03:32,415 and then just paste those right in. 77 00:03:32,415 --> 00:03:35,610 So we have class and push already built into the project. 78 00:03:35,610 --> 00:03:38,070 Going back to VS Code, so now this will work. 79 00:03:38,070 --> 00:03:39,930 I'm also going to include class. 80 00:03:39,930 --> 00:03:43,050 Class is equal to require of class. 81 00:03:43,050 --> 00:03:44,820 Now, what I'm going to want to have happen 82 00:03:44,820 --> 00:03:47,280 is go straight to object-oriented programming 83 00:03:47,280 --> 00:03:50,070 this time, as opposed to really sort of create 84 00:03:50,070 --> 00:03:54,300 a bunch of data structures and variables outside and muddy up main.lua. 85 00:03:54,300 --> 00:03:57,120 What I want to have happen is I'm already 86 00:03:57,120 --> 00:04:00,330 going to want to think about my world in terms of a map object. 87 00:04:00,330 --> 00:04:04,410 I want to sort of conceptualize all that it means to store a map and render it 88 00:04:04,410 --> 00:04:07,680 and so forth inside of a class so I don't have to worry about that in main. 89 00:04:07,680 --> 00:04:11,490 I can just say, for example, map is equal to a map. 90 00:04:11,490 --> 00:04:13,470 And maybe I can go down to love that draw 91 00:04:13,470 --> 00:04:15,545 and just call map render like this. 92 00:04:15,545 --> 00:04:17,670 And this is a good way to sort of mock up your flow 93 00:04:17,670 --> 00:04:20,378 as you're going along with an object-oriented code base up front. 94 00:04:20,378 --> 00:04:22,845 If you know the objects that you want front, 95 00:04:22,845 --> 00:04:24,720 you can write them before you implement them, 96 00:04:24,720 --> 00:04:29,460 and then sort of backwards retrofit your code by creating those classes. 97 00:04:29,460 --> 00:04:33,610 So up here, I know I'm going to need a map class. 98 00:04:33,610 --> 00:04:35,700 So I'm just going to say, require map. 99 00:04:35,700 --> 00:04:39,540 I'm going to go ahead and create a new file called map.lua. 100 00:04:39,540 --> 00:04:43,110 And in here, I can say function map-- 101 00:04:43,110 --> 00:04:46,770 or rather, first thing I should do is say map is equal to class, 102 00:04:46,770 --> 00:04:48,210 because it is a class. 103 00:04:48,210 --> 00:04:51,950 I'm going to say function map init-- 104 00:04:51,950 --> 00:05:00,050 whoops-- function map update, and function map render. 105 00:05:00,050 --> 00:05:04,150 And we will have an update applied in later iterations of this track. 106 00:05:04,150 --> 00:05:08,420 But for now, we're just going to use primarily render and init. 107 00:05:08,420 --> 00:05:12,510 So I'm going to go back to Main, and then I'm going to go over here 108 00:05:12,510 --> 00:05:13,740 and I'm going to say-- 109 00:05:13,740 --> 00:05:15,740 well, actually the next thing that we need to do 110 00:05:15,740 --> 00:05:19,250 is, in love.load, I should get the screen setup. 111 00:05:19,250 --> 00:05:21,690 So I'm going to say push setup screen. 112 00:05:21,690 --> 00:05:25,370 It just takes in our WINDOW_WIDTH-- or rather, it takes our VIRTUAL_WIDTH, 113 00:05:25,370 --> 00:05:32,550 VIRTUAL_HEIGHT followed by our WINDOW_WIDTH and WINDOW_HEIGHT. 114 00:05:32,550 --> 00:05:34,540 And again, it takes in an object full screen. 115 00:05:34,540 --> 00:05:37,440 We're going to make that false, resizable equal to false, 116 00:05:37,440 --> 00:05:40,020 and vsync equal to true. 117 00:05:40,020 --> 00:05:46,830 Down here in love.draw, remember, I need to push apply start and push apply end 118 00:05:46,830 --> 00:05:50,140 around the things that I want to draw. 119 00:05:50,140 --> 00:05:55,020 So I'm going to go ahead and also I'm going to say love.graphics.print 120 00:05:55,020 --> 00:05:59,070 hello, world, just as a sanity check. 121 00:05:59,070 --> 00:06:01,860 And then one other thing I want to do is clear the screen 122 00:06:01,860 --> 00:06:05,590 with that sort of Mario blue that we saw back here. 123 00:06:05,590 --> 00:06:08,280 So in order to do that, remember, we have love.graphics.clear. 124 00:06:08,280 --> 00:06:11,100 So I'm going to say, love.graphics.clear, 125 00:06:11,100 --> 00:06:20,196 and then I'm going to say 108 over 255, 140 over 255, 255 over 255, 126 00:06:20,196 --> 00:06:22,290 and 255 over 255. 127 00:06:22,290 --> 00:06:24,420 And these last two you can actually just write 1 128 00:06:24,420 --> 00:06:27,100 because that it will equate to the same exact thing. 129 00:06:27,100 --> 00:06:27,930 So let's go ahead-- 130 00:06:27,930 --> 00:06:32,748 I think everything is well enough in order to at least give it a test run. 131 00:06:32,748 --> 00:06:34,290 So why don't we go ahead and do that? 132 00:06:34,290 --> 00:06:35,420 I'm going to run that. 133 00:06:35,420 --> 00:06:38,550 And we do indeed see that screen being cleared in that blue color. 134 00:06:38,550 --> 00:06:41,220 We see the text, hello, world, up at the very top left. 135 00:06:41,220 --> 00:06:44,430 It is blurry, so what I want to do next is-- 136 00:06:44,430 --> 00:06:50,310 remember, we have the ability to say love.graphics.setDefaultFilter 137 00:06:50,310 --> 00:06:53,910 of nearest and nearest, which gets rid of the pic-- 138 00:06:53,910 --> 00:06:55,980 or it pixelates everything in that. 139 00:06:55,980 --> 00:07:00,330 It doesn't interpolate the pixels being rendered, so it solves that problem. 140 00:07:00,330 --> 00:07:03,330 So it's a little maybe difficult to see because of the background color, 141 00:07:03,330 --> 00:07:07,390 but we do indeed have the hello, world text here nice and crisp. 142 00:07:07,390 --> 00:07:07,890 Cool. 143 00:07:07,890 --> 00:07:08,973 So I'm going to quit that. 144 00:07:08,973 --> 00:07:10,540 I'm going to go back over here. 145 00:07:10,540 --> 00:07:13,590 Now what I want to have happen is get into the flow 146 00:07:13,590 --> 00:07:15,240 of chopping up that sprite sheet. 147 00:07:15,240 --> 00:07:17,162 So we need that sprite sheet, first off. 148 00:07:17,162 --> 00:07:18,870 I'm actually going to cheat a little bit. 149 00:07:18,870 --> 00:07:23,970 I'm going to go into my sort of pre-baked code 150 00:07:23,970 --> 00:07:29,032 here, where I really have Mari0. 151 00:07:29,032 --> 00:07:30,990 And I'm going to go into graphics, and I'm just 152 00:07:30,990 --> 00:07:33,680 going to copy this sprite sheet. 153 00:07:33,680 --> 00:07:35,740 I'm going to go over to games, Mario, Mari0. 154 00:07:35,740 --> 00:07:39,030 I'm going to create a new graphics directory and then paste that right in. 155 00:07:39,030 --> 00:07:42,450 So now we have this sprite sheet that I showed earlier in the slides. 156 00:07:42,450 --> 00:07:44,100 It's the exact same file. 157 00:07:44,100 --> 00:07:48,690 And one thing that I want to also talk about before we really do that is there 158 00:07:48,690 --> 00:07:50,650 is going to be a mapping. 159 00:07:50,650 --> 00:07:52,950 So let's find that again. 160 00:07:52,950 --> 00:07:54,990 If we look at this sprite sheet here, we're 161 00:07:54,990 --> 00:07:59,000 going to chop this up into individual components, like I said before. 162 00:07:59,000 --> 00:08:02,430 And we want associated number with each of these individual tiles. 163 00:08:02,430 --> 00:08:06,900 So the most sort of intuitive way to do this, and the way we will do this, 164 00:08:06,900 --> 00:08:11,350 is to just go down from the top, work our way to the right, so top to bottom, 165 00:08:11,350 --> 00:08:12,240 left to right. 166 00:08:12,240 --> 00:08:16,950 So we'll end up saying this is 1, this is 2, this is 3. 167 00:08:16,950 --> 00:08:20,190 This empty space is 4, and then we'll go back down to the next line 168 00:08:20,190 --> 00:08:26,190 where this is 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16. 169 00:08:26,190 --> 00:08:29,460 And so we're going to need to chop up these individual tiles. 170 00:08:29,460 --> 00:08:35,280 And to do that, I want to introduce you to the love.graphics.quad function. 171 00:08:35,280 --> 00:08:38,620 Now, first, for something like chopping up a file, 172 00:08:38,620 --> 00:08:42,713 It makes a little bit more sense to put that in something like a utility class 173 00:08:42,713 --> 00:08:45,630 because this is something we could use in a bunch of different places, 174 00:08:45,630 --> 00:08:48,070 potentially, for working on a larger code base. 175 00:08:48,070 --> 00:08:52,080 So what I like to do is, for something that's a little bit more utility-based, 176 00:08:52,080 --> 00:08:55,620 I'll create something called util.lua or something similarly. 177 00:08:55,620 --> 00:08:57,850 Capitalized or not, that's kind of up to you. 178 00:08:57,850 --> 00:09:01,963 But in here, I essentially just want a function called generateQuads. 179 00:09:01,963 --> 00:09:03,880 And generateQuads is a pretty simple function. 180 00:09:03,880 --> 00:09:05,520 Should take what's called an atlas-- 181 00:09:05,520 --> 00:09:08,450 so you can think of sprite sheets and texture atlases 182 00:09:08,450 --> 00:09:10,440 as is being sort of the same thing. 183 00:09:10,440 --> 00:09:13,830 In some environments, the atlas will have other connotations 184 00:09:13,830 --> 00:09:18,210 in that it has some sort of stored information, usually JSON information, 185 00:09:18,210 --> 00:09:21,150 where you don't have to necessarily rely on things being grid-based. 186 00:09:21,150 --> 00:09:23,480 But for the sake of, I guess, convention, 187 00:09:23,480 --> 00:09:25,230 we'll use the term atlas here. 188 00:09:25,230 --> 00:09:29,820 So it's going to take in an atlas, which is going to be the path to the texture. 189 00:09:29,820 --> 00:09:32,610 Actually, in this case, it's going to be the actual texture data. 190 00:09:32,610 --> 00:09:35,568 And then we're going to say that it should have a tile width and a tile 191 00:09:35,568 --> 00:09:38,160 height as well that it needs to know about in order 192 00:09:38,160 --> 00:09:40,200 to chop this up sort of uniformly. 193 00:09:40,200 --> 00:09:42,010 So I'll figure out the sheet width. 194 00:09:42,010 --> 00:09:47,320 So the sheetWidth is going to be atlas getWidth divided by tileWidth. 195 00:09:47,320 --> 00:09:50,435 So the getWidth is going to be in pixels, and so will the tileWidth. 196 00:09:50,435 --> 00:09:52,310 And this sheetWidth is essentially just going 197 00:09:52,310 --> 00:09:55,500 to be how many tiles wide is our sheet. 198 00:09:55,500 --> 00:09:59,278 And we're really the same thing for the tileHeight by sheetHeight. 199 00:09:59,278 --> 00:10:01,320 Now, you'll notice I'm using the word local here, 200 00:10:01,320 --> 00:10:03,690 which we talked about before in another section. 201 00:10:03,690 --> 00:10:06,210 Local just means that these variables are not 202 00:10:06,210 --> 00:10:09,070 accessible from anywhere outside of this function. 203 00:10:09,070 --> 00:10:12,460 So we won't be able to have sheetWidth and sheetHeight accessed 204 00:10:12,460 --> 00:10:14,700 in our main.lua, for example, which we wouldn't want. 205 00:10:14,700 --> 00:10:17,640 We usually don't want to muddy up what's called your namespace, 206 00:10:17,640 --> 00:10:21,330 sort of all the variables accessible to you at any given point in time 207 00:10:21,330 --> 00:10:23,220 by making too many variables global. 208 00:10:23,220 --> 00:10:26,670 That's why globals are generally said to be a bad thing in programming. 209 00:10:26,670 --> 00:10:28,980 In game development, it's actually quite common 210 00:10:28,980 --> 00:10:31,318 to see a lot of global variables. 211 00:10:31,318 --> 00:10:33,110 But try to mitigate that as much as you can 212 00:10:33,110 --> 00:10:36,230 because especially with very generically named variables, 213 00:10:36,230 --> 00:10:38,873 it can come to bite you down the line. 214 00:10:38,873 --> 00:10:40,790 Now what I'm going to do is I'm going to say-- 215 00:10:40,790 --> 00:10:41,750 I'm going to have a sheet counter. 216 00:10:41,750 --> 00:10:43,580 This is where our quads sort of-- 217 00:10:43,580 --> 00:10:45,650 our numbering is going to begin at 1. 218 00:10:45,650 --> 00:10:48,140 And this isn't strictly necessary with another way 219 00:10:48,140 --> 00:10:50,702 to do this, which I can talk about very briefly. 220 00:10:50,702 --> 00:10:52,910 But we're going to use it here just for illustration. 221 00:10:52,910 --> 00:10:55,250 And I'm also creating this thing called quads, 222 00:10:55,250 --> 00:10:58,588 and this quads is a table that's going to store all the quads we're 223 00:10:58,588 --> 00:10:59,630 going to actually create. 224 00:10:59,630 --> 00:11:02,240 Now, a quad is literally just a rectangle, 225 00:11:02,240 --> 00:11:05,330 and it represents a chunk of a texture, basically. 226 00:11:05,330 --> 00:11:08,330 And we're going to create this table full of quads, 227 00:11:08,330 --> 00:11:09,710 and we're using it like an array. 228 00:11:09,710 --> 00:11:13,250 Now previously, you saw tables being used like dictionaries 229 00:11:13,250 --> 00:11:15,680 or like hash tables, key value pairs. 230 00:11:15,680 --> 00:11:17,930 In this case, we're literally using it like a C array. 231 00:11:17,930 --> 00:11:19,263 We're just filling it with data. 232 00:11:19,263 --> 00:11:22,670 It's more akin to, really, a Python list because we don't have to, upfront, 233 00:11:22,670 --> 00:11:24,510 say how big the array should be. 234 00:11:24,510 --> 00:11:29,060 But we are literally just storing information or data chunk after chunk. 235 00:11:29,060 --> 00:11:31,550 We're not associating keys to values. 236 00:11:31,550 --> 00:11:35,630 Really, we are, in the sense that we're associating 1, 2, 3, 4, 237 00:11:35,630 --> 00:11:38,600 5 to those values numerically. 238 00:11:38,600 --> 00:11:42,350 And the interesting thing, too, about Lua is that everything is 1 indexed. 239 00:11:42,350 --> 00:11:45,680 It's not 0 indexed, at least by norm and by default. 240 00:11:45,680 --> 00:11:49,910 You can explicitly index things at 0, but the norm very much 241 00:11:49,910 --> 00:11:51,770 is in Lua to start things at 1. 242 00:11:51,770 --> 00:11:54,980 And if you use some of the default sort of array 243 00:11:54,980 --> 00:11:58,190 generating functions, or creating or appending functions, 244 00:11:58,190 --> 00:12:03,320 you'll find that it does start things at 1 if you don't specify an index. 245 00:12:03,320 --> 00:12:06,800 So we're going to iterate through our texture, going from top to bottom. 246 00:12:06,800 --> 00:12:13,400 I'm going to say for y is equal to 0 from the sheetHeight minus 1. 247 00:12:13,400 --> 00:12:16,820 So this is going from 0 to-- 248 00:12:16,820 --> 00:12:18,410 in this case it would be 3. 249 00:12:18,410 --> 00:12:23,300 And we're starting this at 0 because we want to actually specify 250 00:12:23,300 --> 00:12:29,000 the quad in pixels on our sprite sheet, and the pixel specification is 0 based. 251 00:12:29,000 --> 00:12:33,058 So pixel space is 0 indexed, but array indices are 1 indexed. 252 00:12:33,058 --> 00:12:35,600 So this is kind of a game you have to play to sort of balance 253 00:12:35,600 --> 00:12:37,760 this out a little bit when you're dealing with Lua. 254 00:12:37,760 --> 00:12:41,480 So we'll say for x is equal to 0, sheetWidth minus 1 do. 255 00:12:41,480 --> 00:12:43,958 And you'll notice that this is a double nested for loop, 256 00:12:43,958 --> 00:12:47,000 much like you might have seen in Mario, for example, fittingly because we 257 00:12:47,000 --> 00:12:49,520 are literally programming Mario. 258 00:12:49,520 --> 00:12:53,000 Now what I'm going to do is I'm going to say quads at sheetCounter 259 00:12:53,000 --> 00:12:57,410 is going to be equal to-- and this is just indexing into the table 260 00:12:57,410 --> 00:12:59,870 that we declared already. 261 00:12:59,870 --> 00:13:04,490 So I'm going to say love.graphics.newQuad. 262 00:13:04,490 --> 00:13:07,910 Now, this is a love.graphics function, which expects-- 263 00:13:07,910 --> 00:13:11,930 as you see here, VS Code is kindly telling me what arguments it expects, 264 00:13:11,930 --> 00:13:13,320 which is super convenient. 265 00:13:13,320 --> 00:13:16,310 So it expects an x, a y, a width, and a height, and then 266 00:13:16,310 --> 00:13:20,060 as well the dimensions of our texture atlas, which we can actually 267 00:13:20,060 --> 00:13:22,980 get through a shorthand function call. 268 00:13:22,980 --> 00:13:29,090 So I'm going to say that this should be set to x times tileWidth and y times 269 00:13:29,090 --> 00:13:30,860 tileHeight. 270 00:13:30,860 --> 00:13:35,300 So again, this is using x and y 0 based. 271 00:13:35,300 --> 00:13:36,530 We need to figure this out. 272 00:13:36,530 --> 00:13:41,600 We need to basically find where that little sub part of our texture begins, 273 00:13:41,600 --> 00:13:43,480 coordinate wise, on our texture. 274 00:13:43,480 --> 00:13:48,350 So if we're starting at 0, let's say our tile width is 16 pixels 275 00:13:48,350 --> 00:13:50,750 and the tile height is 16 pixels. 276 00:13:50,750 --> 00:13:53,030 If we're at y0, y0-- 277 00:13:53,030 --> 00:13:55,760 or x0, then we're just going to start at 0,0. 278 00:13:55,760 --> 00:13:59,510 And the width, of course, is going to be tileWidth and tileHeight, 279 00:13:59,510 --> 00:14:01,700 so getting to be a little bit of a long function 280 00:14:01,700 --> 00:14:08,913 call, tileWidth and tileHeight, and atlas colon getDimensions. 281 00:14:08,913 --> 00:14:11,330 Don't necessarily need to worry about atlas getDimensions. 282 00:14:11,330 --> 00:14:14,240 Think of this really as sort of a necessary requisite 283 00:14:14,240 --> 00:14:15,590 for using this function. 284 00:14:15,590 --> 00:14:18,980 It just essentially expects the dimensions of the texture following 285 00:14:18,980 --> 00:14:22,160 the declaration of the quad, and for reasons that are related to how quads 286 00:14:22,160 --> 00:14:26,270 are stored at the C++ level in Love. 287 00:14:26,270 --> 00:14:31,730 But if we're starting at x0, y0, or x1, y1, really, 288 00:14:31,730 --> 00:14:35,870 our first tile up at the top left, it needs 289 00:14:35,870 --> 00:14:39,290 to start at 0,0 because that's where the coordinate space is based, 290 00:14:39,290 --> 00:14:43,310 and grab 16 pixels down into the right, which is where the tile width and tile 291 00:14:43,310 --> 00:14:44,180 height come in. 292 00:14:44,180 --> 00:14:51,080 And if we're, let's say, starting at x2, y0, then this becomes 1 times 293 00:14:51,080 --> 00:14:52,610 tile height, which means-- 294 00:14:52,610 --> 00:14:55,670 or x1 times tile height-- 295 00:14:55,670 --> 00:14:59,690 x1 times tile width, which means it starts at 16 296 00:14:59,690 --> 00:15:02,300 and grabs the 16 pixels and 16 pixels. 297 00:15:02,300 --> 00:15:07,440 So it's essentially offset, depending on what our counter is in our loop here. 298 00:15:07,440 --> 00:15:10,490 So this is how we end up iterating throughout our loop. 299 00:15:10,490 --> 00:15:14,450 And remember, it's nested, so we iterate through our columns 300 00:15:14,450 --> 00:15:16,860 because this inner loop is on the x-axis. 301 00:15:16,860 --> 00:15:20,030 So we go x1, 2, 3, and then we loop back to y 302 00:15:20,030 --> 00:15:24,440 where y is 1, which means we end up going back down to here. 303 00:15:24,440 --> 00:15:29,030 So y will be 1, x will be 0, so 0, 1, 2, 3. 304 00:15:29,030 --> 00:15:33,410 Then y is 2 here, so 0, 1, 2, 3, and y is 3, 0, 1, 2, 3. 305 00:15:33,410 --> 00:15:36,380 So that's essentially how we're iterating through our sprite sheet 306 00:15:36,380 --> 00:15:37,670 to create all the quads. 307 00:15:37,670 --> 00:15:40,130 Now we've assembled this quads table. 308 00:15:40,130 --> 00:15:43,350 So at the very end, what I want to do is return it. 309 00:15:43,350 --> 00:15:46,280 So now we can call this from within anywhere in our program, 310 00:15:46,280 --> 00:15:51,290 as long as we require util, which is what I going to do right here. 311 00:15:51,290 --> 00:15:54,680 And actually, what we're going to do is use this NMAP, 312 00:15:54,680 --> 00:15:57,350 so I need to make sure that this is done sort of upfront 313 00:15:57,350 --> 00:15:59,960 before map actually gets called. 314 00:15:59,960 --> 00:16:03,320 Now I want to actually go through the process of creating the map, which 315 00:16:03,320 --> 00:16:05,990 is a little bit more involved. 316 00:16:05,990 --> 00:16:10,348 So what I need to do here is say self.spriteSheet 317 00:16:10,348 --> 00:16:12,140 because we need a sprite sheet to reference 318 00:16:12,140 --> 00:16:15,920 to actually map the tiles to quads. 319 00:16:15,920 --> 00:16:20,607 self.spriteSheet is equal to love.graphics.newImage. 320 00:16:20,607 --> 00:16:21,690 So this is a new function. 321 00:16:21,690 --> 00:16:26,720 This function is really just a way of pointing to a texture file 322 00:16:26,720 --> 00:16:31,190 and loading it into memory so it creates a texture object. 323 00:16:31,190 --> 00:16:34,020 I think it literally is a texture object. 324 00:16:34,020 --> 00:16:35,270 And then it just takes a path. 325 00:16:35,270 --> 00:16:40,130 So I can just say graphics/spriteSheet.png, 326 00:16:40,130 --> 00:16:41,400 which is great. 327 00:16:41,400 --> 00:16:44,250 And then now that's just the texture data itself. 328 00:16:44,250 --> 00:16:47,780 Now, we haven't chopped it up yet so the goal of that 329 00:16:47,780 --> 00:16:52,610 is going to be to use that generateQuads function that we just wrote ourselves. 330 00:16:52,610 --> 00:16:57,560 But first I want to say self.tileWidth is equal to 16. 331 00:16:57,560 --> 00:17:00,800 self.tileHeight is equal to 16. 332 00:17:00,800 --> 00:17:05,480 self.mapWidth-- and this is where mapWidth and tileWidth are a little bit 333 00:17:05,480 --> 00:17:05,980 different. 334 00:17:05,980 --> 00:17:08,750 So the tile width here is literally just pixel size. 335 00:17:08,750 --> 00:17:11,510 I know that all these tiles are 16 by 16 pixels. 336 00:17:11,510 --> 00:17:14,770 The map width is going to be how many tiles wide and tall 337 00:17:14,770 --> 00:17:15,980 our map actually is. 338 00:17:15,980 --> 00:17:18,272 So in this case, let's just choose an arbitrary amount. 339 00:17:18,272 --> 00:17:20,660 Let's say the map is going to be 30 tiles wide 340 00:17:20,660 --> 00:17:24,380 and the height is going to be 28, let's just say, arbitrarily. 341 00:17:24,380 --> 00:17:28,820 And then I'm going to have the actual tile map, so the numbers 342 00:17:28,820 --> 00:17:31,192 that I alluded to before, this information, 343 00:17:31,192 --> 00:17:33,650 because I'm going to essentially iterate over this and then 344 00:17:33,650 --> 00:17:37,280 draw the right texture with a right quad down the line. 345 00:17:37,280 --> 00:17:39,260 But I need to store of this in the map as well. 346 00:17:39,260 --> 00:17:43,130 This is really the foundational data that comprises our map. 347 00:17:43,130 --> 00:17:45,950 So I'm going to go back here, and what I want to do 348 00:17:45,950 --> 00:17:50,810 is say self.tileSprites is equal to generateQuads. 349 00:17:50,810 --> 00:17:53,240 So sprites, quads, we're kind of using them 350 00:17:53,240 --> 00:17:55,890 sort of synonymously in this context. 351 00:17:55,890 --> 00:17:58,190 But it's going to take in our self.spriteSheet. 352 00:17:58,190 --> 00:18:00,740 And remember that takes in the tile width and tile height. 353 00:18:00,740 --> 00:18:02,780 So I can say 16, 16 here. 354 00:18:02,780 --> 00:18:06,250 I can say self.tileWidth, self.tileHeight. 355 00:18:06,250 --> 00:18:09,650 That might be a little bit more best practice to do that. 356 00:18:09,650 --> 00:18:11,360 So we're going to do that. 357 00:18:11,360 --> 00:18:14,810 And then we actually need to populate our tile map with those numbers. 358 00:18:14,810 --> 00:18:19,190 Now, over here we can see that we're using zeros and ones just as a mockup. 359 00:18:19,190 --> 00:18:24,020 But if we look at our actual tile sheet, we can see here that 1 is the brick. 360 00:18:24,020 --> 00:18:24,620 We know that. 361 00:18:24,620 --> 00:18:26,540 That's the important tile. 362 00:18:26,540 --> 00:18:30,800 And then if we go over here, 2, 3, 4, we see that 4 is actually 363 00:18:30,800 --> 00:18:32,550 an empty tile, which is great. 364 00:18:32,550 --> 00:18:36,420 We can use that to represent the sky because if it just draws 365 00:18:36,420 --> 00:18:38,930 no information, if it just draws blank pixels, 366 00:18:38,930 --> 00:18:42,170 it'll be the equivalent of no tiles actually existing there. 367 00:18:42,170 --> 00:18:44,880 So I'm actually going to go up here to the very top. 368 00:18:44,880 --> 00:18:46,630 I'm going to define a couple of constants. 369 00:18:46,630 --> 00:18:48,750 I'm going to say, TILE_BRICK is equal to 1, 370 00:18:48,750 --> 00:18:52,310 and let's just say TILE_EMPTY is equal to 4. 371 00:18:52,310 --> 00:18:53,760 OK, perfect. 372 00:18:53,760 --> 00:19:00,050 And then we'll say, for y is equal to 1 self.mapHeight 373 00:19:00,050 --> 00:19:03,290 do, and what this is going to do is it's going to iterate top 374 00:19:03,290 --> 00:19:05,750 to bottom throughout the entire table. 375 00:19:05,750 --> 00:19:08,120 Or really, the table is empty now, but it's 376 00:19:08,120 --> 00:19:12,500 going to iterate through the entire map that we have decided on our dimensions. 377 00:19:12,500 --> 00:19:15,980 And what we can do is we can say for x is 378 00:19:15,980 --> 00:19:22,130 equal to 1 self.mapWidth, so remember, columns and rows at this point. 379 00:19:22,130 --> 00:19:28,950 We'll do self setTIle x, y, TILE_EMPTY. 380 00:19:28,950 --> 00:19:32,200 And we haven't written this function yet, so why don't we actually do that. 381 00:19:32,200 --> 00:19:37,590 So let's say function map setTile, which takes in an x, a y, 382 00:19:37,590 --> 00:19:39,315 and we'll say a tile. 383 00:19:39,315 --> 00:19:41,190 So in this case, the tiles are literally just 384 00:19:41,190 --> 00:19:45,120 numbers, very simple way of representing tile information. 385 00:19:45,120 --> 00:19:48,990 And for this, let's just say self.tiles-- 386 00:19:48,990 --> 00:19:53,460 remember, that's our data structure, sort of our array for storing our tile 387 00:19:53,460 --> 00:19:56,060 information-- self.tiles at-- 388 00:19:56,060 --> 00:19:59,700 and this is an interesting way of storing information 389 00:19:59,700 --> 00:20:02,290 in a sort of compact way for tile maps. 390 00:20:02,290 --> 00:20:04,600 And you can do this in multiple different ways. 391 00:20:04,600 --> 00:20:07,830 But this is essentially storing 2D map representation 392 00:20:07,830 --> 00:20:09,430 in a one-dimensional way. 393 00:20:09,430 --> 00:20:16,440 So we'll say y minus 1 times self.mapWidth plus x. 394 00:20:16,440 --> 00:20:18,390 And so by virtue of that, we're essentially 395 00:20:18,390 --> 00:20:22,660 saying, wherever our y is minus 1 because everything is 1 indexed. 396 00:20:22,660 --> 00:20:28,110 So for example, if this is our first row and y is equal to 1, well, 397 00:20:28,110 --> 00:20:31,650 it's going to equate to 0 here times the self.mapWidth 398 00:20:31,650 --> 00:20:33,450 because that'll essentially get us-- 399 00:20:33,450 --> 00:20:38,050 if we're looking at our array, if we go back over here-- 400 00:20:38,050 --> 00:20:41,130 so if we're looking at this, have a two-dimensional map here. 401 00:20:41,130 --> 00:20:43,770 But really, we have a one-dimensional map 402 00:20:43,770 --> 00:20:46,230 that's representing a two-dimensional map space. 403 00:20:46,230 --> 00:20:50,580 If y is equal to 1 and we subtract 1 and we add x, 404 00:20:50,580 --> 00:20:54,370 then we're essentially getting anything within this top row. 405 00:20:54,370 --> 00:20:58,680 But if, let's say, y is equal to 2, we're going to end up becoming 1. 406 00:20:58,680 --> 00:21:01,770 So y will get subtracted to 1 times the map width, which 407 00:21:01,770 --> 00:21:04,530 is this, which will put us right here. 408 00:21:04,530 --> 00:21:05,782 And then we add x to that. 409 00:21:05,782 --> 00:21:07,740 Well, really it puts us right here, and then we 410 00:21:07,740 --> 00:21:10,020 add x to that, which-- let's say x is 1. 411 00:21:10,020 --> 00:21:11,070 We get this tile. 412 00:21:11,070 --> 00:21:13,690 x is 3, we get this tile, so on and so forth. 413 00:21:13,690 --> 00:21:15,740 So y gets multiplied by the map width. 414 00:21:15,740 --> 00:21:18,300 And you add x to that, and that allows you to index 415 00:21:18,300 --> 00:21:21,690 into your one-dimensional array as if it were a two-dimensional array, 416 00:21:21,690 --> 00:21:22,530 essentially. 417 00:21:22,530 --> 00:21:25,950 Now we're going to write another function for getTile, 418 00:21:25,950 --> 00:21:28,590 and we're just going to get an x and a y that goes into that. 419 00:21:28,590 --> 00:21:34,650 We're going to return self.tiles at y minus times self.mapWidth plus x. 420 00:21:34,650 --> 00:21:37,350 So essentially, the same thing, but all we're doing in this case 421 00:21:37,350 --> 00:21:40,270 is just returning that same value. 422 00:21:40,270 --> 00:21:43,800 So what we're doing here is just essentially filling 423 00:21:43,800 --> 00:21:47,190 the map with empty tiles. 424 00:21:47,190 --> 00:21:49,740 Now what I want to do is not just have purely empty tiles. 425 00:21:49,740 --> 00:21:52,260 I want to also have some actual physical tiles at the bottom 426 00:21:52,260 --> 00:21:53,670 of the screen, some bricks. 427 00:21:53,670 --> 00:22:00,570 So what I can say is, for y is equal to self.mapHeight divided by 2 do. 428 00:22:00,570 --> 00:22:04,650 So we're starting halfway down the map in this case. 429 00:22:04,650 --> 00:22:11,780 And then I'm going to say, for x is equal to 1 until self.mapWidth do. 430 00:22:11,780 --> 00:22:14,280 Or actually, for y is equal to mapWidth divided by 2, 431 00:22:14,280 --> 00:22:19,270 and then I need to make this go until self.mapHeight. 432 00:22:19,270 --> 00:22:24,180 So for x is equal to 1 until self.mapWidth do, 433 00:22:24,180 --> 00:22:28,590 self setTile x, y tile, and I think I called it BRICK. 434 00:22:28,590 --> 00:22:29,700 Yep, TILE_BRICK. 435 00:22:29,700 --> 00:22:35,700 So this starts halfway down the map, populates with bricks. 436 00:22:35,700 --> 00:22:38,670 So we're starting at map height divided by 2, not 0. 437 00:22:38,670 --> 00:22:40,800 So we started at 1, rather. 438 00:22:40,800 --> 00:22:43,740 We started at 1 here, and then we ended up going all the way down 439 00:22:43,740 --> 00:22:44,490 to the mat height. 440 00:22:44,490 --> 00:22:47,270 We could have made this mapHeight divided by 2 if we want to. 441 00:22:47,270 --> 00:22:49,260 Would be totally viable. 442 00:22:49,260 --> 00:22:52,200 But we ended up going all the way down, essentially populating it 443 00:22:52,200 --> 00:22:55,090 with some default data, the TILE_EMPTY. 444 00:22:55,090 --> 00:22:57,870 And then we went from the halfway mark downwards. 445 00:22:57,870 --> 00:23:02,940 So we started at self.mapHeight divided by 2 going down until self.mapHeight. 446 00:23:02,940 --> 00:23:05,100 And this is, by the way, how for loops work in Lua. 447 00:23:05,100 --> 00:23:09,790 You have a 4 something equal something, comma, and then the end of that. 448 00:23:09,790 --> 00:23:15,630 So if you have y equal to 1, you can say until self.mapHeight divided by 2, 449 00:23:15,630 --> 00:23:18,035 and it increments sort of implicitly. 450 00:23:18,035 --> 00:23:19,660 So it knows that that should be plus 1. 451 00:23:19,660 --> 00:23:22,100 Now, you could specify a negative just like this, 452 00:23:22,100 --> 00:23:24,600 if you wanted to do a backwards crawl through your for loop. 453 00:23:24,600 --> 00:23:26,475 But in this case, since we're going forwards, 454 00:23:26,475 --> 00:23:27,932 we can leave that off altogether. 455 00:23:27,932 --> 00:23:30,390 So it's a little bit of a different way of using for loops, 456 00:23:30,390 --> 00:23:33,810 but this is the Lua sort of syntactical way to do that. 457 00:23:33,810 --> 00:23:35,730 So that's how we initialize our map. 458 00:23:35,730 --> 00:23:40,770 So we set our tile at x, y to be TILE_EMPTY, which is essentially 4. 459 00:23:40,770 --> 00:23:44,740 And then we set it from halfway down to TILE_BRICK, which is 1. 460 00:23:44,740 --> 00:23:47,660 And again, that maps to how we have laid out our texture. 461 00:23:47,660 --> 00:23:52,600 Now, if we go to map render, we can use a similar loop. 462 00:23:52,600 --> 00:23:58,380 So if I say, for y is equal to 1 until self.mapHeight do, and then we're 463 00:23:58,380 --> 00:24:04,260 going to say, for x is equal to 1 self.mapWidth do. 464 00:24:04,260 --> 00:24:08,100 And then what I want to do is say love.graphics.draw, 465 00:24:08,100 --> 00:24:10,530 which is a new function, so this just draws a texture. 466 00:24:10,530 --> 00:24:12,510 We'll say self.spriteSheet. 467 00:24:12,510 --> 00:24:15,690 So we're drawing the sprite sheet, but here's the important thing. 468 00:24:15,690 --> 00:24:19,410 After we actually draw the sprite sheet and we're dealing with quads, 469 00:24:19,410 --> 00:24:22,380 we need to specify what quad we want to draw. 470 00:24:22,380 --> 00:24:28,170 So what I can do is say, self.tileSprites tile sprites 471 00:24:28,170 --> 00:24:33,930 at self getTile x, y. 472 00:24:33,930 --> 00:24:38,430 And then that will end up giving us the actual value of the quad, 473 00:24:38,430 --> 00:24:42,810 whether it's 1 or 4, into self.tileSprites, 474 00:24:42,810 --> 00:24:45,240 which is mapping to the quad that we chopped up 475 00:24:45,240 --> 00:24:46,800 from the texture information. 476 00:24:46,800 --> 00:24:49,170 So we have this sprite sheet, we have the quad. 477 00:24:49,170 --> 00:24:51,360 Now we need to specify the x, y. 478 00:24:51,360 --> 00:24:54,310 So I'm going to go ahead and just do an Enter here. 479 00:24:54,310 --> 00:24:56,610 So in order to do that correctly, now remember, 480 00:24:56,610 --> 00:24:59,070 our x and y are 1 indexed here. 481 00:24:59,070 --> 00:25:01,470 So we're starting y at 1, x at 1 just because of the way 482 00:25:01,470 --> 00:25:03,510 that tables generally work in Lua. 483 00:25:03,510 --> 00:25:05,040 But pixels are not 1 indexed. 484 00:25:05,040 --> 00:25:05,910 They're 0 indexed. 485 00:25:05,910 --> 00:25:13,980 So to do that, all we need to do is say x minus 1 times self.tileWidth, and y 486 00:25:13,980 --> 00:25:17,552 minus 1 times self.tileWidth. 487 00:25:17,552 --> 00:25:20,010 And this, essentially, is drawing everything out on a grid. 488 00:25:20,010 --> 00:25:23,580 So it's figuring out what our x, y is, offsetting it by, 489 00:25:23,580 --> 00:25:27,030 in this case, self.tileWidth and self.tileHeight, this should be, 490 00:25:27,030 --> 00:25:28,203 actually. 491 00:25:28,203 --> 00:25:30,370 They're the same in this case, so it doesn't matter. 492 00:25:30,370 --> 00:25:34,650 But those are 16 pixels, so we're essentially drawing a 16-pixel grid. 493 00:25:34,650 --> 00:25:40,290 We're subtracting 1 from our table index so that it maps to are 0-positioned 494 00:25:40,290 --> 00:25:42,220 coordinate system. 495 00:25:42,220 --> 00:25:44,730 Now I'm just going to go ahead and give it one last look. 496 00:25:44,730 --> 00:25:46,870 That should be just about everything. 497 00:25:46,870 --> 00:25:48,240 So let's go over to main.lua. 498 00:25:48,240 --> 00:25:49,990 We're creating our map here. 499 00:25:49,990 --> 00:25:52,410 We're drawing our map here. 500 00:25:52,410 --> 00:25:53,710 And we have this text. 501 00:25:53,710 --> 00:25:56,030 I'm going to get rid of this text right here, actually. 502 00:25:56,030 --> 00:25:58,960 I'm going to go ahead and save that, run this, make sure it works. 503 00:25:58,960 --> 00:26:00,890 And then I have an end error in main.lua, 504 00:26:00,890 --> 00:26:03,480 so I'm going to go back to map line 33, make 505 00:26:03,480 --> 00:26:06,270 sure this is working appropriately. 506 00:26:06,270 --> 00:26:08,430 So self.tiles-- oh, OK. 507 00:26:08,430 --> 00:26:10,830 I made a mistake right here. 508 00:26:10,830 --> 00:26:13,980 This needs to be equals tile, like so. 509 00:26:13,980 --> 00:26:14,820 OK, perfect. 510 00:26:14,820 --> 00:26:17,490 Let's go ahead and rerun that one more time. 511 00:26:17,490 --> 00:26:18,980 And then I have another issue here. 512 00:26:18,980 --> 00:26:20,280 So bad argument to draw. 513 00:26:20,280 --> 00:26:22,830 Quad expected got nil, map 46. 514 00:26:22,830 --> 00:26:27,707 So that just means that it found a nil object 515 00:26:27,707 --> 00:26:29,790 where it should have expected a quad, so that just 516 00:26:29,790 --> 00:26:31,650 means I have a slight little bug here. 517 00:26:31,650 --> 00:26:35,940 So self.tileSprites, let's just make sure that we're actually getting that. 518 00:26:35,940 --> 00:26:40,560 So self.spriteSheet, self.tileWidth self.tileHeight. 519 00:26:40,560 --> 00:26:41,810 I think I found the issue. 520 00:26:41,810 --> 00:26:45,072 It looks like I forgot to increment the sheet counter inside of util.lua, 521 00:26:45,072 --> 00:26:47,280 the generateQuads function, so I'm just going to say, 522 00:26:47,280 --> 00:26:50,580 sheetCounter equals sheetCounter plus 1. 523 00:26:50,580 --> 00:26:52,330 So essentially, it was never incrementing. 524 00:26:52,330 --> 00:26:57,690 So all I was doing was resetting the first quad to whatever 525 00:26:57,690 --> 00:26:59,410 the next quad was in the loop here. 526 00:26:59,410 --> 00:27:02,160 So there's only one quad in the sheet, ever. 527 00:27:02,160 --> 00:27:03,660 So I'm going to go back to main.lua. 528 00:27:03,660 --> 00:27:05,743 Let's try running this one more time. 529 00:27:05,743 --> 00:27:08,160 And we do indeed have it working, so that feels very good. 530 00:27:08,160 --> 00:27:09,780 So apologies for that. 531 00:27:09,780 --> 00:27:13,110 But yeah, there is the sort of tile level up and running. 532 00:27:13,110 --> 00:27:15,270 And it works just like we saw in the screenshot. 533 00:27:15,270 --> 00:27:18,000 That was a lot of work for Mari0, and that's 534 00:27:18,000 --> 00:27:22,105 just there's a lot of scaffolding required to actually get this working. 535 00:27:22,105 --> 00:27:24,730 The next few were going to be a little bit on the shorter side, 536 00:27:24,730 --> 00:27:26,910 so stick with me soon for Mario 1, where we actually 537 00:27:26,910 --> 00:27:29,940 get our map scrolling, which is a very important part of sort 538 00:27:29,940 --> 00:27:32,490 of side-scrolling platformers, by the very name of it. 539 00:27:32,490 --> 00:27:35,480 So see you soon in Mario 1. 540 00:27:35,480 --> 00:27:36,417