字幕列表 影片播放 列印英文字幕 COLTON OGDEN: All right, hello world. This is-- oop, you're a little cut off there. This CS50 on Twitch. My name is Colton Ogden. I'm joined briefly by-- DAVID MALAN: David Malan. Nice to see everyone again. COLTON OGDEN: Today we're doing-- well, first of all I should say happy Thanksgiving, happy late Thanksgiving. Hope you enjoyed your holiday if you were celebrating Thanksgiving here in the US or abroad. We had a bit of a weekend here but we're back. We're streaming today. I just came back. I'm a little tired. I don't know about you. Did you enjoy your Thanksgiving? DAVID MALAN: I'm very rested, ready to start the day. COLTON OGDEN: Ready to start the-- you have the hackathon this week. DAVID MALAN: Indeed. COLTON OGDEN: CS50 Hackathon. DAVID MALAN: CS50's all night hackathon, 7:00 PM to 7:00 AM later this week on campus. COLTON OGDEN: Yeah. So that'll be a good time, an all nighter with IHOP and a bunch of other stuff. Today on Twitch what we're going to do is a from scratch implementation of Space Invaders. Did you play Space Invaders when you were-- DAVID MALAN: I did. When I a kid this was the state of the art. Space Invaders probably came out a few years before I really got into games, but this was high tech. COLTON OGDEN: Yeah, this was-- oh, and thanks Jumpjump123 and Lifedevourer22 and DZsniper. I saw that you guys followed before the stream began, so thank you very much. But yeah, so we're going to do a from scratch using Love and Lua, just kind of like how we did Snake and how we did Memory Game, and what was the-- we did one last one that I can't remember. Having a brain fart. But yeah, so I'm going to jump to my laptop. DAVID MALAN: I'm going to jump out, but I'm going to hop into chat later and stay in touch. COLTON OGDEN: Cool, cool. Thanks a lot. DAVID MALAN: Good too see everyone. COLTON OGDEN: All right. Oh, and this is funny. You'll like this, by the way, before you leave. I made the mistake, and everyone else from Twitch can enjoy this. I'm wearing a green shirt. So I am wearing an invisible shirt today. DAVID MALAN: Oh, this is great. So we could actually see things through you. COLTON OGDEN: Yeah, exactly. A little bit less screen coverage of me, more of the screen. DAVID MALAN: Such a professional. OK, on that note. COLTON OGDEN: But yeah, that was a funny thing I realized when I was coming in here to set the green screen up. Let me just say hello to everybody before we actually begin. I know we had quite a few people in the chat prior to everything starting up. So, [INAUDIBLE],, Bavich_knight, Forcelnight, Fatma, Brenda, Realcuriousqe, thank everybody who-- all the regulars I see are here. Nishthegamer, hello! Bavich_knight, hello! Rajeshcanonrk, I think I have seen that name before. Correct me if I'm wrong. Good to see you. Cloudxyzc definitely has been here before, hello. Nuwanda3333 is [INAUDIBLE] and Goal1. So thanks everybody who is already here. So yeah, Space Invaders! This is going to be fun. So, Space Invaders we actually taught in the games course that I teach, GD50. It was a project that we implemented. We ended up scrapping the project idea from the course for the second iteration of it just because it was a bit too much work. But it's a great game because it's similar in some ways to Breakout which we teach just prior to that, because it's kind of this vertical oriented game where you play as a spaceship that shoots up towards the top of the screen. And Breakout is a game where you're a paddle that moves left to right and you bounce a ball back up to the top of the screen. So I sort of feel like the two games were sort of related in a sense. I think certainly the folks that implemented Space Invaders probably played Breakout and were inspired. And Breakout I do believe was implemented by Steve Wozniak although I'm not 100% sure on that. So let's do a quick old Wikipedia and just verify whether that's true. Breakout the game which is here-- so it's by Atari. I know that he implemented the Apple II version. I'm not 100% sure if he-- oh yes, Steve. So he did design breakout for Atari. And then he implemented it for the Apple II. And from what it says here, it was a big influence on the actual architecture of the Apple II. So gaming has a huge influence in the history of personal computers as well. Inolove19 watching from the Philippines, thank you very much for tuning in. I would like to read your comments [INAUDIBLE].. I'll try to comment as much as I can. What did I miss? I opened and David was already leaving, says Aslee. Yeah, you missed-- unfortunately he made a brief appearance and you missed it. But surely we'll get him on stream again, maybe next week even. Jebcochasin says, what are-- I apologize if I'm mispronouncing that name. It could be Yobcochasin or Yobcochosin. What are we going to do today? We're going to implement Space Invaders from scratch. So if you're unfamiliar, in prior streams we've used a framework called Love2d and a language called Lua. So you can download the framework Love by going to Love2d.org. So I'm just going to go straight to that main page. And for whatever operating system you're using you can download it, if you're Windows, Mac, Linux, or otherwise. And you can download prior versions, newer or older versions. It's a super nice, super easy to use framework that gives you a lot of abilities to do drawing and input and sound and all kinds of other stuff. So we'll be using that. I'm sort of going to be assuming that you've maybe watched the prior streams. But we'll still take it fairly slow so that folks that are tuning in for the first time can follow along without too much difficulty. But definitely if you haven't downloaded the framework yet, download it here. It's going to download an executable that you will then use to run game code. And if you go to the wiki page which is at the top right, you go to this getting started page. This getting started page will tell you, depending on what operating system you're running, how to actually run your game. So if you're on a Windows machine for example, usually the easy way would be clicking and dragging your game folder onto the executable or the shortcut. But there are other ways to do it. If you're on a Linux or a Mac machine, you can alias the location of the actual executable file and then call that. I have it set up so that in my terminal, for example-- I'm going to clear this-- I can just type love dot like that. And that will actually call the love executable and run it with dot, which is the current directory that I'm in. So if I'm in a directory that has a main dot Lua, this will work. It won't work right now because I'm not in a folder that actually has that. So by default, if you just type love or type the path to where it is on your machine and you have it actually there, you should get a box that looks something like this with a balloon-- if you're running 11.1, with a balloon and a scrolling background. So that's Love. That's sort of the basics of how to actually find it and get started with it. Thanks for this game. It's one of my favorite games, says Goal1. Yeah, it's an awesome game. I love the Breakout pset of old, says Bavich_knight. Yeah, it was a great pset. And I remember, I contributed a little bit to that pset as well. That was right when I started working for CS50 roughly. We had a laser part of that pset if you remember that, and that was the part that I contributed. Hi everyone's, says Lintzpotaguara. Hey, Lintz, thanks for joining us. Taking CS50, this is my first time watching your stream, says Jacobchaussen. Yes, thank you very much for joining. So typically what we do is, we have a bunch of people come in, people that work with CS50, myself included. And we usually implement something from scratch from the ground up or we'll take a look at something and we'll cover a broad overview of that subject. For example, Nick Wong has come on to talk about Linux commands and machine learning. And we've had Veronica talk about Python. David's come on to talk about regular expressions and going from C to Python. So we cover a bunch of stuff. I focus on game development so if you like games, yeah, game programming, tune in to my streams. And I also co-host with everybody else that comes in to sort of give a little bit of a conversational feel to the stream. And I notice the camera shot is a little bit lower, too, today so you can actually see the top of my laptop. You can see I have an Inn and Out sticker representing my home state of California. Virtual office hours, says Fatima. Yes, very much so. Hanscost, hi everyone from Italy. Thank you so much for joining us all the way from Italy-- super awesome. Every time somebody in the chat says that they're from some place abroad, someplace that's not in the United States, it's just such a cool feeling to know that everybody around the world is tuning in-- especially considering how late it probably is over there right now. Although maybe in Italy it's not so bad. I think in the Philippines, or was it somewhere else, it was like 2:00 AM or 3:00 AM typically for a lot of these streams. Curase is from Italy as well. Jakobchaussen just signed up, yeah. Thank you so much. First stream ever on Twitch, wow. We got some new people in today. This is exciting. All right, so Brea-- not Breakout. Breakout is the game that we were comparing Space Invaders to. We want to actually take a look at Space Invaders. Thank you very much, Fearblaise, for following. So, Space Invaders was I think one of the first big arcade games that came out. And this was around the era where arcade games were kind of like the games to go play. There weren't really very many home consoles. Although in 1978 you did have the Atari 2600 which was a very big home console. But just prior to this for several years there were very much not any console games. It was all arcade games. So if you wanted to play Pong for example, or other games-- typically Atari was the big contender in this era, although other companies came out. Whoever made PacMan, I forget offhand, that was a huge one. PacMan was Namco. And that was in-- oh, that was in 1980 actually. I apologize. So that was actually afterwards. But this era, the early '70s until the '80s and going into the '90s even a bit, was dominated by arcade machines. But as the '90s came about, arcades went the wayside and now arcades are very rare and almost like novelty-- not novelty. They're the opposite of novelty-- antiquated. And therefore they have this particular unique value to them. But this era was very arcade dominated. So Space Invaders, if we can get a screenshot. It looks something like this. It's a little bit hard to see. But essentially, as I sort of described earlier, you have this ship at the bottom which is this guy right here. And these things right here are defenses, barriers you can hide behind, bunkers or barricades. And your goal I guess is, you're defending Earth from an alien invasion. And that's what all these aliens up here are. So there's five rows of, it looks about-- what is that? Like 10, 11, 12-- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10-- 11 aliens per row. So 55 aliens, give or take. And they sort of go around the screen left to right in this formation. And they gradually progressed towards the player. And your goal is to shoot them. And if you take any damage from them, as by colliding with them or by getting hit by one of their laser beams, you obviously lose a life. And you get some points every time you destroy an alien. And so they just come in waves. You clear the waves. It's this loop of defending Earth in this abstract form from an alien invasion. Hello from Morocco, says Elias. c4reni-- Estonia! That's the first time I've seen Estonia, I think, in the chat. So c4reni, thank you very much. Georgia, not the state. Hey, guess what? I customized the prompt after that stream with Nick. I welcome all the new people tuning in says Bavich_knight. Oh, awesome. Yeah, yeah, that stream was super cool. Definitely watch that if you haven't already. And tomorrow Nick is coming in to talk about setting up a web server with AWS. So if you enjoyed that stream with me and Nick, definitely tune in tomorrow for that. That's going to be super cool. Fearblaise, hello from Estonia. Hello, Colton, is there a vacant job in the CS50 team, says Gaston? Unfortunately there is not per what we've talked about before in the stream, Gaston. So currently there is not. If there is one, there will probably be a notification on the CS50.harvard.edu website. Jacobchaussen, thank you so much for following, tuning in and following today. Game history lesson, yes, a little bit, although my dates sometimes are a little bit skewed because I don't study it too reliably. But certainly that rough-- I get a rough sense of it, you know, a rough history. Bavich_knight, Unix prompt customization, I was patiently waiting for that in Linux basic stream. I do remember that. I do remember that, sorry. That stream was heavily dominated by awesome tangents. But yeah, there were a lot of cool paths we went on. But sometimes it took us a while to get to some of the things that we wanted to talk about. Redhat236, this is really cool for me. Thanks, Redhat, I appreciate it. And Isotv, finally CS50 time again. Yes, the holiday break was long. The winter break is going to be kind of long as well. But we have a couple weeks of streams set up for all of you, so definitely tune in for that. So, Space Invaders. So, we know what Space Invaders is. So now our goal is to figure out how we want to implement it. I'm way too zoomed in right now. So, the way that we did this in the games course-- and this is completely from scratch. I'm not using any source code to reference or anything. We're just going to completely implement this from scratch. So if you have ideas as we're implementing this, definitely chime in with them. We can collaboratively do this. But I'm not using any sort of backup code. We're doing it completely from scratch. But I have a sense of the things that we'll need to do, the pieces that we'll need to integrate in order to make it work. The first thing that we need to take into consideration is the fact that this game is designed to be a vertical game. So the games that we have looked at so far are horizontal. And we've been implementing them with a 16 by 9 aspect ratio more or less. So today what we're going to do is we're going to actually implement our first vertically oriented game. And games now are not typically vertically oriented because that would be-- it's not conducive to modern screens. The typical modern screen is a 16 by 9 raster, meaning that the width to the height ratio of the pixels is approximately 16 by 9, 16 to nine. So your typical 1920 by 1080 or 1280 by 720p, or many 4k resolutions or the like, are typically 16 by 9. And you can get ultra wide monitors, which I believe is 21 by 9. And those are the really long width monitors, the opposite of what we're going to be implementing today. But the game that we want to do today, since the nature of the game is vertical, just like Breakout is for example, we're probably going to want to do a resolution that's more catered to this sort of gameplay. And let's see if they actually have the resolution, which they do. So we can see here on Wikipedia if we go over here, the specs are that the display was a Fujitsu MB14241, which was the video display controller that they actually had in the arcade machine, the physical hardware. And it output at a 224 by 256 raster. And what this means is, it's going width by height. So normally nowadays resolutions are wider than they are taller. But this was actually taller than it was wider, which is why the second number there is larger-- 224 by 256. And so that's what we're going to do. We're going to actually use the resolution native to this game, the base resolution, and we're going to implement that for our game now. But we're going to put it in a window that's much larger than that so that it does-- because if it were on our screen, 224 by 256 pixels, it would be pretty tiny. It'd be hard to see what's going on. So we're going to do what we did in the last stream for tic-tac-toe, where we actually create a virtual raster using that library that I showed before, the push library. We're going to say that that raster is to 224 by 256 pixels. And then we're going to put it into a window that's about four times larger than that-- actually probably three times larger than that so it fits on the screen. And what it's going to do is, the window is going to be something like approximately 700 by 800 pixels. And then it's going to stretch that to 224 by 256 raster. And basically it's going to have the effect of growing all the sprites and everything in the game by three times its size. And that's how we can achieve the sort of retro look while also getting it to fit on the screen and be playable and not super tiny. OK, let's see. Apparently Tetris was made in 1984, so after Space Invaders and PacMan, says Estley. Yes, yes, roughly around the beginning of the NES era. And I believe it was super popular for the Gameboy because they actually bundled Tetris and the Gameboy together. And so that's why Tetris is one of the best selling games of all time, is because they take into consideration that bundling and the Gameboy was just ridiculously high selling console, handheld console. And so that's sort of the history of Tetris and why it also blew up. It was a perfect game, also, for mobile, just given the fact of how addicting it was and how simple was and how on the go it was, right? Only game I played was Tetris. I could not handle being eaten and getting chased, so that's Forsunlight. Assuming that's a reference to PacMan. Yes, and PacMan is a game I would like to very much implement on stream as well another day in the future. So we'll take a look at that. I'm going to set up a project. So I'm going to go into my Streams folder. I'm going to create; a new folder. So whatever operating system you're on, this process will look very similar. I'm going to call this Space-invaders. I'm going to open that up in VS Code, as by just on Mac clicking and dragging it over to VS code which I have on my doc. And what that will do, because I clicked and dragged the folder over, it'll actually open it up in my editor as a project, not just an individual text file. And if you're unfamiliar with VS Code, this is Visual Studio Code. It's a pseudo IDE, so it's a text editor but it has some IDE features like debugging and a really good plugin system. And it's very heavily developed. It's sort of the contender with Adam. And Adam is also a very good text editor. Sublime is also another very good text editor. Those are sort of the three big ones. So if you're looking for a text editor, VS Code and Adam are free. Sublime Text is sort of free in that it'll bug you with a prompt over and over again if you haven't bought it to register but you don't technically have to do so. I prefer vS code, so that's what I'm going to be using today. It wants to install a helper tool, so I'm going to deny that. And also a great thing about VS Code which I'll demonstrate is that it gives me the ability with a certain plugin to just hit Command L, and that'll load the game into-- command L for Love, I guess. It has a integration with Love such that it will load my project in Love without me having to go, say, to the terminal and type Love dot every time, or go to my desktop and click and drag the folder over to the executable, which is what you would have to do in Windows if you don't have a similar setup set up. So, I'm going to create a new file on my project called main.lua. Folks that have been to the stream already know this process. But new folks, this is the entry point for a Love 2D game. I'm going to title it with a comment block. I'm going to give it an author. I'm not the actual author of the original game Space Invaders but I'm the author of this clone. So this is a comment block in Lua. So just dash dash square bracket square bracket, just like the slash star in C or in JavaScript. And then I'm going to just create a few functions. I'm going to command b to get rid of my sidebar there. So these functions are functions that are part of the Love framework. They are called by the Love framework. Every frame, or in the case of Love.load, just once at the beginning of your application. And these are functions that almost every single Love project will have. You don't technically need a Love.load load definition in your game. For a lot of games-- well, some games-- you don't necessarily need love.update, for example the tic-tac-toe game we implemented last week. You can do it with Love.update, or you can do it with Love.keypressed which is another function that we will probably need. So Love.keypressed takes a key. And so these are kind of like the base functions that are part of the Love 2D framework. These are functions that Love 2D will just sort of call. It'll look for these functions by name. So it'll see whether I've implemented a Love.load function, whether I've implemented a Love.update function. And if I've implemented them it'll call them in a function called Love.run, which is actually hidden but you can override it yourself. And Love.run basically is just a function that gets executed as the main game loop of a Love 2D the application. Ba ba ba ba, Bavich_knight says, Vim. Vim is another text editor that you can definitely use if you're comfier with the command line. It's got a bit more of a learning curve than Adam or Visual Studio Code. But if you're comfy with it you can do a lot of really cool things with it. People upload what are called their Vim RCs to GitHub which allow you to customize how it behaves, sort of the shortcuts and the appearance of it. I'm not a Vim user but David, for example, is a Vim user. So if you're into that aesthetic and into a more command line oriented approach to development, definitely check Vim. Vim is a great text editor. So we have the main functions for our game. The first thing that we talked about was the actual setting up the resolution for our game. So that's what I want to do. I want to create a virtual width and a virtual height. And actually, because we're going to be doing things a little bit more-- this is going to be a little bit larger of a game. And I should preface this whole thing by saying this might take more than one stream because this game is somewhat complicated. Because of that, because it's going to be fairly complicated, I'm going to be a little bit smarter about how I organize my code today. So rather than starting to declare all of these constants, that's why they're capitalized. They're going to be values that don't change. Rather than declare all of these constants at the top of the main .lua file, which is sort of bad practice if you just clump all your code together in the main .lua file, I want to create a separate file called constants.lua. And this will be where I can, as this name implies, put all of my constants, all of my variables that will not change. And there isn't technically a constant semantic in Lua because Lua doesn't enforce constant variable assignment. But by sort of saying, oh, these variables are capitalized and these underscores, as is the common notation in most programming languages, we can, as programmers, sort of mentally declare and associate these variable names with the idea of those variables being immutable, meaning they shouldn't be changed. And that's sort of what you should do as a programmer when you're using a dynamic language and you want to use these constructs that are more common in other programming languages. You want to use proper variable naming, right? So virtual width and virtual height-- so the virtual width and virtual height are going to be the height and width of the arcade machine as it was developed originally, right? So the width of the arcade machine was 224 and the height was 256. So I'm going to say the virtual width should be to 224 and the virtual height should be 256. As you can see, that means the width will be smaller than the height, which means we'll have a more vertical looking window when we actually load the game, when we actually render the game. I'm going to take these declarations. I'm going to remove them from main.lua and I'm going to put them in my constants.lua. And these variables, because I haven't specified what's called local scoping on them by writing that, this means that they'll be accessible within any source file, any main file, or any Lua file, that requires that source file. So for example, if I say require constants, this will load all of the global variables from the constants.lua file into my main .lua file. So it essentially has the same effect as me copying these and putting them into here, effectively. SmartScreen is prohibiting the execution of the editor installer. Also there is only a Vista plus version, says Gaston119. Are you referring to VS Code? And then Bavich_knight says, more modularized. Yes, more modularized. Modularization is a very good virtue in programming generally. Making sort of building blocks of your code and building blocks of your project in terms of source code files, tends to make things easier to debug, makes it easier for other people to work on separate pieces at a time. So you want to typically do that. For small projects like we've done with tic-tac-toe and with even Snake and the memory card game, those are small enough such that you don't have to worry so much about these ideas. But it's good practice. Anything that's larger than 100 or 200 lines of code, generally you want to think about breaking that up into separate modules, a module being a piece of code in a text file. But it can also mean a class or a function. It just means building blocks that you can assemble and use interchangeably. OK, so we've done that. So we've put our constants into a constants.lua file. Another thing that I want to do is, I want to actually make a window within a window height. So we have a raster. We have a-- basically, you can think of drawing to a texture almost, that we're going to expand to fit some window size, an actual window on our desktop. Because our window, we don't want to actually render a native 224 by 256 window onto our desktop because that'll be super small. We won't be able to play it. I want to declare a separate window width and a separate window height. And I don't really want to think too hard about this. So what I'm going to do is, I'm just going to say virtual width times three. I'm going to say virtual height times times. And what this will do, is the window height and the virtual width and virtual height will always be three-- well, they won't always be, but they will at the start of our application be three times the size difference. So this will allow us to zoom in while retaining the aspect ratio that we have established. Another thing we have to do is, I want to use that library, that virtual raster library we used last for tic-tac-toe, which was a couple weeks back actually, called Push. And so if you're unfamiliar, the Push library, you can find it easily on GitHub. So Push library Love 2d, it's GitHub.com/ulydev/push. I happen to already have it on my machine but you'll see an image that looks like this demonstrating how they're rotating that high text. And it's staying pixilated. It's not interpolating. It's actually being rendered at this 50 by 50 pixel resolution while being zoomed in so that we can actually see it like it's a retro zoomed in look on our machine, right? I'm going to use the version that I already have on my machine, so it's in my lib folder. I'm actually going to copy this lib folder from the tic-tac-toe project. So I have a lib folder with a push.lua in it. If you download this library you'll get a push.lua. That's the file that you want from that library. I'm going to copy that lib folder over into my Space Invaders folder right now. So now in my Space Invaders project, if I go back to here for example, I see I have a lib folder with push.lua. In my main I can say, at the very top, push equals require push-- sorry, lib/push. And remember, when you're requiring something in lua you don't need to say .lua. It'll infer that you're referring to .lua. So you can just say, lib/push or constants, just the string constants, and that will have the same effect as if you were saying require constants.lua. All right, now what I can do in my Love.load function-- remember, this function happens one time at the very beginning of the applications running. I can say push colon setup screen. So, colon just means on the push object, which we're initializing here, so requires returning essentially a table. But push, a table that has a function in it called setup screen. And I'm going to say, push colon setup screen virtual width, I'm going to command b to get rid of that virtual width, virtual height, window width, window height. And remember, these variables were in constants.lua. But because I required it, they're actually visible within my main.lua. I'm going to say full screen is equal to false, Vsync is equal to true, and resizeable is equal to true. So Vsync just means it'll sync to your monitor so you won't get screen tearing if there's any scrolling. It's not really something that we'll have to worry about here. Full screen just means we don't want it to be full screen because that'll be a little bit weird looking. And then resizeable just means that if I want to resize it during the games running, I actually can and it'll resize accordingly. And if I go into my update function-- sorry, not my update function, in my draw function I can say, push start push finish. And whatever happens between start and finish on the push library will get rendered at this fake resolution. If we decided to draw something without it being within the push start and push finish, it just draw to the native window, the window that we've declared was three times the size of virtual width and virtual height. So anything that we want to draw at this retro look, this 224 by 256 resolution, almost like we're drawing to a virtual arcade machine that we're then rendering to a proper window, we want to do it within the push start and push finish function calls. And we can test this by saying love.graphics.print hello-- actually, we'll just say Space Invaders. I think we've done hello world already before. And if everything goes according to plan and if I run this, I have a syntax error on line 13 which is-- oh, I accidentally put a period there instead of a comma. Don't do that. If I run it, now we see indeed our game window is vertically oriented and taking up the majority of my screen actually. Hopefully it's not-- I think it actually is just ending at the bottom of the screen. So it be OK. That should be just the right size. I can't tell if it's super visible on the stream itself but the text for Space Invaders is actually blurry. It also doesn't have a window title and I can't quit with escape. So those are a few things that I probably want to add to it. If you notice, if you're running anything with this push library and you notice that the text is blurry or anything that you draw is blurry, that's because you have filtering enabled on all of your textures. So love.graphics.setdefaultfilter nearest and nearest will fix that. So that'll set the filter of anything that you render to the screen, or anything that you load and then render to the screen after this function call, to nearest neighbor filtering meaning that it will be pixilated. Jebcocheson, if you are using VS Code make sure that you have the-- let's see, where is it? This is the extensions. The Love 2D support by Pixelbite Studios will allow you to command-L or Alt-L if you're on a Windows machine. But you have to have the Love binary in a specific place. Wherever you do have it, if you click on the cog menu here you can actually-- oh, the cog menu doesn't work there. If you go to the settings, which is-- I don't think you can access the settings from here. It doesn't look you can. So you actually have to go to your preferences, settings, and then go to your extensions down here. It's a little bit because I'm running at seve-- my computer is at 720p so everything is a little bit zoomed in. But eventually you'll see Love 2D. Oops. And the Settings menu is also super jammed full of stuff. Let me see, where is it? CSS-- oh, right here, Love 2D config. You'll see that there is a-- ba ba ba ba-- a default location that it will expect it in. If you just have it in your applications folder on a Mac it should work normally. If you're on a Windows, it sort of expects that Love exists at C:/programfiles/love/love.exe. So that'll be where, I think by default if you just get the installer for Windows, it'll install it there. And on a Mac, just make sure that it's in your applications folder. So applications and then Love.app whatever it's named by default. And then it'll know that it's there and it'll work. But yeah, that's how you get it to work in Visual Studio-- in VS code, sorry. If you're on a PC, you can do the same thing in VS Code. But if you want it to run at the command line it's a little bit complicated with Power Shell. You can click and drag your folder over to the shortcut on your desktop but it's a little clumsy. So I'd probably recommend getting this configured, downloading Love, installing it through the installer, and then using the alt L shortcut with the Pixelbite extension. So definitely take a look at that. Now let me zoom back in a little bit just so that everything is a little bit clearer on the stream. Actually, is this still clear, still relatively clear? Because it'd be a little bit easier to fit code onto the screen, I think. So let me know if that's still pretty visible. That bottom left gear stuff on VS Code is Settings. Right-- oh, right here, yep. The code right here-- or, the settings right here. But yeah, that's how you do it on Windows and Mac. Awesome, Jacobchaussen, if I'm pronouncing that right. I apologize if I'm not. Glad you got it working. Seafloorrenny says, it's OK. OK, but basically now I can hit Command L. And now it's more pixilated. On my preview of the stream it looks the same as it did when I ran it without filtering, maybe a little bit crisper. I'm looking at a somewhat small preview of it. But you should have seen a difference with the default filter function call, so definitely set that if you haven't already. A couple of other things that we can do, Love.window.settitle to Space Invaders. And then, in love.keypressed I'm going to say, if key is equal to escape then love.event.quit. So love.window.settitle as its name says just sets the titles, so the title bar of your application so when you run it it doesn't say untitled, which just looks a little bit sloppy. And then this live.keypressed function will fire every time you press any key on your keyboard. So you can define the behavior that you want Love to sort of determine when you press a key. You can check which key it is but he's using an If statement. So if the key is equal to the string escape-- all keys are given a string value, so for example any of the letters, the individual letters or numbers on your key, would register as that particular key. Space, enter, return, tab, alt, option-- all these keys have a string value that you can call. I'm going to say if key is equal to escape, then love.event.quit. And that will have the effect of, when I run the game I can press escape. Also notice that the top of the window has the Space Invaders. Asley says, still looks pretty blurry only to me. Perhaps a different font would be less blurry. Yeah, so what you're seeing is the aliasing in the default font. So it is blurry and in a sense. It's not filtered blurry, so it doesn't have the actual fined tuned-- or that, not fine tuned, but it doesn't have a higher resolution blurriness to it. What you're seeing is the individual pixels are just different colors. But you are seeing individual pixels now at a zoomed in level. And yes, that's because this font does have different color pixels to give a smoother appearance, or more rounded appearance, when you're actually viewing it at a native resolution on your monitor. So it's not conducive to zooming in but it is more appealing to look at when you're viewing it at a smaller level. So yes, we do want a different font. And we can do that, as we did last week, by going to dafont.com. So we go to dafont.com and I go to pixel fonts. You can browse a lot of these in pixel/bitmapfonts section right here. You can browse all kinds of cool different fonts that are all pretty retro looking, and all their different licenses are here on the right. So you can see some of them are 100% free, for example. Some of them are free for personal use. You can't use it in a commercial project but you can use it for your own projects and not worry about getting into trouble. Some of them are public domain. Some of them are-- all of these are actually pretty generous. Some of them are not free and you have to actually pay for a license. This one here looks like a retro sort of Western font which is actually pretty cool. So if were to make like a pixilated Red Dead Redemption or something, that would look nice. I'm going to choose a font that makes it look kind of like NES font, I think. Something like Press Start To P is pretty good-- press start to play. Let's see, what's a good font? So, Space Invaders has sort of this-- well, I guess that era, the Atari arcade era, had a sort of vectorized look. So the fonts were all kind of angular looking. So something like this would actually be pretty-- or not this, not Visitor but-- well, I guess Visitor. Visitor would be pretty appropriate. It's 10 pixels. I kind of want an eight pixel font. 04B03 is actually a pretty good font. I kind of like that one. That's one I think I actually used a lot in the games course, because it had a nice look and it's eight pixels. Pixel Aerial 11-- so on the last one, I thought I saw on an NES looking one. I think I might want to use that one. So, which one was it? Was it this one? No, this is pixel mix, free for personal use. It was Minecraftia, very appropriate, eight pixels. Retro Computer, Press Start to Play-- we'll do this one. So the Press Start to Play one, I think that one's good. So I'm going to grab that one. Oh, it looks like I already downloaded it. So OK, that's good. I might have actually used it last time in the last stream for the tic-tac-toe, actually. Fonts, 8-bit-- oh, 8-bit and Press Start to Play. Yeah, I think I did use it last time. So, in my Space Invaders folder I'm going to create a Fonts folder. I'm going to then paste that font in there, the Pressstart.ttf. You'll get a ttf file from dafont.com typically when you download a font. And so that's just the file that you need to have to use that font. And then if I go into my project, I can say-- after I do the filter, set the filter, because if you do it before the filter your font will be blurry, I can say gfonts-- or, I'll just say gfont equals love.graphics.newfont and then fonts/pressstart.ttf. And then I can say, love.graphics.setfont to gfont. So the lowercase g is another semantic sort of token I'm adding to the symbol of the variable to basically tell me that that's a global variable. I should know that whenever I see gfont, it's accessible anywhere in my application. And this is to help me from clobbering different variables and using global variables unintentionally, or using local variables as global variables unintentionally. So whenever you use a variable that should be accessible throughout the whole application, try in a dynamic language like Lua, typically try to use something to differentiate it, to make it clear that it's global. So that lowercase g is intended for that purpose. Whoa, this stream just reset for some reason-- or the chat reset. I accidentally went backwards. And because I went backwards, I got rid of the chat in the monitor that I'm using to the side. So I'm going to use my other monitor. Ba, ba, ba-- Space Invader, the editor fonts are fine. Out of interest, how much do you prepare in advance for these streams and how much of this syntax do you know by heart, says Isotv. So, for a lot of these games streams-- so, it depends on the stream. So, for the Unity one I did a lot of prep in advance to make sure that I understood what I was doing and all the different corner cases that I could run into. And it turns out I still ran into a few. Thankfully Andre and Irene are typical regulars in the chat and they kindly helped me out with a couple of things I stumbled upon unexpectedly. But for a lot of the Love and Love 2D stuff, I actually don't do much prep in advance because I've done a lot of this stuff before. So Space Invaders, for example, I programmed before in the spring for the games course that I taught at the Harvard Extension School. And for tic-tac-toe and for memory game and for Snake, they're simple enough games that don't really require a whole lot of thinking in advance. Now, there were some situations where I did stumble upon an algorithm or a piece of the algorithm that I had to think about a little bit on stream. But I think that also there is a double edged sword in prepping and not prepping. So, by not prepping you do run into those cases where I don't have the code in front of me so I don't necessarily know what I'm going to run into and I don't necessarily know in advance the algorithm and I don't have something as a crutch to fall back on. But if I have a strict script to follow, then I have no real opportunity to go off on a tangent or to explore a different way of doing things or to take suggestions for the chat or to change fundamentally how the game is working because if I do that, then the game has strayed potentially away from the original goal of the stream. And that won't work super well live and be a super conversational system. So the intention for these streams is to be much more conversational and casual. And so for a lot of these, there is nothing really prepped in advance per se, aside from some research and for the Unity stuff I typically tend to do a bit more prep. Now, for certain streams, like David does a lot of prep in advance and we have more of an outline that we follow a little bit more rigorously. And for some of the other streams we've done it's been that way. But mine are a bit looser. So, thank you for the question. Yeah, because I'm getting the same error on line seven saying there's unexpected syntax near the parentheses. I only wrote commas. Unexpected symbol near traceback in the function require. Function [INAUDIBLE] function XP call main net seven. Post your code on pastebin, I'll take a look, says Twohourstrike. Line seven, push setup screen-- so make sure that setup screen is getting a capital S for the second S. Virtual [INAUDIBLE],, virtual width, window width, window height of-- you do have a redundant comma there in your function calls. So at the end of window height you do have an extra comma. So that's what Twohourstrike said. Search Space Invaders on dafont, says Karalse. Sure, let's do that. I actually didn't think to do that. So, with Space Invaders-- oh, that's cool. They have a font that's actually made out of aliens. So that's interesting. That'd be an interesting way of actually making a Space Invaders game, would be only with fonts. It doesn't look like it would be necessarily possible because it doesn't like they give you the spaceship. I guess you could construe the third alien as a spaceship, in a sense. But yeah, that's a cool font. Thanks for tossing that in. You'd use that font instead of sprites, Karalse. Yeah, in a sense we could. But the problem with that is that then we have to worry about drawing these sprites, the text objects, in sort of weird-- first of all, we'd have to call love.graphics.print. And we would have to know which character maps to which sprite. So we'd have to say-- it's probably something like 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. So we'd have to say, love.grapics.print, the string 0, 1, 2, 3, 4, 5, 6, 7, 8 or 9. And then we have to give it an x or y-coordinate in much the same way that we would have to do with a actual graphic object. But no, it could work. And we could get coloring through it. But I don't think this is an approach that you would typically see outside of a console game. So if you were, say, implementing this at the CLI like this, then it would kind of make sense-- or I guess a-- well, I guess maybe it wouldn't work necessarily for a typical shell. But you could emulate a shell in Love 2D as by something like a roguelike game. And rogue like will do this a lot, where for example-- let's go, if we go to Images, roguelike. So the typical roguelike is an ASCII game. Your typical classic roguelike looks something like this, where all of the game is actually rendered using text, using ASCII text. And nowadays, roguelikes can be implemented using Unicode text as well, and that's a much more flexible option because then you're not limited to just-- how many characters is in ASCII? Is it 127? I don't remember offhand It's either 127 or 256, one of those two. Or actually, it might be less. It might be 7-bit. I don't remember offhand. But you can only render a very limited set of characters in ASCII compared to what you could do with a Unicode text file, for example. But yeah, you could do something like this, run this and make a virtual shell and then have those guys move around. It's just a little bit strange. I don't know if I would typically-- you could technically do it, but it's kind of a roundabout way of getting that behavior. It's much better to actually use sprites, I think. I would imagine that would be a much better way to do it. What is an EOF error? Says, EOF is expected near end. It means that it's finding the End Of File without an end statement, I think. You are not ending your function or an if statement with the end keyword. So notice that for every if statement, like we only have one if statement now. But notice that I have an end keyword that ends this if block. So every if block needs to have an end at the end of it, to sort of be the start in the end syntax. And whatever was within that is the body of code that gets executed if this if condition is true, right? And every function similarly has an end keyword to tell Lua, this is the start of the function and then the end means that it is the end of the function. If you don't have a matching start and end-- 128 ASCII says. OK, so it's a sort of limited unsigned-- what would that be? Yeah, 7-bit-- 7-bit unsigned. If you don't have an end key for every function, then that's a syntax error and you'll get an EOF issue like you tossed in the chat there. You could use that font as sprites. Those are seminars, prepped and planned, says Fatima. Yes, yeah, those are seminars. And to a degree these are kind of similar to seminars in spirit, just a little bit looser and a little bit more conversational. Raw, live coding, yes. I think it means you have redundant ends somewhere. Perhaps-- oh, and that's true too. You can get an extra end and get an error similar to that. I don't remember the syntax differences between the two issues. But yeah, one of those two is likely the situation. Mkloppenburg-- oh, and Andre, perhaps the font could be used in text. For example, instead of kills you could have an alien character or something. Yeah, that'd be cool. That'd be cool. Mkloppenburg, be late to the show and of course I won't be able to watch everything. Will watch it later on. Keep up the good stuff, Colton. Thanks, Mkloppenburg, for tuning in as always. Hope you tune in on YouTube afterwards. So kind of like curly braces, says Isotv. Yes, exactly like curly braces. Lua does not have curly braces, which is nice because I'm not a fan of curly braces. I'm also not a fan of the end keyword. I'm actually much more of a fan of the way Python does it, with enforced blocks, indentation blocks. I think that's a much cleaner way to do it. But Lua is still nice enough of a language where it's not a huge deal. But typing these end keywords can be a little bit too much. And also the function word, I just think Python does things a lot better in a lot of ways, so function being deaf and not having ends. You do need colons in Python, which is something, an extra bit of syntax. But it's a very small thing so it's not a huge deal. And then end would be like a semicolon? Yeah, kind of. The end is a little bit more like a closing curly bracket, you could think of. OK, so we've been at it for a little while. And now that I reload the game we see that we do have Space Invaders being rendered. It's a bit large. It shouldn't be that large. And that's because by default Love assumes that we want our font to be, I believe, 16 pixels. So if I just specify eight pixels when I actually create the font in the love.graphics.newfont function call, it will make it much smaller. So now it is indeed an eight pixel size font. Is that eight pixels? Yes, I do believe that is eight pixels. So, similar to how we had it before-- I apologize if I keep hitting my mic. I don't think I am. I think I'm hitting my jacket. But now the font object is smaller, it's not supermassive, and we can get on with the actual implementation of the game, of the moving around and the aliens and whatnot. So the way that I liked to do it for the games course that I taught was, I kind of liked the randomization approach to it. And so there is a sprite generator-- and I don't remember offhand, actually, which website it was. It's not a CSS sprite generator. It's like a retro sprite generator. Had to look for it. I don't remember the exact URL offhand. Is it this one, the Ludum Dare one? Could be this one. Sprite generator-- oh no, this is the Java one. There's a sprite generator that's an actual web application. So sprite-- pixel sprite generator-- pixel art sprite generator maybe. Is it this one? Nope, not this one. I apologize. I might have to find the URL for it. Where is it? There is a URL that actually makes aliens. Oh, I think it's a robot sprite generator, maybe, or alien sprite generator. Crap, where did it go? This is used to be like an instant top of the Google results search. I might cheat and go back to the-- so I have the source code for the actual Space Invaders that I did. I should. Dev-- it's not in summer. Crap, where was it? [INAUDIBLE] project zero-- that wasn't a project. I apologize. I'm going to find this. We're going to find this. Sprite generator, alien-- is this going to work? I hope they didn't remove it off of the website. Ba, ba, ba ba. Kind of embarrassing. They had a whole website that was-- retro pixel art sprite generator. Forsunlight saying hello, everybody. Any good sprite generators out there? So this is why prep is sometimes good. This was like a top Google search result. I have no idea where it went. Randomized sprites-- no, not ALTTP. Is it this one? Here it is. OK, so I just had to type randomized sprites. So, sprite generator wasn't good enough. But this is what it is. So, it's a website, IMG.uninhabitant.com/spritegen.html. I can toss that link in the chat, or I can at least write it in the chat. Unfortunately I'm not in the chat logged in on this account. So IMG.uninhabitant.com/spritegen.html. So you go to that link. You can generate your own sprites if you want to generate them and follow along. Do I like these ones? These ones are pretty good, right? And these aren't zoomed in, are they? Zoom level one, size 16. Do I want 16 or do I want eight? So this is cool. You can actually customize all of the parameters for the generation. So down here-- oh, and you can choose the color palette, too. And by the way, color palettes are a huge thing in sprite art. If you're not familiar, color palette is basically a limited set of colors that you can draw from to create your sprites. And this was a physical limitation of hardware back a long time ago. Nowadays, it's obviously not. You can do whatever color you want. But if you want a homogeneous, if that's-- I always don't know if it's homogeneous or homogeneous. But if you want a homogeneous/homogeneous look to your artwork, you can define a-- oh, Realcuriousqe, the video is about 30 seconds behind the chat for me. Let me type this in. It's already there. Yeah, I'm just ahead of the curve, you know? Just, that's just how we do it. But no, palettes are a limited set of colors. So you have eight colors, 16 colors, 32 colors. Generally the smaller number of colors, the more distinguishable the art is but the harder it is to make things look the way you want. But these are all very famous color palettes that you can choose from-- so NES, for example, Gameboy. Dawn Bringers is a very huge palette in the online sprite art community. So he has several different palettes of 16 and 32. We used the Dawn Bringers palette in the games course that I teach, Commodore 64 being another one. And Arnie's is another really famous one, actually. Arnies and Dawn Bringers are sort of at odds with each other, I think, so they sort of compete with each other. I like Dawn Bringers. Colors per sprite, so this is colors-- not three colors total but just three colors per sprite if you want to limit the number of colors that each individual sprite can have. It's a cool limitation. I'm going to say four colors. Background color index zero, that's fine. We're going to say the size is going to be-- do I want eight pixel aliens or 16? Chat, do we want eight pixel sprites or 16 pixel sprites? You're logged in your Google account, says Bavich_knight. Yes, I am. It always happens to me too, [INAUDIBLE] blah, blah, blah, blah. Eight, eight-- this is around the world, bits traveling. Fatima says yes, eight. So we've got three people saying eight. Whole time it was my internet problem. It was all in the streams, says Bavich_knight. Yeah, the stream will take about-- it depends on how far away you are, I guess. Here directly in this room it's about two seconds. Between here and another place kind of locally, or within the states, it's typically seven or eight seconds. And I imagine super far away it'll be probably closer to 15, 20 seconds. Yeah, New Zealand, 30 seconds. So we have four people saying eight pixels. So I'm thinking eight pixels probably. So I'll go ahead and make that eight pixels. And I think I actually did that for the Space Invaders game that we implemented for the spring. So I think it's apt. Let's just make sure everything looks good. No scalar, zoom level one, 16 tiles, four pixels apart-- and I think that that is-- actually, do I want spacing? No. Zero pixel spacing, I want to be able to slice these up. All right, let's generate. Whoa, and that is tiny! OK. So, if I save that image, save that in the right place. Go to streams, Space Invaders. I'm going to create a new folder here called Graphics. And then I call this aliens.png. And I just went wit whatever this first result is, and a lot of these are going to look a little bit weird probably. So Space Invaders, graphics, aliens.png. Let's open that and let's zoom in, see what we got. It's sort of like the lottery because we have-- wasn't the original Space Invaders entirely black and white, says Realcuriousqe? I think it was. I think it-- well, not black and white as much as it was, I think, black, white, and green? Let's see. Space Invaders-- black, white, and green. So yeah, essentially, I guess, 2-bit color? We're not going to go quite that low, I think. We're still going to add-- make it look visually cool and have different sprites. But yeah, I think if we were going to adhere 100% strictly to the game and its limitations, we would use just black and white sprites. These are looking OK. Some of them are a bit-- there's not a whole lot of detail, is the only problem. They don't look like aliens. We could make some of these work. Like this one, for example, right here, this one could work, right? And this one, actually this one? This one looks almost exactly like the Space Invaders alien. This one looks really good. No read, Colton, I have to leave. Will catch up on YouTube [INAUDIBLE] the day [INAUDIBLE].. OK, Fatima. See you later, thanks for joining. The green was a filter on the screen though, as far as I know. Oh yeah, that would make sense actually, because it was within a limited portion of the screen down here. That actually makes perfect sense. So yeah, in that case then it was 1-bit color. Yeah, they don't look like anything. Jacob saying, let's do 16. Yeah, probably. I'm thinking that too, probably. There's just not a whole lot to work with here, so 16 probably makes sense, right? Sadly. I mean, we could make it work. There's a few of them that do look OK, like this one looks OK. This one looks like it actually looks like a UFO. This one looks OK. Some of these look like ships. This one looks like a sideways UFO. But I think it would get much better results. So, tell you what. Let's generate another one. Let's go back to the website. Let's go over here. Let's go and say size 16. Now, the only problem with this is because we're running at such a low resolution, these 16 size sprites are going to bee pretty big. So what we could do is, we could render the game at-- could you take a screenshot of the font and use that as a PNG? Oh, we definitely could. We definitely could. If we wanted to do that way we absolutely could. I personally think it'll look a lot more fun if we have a different sprites, and we can do some cool stuff with randomizing the aliens this way and it will look a little bit less boring. But yeah, you could absolutely do that if you wanted to. The only problem is, then you're going to-- the screenshots are going to be, because I'm taking a screenshot on my Mac, which is a retina display-- well, right now it's a 720. It's going to be super, super big. It's going to probably be like 200 pixels per alien-- 200? Well, maybe not 200. Depends on how small I create it. I can create it pretty small but it's not going to be an even size. So at the very least, I'll have to go into Asprite and then downsize it. And by downsizing it, I might artifact it-- not artifact it, but I might distort the sprite in a way that it doesn't retain its actual crisp original look. So typically that can be kind of tricky to work with. But yeah, you could get it to work. And with a little bit of touching up manually you could make it work. I think it's a bit too much work, to be honest. I think it'd be easier to find just the actual original Space Invaders sprites, probably. Let's try this. Let's save image. This is a little bit more interesting, too. We can actually create our own aliens and figure out which ones we like. What I like to do is randomize it. So I get something like this and then just make them all random. So that way we don't know what to expect. Aliens16.png. And so now if I open this, and I zoom in-- so, a million times better. All of these are pretty distinguishable. I mean, you do get a lot of them that look kind of samey. Like, they all have sort of this mothership look to them, this sort of weird hive look. But they all look pretty unique and interesting now, compared to the eight pixel ones, right? 16 is too detailed. You could even make an image out of the font by drawing it on the screen and then using love.image.newimage, new image data. Yes. Yeah, yeah, yeah, you could do that, absolutely-- draw effectively a pixel or texture data on the screen and then make an image out of it, yep. That's actually a great way to do your own custom randomized sprite generation, by drawing to a bit of image data the extra individual pixels and then creating a sprite from it that you can then draw in otherwhere-- in other places. Otherwhere being a new word that I just made up. But do we think it's too detailed? Jacobchaussen says it's a little bit too detailed. I hate to spend a little bit too much time on this. But this is also kind of the fun part of game development, is choosing the assets and laying the artistic, the creative design out. So it's not something that we should I think definitely pass over altogether. Themetaleagle, hello. Hello, good to see you. Thanks for joining. But if we think that 16 is too detailed, let me know. We can maybe try 12. We'll try 12 pixels. We'll see what the in-between looks like, right? So 12 pixels, generate. I'm going to save this. And then we can vote one more time on what everybody wants. So we have aliens12 now. So we have the aliens12, 12 pixels by 12 pixels. I'm going to give everybody a chance to take a look at that. Some good ones in here, definitely. Oh, the sprite sheet Brenda tossed in the chat. Oh, yeah. So, that could work, Brenda. It would be a little bit tricky because we'd have to manually go into an image editor, determine the exact pixel coordinates, and then-- we'll cover this, but we're going to have to generate what are called quads. And if you've followed GD50 before you might be familiar with this already. But basically, a quad is a rectangular portion of an image that defines, when you draw that image, the exact portion that gets drawn to the screen. But we would have to manually go through this and figure out the quads. And if the image is larger than, say, 16 or whatever we'd have to scale it down to draw it to fit our screen well. JPguy, welcome! Good to see you. Borrow a phrase from Westworld, it doesn't look like anything to me, says Andre. It seems like a lot of work. It is a bit of work. But we talk about this in-- I did a seminar on Mario. C4renny, eight for the minion aliens and 16 for the top level boss. Is there an actual boss in the original Space Invaders? I don't remember offhand. I don't know if we'd have time to implement like a boss behavior versus the minions. That be cool feature. If we do it over multiple streams we might be able to add something like that. It doesn't look like there is a-- oh, there is right here, the mothership I guess? That'd be cool. That's a cool idea. Let's put that on the potential to do list. These look like they're-- actually, the base sprites look like they're 12 by 12? I think they're 12 by 12. So, the 12 by 12 might be the most appropriate. But yeah, see eight for the minion aliens, 16 for the top level boss. If we're going to make a top level boss I'd probably make it even larger than that, to be honest. Probably make it like, maybe 24 by 24? It's a good suggestion. That's pretty cool, actually. I personally think the 12 by 12 is pretty good. Can the chat tell me what they think, what they like? And I actually haven't watched Westworld so I'm not familiar with it but I've heard great things about it. So, just to ABC, this is the 12, this is the 16, so a bit larger and a bit more detailed. Give everybody a chance to take a look at that. And then this is the eight pixel one, which I just called aliens. So the eight is a bit small. It'd be great if you're making like a Gameboy game or something. I think that would look pretty good. Majordafat, no Westworld spoilers, please. 12 by 12, yeah, Asley says says 12 is good. Brenda says 12 by 12 looks good. Jacobchaussen says-- I'm assuming that he or she is referring to the 12 by 12. I don't like colors. They seem so saturated. I think it's because they're all so zoomed in, yeah. When we see it in the actual game I think it'll look a little bit better. Twohourstrike, I vote for 12. All right, it seems like 12-- yeah, Andre says 12. Yeah, 12 is the way to go. OK, so we got that setup. We're good. Let's go ahead and let me get rid of this. The-- oops, sorry. So, we know we want our sprite sheet then to be the sprite-- whoops, my microphones got caught on the table. We know that we want the sprite sheet to be our 12 pixel by 12 pixel one. So we're going to use that. We're going to load that as an image file. And that image file we are then going to chop up into pieces so that we can render the individual aliens because if we were to just draw the image file to the screen as it is, that is going to result in just drawing this big collection of aliens on the screen which isn't going to be productive for anybody. So let's go ahead and do a couple of things. First thing I'm going to do is, I'm going to create a new folder called Source, S-R-C. This is going to be where I put all of the Lua files besides main.lua, including constants.lua. So I click and drag constants.lua into there, which means now I have to require src/constants, not just constants because it's not in the same level as main.lua anymore. Within source I'm going to create a new file. I'm going to call this dependencies.lua. And this file is going to be the file where we keep all of the libraries and image data. Basically it's going to function as a require and assets file module that we can then include in our main.lua and we don't have to require constants and all of the different classes that we might end up writing and all the images and sounds and all that stuff. We're going to put that all into the dependencies file. So I can say push equals require push. I can say require src/constants. And then I can say, gtextures-- and remember, that lowercase g means it's going to be global-- gtextures equals, I'm going to make it a table. And now a table-- so this is where we get into where curly brackets are used in Lua. A table is a sort of data structure that functions as a-- you can make it use as an array, you can use it as a dictionary, a Python dictionary, or a hash table so you can associate keys with values. It's a multi-purpose data structure and it's also the backbone for how you get classes and objects to work. Un3okapi says, hey, sorry to interject but I just wanted to say I really appreciate and enjoy these Twitch sessions. Thank you for taking your time to do these. Thank you for joining. I appreciate that you're tuning in and that you are enjoying them. But the tables are used for basically putting data together in a data structure. Oh no, NASA just went live! Which stream should I watch? Put them both up. Why not? I mean, have a little bit of outer space. It's appropriate because Space Invaders takes place in outer space. So I feel like the two sort of go hand-in-hand. Un3okapi, cool hair, dude. Thank you. Appreciate it, thank you so much. OK, so gtextures equals-- these empty curly brackets is going to be our table definition. So what I'm going to do is, using this symbol, or this-- sorry, this square brackets syntax, I'm going to say this is going to be my key. The key is going to be aliens equals. And then I'm going to say love.graphics.newimage graphics/aliens.png. And what this has done is it's created a table where I can put data, and it said the aliens string is going to be associated with this love.graphics.newimage object, this image, located at graphics/aliens.png. So now if I do something like gtextures aliens, that's going to be a direct reference to that image object. c4reni, thank you very much for following. So that gtextures is now a table we can index. Just like arrays, you can just index them by their keys. So this is indexing that table at the key aliens. If it were used as an array where the data does not have a key to associate with every value but rather it's just storing data continuously, you can index it numerically like you do with arrays in C-- 1, 2, 3, 4, 5, et cetera. But we're going to be using this gtextures table as a reference to all of our texture data. And another thing that we want to do is, I'm going to create a table called gframes. And this is going to have a reference to gtextures aliens, 12 by 12. This is going to have a reference to the result of a function called generate quads, which we haven't implemented yet. That's our own function. And this function takes as arguments a texture So in this case it's an image data object. So in this case a reference to what we just looked, the gtextures aliens image data, and then the size of the sprites located in that texture, in this case 12 by 12 pixels. And in order for this to work I also need to require src/util, which we have not implemented yet. But this is where we get into talking about sprite sheets. And we haven't actually talked about sprite sheets yet in Lua and Love. But sprite sheets are exactly what these are-- essentially just image data put together in one texture, one PNG file, as opposed to having individual textures which we looked at, for example, in tic-tac-toe. And we didn't even use textures in Snake. And we used individual textures also with memory cards. It's easier to do it this way because we don't have to actually split up the textures. You programmatically, like we're going to do in a second. But it is a bit unwieldy. For example, if we wanted to do it that way with this and we wanted to have the same number of aliens available, this would be whatever this is-- I think it's 14 by whatever this is, 1, 2, 3, 4, 5, 6, 7, 8-- 16, 1, 2, 3, 4-- yeah, it's eight by eight-- sorry, 16 by 16 sprites which is-- I don't even know that is offhand, I don't know my 16 multiplication tables. But it's a lot. It's like 200 something sprites. And that would mean, 200 and something png files in a directory somewhere in here. So rather than do that and have to manually go into my source file and say, OK, that's one texture, OK, so alien2 is going to be love.graphics.image graphics aliens2, it's much easier to be able to just have a single texture and then split that up programmatically into what are called quads, or sprites in a lot of frameworks and environments, and be able to draw those separately. Be able to draw this image data but specify a quad, specify a sprite, and then only draw that specific piece of the texture. And it will have the effect of, as if we had 200 and some odd individual textures that we're using. But we're only using one. We're just making it look as if we have multiple different sprites. Let me make sure that I'm back in the stream chat here. OK. You know what? You're totally right, says JP. Yeah, well, you know, you've got to have a little bit of outer space. Never hurt anybody. OK, so I know that I want this function to generate these quads. First, let's do a sanity check. Let's go into love.draw. Instead of drawing Space Invaders, let's go ahead and say love.grapics.draw gtextures aliens, because we can make a reference to that image data so I can just call it within there. Right, I haven't called the-- so, I put all of this into my dependencies file. So I could say it require dependencies, like that. And source dependencies, I apologize-- src/dependencies. Push is not found. Did I mess that up? Oh, I did-- lib/push. Sorry, util not found because I haven't created the util file. Lots of little bugs here. But now this should work. So if I go into main.lua and run it-- god, generate quads also doesn't work. OK, function generate quads end. There we go. So that was somewhat painful. But you can see that we're drawing the texture to the screen as is. And that's not the effect that we want. We want to draw just individual sprites at a time. And I'm also making a reference to the wrong sprite sheet. We agreed we were going to use the 12 pixel one. So I'm going to say that we're going to load not the aliens.png which is the eight pixels, but the 12 pixel version, so aliens12. And when we do that, we can see that they do look much larger on the screen. And they look like a pretty good size. I can sort of visualize how this is going to look once we've implemented the-- once we've actually split the texture into multiple sprites and made it so that the enemies move around we have a ship and whatnot. It's going to look decent. We should also figure out which one of these we want to be our ship, because we will need a ship sprite. Which one of these would be good, actually? If anybody has any preferences for which ship we want to vote to be the one that becomes our actual ship. This one looks pretty good, for example-- third row, last one. Toss that in the chat, because we got to decide that. Oh, 16 by 16, 256. Yep, I should know that. That's kind of embarrassing, actually. How do you iterate through the image if the only information you have is that the quads should be of size 12 by 12? So we are going to figure that out right now, actually. So going into util.lua I'm going to say utility functions. And then remember we declared that we were going to take in a texture and a width and height, right? And I'm trying to remember exactly how I did this because I implemented this for the game's course. And we could figure it out on the fly. I'm just trying to figure out if there was any particular specific special way I did it. There really wasn't. So, we'll just figure it out as we go. So what we need is, essentially what we should expect to be returned from this function is a table, so a list in this case, of quads. And quads are just rectangles. So if I go over to-- this is actually a somewhat perfect time to do a little bit of fancy-- sorry, draw.cs50.io. . We'll do a little bit of fancy drawing onto the screen. And excuse me while I grab my tablet so I can actually do this properly. So I try to look for any excuse possible to do this. But I have a handy dandy Wacom tablet right here. And we're going to use CS50's handy dandy draw tool, draw.CS50.io. I'm going to plug this in. This should just work. Take the pen out and let's do a little test. Is this working? Yep, perfect. So I have a sprite-- or not a sprite. In this case I have some textures. I have aliens, right? And these individual aliens are going to be my sprites. Blah, blah, blah, blah, blah. They all look the same for this example. But our goal is to take this sprite information, this texture, and split it up into multiple pieces. And I would put this publicly but I feel like I might get a lot of drawing and stuff on there. But if you want to use this yourself, feel free to do so. In fact, we can set up a draw.cs50.io/spaceinvaders. If you go to draw.cs50.io/spaceinvaders, you will actually be able to see this on your screen. So anybody that wants to go ahead in there and draw and mess around, feel free to do that. I'll tune in there in just a second. But if you just go to just draw.cs50.io on your machine without-- if you go onto it without specifying a URL, you will actually just get a local version of the app so that anything you draw is just on your machine. It's not being broadcast anywhere. And Nikolai, scared cold enough about draw.cs50.io. Yeah, that might be-- we might have a couple of non kid friendly issues with that. Fifth one from the left, fourth one up, but the one you suggested looks good to. Fifth from the left, fourth one up? Which one is that? Fifth from the left, fourth one up. Are you talking about from the top or the bottom? So this one? Fifth from the left, fourth one from the top, this green one? Or are you talking about this one right here, this yellowish one? OK, so 1, 2, 3, 4, 5, 1, 2, 3, 4. So, this one right here then. Oh, six-- oh, this one? Oh, I see. Oh yeah, that would be cool. Yeah. Yeah, that would be pretty cool. We can make that one the alien one. JPguy, I should choose Belgian looking space-- I keep getting out of the chat on my side computer here. American looking spaceship near the bottom as well. American looking one at the bottom, which one is that? Near the bottom. Which one was the American looking spaceship, JPguy? How do you erase lines? Oh, how do your erase lines! So, if we go back to the-- I've got so many devices and stuff here. Let me go to this and let me put this on a separate screen so I can flip between them pretty easily. So erasing, if I'm not mistaken you click and drag from the bottom. Can't see super well. Whoops. How do you erase, actually? Is it shift? Oh, it's shift. Sorry, so shift-- it's a little large, so be careful. So shift click will erase, so I can do that. But I don't want to do that right now. Do I dare look into the tab and see what people are drawing? Whoa! Got a lot of erasing going on. So people have stuff they don't want to see. OK, I apologize. We're getting a little bit distracted. Da, da, da, da. Brenda has a sword now to moderate. Oh yeah, that's true. She does have a sword. And Brenda will use it fiercely, she will. I know Brenda. It had some colors but I miss looked, apparently. 11th of the bottom, fourth or third. 11th from the bottom-- 11th from the bottom, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11. Wait, what? I'm not sure which one you're referring to, JP. But anyways, back to the discussion of splitting up sprites. So, we have image data here. And a quad is essentially just-- and if I can change color, actually, that would be super cool. For some reason I can't change color super easily. Yeah, I'm not sure why I can't change the color. But if I could change the color-- if Dan were here he could assist me. But essentially, a quad is just me saying I want to take this chunk. I want to define a rectangle that means this chunk right here. Oh and by the way, Maketsuk666 and Qaerasu, thank you very much, both of you, for following. And so this quad, so this is a quad, also known as a sprite in many circles. Whereas this is known as a texture, or an image. The texture or the image is the data that contains the sprites that you're trying to display on the screen. And what we have done so far is, we have had individual sprites like this where it's equal to both a sprite and a texture because we were just drawing the data as it is onto the screen, right? But we don't want to do that when we want to have all of these sprites together. We want to say, I specify a quad here. And it's going to have an x and a y. So it's going to be x equals 0, y equals 0, width equals 12, and height equals 12, right? Because we want to specify a coordinate for the quad. And remember, everything is top left oriented by default. So this quad here is at x,y zero-- very top left. The width is 12 and the height is 12. Because all of our sprites in this texture data are 12 pixels by 12 pixels tall. This sprite right here is very similar except its x is 12, or rather-- is it 12? Yeah, it is 12. Its x is 12 and its y is 0. And then this one is 24, this one is 36, and so on and so forth until you reach the end of your width. And then you iterate per row as well. Every y, you increase the y by 12. So it becomes not zero but 12. And then you rinse and repeat and do the same thing over and over again, generating all these quads. Go down to the next line and do it over and over again. Now y is 24, x is 0, and so on and so forth. Asley says she drew a masterpiece. Let's see it. Oh, that's-- what's that supposed to be, Asley, please tell me. Oh, there's a-- OK. Man, I forgot how you scroll. Ugh. Does anybody remember the scroll shortcut, because I actually don't remember what it is? Is it alt? I don't want to delete the whole thing. I'll feel terrible if I delete the whole thing. Shift alt? Yeah, I don't remember. I don't remember the shortcut for how to actually pan the image. But this is a-- first of all, I appreciate the artwork, thank you very much. I do see that there's an arrow that's pointing to COL, which probably implies that that's a picture of me. So it's a beautiful picture. But this is the gist behind how we algorithmically take a piece of texture data and split it up into multiple sub sprites, these little individual sprites that we can then draw like they're their own separate images. And so I'm going to go back to the code here. And we can essentially think of it as a two dimensional loop that's iterating y by x. In much the same way that we would draw, for example, a row of tiles, or sorry, a map of tiles along the x,y. We can do the same kind of thing by splitting up the quads. It's sort of the inverse of drawing everything to the screen. But that's how we can think about it, just iterating row by row, column by column, until we've basically extracted each quad and then put it into a table that we then return back to main which we can then reference when we draw an individual sprite. So, I'm going to say local quads is equal to an empty table. And I know that at the end of this function I'm going to return that table of quads. Jacobchaussen says zoom out. Can we zoom out? Oh no, it went away. What happened? Chmod777, thank you for the subscription. Appreciate the name as well. OK, so I want to iterate for every y and x. We can think about it the way that we thought about drawing grids of tiles. So for y is equal 1 until, let's say local texture width, texture height, is equal to texture get width, texture get height. And these are functions that you can call on any image object. So no, we've declared two variables called texture width and texture height. And we have called the get width and get height function on the texture that we're passing in, which is going to be our texture PNG file that we referenced earlier. And then I can say local sprites wide sprites tall is going to be equal to-- and then this is where we can use our actual width and height that we passed into the function, right? I can say, texture width divided by width and texture height divided by height. And what this will do is, it will tell me by dividing the actual width of the texture by the size of the sprite, how many sprites tall and wide the image is. So we have algorithmically figured that out by using a function that Love has given us, which is this get width and get height function. And we do have to specify the width and height of the sprite itself. There is no real way for Love to know how big the actual sprites are because it can't look at the image and say, oh, these sprites are 12 pixels by 12 pixels, or they're 32 by 32 pixels wide, right? That's something that visually you as a human have to tell this function. And then the function can then say, OK, now that I know how big the sprites are I can iterate over it, I can calculate it, and then figure everything out. So for y is equal to 1 until the sprites tall, do. Then for x is equal to 1 until sprites wide, do, end. And so this is iterating over every row, first, so going through the row, and then going down to the next row, iterating over every column, going to the next row, iterating over every column, sort of scan line format the way that like CRTs typically operate, if familiar. And so this is where we're going to have to actually create the quad that represents that rectangle of our image that we want to store in our table. So, we would say table.insert. And so this is how you add anything to any table in Lua. Table.insert into quads-- I'm going to insert love.graphics.newquad. And a quad-- and this is a nice thing about the Pixelbite plugin as well, is it gives you all this documentation that you can see. It tells me that it expects an x and a y. So this is where the top left position is of the individual quad, which is important. And then it tells me it needs the reference height and reference width of the image. And this has to do with Open GL underneath the hood. I'm not entirely sure 100% how that maps to this function. But basically, it maintains a reference. I looked at the source code at one point and understood, but it's been almost a year. But I can say, love.graphics.newquad. And lets just say I'm going to need an x and a y. It's not going to be actual x,y, because those are just going to be 1 to whatever-- 1 to 16 and then 1 to 16. But as placeholders I'm going to put x, y there. And then I'm going to say, texture get-- what is it? Is it get size, get dimensions? Lua-- or sorry, Love 2D image get dimensions? I think it's get dimensions-- yeah, get dimensions. Whoa, and that's way zoomed in. And so what this does is, as its documentation says right here on the wiki page, it returns two variables-- so that's why there's a comma there-- width and height. You call image get dimensions. In this case image is capitalized as the object class, but it can be on any image object, get dimensions. So I can say texture get dimensions. And even though I called this new quad function, which takes in four arguments-- notice that it took four arguments, the x, y, the reference width, and the reference height-- because this function, the get dimensions function, returns two variables we're actually able to populate a function that expects four arguments with three values-- the x, y that we're going to put in and then this texture get dimensions function call. Let me get-- whoops, didn't mean to do that. I'm going to command B so we can see the whole thing on the screen. Now, this x and y are not the variables that we want. We want to actually figure out-- so, back to the amazing draw.cs50.io page here-- we want to figure out where our x and y are for each individual quad a we're iterating through our array. So remember, they were doing for y is equal to 1 until the number of tiles tall-- so that's 1, 2, 3, 4, in this example case. And then for each of those in a nested loop we're also doing, for x is equal to 1 until the number of pixels wide-- so 1, 2, 3, 4-- and then y is equal to 2, 1, 2, 3, 4, y is equal to 3, 1, 2, 3, 4 on the x. For each of those we want to determine what the x and y should be at that particular part of the loop. And we can do that by essentially just multiplying by 12, because that's the size of the pixels. So for our example our loop is set to one on the x and 1 on the y, that'll be the first index that we're at here because remember typically loops in Lua are one indexed. You can index them at zero, but the typical trends are two initialized by one. You started at one and then you can say-- we could probably initialize it at zero actually, for that loop, just because it'll make it a little bit easier. That way we won't have to minus by one before we do the multiplication. Normally you have to minus by one before you do the multiplication when you indexed by one, because everything in graphics is zero indexed but Lua is one indexed. So what we can do is, we can say when our x is equal to zero and our y is equal to zero-- and I'll change it right now, actually, zero and zero. What we can do is basically say, love.graphics.newquad x times 12, y times 12. And then that will give us the position-- wait, quad, love.graphic.newquad, x, y, and then it thought it also needed a size of the quad itself, right? Am I getting that mixed up? Oh, it is. I'm sorry, there's actually six arguments. I was wondering. The function signature did look small. We also have to specify the size of the quad, because just the position is not enough information. We do need the size of it as well. And remember, magic numbers are not good generally. So I'm going to replace this with alien size which is something that I haven't declared yet. So I'm going to go into constants and I'm going to say, alien size is equal to 12. Remember, that's how you avoid magic numbers. Magic numbers are bad. Try to avoid them. It's not always 100% necessary depending on the context, but it's usually good practice-- almost always. So again, alien size, alien size. I'm going to just make it easier to read by putting this on the next line. So an x and y, a width and a height, and the image dimensions, which you can just call with texture get dimensions. And you can kind of just forget about what this is used for. You can look at the C code and it maintains a point a reference to the image data and does some other stuff underneath the hood. And that's why it's there. But you don't really need to worry about this too much to understand what's going on. The important thing is that you understand the x, y, width, and height of your quads. That's the important bit. So we're going to go into-- and by the way, let's also get the x and y to work correctly. So I'm going to say x times alien size and then y times alien size. And because we've laid out our double loop like this and we're starting at zero, when we multiply by the alien size we will get a quad that is at the right x, y because we're doing it in 12 pixel increments and we are making the quad 12 by 12 pixels as by specifying alien size, alien size for the width and the height, and then texture get dimensions here. And then we're inserting this new quad into this quads table here. This quads table gets returned by the generate quads function. And if everything goes according to plan, I can go into here and say gframes aliens. So remember, gframes aliens in our dependencies is getting the result of this generate quads function. And then remember we pass in gtextures aliens. And let's also for good measure say-- oh, you know what I did? Sorry. Alien size-- I made a mistake. This should be not alien size, this should be height. And this should not be-- sorry. This should not be alien size, this should be width. This should not be alien size, this should be width. And this should not be alien size, this should be height. The reason that we're doing that-- and it was a bit of a brain fart on my part. The reason that we're doing this is that this function is general purpose. So now we've taken this function from being specifically intended for alien splicing, alien cutting up the image, to being usable for any image that we pass in. And we can specify any width and height and it'll split it up according to those parameters. So that is the right way to do it. Here is where you actually pass in alien size, where you actually call the function. So now we're actually using it for that intended specific use case of the alien cutting up. And I apologize. There's a bunch of comments in the chat that I will respond to. What happened there? OK, do that again. I will get to all those comments. I apologize. Just trying to make a little bit of room since we are-- this has been an hour and 40 and we're still a little bit behind here. But, this is effectively how you cut up images and do it algorithmically in a way that you have all your data lined up in a way that makes sense. Now, with the image that Brenda showed us earlier in the chat, which was a bit more-- I have it on another computer. But, let's see, where's the [INAUDIBLE] picture at? Brenda, if you wouldn't mind posting that-- was it Brenda that posted that? It was Brenda, right? Yeah, Brenda if you wouldn't mind reposting that just so it's on my other machine. I can click the link I think-- actually no, it wouldn't work on that because it's not on the stream. Sorry, scratch that. I'm just going to go into images and just look up Space Invaders Deviant Art. That should pull it up, right? Maybe not. Not seeing it in here. But if I can find something that looks similar-- so I'll tell you what. I can show you the idea. A sprite sheet, if we look at a spreadsheet that's kind of all over the place. So by the way, these are sprite sheets. What we've been using is called a sprite sheet, where a bunch of your sprites are put together in one sheet, one picture if I didn't already specify that. Trying to look for one that's got a bunch of different data all over the place, that's not uniform to sort of showcase this idea. I guess for example something like this. So this is a bunch of sprites that are different sizes. So notice that these ones are wide, wider than they are tall. But these ones are all-- well, these first four, first eight rather, are pretty uniform. But then these two are kind of wider than the other ones. For sprites like these, and for the link, if you look at the link that Brenda just posted in the chat, it's that same kind of idea. For any sprite sheet that's not uniformly laid out, you can't do what we just did. You can't algorithmically define the individual sprites, the individual quads, because they just don't chop up evenly. They're not along some sort of grid, which we're doing in our examples. These are on a very tight 12 by 12 pixel grid, and these are on a very tight 16 by 16 pixel grid, eight by eight pixel grid. This grid is essential to be able to chop your sprites up algorithmically. And so if you're doing this and you have a bunch of are in your game, try to find a way to put everything on a grid so that you can write some piece of code to chop up your sprites for you and not have to manually lay out where all the sprites are. This isn't always feasible because not all sprites are the same size. For example, even some certain Mario sprites are different sizes. NES Mario sprites have different sizes, different width and heights, and they're laid out in sort of different weird ways. And that is a kind of situation where you have to manually lay everything out and figure out where the slicing occurs, if not chop up your sprites manually in advance and then load them individually. But yeah, for doing most 2D games, most sprite work, you can figure things out in a sort of grid like manner. Now, before I get into the chat I'm just going to make sure we got this working in drawing to the screen. I'm going to go into main. And now down here where I have the draw function, remember in passing gtextures aliens as the image data. So I still need to draw the image. I'm still drawing the image no matter what. The second parameter is this reference to gframes aliens, which is the quad table, remember. This table full of these rectangles that have an x, y, width, and a height. And I need to index into it because I can't just draw the quad table as it is. I need to draw a specific quad. I need to tell the love.graphics.draw function, draw the sprite with this quad. So draw like this window of the sprite, right? And just for kicks I'm going to say one, so the very first sprite which should be this one right here. It can actually be kind of hard to see, this purplish looking one, so I'm going to do the second one, so number two, this bluish looking one-- so, two. And if I run this, fingers crossed, it works. Wow, I actually wasn't expecting it to work. That's actually pretty amazing. Just because live coding is a little bit much. But, it's simple and it works. And now I will read all the messages in the chat. And now, by the way, if that's not clear, we have the capability to actually draw rows of individual aliens and our spaceship, move it around, while only using one texture for everything-- which is also more performant for your GPU, by the way. So, sorry, chat. Got a lot of chat to catch up on here. OK, shift works. I drew a masterpiece draw.io. I think I did see that. We did see the masterpiece. Zoomed out 25% or so, I think it got erased. I did get erased. Asley says, they couldn't handle my talent. No, it was very impressive. Actually, let's see what's there now. Is there anything there now? It's all gone. Everything's erased. That's so sad. We had such an amazing masterpiece there. It'll be in the video for all of eternity that we can look at it and appreciate it. Emoji is Asley-- Asley says she's the kappa face, and Bavich_knight's got the meat boy in there. Isotv-- oh, and by the way, we had a couple subscribers. Bubullah and Xoxoff, thank you very much for following. Isotv, just like printing the pyramid in Mario, yes. It's very similar to printing the pyramid in Mario. The same idea of taking two for loops, combining them, and being able to do some operation iteratively, some two dimensional operation. It's something that you'll see time and time again in computer science. It's something that you'll see time and time again in game development, whether it's drawing maps, splicing tiles, doing whatever-- laying out rows of enemies which we'll do in the future as well. Charles, is Lua like JavaScript? Yes, in a lot of ways it actually is. It uses very similar prototype inheritance model as JavaScript. It uses anonymous functions in this very similar way to JavaScript. So, a lot of code that you see, very promise like syntax exists in Lua just like it doesn't JavaScript, which you might get to in the future. Probably not in this stream, but certainly in future. Don't worry about it too much right now, says JP, but I think the mic is crackling a bit or suffering from static. Uh-oh, that's not good. Hopefully I'm not brushing up against anything. And I hope it's not suffering from too much static. Oh, and let me-- apologies. Tell me if this sounds any better. Does that sound any better? I think I might have forgotten to bypass the EQ, which should make my mic sound a little nicer. Dev, let me know. I have a compressor on, just to limit the dynamic range of the mic, and that might be distorting it a little bit. So let me know about that as well. Can it run in the browser like JavaScript, access the dom, says Charles. It cannot run in the browser and access the dom in the same way that JavaScript can. You can compile Love games and use those in the web browser using what's called Mscript in, which basically takes your game and the Love binary, compiles it into a form of JavaScript, and then executes it. But you don't have dom control with that either. Nicktheway says uptime. What is uptime? I have to figure out what that is-- uptime. Oh, is that how long we've been up? So we've been up an hour 53, I think? Still somewhat new to Twitch, so a lot of these things are fairly new. And Nicktheway aptly just followed. Thank you very much for following. What mic are we using? We're using a Sennheiser-- what's this called-- EW100 G3 lav? I am not a production expert so I could not tell you too much about it, but it is a lav. One day my lav-- will you been making some sort of platformer or a dungeon crawl anytime soon? Good question! I would love to make one of those, either of those. I made a platformer for the GD50 course, which by the way if you're unfamiliar go to CS50.edx.org/games. So that's the game of course that I taught. I taught how to make a platformer, a Super Mario style platformer with randomized levels, using a very cool sprite pack. We didn't cover dungeon crawling and I personally am a big fan of dungeon crawlers. So I tink that'd be a really cool project to work on. That would be a definitely multi stream project. And this project itself is also going to probably be multi stream, multi-day, just because it takes a while to-- we're covering a lot of somewhat new ground today, actually, with quads and stuff like that, which we haven't looked at before. And I want to make sure that everybody understands where we're going and can follow along. Thank you, Onedaymylove, following as well. That was a very funny sentence to say. Are the sprites on a black square uniform with the background or are they on their own? So I missed five minutes and couldn't understand, Asley says. The sprites are on a black background that we are going to make the same color as that background in our game. And we're going to do that for the purpose of avoiding making that background alpha, just because that color I believe with the limited palette is also a component color of many of the sprites. I'm not 100% sure if that's the case. But I believe that black color is part of many of the sprite's actual artwork. So we don't want to necessarily convert the black color to transparent, or do a fill transparency, because that will make some of the sprites look a little bit wacky. So what we'll do is, we'll just make the background of the game the same color as the sprite. And I think it already is. It might be slightly off color. And that will make it look like we're playing on a-- that we're drawing the sprites transparently. There isn't really ever going to be a case where they overlap anything else so we don't really have to worry about transparency. So it should be fine. But thank you for the question. It's a good question. I missed 10 minutes, says Bavich_knight. It just froze. Sorry about that. Hope you're able to go back and scroll through it, or catch what you missed on YouTube. But we haven't gone into anything too intense because all we're doing is just catching up on the chat at this point. I am back, but your masterpiece is gone, Asley. But I'm back again, Xoxoff. But it's back again-- oh, is the masterpiece back again? I do not see it on my screen. I checked my PC volume. It was much louder than what it's usually at, that's why I also heard the crackle. But at normal volume it's actually not even audible. Oh, I wonder if your speakers are just distorting, because that's possible-- hopefully not. What did I do again to the mic? I added a compressor-- not right now, but I just bypassed-- we have an EQ and a mixer. So I have a lab going into a mixer. The mixer is applying EQ settings to the sound that I'm outputting to the stream. So basically, the sound is made up of different bands of frequencies and the EQ is an eight band-- I think it's an eight band. 1, 2, 3, 4, 5, 6-- it's a seven band. So there are seven discrete sections of frequencies that you can lower or accentuate, accentuate or attenuate. And so EQ just takes out some of low frequencies like the HVAC and accentuates some of the higher ones for a more crisp sound. But there's also in the software a compressor to lower the dynamic range and also make up some of the gain. And so potentially it could distort at certain points, although it shouldn't be per what I did to test the volume. Maybe if-- Asley says she hears it now, so maybe there is a little bit. So hopefully it sounds a little bit better now. Otherwise, you can have a chatbot that keeps track of the uptime, says Isotv. I'll look into that. I'll look into that. Still fairly new to Twitch, so there's a lot of stuff I should look into. OK, if you can all hear the crackling I'm going to go into the settings for the audio. And I'm going to lower the output gain. Tell me if that fixes it. So does that sound any better? Let me know. I just lowered the makeup gain on the compressor. Brenda says, no sound issues there. Is there any way to separate sprites completely from the background, says Asley. You would use transparency. So we're not doing it in this example. But you would make your sprites with a transparent background in some editor. A lot of games back in the day didn't have image formats that had transparent backgrounds. So what they would do is, they would-- hopefully it's not the mic itself, because I just lowered all the makeup gain on the compressor. So it should have fixed any distortion that existed. I'm not sure. I can potentially lower that EQ, the upper frequency bands on the EQ and that might work. But yes, you would draw your sprite with transparency in advance. But games in the past didn't have transparency. So let's see. Like, some of these for example-- see this drawing here, this sprite image here of this gorilla? It has a background that's very different than any color that you find in the gorilla itself, and this is deliberately so that there would be no clashing of the background color with the sprite. And so what the game would do is, it would check for any pixels that matched some sort of color key and it would just not draw them. It's sort of similar to how a green screen works, I guess, chroma keying. But it would just basically do an, if color is equal to blah, blah, blah, don't draw that pixel, but much more efficiently than that using hardware. But that's essentially how games used to work back in the day. Now, the image format itself can store the transparency value with what's called an alpha channel, the A, the RGBA. And that basically tells the-- when open GL is rendering, whether to draw it with the opacity or not effectively. In the middle column from the fourth, bottom row, looks like a spaceship. In the middle column-- 1, 2, 3, 4, 5, 6, 7, 8, so roughly around here. Oh, this one? This pink cyan looking one? Yeah, that looks pretty good. I like that one. And I think someone else suggested-- what was it? Which one was it? It was another one of these. Crap, I forget the exact one that someone mentioned. But yeah, that's a good one. Crackling sound is still there. OK, I'll try to investigate the crackling sound afterwards. I'm not sure what it is. I apologize. Hopefully it's not too distracting. Oh, and shout outs to NASA for landing the Mars Lander. People in the chat are-- I'm not following it right now, obviously, but people in the chat are saying that it's landed. So that's awesome. You heard it today on CS50 on Twitch as well. I'm going take a drink and then we're going to dive into drawing a bunch of aliens, and our spaceship, and moving the spaceship. See the Snapple there in the screen as well? Yeah, stay hydrated, chat. OK, so we haven't done a lot of coding. We have done a fair amount of new conceptual work. So today, the leap from going from individual textures to quad generation is kind of a hard step for some folks. And being able to go over it slowly and make sure everybody's on the same page and being able to draw through the algorithm, even though this now looks like kind of a mess, it's kind of important that we understand how that works. So that's why we took our time. But now we're going to go into some stuff that's fairly straightforward-- not as groundbreaking, I guess, or as revolutionary as generating quads. But that's a big piece that you'll use in probably any game going forward. Markv101, thank you for joining. Hello, good to have you with us. Next idea for your game, says Andre, should be a Mars Lander game. What is that game? There is a game like that. It was in-- was it just called Lander? Lunar Lander? Oh, it's a video game genre. It's not even an individual game. Oh, OK, it is an individual game but it's also a genre. Something like this. This is like vectorized landing of the Lander onto the moon. Yeah, it'd be very appropriate for today if we were doing something like that. I'd have to research how-- because this looks like it would probably use a different collision formula. It would have to actually calculate the vector angle, the-- no, actually rather the surface normal of a vector of-- sorry, the normal of a-- the individual normals of the terrain, the terrain vectors, and the surface normal. Basically if you have a surface, imagine my hand is a plane, the surface normal would be whatever arrow is coming out of it towards the basically exterior. And the surfaces tend to only have one normal for lighting calculations in 3D engines typically, but you can also use it for various other things. For example in Super Mario Galaxy-- let's load that real quick. And I don't know if this has been officially confirmed by Nintendo, but the world was made up by circular, or spherical, oblate spheroidal, masses to simulate planetary interactions and just to make it feel more like you're in outer space. And so Mario could actually walk around 3D spherical objects as if they were flat, right? And the way that it's thought that that worked was that you would just calculate the surface normal off of any given part of the mesh and orient Mario that way on the terrain so that his body was always kind of-- basically he was the surface normal. His body was parallel with it. And that gave it the appearance of him walking on the planet. And so you would do kind of the same thing with this. You would figure out, what's the surface normal of the individual vectors making up the-- or I guess they'd be rays. Would they be rays? No, I guess they would be-- they'd be line segments but you could think of them as vectors. You'd figure out the surface normal of all of these and then you would just orient the lander kind of in the same way, is my intuition. I'm sure there are many different ways you could do it. Kavabongo and Ki11ercrow, thank you very much for following. So anyway, back to what we were about to do. Let's take this new quad idea that we have implemented and draw some aliens. Well, let's actually, let's draw a ship first. We were going to decide what the ship was going to be. So I'm going to, in my source folder-- do I want to make subfolders, or do I want to just make it all into one? We can probably just do it all in one for today. We don't have to go into separate subfolders. I'm going to create a ship.lua class. And what this is going to do-- Bavich_knight, normal is you could draw a perpendicular line coming out of a plane surface, correct. [INAUDIBLE] said I'm too lazy to go to bed. Can't wait to start the CS50 games course after I finish the web programming course. This is so fun, says Isotv. Awesome, thank you so much. I'm glad you're enjoying it. Quickly, make a robot that brings you water. Oh, wait. Hey, am I very late, says Oshkosh32. Relatively-- we've been up for about two hours. We haven't done a ton of coding because I've been taking it a little bit slow. I've been looking at some conceptual stuff. Scrin26, thank you for following. We're looking at some conceptual stuff. And I just realized the chat-- I don't think the chat's been up this whole time, which is horrible by the way. I apologize for that. That should now be-- it should now be up. Let me make sure. Yeah, the chat hasn't been up this whole time. That's terrible. I apologize. I think I must have hit something in the CS50, in the chat here or something, and then it got rid of it. But no, we haven't been-- you're not terribly late. We basically just talked about how to splice a texture into subsprites. And we set up our window. We looked at a sprite generator, got a texture, decided on our resolution, did a whole bunch of stuff like that, set up our window, used push, went through the process of splitting up a texture into quads, which are sprites-- essentially what are sprites, effectively-- drew it to the screen. And now we're about to get into moving-- well, getting one of those sprites to be our player, moving that player, and then doing the same thing with aliens. Jobcoachin says, I'm still learning C. So wait, really? We are doing Space Invaders-- kind of appropriate, says Asley. Space for the lunar Lander game? Is that what you mean? There's the chat again. Man, I hate the fact that I didn't have the chat up. Ugh, man, because I love seeing it in the video. And now it's not going to be in the video for like two hours. Oh, well, you learn from your mistakes. What are those on your fingers says, Callmegm? Just rings I got on Amazon-- just a red, and a green, and a blue. Fun story, I had a finger on my other-- sorry, my index finger ring, I had it on this index finger. I took it off and I put it on my other finger, on my left hand which you see as my right hand in the stream. And my finger got-- well, it was too small for my other finger. So this finger got swollen. And because it got swollen I was unable to take the ring off. I tried using soap and Windex and the string technique or the floss technique. It wasn't working. My finger got too swollen. My finger was about twice as big as my finger normally is. So I had to go to the emergency room and get the ring sawed off of my finger, which I had never known was a thing that people do. So now I have two rings that match, because I originally got three rings in a set, and then one ring that's sort of a UV light sensor ring that my best friend got me as a replacement for that. And that was actually last Thanksgiving, because very appropriate that we mention that. It's a one year anniversary of going to the emergency room to get a ring sawed off of my finger. So that's a story. OK, what happened to the chat? Should have mentioned it earlier. Yeah. I have a keyboard shortcut on the keyboard here and it's C. If I hit C, the chat disappears from the video. And I must have hit it at some point and it disappeared and I didn't notice it because I was so focused on what we're doing. But yeah. Better the ring than the finger, says Brenda. I agree. And yes, it was terrible. But it was a fun, humorous experience. I'm just glad that my finger didn't get amputated. I'm glad that it didn't get so swollen and so blood starved for so long that it had to get amputated. That would've been like, the worst thing. At least it would've been on my left hand, though. So hey, I still have my fingers on my right hand. Anyways, we want a class for the player. So we want to have a ship that we can move left to right, just to get started, demonstrating this quad technique that we've looked at in comparison with moving something around, moving an image around like we've looked at before. Actually, have we moved images around before? This would be the first time we've actually moved something freely around because with tic-tac-toe we moved a cursor, a virtual cursor, around. With Snake, our Snake was constantly moving on its own and we just manipulated its movement. And with the memory card game, we just-- was that mouse driven? Did we just clicked on the cards? Yes, I believe it was just mouse driven. So, this will be the first time we actually move a sprite around with the keyboard. So I'm going to say, player-- sorry, ship, this will be the player, is equal to class with brackets. And what that's going to do-- and first of all, if you don't have the class library they're unfortunately named. But if you go to the H-U-M-P library, Love 2D, and it's unfortunate that they named it that. But it's one of the biggest libraries for Love. Help Utilities for Massive Productivity, they have a class .lua within that library. Download that. That class library will allow us to use object oriented programming in our game. And I already should have a copy of it already on my hard drive. Actually it's in the folder here. I'll just copy it from there. I'm going to go into Space Invaders, into lib. I'm going to copy it into my lib folder. And then I'm going to go into dependencies. And then above push, I'm going to say class is equal to require lib/class. And now what this has allowed me to do is almost like using a new syntax in Lua. Class is an object that basically does a lot of the object oriented stuff for you that you would normally have to do with meta tables and other weird stuff in Lua. It's a bit ugly. But thanks to the class libraries, part of the H-U-M-P library, you can just use this. You can basically say, ship is going to be an object of type class, which just means it's going to be an object. And any ship object that we instantiate from this definition will have the properties of a ship. Asley said, do not read that as H-U-M-P. I know most people probably don't. [INAUDIBLE],, are you using the 12 inch MacBook? Is this a 12 inch MacBook? Good question. It is a 13 inch, from 2013. It's a bit old school, relatively. So now I've included this class-- class? This class library. I can say ship is equal to class. I'm going to create a new class. Jacobchaussen says, I'm missing too much code. So I'm going to pass this time. Sorry, I'll be faster next time. No, definitely check it out on YouTube. I'm trying to go kind of slow but also kind of relatively quickly and not cover too much of the syntax just because we've covered it in prior streams. And this stream will be very long as it is. We're already at two hours and we barely have much going in the stream. But yeah, definitely tune in next time, tune in when you can, Jacobchaussen. Good to have you with us. Apologies if I am going a little bit too fast for some folks. If you have any questions, specific questions about what we're doing, as always toss them in the chat and I'll be happy to try and answer them as we go. And I apologize if I missed it, but scrin26, thank you for following. Sorry, apologize if I already said that thank you for that. But if not, thank you very much for following. So ship is equal to class. Now what we can do is, I can say a function ship init. This is what's called a constructor. I think we've talked about the basics of objects before, but a constructor basically let's us say something is a ship with parentheses. That'll make a new ship in our code. Every ship that we want-- and there's only going to be one-- is going to update over time and it's going to draw over time. And typically the naming convention is render even though Love uses draw. Most engines will have render as their-- that I've seen, I'm sure it varies-- but they'll have render as the word. And the init function, this is the constructor. This is where we're going to set up the ship to have the information it needs to perform its work. And what the ship is going to want to do is basically just exist at the bottom. And I can't draw too much at the bottom. I'm going to reload actually, and bye bye to that because I don't know how to scroll the screen right now. But our ship is going to be here at the bottom. It's going to have a sprite. So I know that I'm going to want to draw this sprite. So it's going to at least need to have a reference to what sprite we want to use. And I think Babic and Asley suggested specific sprites that we can use for the ship in the chat. But it's also going to, because it's going to move left to right it's going to need to have an x and it's going to have a y, and obviously of course a sprite. In this case, that's going to be a quad, right? And because our table that we created earlier is numerically indexed, this sprite or quad can really just be a number-- one to however large the table is, the table of sprites. And this information is what all of the aliens as well are going to have, but they're going to have slightly different behavior. And if we wanted to be really super good at engineering our application and proper, we would make a base class probably called entity. And that entity class would have an x and y and a sprite and maybe some other information. And we would then create a subclass. We would have something like entity as the parent class, where every entity is anything that we can see. Or rather, it's a little bit hard-- you can't see it super well because of the chat. But this entity class can then split off and have children classes, child classes. In this case we would have a ship and an alien. So notice that the ship comes down from the entity and the alien comes down from the entity. These are children and this is the parent. Now, I'm just saying well we could do. We could do this. It would be a bit too much work, I think. But this is something that we should think about going forward because we will probably implement more complicated games. And for a lot of games this kind of hierarchy, this object hierarchy, makes sense. And ship, you could then have multiple ship types and multiple alien types and they would share the information that their parents all have. Scroll is shift and three fingers or five fingers. Let's do it that way, entity as base class and then subclasses. We're going to avoid that just because it's a little bit too much, and we're not getting a whole lot from the entity. We will potentially tackle that when it actually makes sense, I think, for a later-- no, we will deal with entity, actually. We'll deal with entity. That makes sense. So entity.lua, this will allow us to demonstrate how to use inheritance with this library which kind of makes sense to demonstrate. Entity based class, anything that moves and has a sprite. So entity is equal to class. And then in the init function we're going to say, self.x is equal to x, and self.y is equal to y, and self.sprite is equal to sprite. Where sprite is a number, so one to whatever the-- 256, because that's how many sprites are in the-- is that the 12 by 12? Yeah, that's how many are in the 12 by 12 table. And an x and y to be its position. And that's all we need for the entity class for now. The ship is going to inherit from entity by using this underscore underscore includes equals entity. And this is specific to the class library that we're using. And this is all you really have to remember in order to make a class a subclass of something else. So in this case, this ship class is saying that it is a child class of the entity class. It's going to inherit everything that entity has, all of its functions and all of its data, while adding some of its own. And I have to remember if we have to actually-- do we actually have to call init? I think maybe we might have to. I'm not 100% sure. We'll figure that out in just a second. Oh, actually no because what we do is, we could just-- now what we can do is, we can just call init off of ship. And because entity, the entity class, already specified an init function it will allow us to get this behavior for free. We don't have to actually implement it in the ship class. Same thing for the alien class, actually. So I'm going to create a new file called alien.lua. And alien class inherits from entity, moves toward ship, and can shoot. So, alien is equal to class. And just like we did before with the ship, I'm going to say underscore underscore includes equals entity function alien update function alien render. And now we have two classes, two separate classes, that their behavior is going to branch. The ship is going to be controlled by the player. And we'll be able to check for keyboard input and move it around. The alien is going to be autonomous and it's going to be AI controlled. But it's going to be very simple. It's going to move left to right and then when it gets to a certain point it's going to move down. Then when it gets to a certain point it's going to move left, and then down, and then right, and then down, and then left, and then down, and then right, in this sort of infinite loop until it gets to the bottom of the screen. And then when it collides with the player, we're going to get a game over function-- we're going to get a game over result, the game over state in our game. And so that's what we're doing here. I'm going to get another sip. My mouth is a little dry. Asley says, is self similar to this in JavaScript? Yes, it is. And self as well in Python. Truesound, hey from Australia! Thank you for joining us. Thanks for the awesome stream. Out of interest, where in the series would you recommend someone new should begin? Thank you very much for joining, Truesound. I would recommend looking at the Snake and-- actually, probably the memory card and tic-tac-toe are a little bit easier than Snake. Check out Snake as well. Snake is a two part stream. But the tic-tac-toe and the memory card game are very easy to get into. A lot of the other videos that we do are not game related and are not as from scratch implementational. So like, the stream that David and I did on Python and then regular expressions are a little bit more conceptual and do dive into some programming stuff. But they are separate. So the dependencies between those are not really there. So wherever you want, but game specific stuff I would probably do tic-tac-toe or memory card. And Asley herself actually suggested memory card, so that was awesome. And I'll say it again. If you have any ideas for games in the future, definitely toss them in the chat. All right, cool. So we have the alien class, we have the ship class, and we have the entity base class. So the base class means a class that other stuff can take the attributes of and then become a more specialized version. So a Honda Accord is a special version of a car. A car would be the base class, it's an abstract idea. An entity is an abstract idea. Automobile is an abstract idea of which a car is a subclass and a Honda Accord is a subclass of a car. And then a Honda Accord EX would be a subclass of the Honda Accord. But the EX might be a property of that Honda Accord rather than a subclass. But that's kind of the idea. Asley said, I'm not the first one to suggest it but I'd really love to see the typing test game. I'll think about that one. That would be fun. I would actually like to do that. And that one wouldn't be too hard, either. But yeah, so we've looked at the idea of object oriented hierarchy now. So again, just to recap and make it a little bit more clear what we're talking about, we have the entity based class. And so this would be base class, or parent. And then these would be subclasses, or children. And I can't write super well on the edge here looks like-- children. And this is like day one object oriented programming stuff. But that's essentially what we're doing. And object oriented programming is very prevalent in games so I would encourage you to try to understand what this means and think about ways that you could create your own object hierarchies, think about the real world as object oriented to get your mind used to the idea. And then think about how you can structure your own code to be object oriented because this way you can save yourself a lot of work. We talked about it earlier, how now the ship doesn't need to have its own x, y definition, the alien doesn't need to have its own x, y definition. They both don't need to have their own sprite definition. It's a simple example. I was kind of hesitant to use it just because there's not a lot of data that we're trying to save here. But the idea is simple enough to illustrate the concept. And the concept is very handy, especially as you work on large code bases. So that is object oriented programming, day one. And we might do a stream on more complicated uses of object oriented programming. This concept of the child class adopting what the parent class has is called inheritance. So, I'll write it here. This is inheritance because these are inheriting from the entity. They're inheriting the attributes and the functions of the entity class. And there's other ideas, like polymorphism and composition that we can take a look at in another stream where they become pertinent. But that is the idea. I'm interested in more than games but I do love games. Really appreciate the advice. Yeah, absolutely. Thank you, Truesound, for joining. Glad you're enjoying it. If you're starting from the beginning of CS, CS50.edx.org if you aren't already taking that, says Brenda. Yes, shout outs to CS50. Can you come to the same game without using object oriented programming, like only using functional programming, says Un3okapi? Yes, you can. And if you mean functional programming in the sense of like Haskell or F sharp or functional JavaScript for example, yeah, absolutely you could do it with that. Or you could do it with procedural programming, if you're referring to procedural programming or imperative programming, being like your data is encapsulated in functions that you call and your logic goes top down, which is what C is. And you can actually do it in that as well, because most games actually were all implemented in C back in the day. It was C-- assembly, and then C. There were really no ubiquitous OOP languages in game development prior to the late '90s-- I mean the mid '90s, just because C++ was relatively expensive for the console hardware at the time, although it was used in the mid '90s to late '90s for DOS programming and stuff like that. But if you mean-- either way, functional or imperative slash procedural, which the two names kind of get mixed up with each other but they're very different. You can make games in both of them. You can make games however you want. All programming kind of, at the end of the day, is just a different way of thinking about solving the same problem. So if you can get comfortable in a particular environment you can do pretty much anything in that environment. Studying a bachelor of computer science at Deakin University in Australia, but I figured CS50 would be good supplement material to that. Well, thank you for tuning in. I'm glad that you are finding it to be so. Polymorphism is where I am stuck in Java. Well, take some time to get it, says Bavich_knight. Polymorphism is an interesting idea. It is essentially just being able to use an interface to talk to objects as if they are all sort of the same kind of object. This applies really well in the context of-- like, generic programming is kind of using that idea where you can have lists that use the different data types, that contain different data types but they work the same way. You can have, for example, a monster base class that your function does things to, regardless of whether it's a goblin monster or a elf monster or a demon monster. Like, no matter what it is, because it inherits from Monster, any function that operates on monsters also operates on goblins, elves, demons, et cetera, as long as it implements that parent class. Anything that operates specifically on goblins won't affect elves, demons, et cetera, because polymorphism doesn't apply to sibling classes. It only applies to the classes that implement from a particular common parent. And that's what polymorphism is, just basically allowing different classes to sort of function, behave, differently according to the same interface, or behave similarly but differently using the same interface. It's a bit of a weird thing. You kind of have to actually use it to understand it completely. But it's pretty simple once you get the hang of it. And you'll see it quite a lot. Staypeaceful, thank you very much for joining. Started creating a Pong game in Python. Ooh, Pygame, yeah, that's-- well, Pygame might be what you're using. You might be using something else. You might be using PyGL or something like that. So, yeah Python would be great to make some games in sometime as well. Un3okapi, got it mixed up, yeah. Functional programming is very fun as well. So check that out. Functional programming is super cool. I want to get better at that. Functional programming is definitely a different way of thinking. Hi, Colton, thank you very much for the work you are doing, Unsigned. I started in Edx but I got stuck in Pong because I couldn't find the file font.ttf. So If you could please guide me would be great. A big merci from Paris, says Unsigned. Font.ttf just means, I think you just need to have any font.ttf file in your project, so font/font.ttf. Go to dafont.com if you don't have the specific font that we downloaded. So dafont.com, go to pixel bitmap fonts, go to any font. I think we use Press Start to Play in the course, which is on the first page actually if I can find it. Right-- not this one, sorry. Which one did we use? We used-- I forget offhand which one exactly we used. But just name it font.ttf. Put it in a font/font.ttf folder or in the base directory where your main .lua is if the string is just font.ttf, and that will load it. OK, cool. So, let's go ahead and do some actual drawing of stuff. So, ship.lua, we're going to-- also, another cool thing. Because all of the ship and the player-- sorry, the ship in the alien class don't really have different logic in terms of how they render stuff we can just say, entity render, define an entity render function, and just say love.graphics.draw gtextures aliens and gframes aliens. And then remember, this is where we can actually make it different for each individual alien or each individual ship. We can just say, self.sprite. And then we want to say self.x and self.y. So this function here will take whatever this current entity that's calling it and use its self.x, its self.y, and its self.sprite, and draw it on the screen. So now what we can do is, first we have to go into dependencies and we have to actually include all these files. So in dependencies.lua require a src/alien, require src/entity, and src/ship. And what I like to do is alphabetize everything. So notice that we're requiring the library stuff first. So this is actually something-- this is not an alphabetical thing, but more of a semantic detail where I have the libraries being loaded first so that they're at the very top. And then all of my own code is loaded after that. And everything here is in the same directory, which is the source directory. But every module that we're importing follows-- it's in alphabetical order, so alien, constants, entity, ship, util-- just easier to scan through the list and see whether we're missing a module or not. Because if you have things out of order it's very hard to mentally sift through all of your requires, especially if this were like 10 times longer than this. It can be a bit overwhelming. So I'm going to go into my main.lua and I am going to create a ship. So, I'm just going to say ship is equal to ship. And we're going to put its x, y-- we want to be at the bottom of the screen approximately. So we can say virtual width divided by two minus alien-- do we have, is it alien size? What is the-- alien size. Minus alien size divided by two. And then that's going be at virtual height minus 32. It's an arbitrary amount. Could make it 24 as well. Let's do 24. And then it also takes in a-- because remember, we inherited from entity. So notice that alien doesn't have an init function. Ship doesn't have an init function. But entity has in an init function. And because any alien and ship is also an entity, because we did that includes equals entity, it'll have access to the init function. The init function being this ship constructor here. This ship with parentheses, this is the same thing as the init function. This is just the way that the class library works. So I can specify, then, as my third parameter, if I'm not mistaken, the sprite. And we didn't actually decide yet what we wanted our sprite to be. So let's go back into the sprite image. We had some suggestions in the chat. I think the last one I saw was Bavich_knight say this one right here, the cyan one. I think Asley also recommended another sprite. I'm not sure-- I thought it was sort of like a small yellow-- oh this one, right here. So between the two. So let's vote in the chat, between the two, the contenders are this one, so we'll call them red, so red ship, this is Asley's ship. And then we'll get a little bit of a competition here. And the blue slash teal pale ship, the cyan one, between those two. That'll be our player ship. Cast your votes. So JPguy saying red ship. Jacobsen, red ship. Oh, we got a blue. Unsigned saying blue. It's two to one. Blue-- oh, it's even, 2-2! Uh-oh, blue came in the lead. Blue came in the lead! Oh, it's 4-2. All the people who say red, pizza's on me. Oh, that's compelling isn't it? Red, so 3-4 it looks like. Sorry 3-5, 3-5. 3-6, 4-6. Tell you what. We'll do it fun. First of all, let's figure out what index these sprites are located at. So, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16-- because we iterated through our entire sprite sheet, row by row, and we just table dot inserted and we just went to the next line, da da da, went to the next line, da da da, they're ordered 1, 2, 3, 4, 5, 6, 7, 8, da da da up until 16. And then it goes to the next line 17, 18, 19, 20, 21, 22, 23, 24. So what we can do is, we can basically figure out which row it's on. So in this case, Asley's is on the fourth row, right? So, let's say we wanted to use Asley's which I don't think won. I think Asley's was-- it was 4-6, or 4-7. Unfair. Jacobchaussen says. People are now asking, what pizza? What pizza? What pizza is Asley going to get? We're going to say, if we want Asley's ship index, ship index is going to be equal to. So hers is on the fourth row. So basically what we need to do is, we need to multiply 16 by 1 and then add the x offset, right? So multiply 16 by the number of rows. I guess before the actual-- before this one, the number of rows, so in this case it would be three. So three rows plus 1, 2, 3, 4, 5, 6, right? So, 16 times 3 plus 6. Right? So, 16 times 3 plus 6-- so 16 being the number of aliens per row and three being the number of rows before our alien and six being the x index of that row. And then I'm going to say, this is ship index right here. So if I load this and I run this, is it not visible at the bottom? It might not be. Oh, you know why? Because we're not rendering the ship. So we actually have to call ship render. So is it up here, right? Yep, ship render. So now it's still not rendering. So, let's make sure that it is-- let's try and render it. Let's set it up. The screen might be too small to see it, actually. So let's say 64. Let's make sure that I am seeing it appropriately. Oh, wait. No, I did that right. Yeah, I did that right. Right? No? Uh-oh, live coding time! What did we get wrong? So virtual width divided by 2 minus the alien size divided by 2. This is correct. So that's the x position that we're going to draw the ship at. Entity draw self.x, self.y. Yep, that's correct. Lua knows I didn't win and refuses to render. But we're drawing your ship. This is your ship. OK, so let's figure this out. So 16 times 3 plus 6, that's the ship index. Virtual height minus 64. So that should be in the middle of the screen. Love.graphics.draw aliens. Ship-- oh, this is why. So, if you define an empty render function in the subclass it will overwrite the entity render function. And that will make it draw nothing, which is not productive. So now we have the ship. Now it's there on the bottom except it's the wrong ship, unfortunately. So where did we go wrong? It looks like it's-- wait, is it this one? 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16. 1, 2, 3, 4 5, 6. Did I do the math on that wrong? I don't think so. Let's see, 1, 2, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8 [INAUDIBLE].. So why is that not drawing the ship index, the ship, the correct ship? So if I add-- OK, so let's add nine to that. See if it changes. Why is it-- is my math wrong? That's the right ship. Are there 17? Are there? 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16-- no there are 16. I count 16. 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16. I count 16. No, the sprites are not padded. The sprites are not padded at all. Huh. What in the world? 16 times 3 plus 9. What? We didn't screw that up, did we? Oh. So this is why I like to start for loops in Lua by one, because the loops are inclusive. So we actually created quads that are one tile past the edge for each row. It's not that it was zero. It's not that the table was zero indexed. It's that the quad generation was zero indexed. So what we needed to do was, we actually needed to say, until sprites told minus one, because the loops are inclusive, for one until some range. And it includes that range. It's not a less than or equal to-- or, not a less than, it's a less than or equal to for loop. So that was the bug. So modify, if you're following along, your loop to be minus one there. And now if we go back to main.lua and make that six, boom. It works. So that was fun. You'll also notice that the background color is black and the sprite background color is a slight purplish look. The reason, Andre, is because they one index everything so it has the same effect for a lot of algorithms. The reason that it's less than for most algorithms in C and other languages is because it's zero indexed. The bug, Asley, was the ba ba ba ba ba-- in our util.lua. Because we decided to zero index this loop and I one index in the course. But we zero indexed it just to make the math here for the calculating the quad a little bit easier. You don't have to minus one on the x and y anymore. But we do have to minus one from the sprite's tall and sprites wide to the end of the loop because it's inclusive. It's not exclusive like it is in for loops and like a less than loop in C. It's like a less than or equal to loop. So you want to make it minus one because we were going past the edge of the texture and basically creating an invisible sprite every 16 sprites. So we were essentially filling our sprite table with empty image data-- not useful. But now we have a sprite. So let's move it. Let's move the-- oh, sorry, sorry, sorry. I wanted to humor both both people that decided to contribute an idea for their-- so, shout outs to Alsey and Babic. Their names are now in the source code. Ship index Babic will be-- and am I spelling that right? I apologize, Babic, if I'm spelling your name wrong. Nope, I'm spelling it right. Babic decided we wanted to use the sprite down here, way at the bottom. Where is it though? Right here, this one. So it's actually kind of like the opposite. It's the very bottom here. So, which row is that? That's 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12. So it's 12 and then 1, 2, 3, 4, 5, 6, 7, 8, 9-- 12 and nine. So now if I wanted to use Babic's I would say, ship index Babic. And there's Babic's ship. So shout out to Babic. But we can do it even better. We can say, ship index equals math.random 2 is equal to 1 and ship index Asley or ship index Babic. This is a pattern for the ternary operation that you would see in C or Java, which is the question mark colon. So now we have a random chance of getting either of these two. So math.random 2 will give us either one or two. And if it's one, then ship index Asley, or ship index Babic. Easy. So, 50/50 chance, one or the other. And what we're going to do is make sure that we seed our random number generator. So math.randomseed os.time. And then if I run this, this time is Babic. This time it was Babic. This time it Babic. This time it was Babic. Hopefully I didn't get this wrong. Did I get this wrong? Maybe I did. No, the random is equal to in ship index Asley or ship index-- oh, sorry, because I didn't actually write ship index in here. That'd be my bad. There's Asley's, there's Babic's. So now we get both. Both of the ship contenders are in the game. Yes, I apologize. I'm a little-- the chat is now catching up to let me know that I was wrong. I caught the bug quickly enough. But thank all of you for letting me know. JPguy with the-- what's the name of that emoji? Crap, I forgot. Pog Champ. Game knew which ship was better, since Isotv. Ooh, burn on Asley. But yeah, so now we can humor both sides using awesome random number generation and say a 1 in 2 chance that it's either or, right? So now we have Babic or Asley. We don't have to hard code one or the other. Now what I want to do is, I want to test to see whether the user has pressed left or right on the keyboard so we can move the ship left or right. And thank you Rangero T-W-T, Range-o-tweet, if that's how you pronounce your name, for following. Thank you very much. Change that index there. Hides all the pizza from Isotv, says Asley. OK, so I want to say if the user's pressed left or right, move the ship left or right. Now, what we've normally been doing is in love.keypressed we've been doing that. Love.keypressed is a callback function that executes only when the key has been pressed one time and will not trigger when you hold it down. What we want to do is hold the key down and move left or right. So, Love has a handy feature, a handy function, called love.keyboard.isdown which can take a string key. So you can say, if love.keyboard.isdown left then-- remember if statements need thens-- ship.x is equal to ship.x minus ship speed times delta time. And we haven't defined ship speed. But remember, delta time is how we scale any movement operations such that if one frame has elapsed, we'll move one frame's worth, one sixtieth of a second's worth, of that whereas if two frames have passed, it'll be twice as much, the delta time will be twice as much, so it'll move twice as much the amount. So it will effectively scale to however much time has passed since the last frame, which won't be much of a problem in this example. But when you do have games that are more complicated and a lot of time can potentially pass between an individual frame and you want it to run smoothly and consistently on different people's computers, you want to scale it by delta time. If you don't, this will run as fast as the CPU is capable of running it and that will not serve us any good. Now, this is a design decision. You can say, do I want to put ship speed in the ship class? Or do I want to put it in the constants.lua? And I don't know if I have strong feelings on it. But probably putting it in constants.lua will be what we do. And let's say I want to move-- every second I want to move 80 pixels. I'm not sure how good that's going to be. A second is a fair amount of time and that's about a third of the screen. That might be too slow. Let's set it to 100. We'll see how that feels. So remember, since this is getting multiplied by delta time, this is effectively how much time the ship move per second-- 100 pixels per second. So now that works. If-- and let's say else if here-- love.keyboard.isdown right, then. And I had a bug where I did-- there's a subtle bug where you can do elseif and that creates a new block and triggers a syntax issue. So be careful about doing that. Elseif is different syntax in else if. Elseif as one word can trigger scoping issues differently than else if. I don't know if I said that right. The two are different. Ship.x is equal to ship.x plus ship speed times delta time. So now if this works, which it doesn't. The reason that it's not working is because I'm not actually calling the update function in main. And that's important. We want to actually do that. I'm going to go to main. And I'm going to, in my update function-- notice that love.update takes in delta time. So you can pass that delta time to anything you want. And any class or any object that needs to do these overtime operations can scale that operation with that same delta time value, that same float, right? So I can say, ship colon update delta time. And now I can move my ship left to right. Now unfortunately, there's a bug where it moves past the side of the screen. We don't want that to happen. That fortunately is pretty easy to fix. I can just say math.-- what is it? Math. It's going to be the greater of these. So, math.max will always set the value to the greater of two values. It will return whatever the greater is of two values. And basically by saying the max of zero or this subtraction on the ship.x, we're basically saying if it goes less than zero, if we set the x to negative 1, well math.x is going to say that 0 is greater than negative 1. So it's always going to only set it to zero if we try to move beyond that point to the left. So it's effectively clamped our value within the left border of the screen. And I do the same thing with math.min to virtual width minus alien size-- or alien size is fine because the ship's the same size. Do the same thing with math.min because math.min will return the lesser of two values. So if I'm smaller, if I'm at an x-coordinate that's less than the virtual width minus alien size, which will be taking into consideration the alien being 12 pixels and reaching the right side of the border, we don't want it to go farther to the right than that-- the value of that is going to be less than the ship increasing its x by some value if we reach the edge of the screen. And so it clamps it to the right side. And my hands are in the wrong spot because I'm flipped-- right, left. But that's what math.max and math.min-- those are hard to say, especially when your mouth is dry. Those are hard to say but those are the functions that allow you to restrict a value from exceeding some value in the negative or positive direction-- math.min, math.max. And so now I should be able just move left like that. Pressing left, can't go any farther than that. Pressing right can't go any farther than that. So we've effectively clamped our value within the edge of the screen. So we're getting and closer to the Space Invaders actual implementation. After pizza, liquid beverage [INAUDIBLE].. I would guess the ship because the aliens have their own specific vertical speed, right? I apologize I don't remember what I asked. Keep the information encapsulated. Hopefully we don't run into another wall passing entity like with Pong. That was unfortunate. 3D physics can be quite challenging. But Ereney did a great job helping us fix that. That was awesome. I think I missed that stream, but I had the same thing happen in the Pong version in Love 2D in the course, says Majordufai. Oh, yes, because in the course if you-- we don't implement interpolated collision detection. So if-- I appreciate Bavich_knight, three sodas for Colton. If you don't interpolate your collision detection and you have something increasing in speed, which is what we did in Pong. If you kept playing the game long enough the ball would just keep going faster and faster and faster. If it gets to a certain point between one frame and another frame it'll actually move beyond the paddle. And so you won't be able to do an A, A, B, b collision check between the two objects because they don't ever overlap. It moves so much in one frame that they never touch. So you need interpolated collision detection for a case like that. And interpolated collision detection just means between frame A and frame B, if the distance between them is larger than the size of the object then you need to basically check for every object size, check the gap of every object size between the object and the target-- sorry, between the object and its current position between the two frames and then check for A, A, B, B on each of those points. And it's a more sophisticated collision system. And you're thereby adding collision checks, more collision checks than one every frame which can get expensive. But that's how you do more realistic collision detection and that's how you would want to ship ultimately something that can function properly for an actual release, if you're going to release your game. Otherwise you have a bug. But yes, good point Majordufat for mentioning that. OK, cool. So we have ships. Sorry, we have one ship. And we have a ship that can actually randomly change to accommodate both Asley and Babic's artistic decision, so two ships. Now what we need to do is decide how to make aliens appear. We're approaching the three hour mark. We're probably going to keep going for a little bit longer so we can kind of cement this, because we're at a point where-- I don't think we can comfortably leave the stream with as much as we've implemented today. But we have the ship moving. The next big piece would be having aliens appear, so having a bunch of aliens in a grid at the top of the screen, like we do in Space Invaders. And then another important piece of the puzzle is having the ship be able to fire projectiles. And the projectiles are fairly easy. The aliens are fairly easy. We might be able to do that within the stream, and then maybe finish the next stream with actually making the aliens move down, have different levels, have a score, and all that sort of thing-- and be able to die and lose lives, and have lives, and have a screen that tells you how many lives you have. That could be a good part two of the stream. But today we'll try and get all the aliens on the screen with the laser shooting from the ship and call it a stream for today. So, alien is going to be the class that we use for this. And what I think would be the best way to do this is to simply have a table of aliens that we can just iterate over and render, right? So I can say-- actually, let's do-- let me go up here and here? I think it's aliens. So I'm making his global. And I'm actually not defining them as galiens, just because in this particular context it's a little bit-- doesn't feel right. I guess technically you'd probably want to do it, galiens, gship. But we're not going to use them throughout our project. We're only going to use them in main.lua. So it doesn't really necessarily make sense, but you could go either way. But I'm going to create a table. And basically all I want to do is, I want to say for y is 1 until-- how many aliens do we want wide and tall? How many was it in the actual Space Invaders game? Let's go into this, Space Invaders. We have 1 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 4, 5, 6, 7, 8, 9, [INAUDIBLE].. Looks like 11 between both of those screen shots. So we'll do 11, 11 ships wide by five tall. OK, so ships tall, it's going to be five. Do for x equals 1 to ships wide, do-- and ships tall, that'll be five. Ships wide will be 11. So we can go into constants. We all need util any more. We can get rid of that. I don't know if we'll need entity anymore. I'm going to just close that. Ship we'll leave open for now. Alien we'll leave open because we will need that. And dependencies we will need in a little bit. So constants, ships wide equals 11 and ships tall equals five. Remember, avoid magic numbers. So, generate rows and columns of aliens. So again like I said before, the 2D loop, the iteration, this pattern, we've seen this many times. We saw it before when we did Snake, when we had the grid. We saw this in the memory card game where we had cards laid out. We saw this in the-- oop, what did I have? Yes, not ships wide says Brenda, aliens wide. Yep, you're right. Thank you very much-- aliens tall, aliens wide. Let's fix that. Yes, ships, since it's not the ship, yes. Yes, correct. You get 256 sprites, well, minus the two for the ships, from which you can choose, right? So if you convert a random int in one 256 position of the grid it makes it more readable and less of a headache to implement. Makes it more readable and less of a headache to implement. [INAUDIBLE] JPguy, we are getting to selecting the sprite but I'm not sure what you are referring to because this particular part here is going to create the alien objects. Oh, well, that's even easier. That's even easier, yeah. No, yeah, to generate a random alien yeah, we just need to pick a number between one and 256. Absolutely. But what we're going to do for now, we're just going to create a new ship. So table.insert aliens. We're going to insert into the aliens. I got something in my eye. Ouch. That hurt a little bit. Into the aliens table we're going to insert a new alien object with the-- and we're calling the constructor, the alien with parentheses. And I'm going to say we want to generate them in a grid. So we're not going to worry about the perfection of the grid at this moment. We're not going to make sure it's perfectly centered and aligned. We're just going to say, x-- [INAUDIBLE] off of x? Oh, you know what I should do also? Is get rid of this arbitrary draw call here, love.graphics.draw, because it was just drawing an alien in the top corner. Sirthranduil56, thank you very much for following. We'll start off at the top left. So we'll say x times alien size and y times alien size. And then this is what JP was referring to. We're going to say math.random 256. And what we really should do is say, aliens-- is that what we want to do? Do we have any constants in here? We have the width and height but we haven't made this constant. So we'll just say 256. 256 for now is fine. And so what that's going to do, that will now give it a random sprite value. So we are creating a table of 55 aliens. We are giving them a random-- or, not a random x. We're giving them an x and y that's deterministic by this nested for loop we've created. So it'll create a grid. They'll be right on each other, so they're not spaced out. They're not padded, which we probably want to fix. We can do that. We can do that fairly easily. We're going to not only generate them, but we have to actually render them as well in our draw function. So not only we're going to render our ship, we're going to also render in a loop, draw all aliens, again following this 2D pattern that we've seen time and time again. For y is equal to 1 aliens tall do-- or actually, no. We don't have to do that. Because each individual alien is keeping track of its own x, y we can do something even simpler. We can use iteration. You can use Lua's pairs enumeration function, iteration function. So we can say for blank alien in pairs aliens do. And if you're unfamiliar with what this is, this is how Lua does iteration over a collection-- iteration when there's only one collection type in Lua which is the table. And so it receives a key value pair for every index in this function call or every result of this function called pairs. This function called pairs is part of Lua and it takes a table as an argument and it will give you every key value pair in the table. And we don't care about the key because we only did table.insert here. Table.insert just inserts things starting at one. So all the keys are going to be 1, 2, 3, 4, 5, 6, 7, blah, blah, blah, blah, until we're at 55. We don't care about the key. We care about the alien itself. The value is the object, the actual alien object. So I can just say, alien render at that point. Because remember, alien-- and we have to make sure not to overwrite the render function. Alien inherits from entity, and entity has a render function that just draws itself, its x, y, and the sprite that it has reference to. So super, super basic. It's just going to maintain a reference because we specified a random sprite index here as part of the constructor for the alien object. You'll have a reference to that. Each individual alien object has a reference to that, self.sprite, and has its own self.x and self.y. So we can therefore draw it. So if I do this, I have a bug. Main 59 alien render-- render. Did I not to-- include equals entity, yep. Mm-hm. Entity does have a render function, yep. Something is wrong. What did I do? Table.insert into aliens blank in pairs aliens do-- uh-oh. Live coding, here we go again. Attempt to call method render a nil value. Did I-- I did include alien, right? Yep, I did. OK, it's definitely inheriting from entity. Alien is doing that, includes equals entity. The init function-- yeah, it is inheriting from the init function. What's going on? In pairs of aliens to alien render, yep. There's a render function. This is very strange. We can do it, chat. We can figure it out. Let's make sure-- so it's saying it's trying to call the method render which is a nil value, which means that the alien object that it's getting from pairs aliens doesn't have a render function. But that's not-- wait. Aliens is here. It's global. We have access to it. This is where I want to have-- Twohourstrike, should it be called class with the curly brackets instead of the curly brackets? No, you do need the curly bracket. So in Lua, if a table or-- what is it? How does the exact syntax go again? If you have a function that takes a table as its only argument, you can call that function using dictionary syntax. Sorry, I keep saying dictionary interchangeably with table. You can use these curly brackets as the function call. So for example, I have a function called hello which takes in a table, in print tab.text. So let's assume that I wanted to call hello and pass it in a table then like that, which has a key called text that has the value of the string, hello. So calling this function will basically print whatever that table's text field is, which is hello, right? So it'll just print hello. However, part of Lua allows you to actually do this. If your only argument to a function is a table, you can just call hello with the curly brackets. And it's the exact same thing. And that is what the class, class, does is, its only argument is just a table and so therefore you can call class with just the curly brackets. Do we need the parentheses around the alien in the for enum loop? Yes. Yes. The-- oh, wait, sorry, in the underscore alien? No. No, you do not need parentheses around these two. These will work just fine. Why is alien not rendering? That is so-- that bothers me. OK, we have a subtle bug here that we need to figure out. Oh! Nope, I figured it out. It's stupid. Well, it's not stupid. It's a good thing to take into consideration. Because alien was imported before entity, the alien constructor-- this still gets called. This gets called when we require it. When we call require, this executes this line. And when you see this dash dash includes equals entity, it's going to look for this thing called entity but because it actually came before this, it's not going to know what that is. And so it's going to be nothing. It's going to be nil. So this actually needs to go up above this. Entity, when you load base classes because the way the library works, you need to import that first. And then what do you know? It works. But yeah, that was the bug that we ran into. Be careful when you're doing that. Base classes do need to be imported first. JPguy says, damn. All right, well again, that was cool. Now we have aliens! Now we have random aliens rendering to the screen, right? Good thing you discovered that. I would never have found it. That is, yeah. It's something that I struggled with at one point, I think. It was a subtle issue. Yeah, it can be tough to reconcile the alphabetical stuff with that. But it's not too big of a deal often, because it's not usually huge dependency chains where a bunch of stuff out of order relies on other stuff out of order. It's kind of like, we can kind of do that and kind of make them separate. And so now it's alphabetical and we've sort of separated the idea of what those are-- these being dependent classes and these being parent classes. But yeah. Quick call it entity AE. I don't-- oh, AE entity, yes. Yeah, yeah, that's beautiful. Good thought, good thought. OK, so we've got our aliens all running to the screen. And the other problem is, if you're running it, if you're running the code, you can see that the color is very different. You can especially see it now that we have all of those ships rendering. It may or may not be easy to see on stream. I can certainly see it on my screen here. And you can probably see it if you're running it on your screen at home. So what you can do-- on a Mac at least, and because you're on a Windows machine-- if you're going to Windows machine you won't be able to do this as easily. But you can't require entity in alien, says Math08. You can. You can certainly do that. Putting it all in one place is better practice, but yes you can require it in alien if you want to. And I don't recall offhand whether Lua re-requires it at a certain point and either overwrites it or includes it twice. I think if it knows that it's already required it, it'll be smart enough to not do it twice. It's been a while since I've looked at it. But yeah, that'd be another way to do it too. You can require entity in alien so that you don't have to worry about the dependencies getting screwed up like that. But then you're searching through potentially dozens or hundreds of files looking for proper dependencies. So it can be kind of a double edged sword, so I'd be careful about doing something like that. Oh, Jacobchaussen says, why not change the game background to match it. Well, good thing you brought that up. That's actually exactly what we're going to do right now. So I'm going to render the game. I'm going to use an app on my Mac called Colors. I'm going to click on this, click on my little handy dandy color picker, click on that pixel. It's going to tell me somewhere-- well, it should have. It used to. Why-- well, now I feel like kind of an idiot. Normally, this application tells you what the color is, spectrum. Thank you very much, Meeedo-- what is that? I can't read that. Meeedooe, thank you very much for following. Crap, where's the-- normally there's a-- it gives you the hex code. Where did the hex code display go? Developer? Grayscale slider, RGB sliders-- oh, there we go. 20, 10, and 30. So those are the alpha-- sorry, the RGB components of that background color. So twice as much red as green, three times as much blue as green, and therefore a little bit more blue than red. It adds up to be kind of this off purplish color, which you can't actually see individually on the color picker here by itself because it's not juxtaposed against the pure black background. But you can certainly see it if you're looking at the black background with the sprites. So, what we're going to do, take that color picker. And then I'm going to-- so, it's 20, 10, and 30. So what we can do is, get rid of this. 20, 10, 30 RGB, 20, 10, and 30. I can quit Colors, or at least get rid of it. Let's quit it. Now, Love 2D changed the way they did RGB with 11.1. It used to be that you could just literally write in 20, 10, 30 in your Love.graphics.setcolor. Now we actually have to do it a slightly different way. So here's what we're going to do. I'm going to say, Love.graphics.clear. Love.graphics.clear will just clear the screen, will just wipe the screen with a given color. So, 20, 10, 30 was how we used to be able to do it. We can't do that anymore. Now we have to basically say, what is it? It's over one, so it would be 20 divided by 255 and then 10 divided by 255 and then 30 divided by 255. You don't have to put the parentheses there necessarily. But that is how you now have to do it, because now the love.graphics.clear, love.graphics.setcolor, all those functions, they take in numbers from zero to one. So by dividing all of those numbers that we just got by 255, which is the upper bound for any of those numbers, we get a value from zero to one. So if it's 255, it'll just be one. If it's 20 it's going to be whatever the fractional amount is over 255. So that will give us the color that we want. If we render the background now, it is indeed the same sort of purple black color and everything looks homogeneous. Not homogeneous, consistent. So, yes. Lua requires once, says Twohourstrike. OK, good to know. How can we stop Lua from rendering the same aliens twice? Well, a couple ways you can do that. You could have a table within which every time you generate a new alien, you just insert that value and then you check to see on every iteration whether that alien index exists in the table. So just fill a table with numbers and basically do a check to see if that number is inside the table. Good question. And you could optimize for speed by setting the actual index, the actual key of the table to that number, rather than making them values. So that way you can do a simple almost constant time check to say, if index table bracket five, which will return a value. You can set those all to true and then that will end up giving you the correct Boolean value for that. But yes. I don't think we'll implement that in this case. But you could certainly add it fairly easily. I would encourage you to try and implement it yourself actually and let me know if you can or if you have any luck doing so. Nestleeric, or Nestleeric says, finally caught a CS50 stream. Feels bad, man. Yeah, thank you very much for joining. Glad you came. It's about to end fairly soon. We're going to finish a couple of things. We're going to do the projectiles, or shooting up towards the screen, and then maybe lay the aliens out a little bit nicer. And then we'll have the foundation upon which we can actually do the game behavior for the next stream. And this stream was a little slower and didn't cover quite as much, we didn't get it quite as far, I should say, in the game implementation as we have done for prior streams. But this stream is a little more complicated. We've been covering new stuff, like the texture splitting and stuff like that. So it's therefore in classes I've talked about object oriented programming and using that and getting more modular with the design of our game. But I'll post this on GitHub and everybody can mess around with it a little bit. And then on the next stream, which probably will be-- it's going to be tough this week because we have the hackathon, so Nick and I are streaming tomorrow. And then Andy and I-- Andy Chen, who is going to talk about biostats in r, the programming language r if you're familiar, he's going to come in on Wednesday. Jacobchaussen, sorry host, what's your name? My name is Colton Ogden, so thank you for joining for today's stream. You've been in here for the whole stream. It's been a pleasure having you. We're not done just yet, but just to give you a sense of what we're doing for the week. We have that CS50 hackathon this week on Thursday, which is a very busy, very long event. We probably won't stream on Thursday. And it's an all nighter so probably won't stream on Friday as well. And then it's the weekend. We don't typically stream on the weekends. The week after that I think we'll probably have time to finish it up. If not this week, definitely next week we'll finish it up. But we'll at least get it off the ground so that you can mess around with the code if you want to before the next stream, and I encourage you actually to tinker with it just to get a-- try and see you would do it compared to how I would do it for the next stream. OK, so we've done all of that. Aliens are now getting drawn to the screen. We have the ship moving. We have the same color background so everything looks good, looks consistent. So what we need to do next is shoot stuff. We want our ship to be able to move, and not only move but also shoot bullets, shoot lasers, shoot projectiles. And I'm just going to make a projectile class. These top level comments aren't super helpful in this instance, but generally you want a top level comment detailing how your class operates. And the projectile itself you could think of as an entity. It has an x and a y. However, it doesn't have the same size as everything else. And I'm not actually going to draw it with a sprite. So, I think I'm going to make it its own separate class without deriving from entity. We're not going to derive from entity. We're not going to worry about inheritance in this case. Create an init function, our constructor. We will need a update function. And we will need a render function. And we will also need to make sure that we require it so that it can actually be used. And so now, what we want to do is basically say, in our main.lua-- and we could do this differently elsewhere. We could do this in the player class. But the Player class will therefor, if we want to modify the list of aliens, which actually we do anyway, so. Essentially what we're going to want, the projectiles are going to be their own objects. And the projectiles are going to have a velocity. And it's going to be negative or positive on the y-axis. Shouldn't projectile be required after ship, assuming that the projectile will fire at the ship's position and that will need access to that information? No, actually it will not. The only reason that we need to require the-- it's good instinct, yes, and as I said, it's good thinking. It is good instinct. But technically we don't need to do that. The reason that we needed to, for example, include the-- I forget what it was, alien? We needed to include entity before alien, is because this code, this line right here, executes on require. So when require actually imports this file this line gets executed. And that includes looking for this thing called entity because we make a reference to in this call right here, in this table definition. However, if we only make a reference to a projectile in a function-- so in this case, any of the ship's functions or the alien's functions-- that code does not get executed on require. So the symbol, projectile or whatever, if the projectile symbol itself gets called-- which it will because we need to instantiate a projectile in the update function-- that doesn't get called on require. So by the time that actually gets executed, require has gone through every single module and required it in advance. Does that make sense? It's different when you're calling it from a function versus when you're actually calling this top level includes statement. It's a little bit of a different use case. But good intuition, good intuition. So, in the ship class, basically in ship update-- you could do this in a couple of different ways. You could do this in main. I could do this in love.keypressed. If I do this in love.keypressed, it's a global operation. Pressing space is not something that the ship really does. It's more the global game object. So because projectile isn't vital to make ship compile or vise versa, the order doesn't matter. Got it. Essentially, yes. There is no technically compiling going on. It's more, the time at which it looks for the symbol projectile is different in the context of it being called in a function versus it being at the top level of the module. When you require a module everything gets interpreted top to bottom. But function bodies don't get called. They only get called when the function itself gets called with parentheses. The function bodies are just, it basically just tells Lua that this function exists in this place but it isn't run through the code. It's just saying, when you want to run through the code, when you want to call this function, it's here, it's in this location, if that makes sense. Hope that makes sense. So it's kind of not ideal to look at love.keypressed, the global callback function, as the source of firing projectiles. For escape it kind of makes sense because you're looking at the whole game and trying to escape the whole game, as not the ship but kind of the player of the game. It's kind of a player action. The ship action, even though you're controlling the ship, it's not as wide reaching. You're only controlling one ship and the ship should have control over when it fires the projectile. And this kind of comes down to how you sort of see the game. And there's multiple different ways you could see the game and program this. What I'm going to do is, I'm going to do it in the ship class. I'm going to say, if love.keyboard.waspressed-- and this function doesn't exist, by the way. We're going to make this function ourselves. If love.keyboard.waspressed space then table.insert projectiles projectile up. Now, this might look a little bit strange-- rather, not up. Not just up but also x, y up. And this x, y should actually be self.x, self.y up. A couple of things here. So first of all, we don't have access to this projectiles table. And we could do this in multiple ways. We could have a global projectiles table which is in our main.lua, which is one way you could do it and it would be fine for a game like this. You can also pass in projectiles to the update function. And this is a bit more of a better engineered approach to doing it. And this is probably how I would encourage you to do it. I would say in main.lua, therefor, we can say local projectiles equals an empty table. And now this projectiles table is in our main.lua but is not global. We can't edit this arbitrarily across modules. This is only visible within main.lua. So we've tightened the scope on its accessibility, which is important for something that is going to be accessed by multiple things in different modules. We're making it harder to screw it up, making it harder just willy nilly add stuff to it. And ideally the ship should be this as well, and the aliens should be this as well. So I can actually say, local aliens is equal to that and then get rid of this, and then local ship. And we're not going to instantiate the ship in this case. But we'll instantiate it in the load function, as we've done. And let's go ahead and, in our update function remember I specified that the ship should also update with the projectiles passed in as a parameter. So now we can do that. We can say projectiles. So now the ship, when it updates, not only will it be able to get the DT from the update function it will also get the projectiles table and it'll be able to make changes to that projectiles table. Which means we can add projectiles to the projectiles table. Now, when we add a projectile to the projectile table what we want that to mean is that each of these projectiles should update as well. And by update, this will typically just mean moving up or down, depending on whether they're going up or going down. And as we saw in the ship class we specified in the constructor for the projectile object here that it gets the string up, because if we're a ship and we fire a projectile we always want that projectile to go up towards the aliens. And we want it to take an x and a y as well. So this is how we get access to information that's in our main.lua, but within our ship class. So we're modularizing our game a little bit more by doing this and passing a bit more information across functions, but we're tightening things up a little bit. Yeah, uptime-- we should get an uptime bot. It's been up for three and a half hours approximately. We'll probably finish before the four hour mark with projectiles. And then on the next stream we'll finish everything up with the gameplay, the different screens. We can use a state machine, actually. It'll be a great way to introduce the idea of a state machine into our game engine-- our game engine? Our game here. The state machine just being a way to move between different states of our game, whether we're at the title screen, whether we're at the actual playing the game, whether at the, like, lost of life, et cetera-- game over. We'll look at that, because we'll have time on the next scene to do all of those fancy things. But here we're passing in the projectiles table. We're going to be able to add to it here. So now I actually have to define what a projectile is, how to render it, et cetera, right? So what I want to do is probably just love.graphics.line because a projectile really, really needs to be a line, just a vertical line. Whether it's going up or going down it's going to look the same, right? So love.graphics.line. It's going to take in an x and a y, so self.x, self.y. And it's going to have a-- it takes in another two arguments as well, the second x and the second y, meaning where the endpoint of that projectile is. An Isotv, yes, good intuition. Can we use the projectile table to have the enemies shoot at us? Absolutely, and we will. We will be doing that eventually. That is good, good intuition. So let's first of all fill out the constructor definitions. We have an x, a y, and a orientation. And we can say self.x equals x, self.y equals y, self.orientation equals orientation. And the draw function should also, by the way, set its color appropriately. And let's, just for the sake of ease, just say pure red-- that's it, just R001. So we'll just say 1 0 0 1. That's full red-- zero green, zero blue, full opacity. And then we want to set it back to white. White is the default color for drawing operations in Love. So, 1 1 1 1. And then once we've started drawing the line at self.x, self.y, we're going to say, self.x plus projectile-- oops, sorry. We're going to say, just at self.x-- this is going to be the same x-coordinate, but self.y plus the projectile length. And projectile length is a constant we haven't defined yet. So we can therefore define it. Projectile length is going to be equal to, let's say five pixels. And what I want to do actually is, when I press spacebar as the alien I don't want to just instantiate the projectile on the player. I won't actually instantiate it above the player. I want it to look like it's shooting out from the player because if I instantiate it at just where the player's x, y is, it's going to spawn on top of the player. It's going to look like the player's shot like from the middle of itself. It's going to look kind of weird, and especially if we do flexible collision detection where the projectile and the-- where the projectile and the-- ba ba ba ba-- where any projectile can interact with both the ship and any alien, we're going to want to make sure we don't spawn the projectile on top of the player or we'll kill ourselves, which is not ideal behavior. And actually I just realized, because we're using A, A, B, B eventually to do the collision detection for this, I'm going to make the projectile a rectangle-- love.graphics.rectangle fill. And now instead of saying an x and a y and then a second x and a y, I'm setting an x and y and then a width and a height. So in this case, the width is going to be one and the height is going to be five. It's going to start at x, y, the player's x, y. And it's going to be one pixel wide by five pixels tall. But I want to make sure that I set that to minus-- sorry, not on the x, on the y-- minus projectile size-- is it projectile size? Projectile length. Projectile length-- wait no, this is the draw function. This is not where I want to do this. Sorry. Projectile length, yes. So that is good. So this is good. I apologise if the chat is making it hard to see what's going on there. I want to draw a rectangle of type fill at self dot-- and it's going to be a red rectangle because we're setting the color to red right before it at self.x, self.y, width of one, projectile length height-- so five pixels tall. And then in the ship class, this is where we're going to actually subtract the projectile length from the y of the spawn position of the projectile, right? And then in our main.lua now, just like we drew all of the aliens-- actually, I'm going to draw, before I draw the aliens, I'm going to say for_projectile in pairs projectiles do projectile render. And now every time I hit spacebar, I should get a projectile that-- sorry, one more thing, one more thing. We have to actually move the projectile. So now if I hit spacebar, it will spawn a projectile and it will draw it, but it won't actually move anywhere because it's not getting an update operation applied to it. So that's important. We have to do that. So I can say, if self.orientation is equal to up, then-- or I guess direction would probably be better? Let's change that to direction. Orientation kind of implies like a rotation, so let's direction-- self.direction, the direction it's moving. So if the direction is equal to up, so if we're going in up direction, then I can say self.y is equal to self.y minus projectile speed times delta time. Projectile speed is not a constant we've defined so let's do that. Projectile speed is equal to, let's say, 120. I don't know if that's good or not. I don't know if it's balanced or not. This is game balancing, this aspect of choosing variables that you think will work, choosing values that you think will work, and then sort of fine tuning them as you go to make sure that they're not too overpowered or too underpowered, too frustrating. So we've done that. And then else if-- or I guess I should say else, because there's not going to be any situation in which the direction of the projectile is going to be not up or down. So self.y is equal to self.y plus projectile speed times delta time. And now if I run this, hopefully it should work. Was not-- oh, right, we didn't do the key press thing that I talked about. That's an important piece of the puzzle. So Love 2D does not give you the ability to do single key press checking outside of the main.lua function. In the main.lua function you can go to your love.keypressed function here, this love.keypressed key, and then check to see whether a key was pressed on the current frame, or the last frame. And that's what we're doing with Escape. But it doesn't work if you're trying to test for this, the single press behavior, inside of another module because there's no way to check for love.keypressed for that given key. You have to actually extend this class, the love.keyboard namespace with your own functions or figure out some way to do it. The way that I like to do it, and I believe I saw this in a forum a long time ago and I decided to adopt it, is you say, love.keyboard.waspressed index key is equal to true in that key press function, because remember this gets called in main regardless every single frame. So I press spacebar, this will fire in main. It just won't do anything if I haven't specified any logic for key being equal to Space. We've only specified if the key is equal to Escape we should quit. But this actually fires for every single key press at all times for anything we do. It doesn't have any logic, so it's just empty function calls essentially until we've defined some of our own custom logic. So we can say, love.keyboard.waspressed key is equal to true. And this is a table that we're creating called Was Pressed, part of the keyboard namespace. You can add your own stuff to any namespace you want to just like that. It doesn't exist right at this moment in time, because we haven't actually defined it. So what we want to do is, in our love.load say, love.keyboard.waspressed is equal to empty table. And this doesn't exist in Love by default. Love.keyboard exists and it has a lot of its own stuff, but was pressed? Love.keyboard.iskeydown, for example, is a function that exists. It's part of Love. Was Pressed is nothing. It's not a function or a table. It's something that we're implementing right now ourselves. So I can say, love.keyboard.waspressed is equal to an empty table. Then, in our love.keypressed callback, whenever we press a key we can say, love.keyboard.waspressed key is equal to true. However, as some folks may be thinking, this could cause issues because as soon as it happens once it'll always be true. So if you want to check to see whether it's true at any time after that, it's not really going to be meaningful. So what we need to do-- I'm just making sure I didn't actually turn off my belt pack, which it doesn't look like I did. What we need to do is, we need to reset this every frame. So every frame I need to say, love.keyboard.waspressed should be back to an empty table. And we can do that by saying, love.keyboard.waspressed is equal to empty table. And now it'll just get reverted back to an empty table once we've applied all of our updates. And all of our updates should be what has the checking to see whether there's anything in this table. So once you've done all that stuff, it doesn't matter. We can then just clear it. We don't need that information anymore. It's used, it's done, it's gone, right? I believe that is everything. Oh, nope, sorry! One last very important thing that we need to do is, we need to actually define the function, love.keyboard.waspressed. This function does not exist in Lua. So we define it ourselves. Could we also use false instead of an empty table, says Asley? No, because we want to be able to check to see whether multiple different keys have been pressed. You could have a frame where any given key is set-- whether space is set, A is set, Enter is set. If we just set it to true, any of those keys will set it to true, and so any condition that checks for any keyboard input will be true and that's not what we want. We want specifically to check to see whether Space is checked, or Escape is checked, or maybe some other key, left or right, are checked, because those will get fired when we press them. If we press left or right and if we set the table equal to true, then if you press left or right we'll also shoot and we won't move because left and right will also be checked, right? Well, actually in that case love.keyboard.isdown would fire, so it'd be a little bit different. But every time we pressed left or right it would fire, it would be true. And we're going to index into it that table anyway so it wouldn't work. We need it to be a table that we can index into. So all I'm going to do here is say, return love.keyboard.waspressed-- oh sorry, keys pressed, key. And I apologize. This should be keys pressed, and this should be keys pressed, and this should be keys pressed. I apologize if that's confusing. Keys pressed is the table. I apologize. I mentally made an error here. Love.keyboard.keyspressed is the table. That's the table of keys-- sorry, that's why I think Asley said what she said about, was pressed being a Boolean. This should be called keys pressed. I made a mistake. Love.keyboard.keyspressed is going to be a table. There's going to be all of the keys that were pressed on the last frame, because we're going to, anytime somebody presses a key, record it in that table with this line here-- love.keyboard.keyspressed at index key is equal to true. So if somebody presses Shift, then love.keyboard.keyspressed shift is going to be true. If someone presses spacebar, love.keyboard.keyspressed space is going to be set to true. So we can then say, if love.keyboard.waspressed space-- because what we're going to do, this function, love.keyboard.waspressed, this is the actual function that's going to return true or false. Love.keyboard.waspressed key takes in a key parameter. And we can say return the Boolean that's at key indexed into that keys pressed table. So if someone says love.keyboard.waspressed shift, it will return whatever love.keyboard.keyspressed shift is. And if it's nothing, if nobody's recorded shift in the table, it's going to be equal to nil. And nil and false are interchangeable for how we're going to use it, for an if statement. So that is sort of the dichotomy between those two, between the was pressed table and the keys pressed-- sorry, the was pressed function and the keys pressed table. So let me know if you have any issues with that, if I wasn't clear. I did make the mistake between the two but hopefully I was able to explain it clearly enough. This, therefore, allows us then on line 14 in the ship class here to say, if love.keyboard.waspressed space, because now whenever we press space in main.lua it will record that in the table, table.insert into projectiles a new projectile self.x, self.y, minus projectile length up. And then in main.lua what we need to do also, one very important thing, is for_projectile in projectiles pairs projectiles do projectile update delta time. And then, ba ba ba ba, make sure that's going well. We have them rendering, we have them updating. Now if I run this, this should work. Whoops, global ship at line 11. Oh, sorry, I wrote ship.x. It should be self and I happened to get lucky in that I named-- see, this is why global variables are kind of an issue. I named ship. We had previously ship as a global variable in the main.lua module. Because of that, here where I wrote ship.x and then it changed its x value, it was actually manipulating that global ship value which it shouldn't have been doing. It should have been manipulating its own x value. So here changing it to self. But that wasn't a terrible problem. But that's an example of where you can easily get global variables causing issues with each other. Now if I move left to right and I hit spacebar, well, now I get projectiles that go through the screen. The only problem is that these projectiles will go on forever and ever and ever and keep rendering and keep updating. So we could theoretically have like, hundreds of projectiles-- thousands, millions, I mean infinite, however many until your computer exploded, right? Which would be a lot because these aren't taking up very much memory, all of these projectiles. But it is iteratively updating and rendering all of them, every single frame. So you want to be careful of doing stuff like that. What we're going to actually do is use what's called an object pool. Actually, well, not really an object pool. But we're going to enforce the lifetime of these projectiles such that when the projectile goes beyond the edge of the screen after up or bottom, it will just delete itself, remove itself from the table. Or, main will clear it from the table, right? But yeah, I mean, that's the basis for getting the ship rendering and the projectiles rendering. But there is a much more efficient way that we can do it involving lifetime management. Hopefully it won't cause a stack overflow, says Bavich_knight. Yeah, I don't think in this case it would cause a stack overflow. I think this would be a-- this is heap memory, so I don't think it would get a stack overflow. I'm not sure exactly what issue you would get after a certain point. But yeah, all of those things are not good. Looks so cool, says Asley, yes. And then JPguy says, alrighty. Alrighty indeed. We're at the 3:50 mark. I'm going to commit all this code to GitHub. It's been a long stream. We've been taking things kind of slow and that's part of the fun of it. And hopefully it's been fairly digestible. I can try to go a little bit faster next time. But I think we covered a fair amount of ground. We covered projectile-- or not, well, we did just cover projectiles. But we covered moving stuff continuously, which we haven't really done. I guess we did it in Pong for the Unity session, which is little bit different. Same idea, but we haven't actually done anything like this in Love yet. So it's actually been cool. It's been very fun but my brain doesn't work anymore. Lua is a stack language. It's a language that runs on top of C, so yeah, by virtue of that it is. You can get stack overflows with Lua because underneath the hood you're still running the same memory model for your application. Twohourstrike, you are overwriting love.keyboard.waspressed table. Yes. Yep, overwriting it, setting it back to an empty table. I like the interaction but I can understand that you want to finish the project during streams. Yeah, yeah. No, I do. I do like finishing the project. Some projects are just infeasible to finish over the course of one stream, honestly, for four hours. Some of them, like if we were to make a dungeon crawler, that's like, that could take weeks of continuous streaming. And even if we were to stream like eight hours a day, some of those could take a long time. But by the way we are contemplating doing like a eight hour super stream at some point, probably in the spring, like a Python super stream or maybe a C super stream. So if you're interested in that, definitely let me know. That would be covering all of the basics of Python for someone who has zero experience, or JavaScript for someone who has zero experience. Lua we've kind of touched on it a couple times. A couple of the streams that we've done have sort of built up the language and the syntax. I tried to go over the lot of that for the Snake stream a while back. But yeah, it's something that we could definitely do. I think that'd be a lot of fun. A lot of people get really exhausted doing streaming for that long. I don't really get that exhausted to be honest with you, if we can keep it casual. I would get exhausted sticking to the script, I think. That'd be the worst. This is much better for me to do long continuous streaming than it is to do a rigorous outlined sort of thing. Having an objective and working towards it, it's also more fun. But that's just me. Let me know what your thoughts are. It seems like everybody's having really positive things to say, though, which is super awesome. Thank you guys so much. The pace was really nice for a newbie, says Unsigned. Good! I'm glad that it was-- because I felt like I was going too slow but I'm glad that you felt the pace was good. Favorite stream so far, says Asley. Awesome, thank you so much. Onedayinmylife says, I would not mind at all if you streamed for eight hours. Yeah, that would be a lot of fun. I think we'd have to do like a Python super stream for that or something that a lot of people would like a lot. I don't know. We'll think about it. This was so enjoyable and educational even though I have never heard of Love. The video was so cool. I'll definitely watch the second part of this. Awesome, yeah, thank you so much, Jabcochasen, Jabe Co Chasen. However you pronounce your, name definitely let me know so I can say it right. Jpguy, 24 hour coding stream, ugh. If I can call in some ringers I think we could do that. If we like David code for a few hours and I code for a few hours-- that's essentially what the hackathon is though, coming up on Thursday. Kudos to Colton for doing this long without a break. Yeah, thanks. I'm glad I didn't drink a lot of fluid in advance because this would be rough. Iso says, eight hours streams would be so awesome but can't even imagine how taxing it might get. It really depends on what you're doing, because I would get super taxed if this were like a-- if I had like a strict script to follow and like, pages and pages of stuff to go through and there was no conversation and no casual feel to the atmosphere, this would be-- eight hours would be terrible. But if we're just having fun and having a conversation and making a game together and working toward something loosely, but like on a path and having goals but not worrying too much about whether we're sticking strictly to a timeframe, I could go forever. It's not a big deal, right? I think explaining it is what's making it slow. John Blow, I watch his streams, dude gets a lot done in two hours. Yeah, yeah, no, John Blow is super talented. I'm waiting. I'm super eager to see his programming language Jai, by the way. I'm very eager to see that and try that out. Yeah, he's very talented. I think if this was just me coding and just saying, ah, blah blah blah, doing whatever, I probably wouldn't be making Space Invaders first of all. This is largely to be educational. But if it were just like casual no explanation, I think I could get a lot done, too. But that's not super useful for a lot of people, I think. I think this is good to converse and make stuff together. Super stream for JS would be good. We'll be in force, says Bavich_knight. Awesome. Yeah, we'll definitely consider doing-- we'll consider doing both of those. You say the last name right. Job Coachuson-- Cochasin. J from Java. Jobcochason, you say the last name right. Cochason or Cochuson? Andre, I can't really say fully about today. I was busy but I was here on and off. But the way you do these streams is great. Awesome, thanks Andre. Glad you think so, appreciate it. Is a good stream. Do you plan on going over entity component system in love 2D and Lua? Entity component system, maybe? It's something that we do a lot in Unity. I would be happy to touch on it at some point maybe for a more complicated game. That's really not something that's meant for a game like this. Like, a game like this does not need an entity component system. It's way overkill. But something like an RPG would benefit hugely from an entity component system, and Unity games generally. And Unity makes it really easy to do it. So Unity is a great environment to test that out and talk about it. We can talk about it more in another Unity stream, perhaps. Brenda says, we should bring back the Breakout in C, piece it as a stream. Yeah, that'd be addressing. Yeah, that'd be cool. We should take a look at it. I'm not a huge fan of the Sanford Portable Library, though. It's a little bit weird. I feel like it's cumbersome. And it's not super widely used. I'd probably prefer doing Breakout in Love. But yeah, good, good, good point. Maybe just for history's sake we could do it. Another interesting concept of a less interactive based stream, the development of a full fledged game in real time where a whole team is just working and communicating without-- mouse is in the way-- looking at the chat. Yeah, maybe. I feel like that'd be boring for a lot of people. I feel like not a lot of people would watch that. Only people that are super engaged, I think, in the process of doing it would be interested. But I can't imagine very many people would want to see that. And also, how we would display like having multiple people's screens and being to see what people are doing? I feel like that'd be like a camera in a room looking at a bunch of people on the computer and just a bunch of conversation. I'm not sure. It's a cool, interesting idea. I think it would maybe work if you could see multiple people's computers at the same time. Yeah, one day can't wait to try out Jai. Yeah, that'd be awesome. The language looks cool. I'm excited to check it out. Is there any particular reason why you choose Lua as a language, says Unsigned? Mostly because I really like the Love framework. It makes it super easy to make games. And the language is pretty nice. It's also very often used in the game industry, so a lot of engines use it as their language, their glue language. It's all over the place. Isotv, are we having another stream tomorrow? Yes. We are having a stream with Nick Wong and he's going to be talking about setting up a web server with AWS. So if you want to create a web application or a lot of different stuff in the cloud as opposed to your own physical system, we'll talk about AWS and using an Apache server, using Ubuntu Linux. And we'll talk about a bunch of other stuff. And knowing me and Nick and how we do our streams, it'll probably go off on a lot of interesting tangents and explore a lot of cool ground. So tune in for that one. Let us know what you want us to talk about. Love a Python super stream, says Otter. Yeah, me too. I think that'd be cool. I think coding videos without comments are boring. Cannot watch them, much prefer these. Yeah, me too Asley, I agree. I think it's much more interactive. I don't think a lot of people want to watch me sit and just code and kind of mutter to myself for eight hours. I think this is a lot more entertaining. And then JPguy agrees. Makes it easier to understand if you can ask. Yep, exactly. And it's good feedback for me to know what I should talk about more, as opposed to just kind of gloss over, you know? Andydicomona says some Unity would be brilliant. Yeah, we'll talk about it another time. Jabcochasin, good to have you. Thanks for tuning in. It's 2:00 AM, wow. So yeah, thank you for joining us. We'll catch you on the next stream. Mocroonderjebed, I'm not sure if I'm pronouncing that right at all. Probably not, but thank you very much for following. Yeah, not the most interesting content to livestream. If I was going to livestream idle stuff, I'd livestream gaming because I like gaming a lot more. I think that's more entertaining. Not on CS50's dime, but probably on something that I would-- maybe for a private stream or something. Although we do have the Super Mario stream that we did with David because that felt appropriate. So check that one out. We did that last week. That was super fun. We beat all of Super Mario Brothers One on stream. And David did most of the game playing. And Kareem also did an excellent job being his sidekick for that. 10:00 AM for Brenda, about 3:00 for Babic Wow, the world is a crazy place. What time will the stream begin tomorrow, says Beerman5000. The stream tomorrow will be at 3:30 PM Eastern Standard Time. So a little bit later than today's stream. It'll go for about two hours. I'll stick around for a couple more questions. We are just hitting the four hour mark now. But if you have any other questions, definitely let me know. And we'll call it a day. Oh, I was going to commit everything to GitHub. Shame on me. Let me make sure I'm in the right spot. I'm not. Let's go into Space Invaders, get init, make a new repository, do the horrible act of Git add dot. Git commit initial commit, which is-- actually we'll do ships and aliens rendering with projectiles. And then Git remote at origin. I got to actually create the repository so I'm going to go to GitHub and create that. Do I have a rubber duck? Ooh, not in here, which is kind of terrible. Do I have a rubber duck in here? This is like the one room that CS50 has that does not have a rubber duck, which is kind of embarrassing. The best we have is the CS50 Muppet. will he stay up? He will. I should have had this on the stream with me the whole time. Aw, man, I regret that. This is the best I have. It'll function as a rubber duck if you need it to. OK, let's create a new repository if we have nothing on there that's private. I don't think so. Space Invaders invaders stream, live coded implementation of-- Jellybelly1991, thank you for following. Live coded implementation of Space Invaders on Twitch. Ba ba ba ba ba ba ba, public. It's going to be public so all of you can clone it, mess with it. Get the Git URL here. Git remote at origin-- I spelled origin wrong, which is not what you want to do. Git remote at origin, blah, blah, blah. Get push upstream origin master. So now all of the files that we just looked at and created today are on GitHub. So I'm going to toss this in the chat. Testing, testing, testing. Testing, testing, testing. All right, that was very strange. So the reason that it crashed is that Facebook events-- we crossed a stream to Facebook. And because we crossed stream to Facebook, the event was a specific length of time. And the event closed at 5:00 on the dot. So what we need to start doing is, make the events longer than broadcasting amount of time. Well, I guess maybe after an hour beyond the broadcast amount of time it'll terminate. So, apologies for that. We also have bad gateway stuff in the stream there. So let's just go ahead and get rid of that and that. As beautiful as that looks-- it doesn't look beautiful. I'll finish saying all the stuff in the chat. I apologize about that. That was not something that I anticipated happening, but it's OK. We're going to end on a good note. We're not going to let the stream die just like that, right? I did not spill the drink. The stream Facebook basically said, nuh-uh and terminated our streaming. And the script that we use for that broke and therefore stopped cross streaming to Twitch and to Facebook. So, yeah. That was that. And yeah, I wasn't answering the chat because I was trying frantically to figure out how to get it back up and running. But we're good. We made it. No PC crash, everything is great. I apologize if it's echoing. I'm not sure why it's echoing. But, yes, just so that we have this at the end of the video and it doesn't end in a very awkward way we'll take this as our farewell. And I'll say, thank all of you for joining us for this long stream. This was CS50 on Twitch. This was Space Invaders part one, so part two on the next stream. Again, we'll be making the aliens centered, making them move left to right and down, making them shoot autonomously-- but only the bottom row, because if the top rows shoot projectiles down they'll shoot the aliens that are right below them and kill them and that's not good. So only the bottom row can shoot. We'll figure out how to do that algorithmically. We'll figure out how to get lives and score, and whatever other-- oh, yeah, the state machine stuff, that should take about three or four hours, right? So this little mini stream is only about a few minutes long. But yeah, this was CS50 Twitch, Space Invaders. Thanks to all of you for coming to join us today. This was a lot of fun. I'm looking forward to the next one. Join us tomorrow for the Nick Wong stream on AWS. And then join us on Wednesday for some r and some biostatistics. It's going to be a lot of fun. I know nothing about r, so that's going to be great. It's going to be very educational for me as well. Oh, and what's his name, the muppet? CS50 Muppet I think is his official name. I'm not 100% sure. But yes, thank all of you. Have a wonderful rest of your day. And I will see you on the next stream. Good bye.
B1 中級 SPACE INVADERS FROM SCRATCH (Part 1) - CS50 on Twitch, EP.17 (SPACE INVADERS FROM SCRATCH (PART 1) - CS50 on Twitch, EP. 17) 1 0 林宜悉 發佈於 2021 年 01 月 14 日 更多分享 分享 收藏 回報 影片單字