Placeholder Image

字幕列表 影片播放

  • COLTON OGDEN: Hello, world.

  • This is CS50 Twitch.

  • My name is Colton Ogden.

  • And happy 2019.

  • Happy new year to everybody.

  • Whether you're new or a regular from last year,

  • welcome to our Twitch channel where we do sort of live coding, demos,

  • tutorials, explanations, and all--

  • excuse me-- all kinds of stuff.

  • We might even get into some hardware-related stuff

  • if some of the staff is privy to that sort of thing.

  • We've got a bunch of people in the chat.

  • I'm going to say hello as usual to everybody who's here with us already.

  • And let me know if I sound OK.

  • We just completely re-setup our stream setup.

  • We took all the stuff that we had setup in this room,

  • on our streaming room last year in December, we took it all, basically,

  • and used it for the fair, brought it back, and then reset a lot of it

  • up today.

  • So hopefully everything looks and sounds like it did last year if not better.

  • But definitely let me know if not.

  • I'm going to go through the chat here really fast, just to see

  • who joined us here to begin with.

  • Looks like MKloppenburg, a regular from last year

  • when I was doing a little test message.

  • This test received.

  • [? BavikKnight, ?] another regular.

  • Hey, everyone.

  • Hi.

  • How is everybody?

  • Happy 2019 to everyone.

  • Happy 2019 to you as well. [? SewerTon, ?]

  • who just followed, actually.

  • Thank you very much for the follow.

  • Hello, CS50 family with the popcorn emoji.

  • They say-- Buddy2610, sup?

  • Nice.

  • Oh, I can't read that name at all.

  • The Gangus-- oh, that's the GangusGoat19.

  • Also thank you for the follow.

  • I see GangusGoat19 just follow as well.

  • Thanks to everybody who followed over the break.

  • It looks like there are about 200 people that followed while we were not

  • streaming over the winter break.

  • So I was in California.

  • I was far away from Boston over the winter for the last three or four

  • weeks or so visiting family.

  • Back in Boston, it's very cold today.

  • It was fairly nice temperature for the last week or so,

  • and now it's, like, 20 degrees today.

  • It's freezing.

  • [? KiraU, ?] with the two clown faces, thank you.

  • [INAUDIBLE] smiley faces.

  • TuxMan29 [? int ?] new year, last year.

  • Return last year plus equals 1.

  • That's TuxMan29, a little bit-- a little code snippet

  • for everybody to increment the year.

  • And then [? SuraTon ?] is another thing.

  • It's basically saying, if last year is null, age is 1, right?

  • Because if we weren't any year-- if we weren't any age last year, null

  • is not a valid a valid age, so we need to be at least 1, right?

  • [? BellaCures, ?] happy 2019.

  • [? ForSunlight, ?] hello.

  • Colton, the regulars, and all, thank you very much.

  • Let me see, make sure I'm not missing anybody.

  • Didn't do the assignments yet, but watched most of it

  • and worked with [? Django. ?]

  • Oh, that's for the--

  • talking about Brian's web dev class, yes.

  • Brian actually may be in the chat today.

  • He and I chatted before the stream started.

  • [? SerotonOFX. ?] Hi, Johnny Bravo.

  • [? OFX is ?] surely a reference to my hair.

  • It is a little Johnny Bravo looking.

  • I need to get a haircut, like, super badly.

  • I haven't gotten it cut for, like, a month.

  • It's pretty insane.

  • Man, some of these names are hard to read

  • because Twitch puts them in this really obnoxious blue font on black.

  • M [INAUDIBLE], hi, Colton.

  • I hope I'm pronouncing that right.

  • I'm probably not pronouncing that right.

  • Sounds good.

  • Hello, regulars.

  • Hello, everybody.

  • OK, awesome.

  • So I think I got everybody that was in the chat.

  • Thank you all so much for tuning in.

  • Looks like my head's getting cut off.

  • I think the camera might be a little too zoomed in, but that's OK.

  • Today's stream is on Space Invaders.

  • Let me go ahead and switch my screen to the full view here.

  • And this is sort of where we left off last year.

  • And I noticed that the GitHub repo was actually from November.

  • So if you're in--

  • if you want to basically get the code base that we work with last November,

  • I'll write the URL here in the chat.

  • Actually, if I do that it's going to screw up the chat overlay.

  • Basically, it's Coltonoscopy-- GitHub.com/coltonoscopy,

  • that's my GitHub handle, slash space dash invaders dash stream.

  • And then that will give you the code base

  • that we worked with last year in November

  • where we basically had the following working.

  • Let me go ahead and run this.

  • So it's not really super feature rich yet.

  • We do have a ship that can move around.

  • And if you press spacebar, you shoot lasers.

  • You shoot projectiles.

  • But it doesn't do anything beyond that.

  • The projectiles don't collide with the ships, and the ships are static.

  • They're basically just sprites that are being rendered.

  • And that's pretty much-- that's pretty much it.

  • That's pretty much all that's going on.

  • One second.

  • But yeah, not a whole lot really going on there.

  • But today's goals are-- we have a few goals, actually.

  • First goal is actually get the ships to do something,

  • to move around left and right.

  • The next goal will be once they're moving

  • to actually have the projectiles collide with the ships

  • in order to trigger a death, essentially,

  • and to give us some sort of score.

  • And then another thing that we want to do

  • is the game is currently in just a single state where we sort of start

  • the game up, the ship's there, all the enemy ships are there, and we do stuff.

  • But there's not really a title screen.

  • There's not, like, a game over screen, anything like that.

  • So I would say that our goals today are sort of triple faceted.

  • We want to get the ships to move, get the ships to sort of come down

  • towards the player, move left and right, which is how it

  • worked in the original Space Invaders.

  • Then we want to have the projectiles that we shoot from the player

  • not only dispose of themselves after they've

  • gone past the edge of the screen--

  • for performance reasons if we just let an unlimited number of projectiles

  • exist when we shoot, eventually, we'll theoretically run out of memory.

  • Although they would be very difficult to do that because the projectiles

  • currently are so small.

  • I'm not sure exactly how many bytes they take up offhand.

  • But relatively few per projectile.

  • But it could eventually end up slowing down the game.

  • And they'll exist and they'll update every single frame.

  • And really, the updating of the projectiles

  • is probably going to be more of a performance issue than the Memory Cost

  • because in our code, and I have to sort of remember how this all works.

  • But we do have--

  • make sure it's an update.

  • In our update function online 51 to 53, and I'll answer some of the questions

  • in the chat in just a second.

  • But on lines 51 to 53, we have this sort of for loop

  • that says for every projectile in a table of projectiles

  • that we have created in advance, an array of projectiles,

  • you can think of it as, we want to call this update functions.

  • Update function essentially just moves a projectile up or down

  • depending on its orientation, whether it's going up,

  • or it's going down, whether it's going towards the enemies,

  • whether it's going towards the player.

  • And so for every projectile we add to this array of projectiles,

  • it's going to add more work that gets done in this loop.

  • And eventually, this loop could get cumbersome

  • and bring us down from 60 frames to maybe 58 frames, 40 frames.

  • It would take a lot of projectiles to do that.

  • I'm sure it would take thousands of projectiles,

  • but it's something that feasibly could be done.

  • So essentially, what we want to do is when a projectile goes

  • beyond the edge of the screen if it doesn't hit an alien,

  • we want to get rid of it.

  • We want to just essentially set it to nil on the table or repurpose it.

  • You can use what's called an object pool.

  • We're just going to delete it, essentially, and then just create

  • new ones as we press spacebar.

  • And so we'll only ever have maybe 5 to 10 projectiles

  • max on the screen at one time.

  • But that's something that you want to take into consideration.

  • If you're spawning objects, you want to de-spawn them,

  • sort of like freeing memory in C. Whenever you

  • alloc memory, malloc memory, you have to free it

  • in order to prevent memory leaks.

  • It's kind of the same idea, although a little bit

  • more complicated in this sense, because you're actually

  • dealing with things that are taking up CPU cycles that are moving and doing

  • stuff every frame.

  • But that's the projectile side of it.

  • The ship side of it we just need to have a loop where the ships kind of move

  • left and right and come down when they've

  • moved all the way to the edge of the screen

  • until they reach the player, essentially.

  • And then the last part we'll be using what's called the state machine, which

  • is a very important concept in game programming which allows us to divide

  • up our game into multiple different states that

  • function sort of as modules.

  • For example, a title menu state, a game state, a game over state.

  • Rather than put all this logic in our main.lua and bloat it unnecessarily,

  • we can divide it up using a state machine class which we'll implement

  • from scratch here a little bit.

  • And this is a very effective technique for modularizing your code.

  • And you can use it to not only create states for the game itself,

  • but for individual entities within the game.

  • For example, an entity might be in an attack state, or an idle state,

  • or a moving state, anything that you can think of.

  • If you can create a state for it and piece it out,

  • it makes your life a lot easier when you're dealing

  • with the actual editing of the code.

  • And you can let other people, therefore, edit different states independently.

  • [? BevIgnite is ?] saying, yeah, he's in the CS50-W course,

  • the web course right now, on hold, concentrating on algorithms course

  • of Princeton, taking on a massive load.

  • KirbytheDonkey says, what language are you using?

  • This is in Lua.

  • So Lua is a scripting language.

  • We are using Lua and also LOVE 2D, which is

  • a framework and sort of an engine for developing 2D games using Lua.

  • Lua is the language, LOVE 2D is the actual framework, the functions,

  • and the runtime that allows us to do all of the things that we can,

  • all the graphics, and sound, and input, and all that stuff.

  • If you haven't downloaded it already, go ahead and go to love2D.org

  • and grab the version most appropriate for your operating system.

  • And also, watch the-- if you haven't seen this,

  • for the first part of the stream, you might

  • want to go back and look at part one because this is a continuation of that.

  • And we're using that code and sort of building on it

  • and adding the new features.

  • But that's on our Twitch channel and our YouTube.

  • You can look up CS50 on Twitch Space Invaders.

  • You can go on our Twitch [? VODs ?] and pull it up there.

  • But we have all of the stuff that we did in November of last year of the Space

  • Invaders is part one on GitHub, the Space Invaders stream,

  • so you can clone that code and also add the stuff that we're going

  • to be talking about today to that.

  • And TuxMan29 also said the same thing.

  • Andre says, Unity's animator controller is essentially a state machine,

  • although it does allow blending animation so not strictly

  • a discrete state machine.

  • Yes.

  • And Andre [INAUDIBLE] actually made a clone of a game

  • that I created for our games course using those animator controllers.

  • If I'm not mistaken, you used those to sort of replicate that in Unity 2D,

  • which was a really cool project.

  • I saw it on Facebook and I believe I commented on it.

  • I'm not 100%.

  • I believe I commented on it, definitely hearted it, or liked it,

  • one of the two.

  • But yeah.

  • So thanks everybody for joining.

  • If you have any questions, definitely toss them in the chat.

  • Watch the prior stream if you're not familiar with this stuff,

  • maybe before digging into this one.

  • On Thursday, we'll have--

  • sorry, on Friday, we'll have a completely different kind of stream

  • with David.

  • David will be joining us to talk about Docker.

  • So that'll be very interesting.

  • And then we'll get into our routine again where

  • we have these weekly going forward, although the schedule itself

  • is kind of in flux at the moment.

  • So the three goals, again, to recap, get the ships

  • to move, to get the projectiles to collide, and then

  • get the game to exist in multiple different states, not just

  • a regular game state.

  • So the first thing that I probably want to do is work on the aliens,

  • get the aliens to actually move left and right.

  • And right now, if I just rerun the game--

  • and again, for those unfamiliar with this, I'm using VS code as my editor.

  • You can use whatever editor you want.

  • It has built in macro for running LOVE 2D from the text editor itself.

  • You can hit command L or alt L on a Windows machine in order to do it.

  • And I think on a Linux machine you can possibly do something similar.

  • So I don't have to click and drag my main.lua or my folder

  • onto a shortcut on my desktop, or whatnot, or on my dock.

  • I can use a command L, run the game, and it's super fast.

  • So I highly recommend VS code for that reason for LOVE 2D development.

  • So currently, these are the ships.

  • These are the aliens in question.

  • And I want them to sort of move from top to bottom--

  • top to bottom, left to right, sort of back and forth,

  • not like a scan line, strictly speaking, but kind

  • of like a bi-directional scan line in that it will kind of go like this,

  • almost like a snake style movement pattern, which

  • is how the function in the original Space Invaders, if I'm not mistaken,

  • which is a project that we created for the games course last spring,

  • 2018 spring.

  • So essentially, what I want to do is I just want these on a tick,

  • on sort of a frame, a time interval, to move maybe

  • 50 milliseconds, 100 milliseconds.

  • That might be too fast.

  • Maybe 300 or 400 milliseconds.

  • I want the ships to move in little chunks.

  • And then once the far right alien has touched the right side of the screen,

  • they should all come down and then start going the other direction,

  • then head down and go the other direction, and so on and so forth.

  • So I kind of want to, in that case, check

  • to see-- we have kind of a for loop to check

  • to see if the direction the aliens are moving is right,

  • check the far right alien.

  • If the far right alien is at the edge of the screen,

  • move everyone down, set the direction to left,

  • and then do the reverse logic when the aliens

  • reach the left side of the screen.

  • Check to see if the far left alien is at pixel zero.

  • If it is, bump them all down and then set the direction back to right.

  • Andre says, yep, exactly.

  • We used your course as assets for [? breakout ?]

  • and use the animator controller to actually control the game's states.

  • Yeah, I thought that was a super cool re-implementation

  • of that project from the game's course.

  • So thanks for doing that.

  • If you have a link to it or to the medium article you guys wrote

  • about it, definitely tossed that into the chat

  • so everyone else can take a look at it.

  • But yeah, so let's go ahead--

  • I haven't done too much review of the code base

  • since we last looked at everything.

  • So let's just go ahead and take a look at what we have going for us here.

  • We have the ship class.

  • So the ship class was what we use to actually control the player,

  • and it just moves left and right.

  • So that's why we have an if, else/if for moving left and right.

  • If love.keyboard is down left, then say your Rself.x

  • to be no farther left than pixel zero using math.max.

  • If love.keyboard is down right, do the opposite with math.min,

  • making sure it doesn't go past the right edge of the screen minus the ship's

  • width.

  • Alien size is the constant that we use for that.

  • And then if we press the space key on our keyboard,

  • notice that here we're doing table.insert.

  • Remember, in Lua, table.insert is how we add something to an array or a table.

  • They're all tables, but we can sort of conceptually think of them as arrays

  • or hash maps depending on the particular use case.

  • In this case, table.insert is going to insert into something

  • that we've called projectiles which we pass into ship update up here.

  • Projectiles is being used as an array, I guess more like a python list

  • than an array, like a c-array, technically speaking.

  • Because when used like this, when using table.insert, it will dynamically grow,

  • but arrays don't dynamically grow, at least in C, most static languages.

  • So in this case, we're using it as a list

  • and we're just creating a new projectile using this constructor here, projectile

  • taking in our self.x and self.y minus projectile length

  • so that it spawns at the top of the ship, at the ship's position.

  • And then notice that we're passing in a string up, which

  • is just how we decide we want to tell this constructor which

  • direction that projectile should go.

  • Should it go up?

  • Should it go down?

  • You can use strings.

  • You can use integers.

  • You can use constants that you have defined to be strings or integers,

  • however you want.

  • I like the readability of strings.

  • And it's actually fairly--

  • what's the word I'm looking for?

  • There's a word I'm looking for to describe whether something is often

  • adopted in a particular ecosystem or community.

  • Conventional.

  • That's what I'm looking for.

  • I can't think of words too well.

  • It's conventional in the LOVE 2D scene to use

  • strings that tell the function or the constructor

  • how something should operate.

  • For example, love.graphics.rectangle is a function that will create--

  • will draw a rectangle on the screen.

  • And the first argument of love.graphics.rectangle, like so,

  • is a string that can be either fill or line.

  • And so that's an example of using a string in the LOVE 2D ecosystem.

  • And that's why I tend to like to do that sort of thing as well.

  • Oh, yeah.

  • Andre's saying [INAUDIBLE] using the animator controller state

  • machine on medium and posted the link in the chat

  • there as well as they GitHub link for the project, so check that out.

  • If you're interested in Unity and interested in state machines

  • and breakout, which is a game that we implemented in the games course.

  • Definitely check that out.

  • It was super cool when I saw--

  • Unity is a really awesome really awesome editor, really awesome tool.

  • So this is the ship class.

  • So this is where we create our-- and really, our ship class is

  • almost completely done at this point.

  • We will need to check to see if a bullet collides with us.

  • But we can do that in our main.Lua.

  • And if it does collide with us, then we want to essentially lose a life

  • and go to the--

  • we can make a simple version and go to straight to a game

  • over if we get hit one time, but in actual Space Invaders,

  • you have multiple lives.

  • And so what we want to do in a fully fleshed out version

  • of this would be to go to a separate screen that just says,

  • you have two lives left, or three lives left, or whatever, current score,

  • and then after maybe three seconds, go back

  • to the game, which is kind of how the actual game works.

  • The projectile class is here.

  • The projectile class is pretty straightforward.

  • It takes in a xy and a direction, which we just looked at from the ship class.

  • So self.x is x, self.y is y.

  • Pretty standard setting member variables of this class.

  • If you're unfamiliar with object-oriented programming,

  • you can think of a class as a blueprint for how an object should behave,

  • what parameters it should take, the functions that

  • define how it should operate.

  • It's fairly common in games to have classes

  • that all have an update and a render function so that in your main.Lua,

  • or wherever your main game loop is, you can just

  • iterate over every object in your game and call update in the update loop,

  • and call render in the draw loop.

  • And if we go to main.Lua, for example, we

  • can see we do have a update function here.

  • Functionlove.update takes a delta time parameter, DT.

  • This is provided by LOVE 2D.

  • And in this, we can see that we do call ship

  • update, which is from our ship class.

  • We call a for loop on all our projectiles, and on each projectile

  • we call update.

  • And then we do something here where we reset a global table for keyboard input

  • so that we can test for discrete keyboard input outside of main.Lua.

  • And if you're unfamiliar with all of this stuff, a lot of it

  • is explained in much more detail in the first part of the stream.

  • This is not an advanced stream, I would say, but a more intermediate level.

  • Certainly if you're going into this blind,

  • I would check out the first stream a little bit more.

  • It'll go on YouTube if you want to watch the first stream

  • and come back to it later.

  • So definitely feel free to do that.

  • But paired with that update function, very importantly,

  • is our draw function, love.draw.

  • And these are functions that LOVE expects to exist in your main.Lua file.

  • And note that we are indeed looking at our main.Lua file.

  • This is the main.Lua file, hence the name,

  • that LOVE 2D looks for when it runs your game.

  • So this is sort of the starting point, just like in a C program

  • where you're looking for the main function or a Java program,

  • where looking for the main function in a class, or even Python

  • if you're running a module and you have if name is equal main.

  • The same idea.

  • Main.Lua is that starting point and it expects a certain set of functions

  • to exist.

  • And if you don't define them, your game will not run them,

  • and therefore, it will not function like you want to function.

  • Love.draw is a function that just is supposed

  • to take care of all the drawing stuff to the screen.

  • So everything updates first and then everything draws to the screen, right?

  • So you move your ships, you move your projectiles,

  • you figure out if things have collided with each other,

  • and then you draw them to the screen, or you don't draw them if they've been,

  • for example, removed from the scene because maybe your ship got blown up,

  • or an alien got blown up, or a projectile is off screen.

  • You don't draw anymore, right?

  • So here we are calling push start and push finish at the start and end, which

  • just gives us a virtual resolution.

  • Again, we cover that in the first part of the stream.

  • We clear the screen.

  • We do a loop over all the projectiles just like we did in update,

  • but in this case we're calling render on all of them.

  • And render's job on each class that we define

  • it is just to draw whatever class it belongs to,

  • draw that information onto the screen.

  • In this case, we're setting our color to red

  • or using a rectangle, again, love.graphics.rectangle,

  • which we just looked at.

  • Takes in a fill string, x and y.

  • This one is a--

  • I'm trying to remember.

  • That's the width in pixels, so we want a one pixel wide rectangle and then

  • a projectile length y length, height, of our rectangle.

  • [INAUDIBLE] width and height.

  • And I guess one should probably be projectile width as well, a constant,

  • but since it's just one, I guess I thought one would be good enough.

  • Nuwanda3333, that's [? Aslee. ?] Says, hi Colton and everyone.

  • Thanks for joining us, [? Aslee. ?] Good to you again.

  • Happy new year.

  • And here, we see that we're setting our color to 1111

  • after we finish drawing the rectangle, and that's to revert our color--

  • our sort of global state for LOVE 2D.

  • It always has one color to draw stuff with at a time.

  • We set it to red here.

  • We draw the rectangle.

  • And then, on the line after the rectangle,

  • we set it all ones, which is all white.

  • So the newer LOVE 2D versions, LOVE 11 and onwards, they use from zero to one

  • to create colors.

  • You might be familiar with RGBA, which takes in--

  • well, RGBA is what we're using, but you might be familiar with 256 valued RGBA.

  • So start at zero, end at 255.

  • That's how LOVE 2D used to do colors.

  • So in the old version, you would say 255, 00, 255.

  • And that would be the same thing as the current version of 1001.

  • But in version 11, they changed it all from zero to one floating point values.

  • So I could say, 0.1, I could say 0.15.

  • It's more efficient.

  • I have to dig I'd have to look back at the reason

  • as to why exactly they had to do it.

  • From what I remember reading about, it's a performance reason,

  • and it also helps with shaders.

  • And underneath the hood, it's just more performance.

  • But offhand, I don't have the exact reason.

  • People are joking in the chat about bringing me

  • pizza from an old sort of stream we had, where [? Aslee ?] kindly

  • offered to get pizza for everybody, which I'm not

  • sure of that offer still stands.

  • Hopefully it does.

  • Also, I think we were looking at projectile.

  • Earlier, before we went back to main.Lua,

  • the projectile update function is just an if-else statement

  • that basically says, if our direction is up, then

  • every time we update we want to essentially decrement our y

  • position, which is what we do here.

  • So if that y equals up to y minus projectile speed times delta time--

  • again, anytime you need to update something in a game,

  • or at least in LOVE 2D--

  • typically, in most games--

  • in order for it to run at a consistent frame rate,

  • every time you move something or do something that can be variable,

  • you want to multiply it by some constant value--

  • some value that's dependent on your frame rate-- in this case, delta time.

  • Delta time will be different depending on how many frames

  • have elapsed since the last frame, whether it's usually one.

  • Sometimes it can be two or three.

  • It's some floating point value that you can scale movement

  • by so that two computers at different speeds will run your game,

  • and things will move at the same speed, even

  • if one is more jittery than the other one, essentially.

  • And that's why we are multiplying projectile speed times delta time.

  • So that's the projectile class, the ship class.

  • The alien class is empty.

  • The entity class is fairly empty.

  • This is the base class from which we inherit--

  • the base class which we superclass the alien class from.

  • So we notice in this particular module, alien gets class.

  • This is the library we're using to use object-oriented programming in Lua.

  • It's not something that you get by default.

  • Again, I apologize if all this is a little too much review.

  • I just want to make sure we're all on the same footing,

  • and this also kind of helps me remember the code base a little bit more.

  • But alien is a new class, and it includes everything that's an entity.

  • And this is part of the class library that we used last stream.

  • So includes entity, and this is the class

  • from which we want to take all the stuff that exists in that class

  • and create a new class from it.

  • So entity is this class.

  • So aliens all have this constructor, xy sprite.

  • And they also have this render function, which draws from our global textures

  • variable in our dependencies.

  • This is dependencies.Lua and it has all of our libraries

  • here, using require statements.

  • It also has a global textures table here,

  • so we can store all of our textures-- all of our 2D images.

  • And then, we can store our frames, so all of our actual sub-images on those

  • textures.

  • And we talked about this last stream, as well.

  • In this case, we're just using an image in our graphics folder.

  • [? SewerTon ?] says, I've just downloaded the 2D engine.

  • What was the name of the editor you were using?

  • I know you mentioned it, but sorry, I forgot the name.

  • No worries, [? SewerTon. ?] It's VS Code--

  • so Visual Studio Code.

  • It's a very popular Microsoft editor.

  • You can get it by going to Code.VisualStudio.com.

  • Super awesome text editor.

  • You can also use Atom.

  • You can also use Sublime Text.

  • They all sort of have the same feature set, but different plugins, often.

  • I haven't used Atom in a little while.

  • I imagine they'd probably have something similar to the plugin

  • that I'm using for Atom.

  • If you want that plugin, it's the pixel byte plugin.

  • So if you go if you're in Visual Studio Code and you go to the extensions area,

  • get the LOVE 2D support plugin by Pixel Byte Studios.

  • Awesome plugin, super recommended if you're doing any LOVE 2D development.

  • Let's go back to here.

  • Click there.

  • So this is our dependencies file--

  • stores all your dependencies, all your libraries, all your textures,

  • all your sounds, all your frames--

  • everything that you want--

  • all your assets and libraries.

  • I like to put them in a dependencies file,

  • but you could also just put this all in your main.Lua and it would be totally

  • fine--

  • not what I would recommend, because then it gets a little bit bloated.

  • I try to keep my main.Lua fairly clean.

  • So notice it only has, at the top, require source dependencies,

  • which basically imports all that stuff that we defined in dependencies.Lua.

  • And then, we just have all the core functions

  • that we need for our game engine, all the core loops,

  • and then we defer all the actual logic as much

  • as we can to the classes themselves-- the projectile class, the ship

  • class, the alien class, entity class-- all that stuff.

  • And our main.Lua file ends up being currently less than 100 lines of code.

  • It will probably be a little bit more than that eventually-- hopefully not

  • too much.

  • In our main.Lua, the actual aliens themselves get

  • generated in our LOVE.load function.

  • So LOVE.load is a function that exists at the beginning--

  • it doesn't have to exist in the beginning of the file, per se,

  • but it triggers at the beginning of your LOVE 2D application running.

  • So before you start updating, before you start drawing and checking for input,

  • you'll call LOVE.load.

  • LOVE 2D will call it for you.

  • You have to define it.

  • But this is all the stuff that you want to happen before you update,

  • before you start drawing all that stuff.

  • In this case, we do a bunch of initialization.

  • We set our filter to nearest, nearest, which

  • allows everything to be very pixelated.

  • And actually this, I realized, needs to go in dependencies.Lua

  • before we actually import any graphics.

  • So what I did was, I took that LOVE.graphics--

  • that's that default filter function-- and I put it

  • before we load any textures, because on certain computers, what this will do

  • is, even though you call a default filter nearest, nearest,

  • we were doing it technically after the texture was loaded.

  • And as a result, certain computers will show the aliens as being blurry.

  • So put this before we actually load the textures to prevent that issue.

  • And I took it right out of my LOVE.load function, here.

  • There will be no change on my computer, because for some reason

  • it works fine on my computer.

  • The nearest thing doesn't cause any issues.

  • It might be a result of the push library overriding that.

  • But on certain computers, if you're using a newer version of the push

  • library, or your graphics card is funky, it will look kind of blurry.

  • So if that happens, just do what I just did.

  • OK.

  • The moment you toss pineapple on a pizza,

  • it becomes a sandwich and not a pizza, says MKloppenburg.

  • We've got a war on pizza in the Twitch chat.

  • Yeah, a long pineapple pizza conversation going on-- which is good

  • stuff.

  • DanCoffeeCCD is Dan Coffee who did a stream last year

  • on Draw 50, which is an awesome tool which we actually used, I think,

  • last Space Invaders stream.

  • Is a huge fan of pepperoni pineapple, so if Dan's looking in the chat,

  • shout-outs to Dan and his pepperoni pineapple pizza.

  • I'm not sure if that's been mentioned-- if pepperoni pineapple specifically

  • was mentioned.

  • I think just pineapple and pizza generally were mentioned.

  • So anyways, back to LOVE.load, which was where we initialized all of our aliens.

  • We also initialized our ship.

  • And actually, shout-out [? Aslee ?] and Bhavik, who are in the chat right now.

  • We, last stream, ended up having a mini war

  • on which ship we wanted to use at the start of the game--

  • the player ship.

  • So I think if I run this over and over again, we see right here,

  • this little ship here-- this little red ship

  • is going to potentially change to a another ship if I keep running it.

  • It's random.

  • Yeah, so right here.

  • So I don't remember offhand which one [? Aslee ?] chose

  • and which one Bhavik chose, but they both had a different ship

  • that they wanted to use, and we ended up using a little math.random

  • two, which just gives you value between one and two.

  • And basically, used a ternary--

  • Lua's ternary statement, which is an and-or--

  • well, a conditional and then an and and an or.

  • And then, what we ended up doing was, the game

  • will randomly choose between [? Aslee's ?] ship and Bhavik's ship.

  • And their ships are just frame indices.

  • So if we look at our file here--

  • let me go into Space Invaders, graphics, aliens.

  • I think was aliens six--

  • no, it was aliens 12 I believe.

  • I'm not 100%.

  • I have to re-look and see.

  • Was it 12?

  • It might have been.

  • Oh yeah, it is 12, because I recognize the ship here.

  • Essentially, all of these individual ships on this one texture--

  • this one 2D image--

  • you can split this image up into rectangles,

  • and it's called using a sprite sheet.

  • In LOVE 2D, they're called quads.

  • They're rectangles that define a sub-image on a 2D image.

  • And what we can do is, we can assign each of these an index between one

  • and however many ships there are, counting top to bottom, left to right.

  • And by drawing our 2D image in LOVE 2D, and then referencing a quad,

  • we can draw that sub-image.

  • And all the quads are-- we split it up by 12 by 12 pixels

  • and then put them into a table, so a one-dimensional table.

  • And so therefore, it's from one to--

  • I think this is 1, 2, 3, 4, 5, 6, 7, 8.

  • It's 16 by 16, 256 ships long.

  • So between one and 256.

  • By sending a particular index and then drawing LOVE.graphics.draw,

  • we can draw a particular sub-image.

  • And so that's what we're doing here.

  • We're getting an index.

  • So we calculated the number.

  • We figured out what row it was on--

  • 16 times three, and then the sixth one over is 16 times 12.

  • And then, the ninth one over.

  • So third row and then the 12th row.

  • And that's effectively how we did that.

  • That's what that bit of code is there fore.

  • We instantiate a ship.

  • We put it in the middle of the screen, which

  • is what this logic here does-- virtual width divided by two,

  • minus alien size divided by two.

  • Alien size is, I believe, 12 pixels.

  • All of the capitalized things are in constants.Lua.

  • Excuse me.

  • Constants.Lua is a good place to store constants.

  • Constants are just variables that should not change.

  • Lua does not enforce using constants, so you have to kind of use programmer,

  • I guess, heuristics in order to use constants appropriately.

  • In this case, we do that by capitalizing--

  • as is conventional amongst many programming languages--

  • the variable name, and using underscore notation.

  • So anything that we see that follows this variable naming

  • convention we can assume is a constant, and should not be assigned a value,

  • only used read-only in our code.

  • In this case, alien size is a constant.

  • It is 12 pixels.

  • Virtual width and virtual height are the dimensions of our virtual resolutions--

  • so how we make our screen look as if it's retro, even though it's not.

  • And there are some other ones here-- projectile length.

  • We don't have a projectile height, but just for good measure,

  • projectile height is one.

  • And then, we can use that in the future when we reference that.

  • Yeah, Colton being very diplomatic about the ships.

  • It's very important that we embrace everybody's opinions.

  • Well, he multiplied it in a second.

  • Someone brushed on math during the holidays.

  • Oh yeah, because last year I was not able to, on the fly,

  • figure out what 16 times 16 was.

  • And I was made fun of for it.

  • It's 256-- something that most programmers should probably know,

  • and I was not included amongst that category of programmers, unfortunately.

  • OK, so I think we've done a rough recap.

  • The last thing, I think, that we need to talk about

  • is the actual loop here that initializes the aliens.

  • In this case, alien is just an inherited version of entities.

  • So it's not really that different from an entity.

  • It's actually pretty empty class.

  • And what we're doing here is, we're instantiating it

  • at an xy with this four loop-- aliens tall, aliens wide.

  • That's how many aliens fit on the screen.

  • Sorry, that's not how many aliens fit on the screen.

  • That's how many aliens we want in our set of aliens.

  • We're doing a nested loop, just kind of like how we do Mario, for y

  • is one to aliens tall, which aliens tall is--

  • we decided will be five.

  • So we want five by 11 aliens.

  • We do that in an yx loop, and then we insert an alien into our aliens table.

  • Aliens just another table, just like projectiles, sort

  • of stored the same way or functioning the same way

  • and that it's one-dimensional and that we just iterate over it.

  • Each alien maintains a reference to its own xy position.

  • And alien is a class, which takes in an xy and a frame.

  • So frame is the number between 1 and 256 that maps to our 2D texture--

  • all of our quads, right?

  • And our quads, again, are this G frames table on line 22 in dependencies.Lua.

  • On line 23, we can see we're assigning aliens--

  • the index string aliens to the result of a generate quads

  • function, which we wrote last week--

  • not last week.

  • It feels like last week-- which we wrote last November.

  • If we go to util.Lua.

  • That's where our generate quads function is.

  • It just splits up a texture into quads based

  • on how wide and tall each sprite is.

  • In this case, the sprites are 12 pixels by 12 pixels.

  • [MUSIC PLAYING]

  • So that's how we split it up.

  • Curehavenomana, thank you very much for following.

  • Hope I pronounced that right.

  • I imagine I did pronounce that right.

  • And [? 08Rock, ?] if I missed you, I apologize.

  • Thank you very much for following, as well.

  • All right, so I think that's a fairly solid recap.

  • I apologize if that was a little bit cumbersome for everybody

  • if you're already familiar.

  • If you're brand new to the stream or brand new to this game,

  • then that might be of some help.

  • Certainly, I need it getting back into the code base.

  • But I think we have enough information now

  • to where we can decide how we want to go about moving our sprites, right?

  • So the first thing that we can do is, in our main.Lua,

  • we do have access to the--

  • we have access to all the aliens, right?

  • So what I can do is-- and we probably want this to be in our update function.

  • So the first thing that we can probably do-- well, first, first thing that we

  • should do is, we're going to need a variable that

  • keeps track of which direction the aliens are moving in, right?

  • Because they can move to the right, they can move to the left--

  • your right, my left.

  • To the right to the left.

  • So what we can do is, I can just call another variable.

  • And ideally, this will all be sort of encapsulated within a game state.

  • And we'll talk about game states a little bit later.

  • And maybe even in future streams, we'll implement things from a more state

  • machine-focused approach from the beginning,

  • as opposed to this sort of ad hoc everything in main approach.

  • But we kind of have to work our way up there, I feel like it,

  • as it might be a little bit too much to jump into that right off the bat.

  • But what I want to do is, I want to create a variable called

  • alien direction.

  • And I'm just going to assign it a string called right.

  • And again, using strings as the way of classifying our variables,

  • because strings kind of make sense.

  • Oh, they're talking about the sound.

  • MKloppenburg, and Nwanda, and Andrej says, the same song

  • from Khan Academy for following?

  • Yeah, it's-- I had thought that we had used that follow sound for a long time.

  • It may be the case that you all didn't hear it

  • prior to this stream when we reconfigured

  • our audio setup a little bit.

  • But yeah, that's just the follow sound that OBS,

  • or I guess Twitch lets you have in your channel

  • every time someone follows or subscribers, all that sort of thing.

  • So it's a nice little sound effect.

  • I like it.

  • When I hear it, I have monitors back here,

  • so I know that somebody has followed.

  • So I can look up at the screen and see and thank them.

  • So that's pretty great.

  • So we have a variable called alien direction

  • that we've declared in main.Lua, which is going to be the direction

  • that the aliens move each frame until they reach the end of the screen.

  • And then, it should get set to the other direction.

  • When it gets set to the direction, they should move to the other side,

  • check to see if they've reached that end, and so on and so forth.

  • And then, when the do reach either end, they should all move down.

  • They should get closer to the player.

  • That's how the game works-- the actual Space Invaders game works.

  • So I can say if alien direction is equal to right--

  • again, just comparing it directly to the string right,

  • because that's going to equal either left or right.

  • Excuse me.

  • And I'm going to preemptively say an else here.

  • It can only be right or left, so we don't

  • need any we don't need another conditional statement.

  • We can just say else.

  • What I want to do is, I want to set every single alien's position

  • to the right just a little bit.

  • Well, I should say, I don't want this to happen every frame.

  • I want to actually be on a timer.

  • So what I should do is--

  • and do I want this to be using a timer library?

  • Probably not.

  • Probably not yet.

  • That might be a little bit too much.

  • So I'm going to say, alien movement timer is zero.

  • And then, I'm going to say, in our constants--

  • I don't have constants open.

  • Constants.Lua, I'm going to set a alien movement interval.

  • [MUSIC PLAYING]

  • That startled me a little bit.

  • Djoker07, thank you for following.

  • That name looks familiar.

  • I thought that Djoker was already falling, but if not, I apologize.

  • Alien movement interval, I'm going to set that to be-- this

  • is in milliseconds.

  • Is it in milliseconds?

  • No, I think LOVE 2D measures DT in fractional seconds.

  • So let's say I want the aliens to move every 0.4 seconds.

  • And again, this is a constant.

  • This isn't going to change.

  • I could make this a variable that does change

  • if I wanted the aliens to move faster the closer they

  • get to the player, which is kind of, I think, how it works in--

  • no, that's not how it works in Centipede.

  • I don't think so.

  • I think it just looks like they're moving faster.

  • I don't remember offhand.

  • But basically, it's going to be a constant in this case.

  • And the timer itself is going to keep track

  • of how much time has passed each frame.

  • And we're going to add to it.

  • And once alien movement timer is greater than alien movement interval, which

  • means 0.4 seconds have elapsed, then I should move all the aliens to the right

  • or to the left, do the condition to check and see if they've reached

  • the edge of the screen.

  • And then, I'm going to reset the timer back to zero,

  • or at least back to timer minus alien movement interval, in case

  • they went over 0.4 just a little bit, right?

  • And shout out to David in the chat.

  • He says, thanks for tuning in today, everybody.

  • Thanks so much for tuning in, as well.

  • Djoker07 says, happy new year, Colton.

  • Thanks, Djoker, very much appreciate it.

  • Everybody's talking about their amount of RAM--

  • 16 gigs.

  • If it's in idle, it won't take any RAM, says OneDayMyLove.

  • I might be lost in the conversation there.

  • Oh, two instances of unity, open, plus one instance of blender.

  • And everybody's saying hello to David.

  • Awesome.

  • Cool, cool.

  • So we have a timer now.

  • We can keep track of how much time has elapsed since the last frame.

  • We can cumulatively add this until we've gone past that amount of time.

  • So what I'm going to do is, before I actually-- well,

  • I'm going to keep the if statement there,

  • but it's actually going to be nested within my other if statement.

  • I can say, alien movement timer gets a movement timer, plus DT, right?

  • Remember, DT is a variable that we get every frame passed into LOVE.update

  • by the LOVE 2D framework.

  • And this is going to be measured in fractional seconds.

  • So it's going to be 0.013, 0.0013.

  • I forget offhand.

  • I think it's 0.013.

  • And if alien movement timer is greater than or equal to the constant

  • that we've defined, which is alien movement interval, then--

  • and this is where we nest that bit.

  • So I say, if alien direction is equal to right-- so they're going to the right.

  • What we need to do is, we need to increase every single alien's

  • x position, right?

  • So that means we need a loop.

  • So I can say, four_alien in pairs, aliens do alien.x is equal to alien.x

  • plus--

  • and then we have to figure out how much we want to move the alien by.

  • So I guess we can say, alien size, alien step length.

  • We'll call it that, alien step length.

  • And we'll say that's going to be five pixels.

  • I'm not entirely sure.

  • A lot of this you're going to have to kind of do by

  • feel, too, especially if you're doing things

  • that are based on time when you're moving stuff, getting speeds right.

  • It's a little tricky to kind of guess everything correctly right off the bat

  • unless you know your specs in advance.

  • I don't know the specs advance, so I'm just going to take a guess.

  • And then, we can fine tune it as we need to go along.

  • And this has a role to play in game balancing,

  • because this movement of the alien x--

  • if it's too small, the game will be too easy,

  • because the aliens will take a long time to get to the bottom of the screen.

  • If it's too big, the aliens will get to the bottom of the screen really fast,

  • and as a result, it'll be harder for the player to defeat all of them in time.

  • So we need to sort of keep this in mind as we're balancing the game

  • out-- tweak it, play test it, and get a sense of it.

  • This is part of the QA testing side of making a game.

  • So we're going to say, alien step length.

  • So we have alien.x is equal to alien.x plus alien step length.

  • In this pairs function, again, is how you iterate over tables in Lua.

  • So it takes in a table, in this case aliens,

  • and will give you every single alien, every single key,

  • and every single object within that--

  • every key value within that table.

  • In this case, all the values are aliens-- alien objects.

  • So we do that here.

  • And then, we do the same exact thing here, right?

  • Except instead of setting the alien x equal to alien x plus the step length,

  • we want to minus the step length so they move to the left

  • instead of to the right.

  • And again, it's mirrored to you to.

  • To the left, to the right, but I have to think of it the opposite way.

  • I can't see that name.

  • It's in black text.

  • WinLogon, happy to be here, taking 650 GD at edX.

  • Awesome.

  • Thank you so much.

  • Well, glad to have you with us.

  • WinLogon?

  • WinLogon?

  • I hope I'm pronouncing that correctly.

  • I'm probably not.

  • Colton's saying, cool.

  • Colton's sounding Jake Peralta.

  • I'm not entirely sure that is.

  • I should look that up.

  • Cool.

  • So this is how we move the aliens on a timer.

  • And then, once we have moved all the aliens,

  • we want to set alien movement timer to alien movement timer minus--

  • what's it called?

  • The alien movement interval.

  • That way, in case we do go over 0.4 to, like, 0.41, or something

  • similar to that, it'll take that into consideration--

  • that overlap into consideration, and we won't kind of

  • get extra time added to our loop, which is

  • what you do if you just set it to zero and don't account for that overlap.

  • Alien movement timer gets alien movement timer minus alien movement interval.

  • That's great.

  • So that'll roughly be zero, if not 0.01, 0.001, or something like that.

  • And that will move the aliens to the right and to the left.

  • Now, if we run this, it should work.

  • They'll move.

  • So this is roughly what we want things to look like.

  • It might be a little fast.

  • But notice that when they go to the right edge of the screen,

  • they just keep going forever, because we haven't actually implemented

  • checking for the edge, right?

  • So that's something we want to do.

  • We want to say, if the far right alien is

  • at the right edge of the screen, then downshift everything down

  • by a few pixels--

  • maybe even the step length, we could do that.

  • Or maybe step height, maybe we want to define step height

  • as a separate constant.

  • So I can say step height.

  • And maybe we want that to be eight pixels, a little bit more

  • than the step width.

  • They're moving a little fast, so I think the interval should maybe

  • be 0.5 seconds.

  • We'll see if that's any better.

  • That should be fine.

  • And this is a lot up to your tastes more than anything,

  • so you kind of want to tweak it depending on what you like best.

  • So we've done that.

  • We've got the aliens moving.

  • Now, we need to actually figure out when the aliens are

  • at the edges of the screen.

  • And for that, what we can do is, we're just

  • going to assume that all the aliens are sort of-- even if they are destroyed,

  • they're there.

  • So we can always test the far right alien

  • and we can always test the first alien to be our left and right edges, right?

  • So our alien, or rather our far right alien,

  • is 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11-- our 11th alien.

  • So we can just kind of know our 11th alien will always

  • be where we can indicate the right side of the screen.

  • So I'm going to say, if aliens at 111- so because we added to our table--

  • actually, I'm going to use best practices here.

  • I'm not going to use 11, I'm going to use far right alien and far left alien.

  • And aliens wide is also the same index as far right alien in this case,

  • but we're going to separate them just so that our code is more readable.

  • This is important.

  • You want to make variables that are readable so we can, at a glance,

  • know that we're checking the far right alien here.

  • We're not checking some random 11, which doesn't make any sense.

  • I'm just reading it offhand, if you're not familiar with the code base.

  • So if aliens far right alien.x is greater than or equal to--

  • and this is where we want to check to see if it's

  • at the right edge of the screen, right?

  • So we're moving right.

  • So I want to say, if aliens far right alien.x is greater than or equal

  • to virtual width--

  • that's the far right edge of the screen--

  • minus alien size, and minus alien size because we

  • want it to be when the right edge of the alien touches

  • virtual width, not the left side of it.

  • Remember, everything in LOVE 2D is based on its top left coordinate.

  • So if we check for just virtual width, the alien

  • will get all the way to the right side-- like, past the right side of the screen

  • before it actually checks the condition.

  • We want it to check when the right edge of it touches virtual width, right?

  • So I'm going to say, if aliens far alien.x is greater than or equal

  • to virtual width minus alien size--

  • if the alien is right at the right at the right edge of the screen,

  • this is where we want to set the direction-- which

  • the variable is up here.

  • It's alien direction.

  • We're going to say, alien direction is equal to left.

  • It's no longer right.

  • We're no longer moving to the right.

  • Now, on the next frame, this is going to be false and this is going to be true.

  • And it's going to tick all the aliens to the left.

  • And this process will continue-- assuming

  • that we fleshed out this if statement here,

  • this else statement-- this will continue infinitely.

  • So what we can do is then say, alien direction is equal to left,

  • and this will have the result of--

  • it could be possible that the aliens move slightly

  • past the right edge of the screen and we need a bit of logic

  • to sort of reverse that.

  • But we can solve that.

  • I guess it'd be fairly easy just to figure out what that difference is,

  • and then subtract that from every alien's x.

  • But we won't worry about that yet.

  • It's not super important.

  • What we're going to do first is just get the loop working infinitely.

  • So in this case, now, we want this to be right.

  • So in the else statement, so when they're moving to the left,

  • we're going to-- we still have it decrementing their position,

  • but now we don't want far right alien to be tested for.

  • We want far left alien to be tested.

  • And we want it to be less than or equal to zero,

  • which is the left edge of the screen, not virtual width minus alien size.

  • Now, we're checking to see if the left side of the left alien--

  • the leftmost alien, the first alien--

  • is right at the left edge.

  • So if aliens far aliens.x is less than or equal to zero, than alien direction

  • is equal to right.

  • So then we're going to go back to the right.

  • And we're missing one last step, and that is, we

  • want every alien to move down when this happens.

  • So what we can say is, again, another loop.

  • 4_alien in pairs of aliens, do alien.y equals alien.y plus--

  • and then we just defined it--

  • alien step height here.

  • And then, I want to bring this same exact code over here, just like that.

  • So if everything is according to plan and my logic is correct,

  • when these aliens reach the far right side of the screen,

  • they should bump down.

  • Let's test and see.

  • Yeah, it looks like it jerked a little bit to the right there a little

  • funkily.

  • So yeah, that's the--

  • am I changing all of their things to the right size?

  • I might want to do greater than, not greater than or equal to,

  • so that it doesn't do that weird sort of diagonal jump.

  • Let's see if that fixes it.

  • No, I don't like how that looks.

  • It looks a little bit weird.

  • This might be acceptable for now, just in the sake of time.

  • 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, boom.

  • Yeah.

  • Another thing you could do is, you could count the number of steps

  • that are ideal between each--

  • oh actually, do we want to do--

  • if we do this logic before the aliens move to the left and the right,

  • it might look a little bit better.

  • And then, do an else, like that, and indent that.

  • So this way, the aliens will move all the way, and then set the direction,

  • and then skip that iteration so that it looks a little bit more

  • like the original game.

  • Let's see if my logic is correct.

  • If not, I'll just revert it and we'll accept it for now.

  • Mechanically, it needs to be adjusted a little bit.

  • But the logic works, it just doesn't look perfect.

  • But I worry if we spend too much time, because we're already an hour in,

  • and we have collision and game states to worry about,

  • we might not have enough time for a three-hour stream.

  • But we'll see.

  • So let me go ahead and do what I just did again.

  • OK.

  • Else, indent this, end.

  • Let's try and see what this looks like.

  • Hopefully this looks acceptable.

  • If not, I'll have to just revert.

  • Oh, that looks great.

  • OK.

  • Does it look OK on the left side too?

  • It's OK.

  • It could be a little bit better.

  • I feel like I need to maybe change the left side so that it's

  • checking for, like, two pixels instead of zero,

  • because it goes a little bit past the left edge--

  • yeah like, four pixels.

  • So if I do single to four, see if that fixes it.

  • While that's going, I will read the chat, or at least try to.

  • You ever heard of [? byte ?] paths, says OneDayMyLove?

  • I'm not familiar with what that is.

  • Oh, that looks great.

  • OK, now let's see, since now it's going off of four pixels,

  • see if it screws up the right side too.

  • Hopefully not.

  • Nope, that's excellent OK, cool.

  • This looks awesome.

  • So let me read through the chat here.

  • So I missed OneDayMyLove saying, have you ever

  • heard of [? byte path? ?] I'm not familiar with what that is.

  • I apologize.

  • Bhavik Knight says, maybe we can make it variable and make levels in the game.

  • With level up, we can increase the speed by some amount.

  • Yeah, we could do that.

  • I recommend definitely taking it and trying to do that yourself, if you can.

  • I don't think we'll have time.

  • I don't think I'll have time to implement that on stream

  • today with the other stuff that we want to implement,

  • but it's a great exercise, certainly.

  • And I think we've--

  • I mean, we've done something similar to that before with Snake.

  • I think we did levels and stuff.

  • [? ISOTV-- ?] hello, everyone, happy new year.

  • Sorry I'm late.

  • I was on vacation until today.

  • Did I miss anything special?

  • So good to see you [? ISO. ?] Nothing too special.

  • We did a review.

  • I imagine you probably saw the last Space Invaders stream,

  • I don't remember offhand.

  • If you did, then most of what we talked about for the first hour

  • was review-- or not first hour, 40 minutes

  • or so was review of the code base, figuring out

  • what the next steps are, kind of saying hello, happy new year.

  • Now, we've actually for the last 20 or 30 minutes

  • got into the meat of the game.

  • We've added aliens moving, so the next few steps

  • are to be collision detection with bullets,

  • make aliens shoot, that sort of thing.

  • Yeah, and game states.

  • That'll be another thing, yes.

  • [? Suraton ?] kindly said, talking about what we implemented last year.

  • Correct.

  • [? Aslee ?] says, why is it doing that jump?

  • The logic was a little bit screwy.

  • It was checking to see if--

  • it was basically moving before it checked every time,

  • and so it would move in a weird position and then jump down.

  • And the logic was slightly messed up.

  • But we fixed it.

  • Everything's good.

  • So now it actually does the check before it adds x, or subtracts x,

  • from the aliens.

  • And it does it with an if-else, so it won't do them both in the same turn.

  • So we do get block movement on the frames--

  • or not the frames, but on the part of the interval where we actually do

  • move the aliens down.

  • We don't move them down and then move at the same time.

  • That was the core issue.

  • It wasn't in and if-else.

  • Now it's in an if-else, so that problem is fixed.

  • Yeah, essentially what Bhavik said.

  • When it reaches either left or right extreme, pause for a time step,

  • go down that time step.

  • That's what we did.

  • Yeah, it felt. Awesome.

  • Cool.

  • So aliens look good now.

  • The movement looks good.

  • The next step is probably collision, right?

  • So I can move around.

  • I can move the ship around and I can shoot, but it doesn't do anything.

  • It actually just goes straight through the aliens through the other side.

  • And that's not ideal behavior.

  • So a couple couple of things we want to do.

  • As we mentioned earlier, we want the projectiles to be only around as long

  • as they're visible.

  • So when they've gone past the top edge of the screen,

  • if they haven't hit an alien, we want to get rid of them.

  • We want to get rid of them if they hit an alien.

  • And if we hit an alien, we want not only to get rid of the projectile,

  • but also to get rid of the alien itself.

  • So a few steps, a few pieces.

  • There's a long block of code, too--

  • 58 to 86 to do the alien movement.

  • We could separate that out, I guess.

  • So you can call this, like, tick aliens.

  • And then go down to the bottom here.

  • Just modularize our functions a little bit,

  • see if it still works, which it should.

  • So it's a good habit to make your update and draw functions kind of as small

  • as you can.

  • In this case, we have this big block of code that does something very specific.

  • So take aliens.

  • We just took it out, made it its own function.

  • Now, our code looks a lot more readable.

  • We can say, OK, in our update function, we update the ship,

  • we update projectiles.

  • We probably can put this in there, as well.

  • And then, we tick the aliens.

  • So we move the aliens.

  • So boom, that's done.

  • Cool.

  • Essentially, just a copy-paste out of the update function.

  • It's not better engineered, necessarily, but it's easier to read.

  • It looks a little bit better.

  • It's more easy to keep track of what's going on.

  • You're not navigating through a monolithic code base

  • to figure out what the functions do.

  • When you have functions that you can reference by name,

  • you can also reuse them in multiple places if you wanted to,

  • and mix and match them--

  • puzzle pieces.

  • It's useful.

  • OK so now, collision detection with our projectiles.

  • So the projectile class, here.

  • What we can do is probably define a collides function.

  • We want a collides function that works with every entity in the scene,

  • because we want to check to see--

  • I mean, essentially, we only want projectiles and ships

  • to collide with stuff.

  • So we could just define a collides function

  • on a projectile that takes in something that has an xy width and height.

  • The aliens-- do the entity have a width and a height?

  • No, they don't.

  • So in this case, we can probably set self.width is equal to alien size.

  • Is alien size the one in--

  • alien size, yeah.

  • It's always going to be 12.

  • But this way, each object has a width and a height now,

  • and we can use that in our collides function.

  • So we can go to projectile.

  • I'm going to create a new function called collides.

  • I'll do it before update and render.

  • And it takes in an entity.

  • And this entity is assumed by this function

  • to have an xy width and a height, which is why we just

  • added width and height to entity.

  • The ship and the alien will all inherit from entity.

  • And therefore, they will all have an xy width and a height.

  • So this is aa, bb collision detection.

  • I think we covered this in a prior stream.

  • I forget offhand which stream it was.

  • Maybe somebody in the chat knows which stream we covered it in.

  • I remember drawing it--

  • or at least I thought we did.

  • Pong, yep, there it is.

  • OK.

  • Did we cover that in 3D Pong?

  • Did we really do that?

  • I thought for 3D Pong we did Unity's collision to systems,

  • so we didn't need to do aa, bb.

  • We cover aa, bb in the games course for the G50,

  • but for stream I don't remember if we covered it.

  • Essentially, check to see whether two rectangles--

  • whether one rectangle's opposite edge is past the other edge

  • of another rectangle.

  • That means that they impossibly cannot collide,

  • and therefore it's a simple check.

  • And it assumes the rectangles are on the same axis.

  • They're not rotated rectangles.

  • They're axis aligned, so completely orthogonal-- is that the right word?

  • Perpendicular to the axes, or orthogonal to the axes.

  • And therefore, it's a very inexpensive operation and a very simple operation.

  • So I can say if self.x is--

  • and so we're checking the left side of the rectangle.

  • So if the left side of this rectangle-- this projectile--

  • is greater than entity.x plus entity.

  • width, then that means that this rectangle's left side

  • is beyond the right edge of another rectangle.

  • Therefore, it's impossible for them to collide, essentially, right?

  • And we do that same logic for all four sides of the rectangle.

  • And then, if any of these are true, then there's no collision.

  • Otherwise, there's a collision.

  • So if self.x is greater than entity.x plus entity.width,

  • or self.x plus self.width is less than-- that's

  • the right edge of the projectile.

  • If it's less than the-- oh this doesn't have a width and a height.

  • So we need that as well.

  • So this needs to have a width.

  • So projectile-- do I not have projectile width and height defined?

  • Projectile height.

  • Oh, that's not projectile height, that's width.

  • My bad.

  • I screwed up the constant in dependencies.

  • I called it projectile height.

  • It should be projectile width--

  • width and length, which is fine.

  • So self.width is projectile width.

  • Self.height is projectile length.

  • So it's one and five, respectively-- small rectangle.

  • If self.x is greater than entity.x plus entity.width,

  • or self.x plus self.width is less than entity.x, or--

  • fairly long if statement--

  • self.y is greater than entity.y plus entity.height--

  • so if this is below the other rectangle--

  • or self.y plus self.height is less than entity.y--

  • above the rectangle-- then we want to return false, so it does not collide.

  • Else, we return true.

  • Both of these.

  • For a long if statement, it gets kind of obnoxiously formatted.

  • But that's essentially it.

  • Check to see if opposite edges are beyond each other.

  • If they are, it's not a collision.

  • If not, it is a collision.

  • Cool.

  • Simple.

  • And now, we can, in our main.Lua, say, for every alien--

  • or I guess for every projectile, rather--

  • projectile update.

  • Now, the thing is, if we want to update each projectile

  • so each projectile takes in--

  • it updates, right?

  • So it ticks.

  • It says, if the direction is up, move it up.

  • Self.y minus projectile speed times all the time.

  • And the same thing goes for moving it down, right?

  • So we can update all projectiles.

  • And maybe ideally this exists in the update function for a projectile,

  • but it would probably be ideal to do that--

  • from within projectile test a collision against each entity,

  • say, for each entity in the scene do this, blah blah blah.

  • What we're going to do instead is, we're going to do that in here.

  • So we're going to move every projectile.

  • And then, since we have access to all the aliens

  • and the player ship within this function here, since we're in main.Lua,

  • we're just going to check collision here in another four loop.

  • So we're going to say, for _alien in aliens, pairs aliens two.

  • So we've moved the projectile, and now we're going to say, if--

  • I'm trying to think.

  • Because if we do that, we also need to get rid of the projectile.

  • So we would have to set that projectile to nil.

  • Hmm.

  • But we can worry about that in a second.

  • So if projectile collides with alien, do--

  • and then, OK, so P key.

  • So projectile key, and A key.

  • So that's what we're doing.

  • We're saying P key and A key, because we want

  • to have access to the key in the table from which we are calling it.

  • We're going to set that key to a nil value in the instance

  • that we collide the projectile and the alien,

  • and then we're going to also add an if nil check here.

  • This will allow us to set that particular alien to nil,

  • as opposed to--

  • do I want to set it to nil, or do I set it invisible thing so that we still

  • have width and height access?

  • No, we don't need to set it to nil.

  • Invisible-- probably want to set an invisible flag on the alien.

  • Defeat it, set an invisible flag.

  • And then, once we set an invisible flag, the alien will still exist.

  • We can still update its position.

  • It'll still be hidden.

  • Yeah, that should work.

  • JPGuy says, everybody and Colton, long time no see.

  • Thanks, JPGuy for tuning in.

  • Glad to see you.

  • Make sure that I didn't miss anything.

  • Hope you had a nice holiday season.

  • Talked about pineapple pizzas and mentioned to you.

  • The audacity, says JPGuy.

  • Does Lua have an interface support like Java interface?

  • It'd be helpful in class, like projectiles

  • shoot by either of ships or aliens.

  • No, there's not really a notion of interfaces,

  • Bhavik Knight Really, everything is very dynamic,

  • so nothing has to adhere to anything.

  • You kind of define whatever functions-- like, the best thing you could get

  • is just by using classes, honestly.

  • There's not really an interface.

  • It's not going to, at runtime or compile time,

  • determine whether you're calling a function that's

  • valid on a class that implements a specific interface,

  • because it's dynamic.

  • It doesn't have that notion.

  • Good question, though.

  • New Year's is old news now, says JP.

  • Yeah, it's getting there.

  • It's like, what, is today the seventh, eighth?

  • What is today?

  • The seventh.

  • OK, so back to the code here.

  • So we're declaring P key as the key for every projectile,

  • and A key as the key for every alien.

  • Normally, we just use underscores, because we

  • don't use the key for anything when we do it in iteration.

  • We just use the--

  • so this doesn't throw away the variable.

  • It's kind of idiomatic.

  • It's a paradigm to use underscore in a lot of languages

  • where you just toss a value away.

  • You don't do anything with it.

  • So that's what we were using it for.

  • But now that we need the value, and because we're

  • using a nested loop where we were using underscore,

  • we need to differentiate these two keys, because we will use both of those keys

  • here in just a second.

  • So one thing that we are going to need to do

  • is aliens are going to need a invisible flag.

  • So we'll just say entities can have an invisible flag.

  • And it'll set to false by default. And then, if not self.invisible,

  • draw it, right?

  • Because if it's not invisible, we should draw it.

  • Pretty straightforward.

  • And then, what we're going to do is, when we actually

  • check to see if the projectile collided with an entity,

  • we want to check the invisible flag.

  • Because if the invisible flag is true, then we want to ignore the collision.

  • So if projectile collides with alien and alien.and not,

  • alien.invisible, then we want to say, projectiles at index P key

  • is equal to nil.

  • And alien.invisible is equal to true.

  • And I think that should work.

  • I'm not 100%.

  • Oh, it looks like I messed something up.

  • Line 56, oh I put a do instead of a then.

  • Whoops.

  • Arithmetic on global delta time on line 99.

  • How did we do that?

  • Oh, I see.

  • OK, this needs to receive DT, and we need to pass DT into here.

  • So remember, functions that you declare don't have access to DT

  • unless they're called a reference within main.

  • So we take DT and we pass it to tick aliens

  • so that tick aliens can do whatever work it needs to do with delta time.

  • So boom.

  • So now, if I do that, oh, it works.

  • Awesome.

  • No sound, so will it work with aliens that are beyond--

  • yep.

  • That's so cool.

  • It works.

  • No sound effects.

  • I feel like sound is always a super important aspect of the game,

  • so I want to make some sound effects.

  • [? Aslee ?] says, AWESOME, in all caps.

  • Yeah.

  • It's pretty cool.

  • I'm happy that it worked sort of on the first try.

  • I mean, I had a couple of syntax errors, but it works.

  • My logic wasn't wrong.

  • That's the more difficult thing to solve.

  • OK, what sounds good?

  • Let's see.

  • Let's turn this up, turn my sound up little bit.

  • So if you're not familiar, this is VFXR.

  • I'm not sure how you pronounce it.

  • It's a sound generation program on Mac and Windows, and I think maybe Linux.

  • I'm not 100% sure.

  • It just generates sounds using simple sound synthesis, different sound

  • waves that you can use, triangles, sine waves, saw waves, et cetera.

  • They even have whistle waves, which is interesting.

  • They have some pre-set settings here that are all kind of randomized

  • and tweaked, so it's super nice.

  • I'm going to make a couple sounds.

  • So the first one I want to create is, like, shooting, like a laser.

  • So they have a laser shoot category here.

  • [LASER SHOOTING]

  • I couldn't hear that.

  • [LASER SHOOTING CONTINUES]

  • OK, that was a bit better.

  • That might be a little obnoxious.

  • I might turn down a little bit.

  • What do you think?

  • Does that sound obnoxious?

  • It sounds kind of nice, right?

  • We'll use that one.

  • Where are we at?

  • In here, Space Invaders.

  • Oh yeah, VFX doesn't let you create folders within the interface.

  • So it's sounds, and then export wave, sounds, laser.

  • Do the aliens shoot back?

  • Yes.

  • Not yet.

  • We will implement that.

  • Sounds, laser, and then we want something for when

  • we defeat an alien, so explosion.

  • [EXPLOSION BOOMS]

  • No, probably not that one.

  • [EXPLOSIONS CONTINUE]

  • No.

  • That's a little loud, but if we turn down--

  • a little harsh.

  • That sounds OK, right, maybe?

  • That sound is so much like the original Space Invaders,

  • though, the first explosion.

  • This one?

  • I, for one, welcome our new alien overlords, says JPGuy.

  • Not without a fight.

  • You need a short sound for the explosion.

  • Yeah, I kind of agree.

  • [EXPLOSIONS CONTINUE]

  • Can I speed up the sound?

  • Yeah, it should be sustain time to K time.

  • How's that?

  • They only eat pineapple pizza.

  • Yeah, nice.

  • OK, we'll go with that one.

  • That's fine.

  • So we'll just call that one explosion.wav,

  • and we'll leave that open in case you want to add some new sounds.

  • I'm going to go to my dependencies.

  • So this is where we have all of our assets,

  • g sounds, this is going to be a new table I'm

  • going to create-- a global table.

  • That's why we're using lowercase g at the beginning of it,

  • because it's a global.

  • We're not doing that in main.Lua with some of the globals we have,

  • because there is no real fantastic reason,

  • I guess, because they're all used within-- well, they're not globals,

  • technically speaking, actually.

  • They're all locals that are highest level in main.Lua,

  • but they're not technically global.

  • You can't access them from other modules.

  • You can access them from main.Lua.

  • So I guess that's maybe the reason.

  • g sounds equals-- this is a new table, so each sound needs a string key.

  • So in this case, a laser equals LOVE.audio.new source.

  • This is how I declare an audio file, an audio object.

  • LOVE.audio.new source, sounds/laser.wav.

  • And then, I think it takes in a word after that

  • to declare whether it's static or streaming, and then explosion.

  • So static or streaming just means whether or not

  • it will sort of load the audio dynamically into memory

  • when it needs to get used, and then sort of free it up.

  • Static means it's always stored in memory.

  • So smaller sound files you can keep in memory and it's not a problem.

  • For us for a game with a lot of music--

  • for example, Super Smash Brothers, the new Super Smash Brothers Ultimate,

  • has 900 songs--

  • all, I assume, CD quality.

  • That's a lot if you keep all those in memory at one time.

  • So those you would want to stream from disk.

  • In this case, we're not streaming them.

  • We're just going to keep them static.

  • They're small sounds.

  • They're very few kilobytes.

  • LOVE.audio.new source sounds, explosion.wav, static.

  • And then, in ship.Lua, this is where we actually instantiate the projectiles.

  • So going to say, g sounds laser play.

  • And actually, right before I do that, I'm going to call stop on it.

  • And what this does is, if the sound's already playing it stops it

  • and then replays it instantly.

  • And this is to solve the problem that happens

  • because if you try to fire a sound or play a sound in LOVE 2D

  • and that sound's already playing, it won't re-trigger the sound,

  • it'll just keep playing the sound that was already playing.

  • So you can't, like, rapidly hit laser and have a do, do, do, do.

  • It'll just play it only as fast as the sound itself is going until it ends,

  • and then it'll trigger a new one.

  • But we wanted to basically, if user presses space bar fast,

  • we want it to trigger every single time.

  • So we're going to call stop and then play, and that will solve that problem.

  • And then, in main.Lua when we actually do the collision,

  • we're going to say g sounds explosion stop, and g sounds explosion play.

  • Just like that.

  • So this should work.

  • [EXPLOSIONS AND LASERS]

  • So a pretty easy game, I would say, at this point.

  • We might need to throttle how fast we can shoot the laser, but it works.

  • It's pretty cool.

  • The other thing that we need to do which we haven't done,

  • which I mentioned we would do, is get rid of the projectile

  • when they're off screen.

  • JPGuy says, got to go for a while, hopefully

  • be back before the stream ends.

  • Good luck with the Space Invaders stuff.

  • Thanks so much, JP.

  • Appreciate having you on.

  • I'll see you next time.

  • Tune in on Friday for David's stream.

  • He's going to do some Docker stuff.

  • It's going to be super cool.

  • MKloppenburg, thanks for the tip.

  • Downloading VFXR now.

  • Awesome.

  • Yes, it's a great sound synthesis program.

  • I love it for simple, like, retro-style stuff.

  • Sustain, yep, did all that.

  • Death to them all, says JPGuy.

  • Death to all the aliens.

  • Normally, when you hear the word love, you think of romantic stuff.

  • Now, immediately thinks of LOVE.update.

  • Yeah, I've ruined that idea for a lot of people, I'm sure.

  • All right, awesome.

  • Everyone's thinking that this is super cool.

  • I think it's super cool, too.

  • I mean, the sounds really sealed the deal, right?

  • [LASER FIRING]

  • Now it actually feels that we're playing a game.

  • It's not just a little weird demo-y thing.

  • We're actually getting somewhere with it.

  • So that's that.

  • Let's also take care of the projectiles being at the top of the screen.

  • So we can say, if projectile.y is less than or equal to zero minus

  • projectile--

  • or I guess we can just say, negative projectile length,

  • then projectile P key is equal to nil.

  • And then, this four loop then needs to be in an L statement,

  • because if we do this, this projectile collide function will break,

  • because that's now a nil object, right?

  • Is that true?

  • I think the object will still exist.

  • We still have reference to the object itself.

  • So I don't think that's necessarily true,

  • but we should still put it into an else just for good

  • measure, because logically we don't want to check to see.

  • If it's beyond the edge of the screen, we

  • don't need to check to see if it's colliding with an alien.

  • It's impossible.

  • It's beyond the view.

  • So we'll do that.

  • So now, we can demonstrate that this works.

  • In my LOVE.draw, if I say, LOVE.graphics.print, projectiles,

  • two string, length of projectiles.

  • So this should print--

  • yeah, projectiles zero, right, up at the very top.

  • [LASERS FIRING]

  • Yep.

  • See, they're all set to zero.

  • So we have no projectiles left.

  • They're all getting set to nil.

  • Pretty cool.

  • And it looks like it renders it.

  • I think it takes it a second to unload.

  • Like, the reference is nil, but for some reason

  • it takes it, like, half a second to actually update that number.

  • So it stays-- it'll, like, instantly go to zero--

  • everything.

  • But it works.

  • It's good.

  • We are freeing our memory as we use it.

  • [LASERS FIRING]

  • So we don't need to worry anymore about sort

  • of having a ridiculously long update loop or running out of memory.

  • Those aren't concerns anymore.

  • We can check if the projectile is nil or now in the loop before shooting.

  • Well, we're not going to--

  • oh yeah, I see what you mean.

  • Yeah, we could do that.

  • It's probably unnecessary and more code than just doing an L statement,

  • though, to be honest, so that's OK.

  • Probably slightly more efficient, too.

  • Oh wait, yeah.

  • Yeah, slightly more efficient, because it's

  • going to update iterate through every alien if we did that.

  • So we're just saving some cycles.

  • It's not a huge optimization, but it saves us, minimally, 55 if statements,

  • if not more logic than that.

  • Maybe it's more complicated than that.

  • So it turns that into just an else.

  • So it's pretty cool.

  • We have a special guest today with us, here.

  • Who do we have on stream?

  • You might be cut off.

  • You might need to crouch a little bit, there.

  • Hey.

  • SPEAKER 2: Hey!

  • Nice to see everyone.

  • COLTON OGDEN: Is this the first time I've seen you since--

  • SPEAKER 2: In like, since 2018.

  • Yeah.

  • COLTON OGDEN: Yeah, I didn't realize that actually, yeah.

  • SPEAKER 2: These streams are like a tour of my childhood, it seems.

  • COLTON OGDEN: Oh yeah.

  • Did you want to play a little bit of crappy--

  • SPEAKER 2: Yeah, how far along are we now?

  • COLTON OGDEN: We've got projectiles, and you can use the arrow keys and space

  • bar to shoot some aliens if you want.

  • SPEAKER 2: All right.

  • Hey, that's a lot of diverse enemies up there.

  • COLTON OGDEN: Yeah, we have it all-- they're

  • set to a random index on a spreadsheet.

  • We just implemented getting rid of projectiles when they're off screen,

  • and so now the memory gets freed.

  • That was the most recent step.

  • SPEAKER 2: I still remember that sound.

  • There's a very prototypical laser sound.

  • COLTON OGDEN: Yeah, we used a-- if you remember

  • from the games course, the VFXR tool which we used to generate sound.

  • SPEAKER 2: Yeah for sure.

  • COLTON OGDEN: You started a new course today, right?

  • SPEAKER 2: We did.

  • We're teaching a new course on computer science

  • and the law at Harvard's Law School, in fact.

  • COLTON OGDEN: How'd that go?

  • SPEAKER 2: Good, good.

  • Doug Lloyd will be co-teaching too, so he takes over later this week.

  • So a few more lectures up this week.

  • COLTON OGDEN: Nice.

  • And then you're coming in on Friday?

  • SPEAKER 2: Oh, yeah, no surprise this time, though.

  • What game are we playing on Friday?

  • COLTON OGDEN: Oh, yes.

  • It's called Docker.

  • SPEAKER 2: Oh, yeah, so we actually use Docker,

  • which is a containerization technology, which

  • is a similar in spirit to virtual machines, if you're familiar.

  • So we're looking forward to talking about how those works

  • and how they drive all the CS50's infrastructure,

  • and increasingly a lot of cloud-based services, as well.

  • COLTON OGDEN: Nice.

  • I'm excited to learn a few things about Docker, as well.

  • SPEAKER 2: Cool.

  • All right.

  • COLTON OGDEN: Have a good time.

  • SPEAKER 2: Well, I don't want to take away from the game.

  • Good to see everyone and see you back in the chat, and thanks for tuning in.

  • COLTON OGDEN: Cool.

  • Thanks for popping by.

  • Appreciate it.

  • All right, so we did the freeing of the memory

  • when the projectiles go off screen.

  • So the next thing that we should probably take a look at maybe

  • is have the ships fire back at us.

  • Because currently, we can fire at the projectiles,

  • but it's a one-sided battle.

  • The aliens cannot actually give us any challenge.

  • Eventually, they will come down towards the player and theoretically

  • collide with us-- well, they will collide with us, certainly.

  • But yeah, actually having an adversary fire at us

  • is probably worth implementing.

  • So why don't we take a look at that?

  • So in the alien class, this is probably where we want that to happen.

  • [MUSIC PLAYING]

  • Aminebenamor, thank you very much for following.

  • And actually, now that I think about it, probably not the alien class,

  • because only a specific subset of aliens are

  • going to want to be able to fire at the player.

  • Do not give them weapons.

  • This is how it starts, says [? Aslee. ?] Yeah, no.

  • Unfortunately, for the sake of having an interesting enough game to at least

  • give us a challenge--

  • is what Bhavik is just mentioning now--

  • we probably want to make this a bi-directional game.

  • So my initial thought was do it in the alien class,

  • but that's not going to work necessarily well, because we

  • need to have access to all the aliens.

  • We need that access to that information because we need to figure out

  • which aliens are at the bottom row.

  • And the reason we need to do that is because if we look at the game

  • and we see the aliens moving, if we allow every alien to fire

  • and the projectiles will sort of indiscriminately collide

  • with other aliens or the player's ship, as you can imagine,

  • all the aliens that are above the other ones will kill the aliens below them.

  • And this will therefore leave only this top layer of aliens,

  • depending on how fast the shooting interval is, once that has passed.

  • So what we want to do-- exactly, Bhavik Knight is saying.

  • The bottom row aliens only, otherwise the aliens will kill themselves.

  • That's correct.

  • So we only want the bottom row to shoot.

  • However, we want the bottom row to shoot,

  • and that includes aliens that we defeat from the bottom rows up.

  • So if for example, if we defeat an alien there, the alien above it

  • should therefore be able to shoot at us now.

  • And you know, the same thing if we do it again.

  • Then, the alien in the third row now should

  • be able to shoot at us, and so forth.

  • So we need to figure out--

  • I guess we could sort of tweak the aliens that can

  • fire based on the aliens above them.

  • I guess whenever we defeat an alien, we can then

  • trigger some code that gives the alien above it the ability to fire.

  • That's probably the easiest way to do it, honestly,

  • is to give each alien a flag.

  • So the alien--

  • I guess we just do it in entity, because ship and alien can fire.

  • So self can fire is false.

  • We don't want every alien to be able to fire necessarily

  • Now Colton is trying to build Skynet.

  • He is one of them.

  • I can neither confirm nor deny such claims--

  • such accusations.

  • I'm going to, in main.Lua where the ship gets instantiated,

  • I'm first going to let the ship fire.

  • Ship can fire is true.

  • This should be true.

  • And the aliens are all going to be, by default, not able to fire.

  • They're going to be sent to can fire is equal to false.

  • So let's go ahead and give the bottom row of aliens the ability to fire.

  • So we generated all the rows and columns, and then I'm going to say,

  • if y is equal to aliens tall, then--

  • So this is--

  • OK.

  • So I'm going to do this.

  • I'm going to say, local aliens equal to this bit of code here.

  • So what I've done is, as I've taken that constructor out of the table insert

  • function so that we can store a reference to it here.

  • So local alien equals alien at xy, math.RAM 256 for the random frame.

  • And then, I'm going to say, if y is equal to aliens tall,

  • which means if we're bottom row, then I'm going to say,

  • alien can fire is equal to true.

  • Right?

  • So this will only give aliens at y level five the ability to fire.

  • And then, in our update--

  • did I do this in update?

  • I guess I can in alien here.

  • Yeah.

  • I want it to be on a timer, and I want to give each alien a random interval

  • of time that it will fire.

  • Because I wanted to be kind of random.

  • I don't want to be on a consistent timer, because if they are,

  • they'll all fire at the exact same time.

  • And it'll be kind of like a blanket of fire, and it'll be weird.

  • And it won't be unpredictable.

  • It won't be fun.

  • So also, each alien needs to have a reference to its own firing interval,

  • right?

  • So it needs to randomly choose a length of time.

  • It will wait to fire, and then fire.

  • And then, when it fires, it needs to reset that interval

  • to some other random value.

  • And we can set it between maybe one and three seconds.

  • Or no, because that would be pretty fast.

  • Maybe one and seven seconds.

  • Yeah, we'll do it between one and seven seconds.

  • One and 10 seconds?

  • Yeah, one and 10 seconds.

  • This is all game design, game balancing, that sort of thing--

  • again, up to taste more than anything else.

  • You want to tweak values, mess with them, figure out what works.

  • So we're going to say--

  • what makes the most sense, here?

  • Do we put it in here?

  • Because if we do that, we need to create a new constructor.

  • Just brainstorming for a second, figuring out what makes the most sense.

  • We can check if the alien has another alien in front.

  • Yeah, we could do that, but that's going to be a little bit less--

  • I think not as clean of a solution as I want to look for.

  • I mean, it works.

  • That's solution would work fine, I think.

  • We can set it so that if there are fewer aliens, they shoot rapidly.

  • With more aliens, they shoot at a slower pace, says [? Bhavik Knight. ?]

  • That's not a bad idea.

  • That's pretty cool, interesting sort of game design idea.

  • Certainly would work.

  • Certainly would be good.

  • I'm just trying to figure out how to structure this in my head

  • before we actually code.

  • So the aliens that can fire need a timer--

  • a fire timer.

  • The fire timer is going to get a random value between one and 10,

  • and that's the number of seconds.

  • Each one will therefore need a timer, itself.

  • So an interval and a timer-- the interval

  • will be the amount of time to wait for it to fire.

  • The timer itself will be the amount of time

  • in delta time in seconds that have elapsed to wait for that interval

  • to have passed.

  • Will then fire, will reset the timer, create a random new interval.

  • So yeah, we have the pieces.

  • So the best place to--

  • I mean, dynamically, we could just do it in main.Lua.

  • We could just say, alien.timer.

  • Everything alien is going to need a timer so that we can update it.

  • Well, it will have a timer.

  • Oh yeah, so alien.timer is equal to zero.

  • Fire timer, we should call it fire timer.

  • Try to be a little bit more verbose when you can.

  • And then, fire interval.

  • And that's going to be math.random 10.

  • So this is the random length of time that will pass.

  • Aliens in the back have less pause, and aliens of the front

  • have more pause to shoot.

  • Yeah, that's a good idea, [? Bhavik. ?] Not bad.

  • I would recommend implementing something like that, for sure.

  • Try it out.

  • That's a good example of an actual mechanic to influence the gameplay--

  • the difficulty of the game.

  • So fire timer is zero.

  • Fire interval is equal to math.random 10.

  • So what we can then do is say, in our update--

  • tick aliens.

  • It could go in tick aliens, too.

  • We'll write it in here, and then maybe we'll make it a separate function.

  • So for _alien in pairs aliens, do.

  • And then we're going to say, if not alien.invisible,

  • because the alien can't shoot if it's invisible.

  • If alien.can fire-- if it can fire, then that

  • means that it will have a fire timer and a fire interval.

  • So we'll say, fire timer equals alien.fire timer plus delta time,

  • if alien.fire timer is greater than or equal to alien.fire interval.

  • So this means that the amount of time has passed that's the same or greater

  • than the actual interval that we're waiting for this particular alien.

  • It's going to be random for each alien.

  • So this alien is set to fire after five seconds, and five seconds have elapsed,

  • then I'm going to set alien.fire timer equal to alien.fire

  • timer minus alien.fire interval.

  • So set to zero, or roughly zero.

  • And then, we're going to spawn a projectile, and it's going to go down.

  • So this is kind of what we did before.

  • Table.insert into projectiles a new projectile,

  • which is going to get alien.x and alien.y plus alien.height.

  • And then, it's going to be set to down.

  • So this projectile is going to move downwards.

  • It's not going to move upwards, right?

  • So let's try this.

  • Oh, hey, whoa, nice.

  • OK.

  • So some of them are firing in tandem, because it's not a floating point

  • value.

  • It's an integer.

  • So the random number--

  • oh, we forgot to change this to another random value.

  • So alien.fire interval is equal to math.random 10.

  • All right.

  • Aliens can't shoot when they're invisible.

  • This is in line with Star Trek.

  • As far as I recall, the Romulan spaceships

  • couldn't fire while they had their cloaking devices on.

  • That's interesting.

  • I've not watched much Star Trek, but that sounds

  • very apt for this particular example.

  • But the aliens are firing.

  • Now, the only thing is, they're not shooting at us.

  • I guess I do want the alien to also do the sound effect for firing.

  • So g sounds, laser, stop, g sounds, laser, play.

  • Now, I kind of almost want to have a different sound effect.

  • [LASER FIRING]

  • It's kind of cool, because it's creating, like, these obstacle

  • courses that I have to fly through.

  • Whoa, like that.

  • Oh, it went right through me.

  • There's no collision for the ship yet, actually, so it's not doing anything.

  • So it can just go straight through me.

  • I forgot.

  • I, like, instinctively wanted to dodge it.

  • Yeah, the sound's not bad, because it stops every one when it fires.

  • A different sound effect would be better, says Andrej.

  • We can do that.

  • Different sound effects, so let's do some lasers.

  • Not that one.

  • [LASER FIRING]

  • That sounds obnoxious.

  • [LASERS CONTINUE]

  • Even more obnoxious.

  • That's not bad.

  • I kind of like that.

  • I'll do that one.

  • Alien laser.

  • Different alien projectile color, as well.

  • That makes it a little bit more complicated,

  • because then we have to add that as a field onto the class.

  • Maybe we can make that an optional feature at the end if we have time.

  • For simplicity, we're going to keep them the same color, just for now.

  • Dependencies-- it's a good idea, though.

  • Don't think that it's not a good idea.

  • I just don't think we have the time.

  • But no, it's an awesome idea.

  • I totally agree.

  • Alien laser equals LOVE.audio.new source, sounds, alien laser.wav,

  • static.

  • Alien laser.wav does not exist.

  • What did I call it?

  • I called the alien laser.wav.

  • Oh, sonds.

  • That needs to be sounds.

  • OK.

  • Oh, so now we have to actually call the right sound effect.

  • So in here it's alien laser.

  • [LASERS FIRING]

  • Yeah, I kind of like it.

  • It's a little bit deeper than the regular spaceship,

  • so it kind of has a more even, like, a weird, very

  • subtle, sinister difference.

  • So I kind of like it.

  • It's pretty cool.

  • Yeah, that's great.

  • We'll do it.

  • Ship it.

  • Now, the projectiles should collide with the ship, as well.

  • So what we should probably do is--

  • updating it if it's less than projectile length, or projectile.y

  • is greater than virtual height.

  • Remember, because these are the projectiles that

  • go below the edge of the screen, we have to keep track of this, as well.

  • Then, set it to nil.

  • We were only checking above the top edge of the screen.

  • We also need to check the bottom edge.

  • And we need to also check to see if it collides with the ship.

  • So we can say, if--

  • we can shortcut this, too.

  • Do I want to do that?

  • No.

  • We'll just say, if projectile collides with ship, ship.invisible

  • is equal to true.

  • So because ship inherits from entity, this should result in the ship dying.

  • Yep, perfect.

  • So I'm still moving the ship around.

  • I can still move it around, but it is not visible.

  • And I want an explosion sound to play.

  • I want a different explosion sound to play than when the ships die, though.

  • I want a more dramatic sound, I think.

  • So let's try here.

  • [EXPLOSIONS FIZZING]

  • Oh, that one was all right.

  • It's not quite long enough, maybe.

  • [EXPLOSIONS CONTINUE]

  • That one's very dramatic, but it's kind of appropriate.

  • Cool.

  • We'll do that one, just for the drama.

  • Death.wav.

  • Let's go over here, we've got got to go into dependencies.

  • Got to add death.wav.

  • Audio.new source, sounds, death.wav, static.

  • Cool.

  • And then, here, set explosion.

  • We want death.

  • Lots of positive words.

  • All right, let's try and die.

  • [EXPLOSIONS BOOM]

  • There we go.

  • A very dramatic death sound, and the player does die.

  • We do not go to a different screen, unfortunately.

  • That is not yet implemented, but that's probably the next step, honestly.

  • We've implemented movement.

  • We've implemented the ships firing.

  • We've implemented death on the player's side.

  • There's no score, yet.

  • Score is important.

  • We probably want score.

  • The title screen, probably important.

  • Yeah, that's probably it for now.

  • The next part is going to be the largest part-- the more complicated one,

  • I suppose.

  • So let's do that.

  • So the title screen.

  • Well, the state machine class first.

  • I'm going to create a new folder called states within source,

  • and then I'm going to create a new file in source called state machine.Lua.

  • And a state machine is a--

  • it's been a blast thus far, have fun with the rest,

  • [? says MKloppenburg. ?] Will have to watch the part later on.

  • Thanks so much for joining, [? MKloppenburg. ?]

  • Glad to have you with us.

  • Hope you enjoy.

  • Be sure to tune in on Friday for David's streaming It'll be super awesome.

  • The state machine class is a-- so a state machine is essentially a finite

  • representation of what the state of something is, whether something is--

  • it can only be in one state at a given time,

  • and going back and forth between states occurs during certain transition

  • criteria, right?

  • So you have a title screen.

  • When your game is in the title screen, it's

  • in a state of being in the title screen.

  • You're not playing the game.

  • You're not at a game over.

  • It can only be in one state at a time.

  • You can't be a title screen and the game over screen at the same time.

  • That doesn't really make sense.

  • You could maybe make a game that tells you

  • you lost, but is also sort of the game over.

  • But it's technically still going to be a different state, more than likely.

  • A state machine is what allows you to transition between the different states

  • of your game.

  • The state machine is kind of like a thing that points at whatever state

  • is currently active and updates it, checks for the transitions,

  • and then transitions when it needs to do so and keeps track of that.

  • The actual states themselves are how we modularize our game,

  • and we can then implement the title screen separate from the play state

  • separate from the game over state, et cetera.

  • So the state machine class--

  • first of all, it is a class.

  • So like we did before, we're going to create it as a class.

  • The state machine has an update and a render function.

  • Just like a lot of the other stuff that we did,

  • we're going to call this from main.

  • It also has a change function, which will allow

  • us to put it into a particular state.

  • And we want to actually be able to create it,

  • so it takes in a state table which we are going to store all of our states

  • in.

  • So when we call change state, we want to do

  • is essentially say, self.current state.

  • We're always going to have a current state.

  • And this state machine can apply to creatures in your game.

  • It can apply to the game state itself.

  • You can basically make a state machine for anything that changes state

  • and that you want to modulize the state of,

  • or encapsulate into a discrete file or module.

  • You can use a state machine for it.

  • It's pretty flexible, and typically you use it

  • for creatures in the game or game states, most often,

  • but you could find multiple uses for it.

  • The state machine will always have a reference to whatever the current state

  • is as its current state field, and that changes depending

  • on what we call a change, and so forth.

  • So I'm going to say self.current state, exit.

  • So every state is going to have an exit function and an enter function.

  • The exit and enter functions function as the transition--

  • sort of an abstraction of the transition of a state from one

  • state to another state.

  • When you transition from one state to another, you call exit on the state

  • that you're in.

  • So it'll clean up whatever it needs to do, do other stuff,

  • and then it will call enter on the state that it's transitioning to.

  • And then, it will set the current state to that state.

  • And so the enter basically lets you clean up stuff in the state

  • they you're currently in if you need to do so, free memory,

  • do whatever you got to do, and then call some initialization code

  • in your new state with the enter function.

  • And enter usually takes params, as well.

  • So that'll be this, if taking the param is a second field of that function.

  • And then I can say, self.current state is equal to self.states state,

  • and then, oddly, I do call it as a function, which we'll see here

  • in a second as to why we do that.

  • You don't have to necessarily do it that way,

  • but it's a nice, clean way to do it when you have a game that uses,

  • like, fresh states every time you do a state transition.

  • And then, I'm going to say, current state enter params.

  • Self.states is equal to states.

  • Self.current state update, and self.current state render.

  • So we have a state machine class that will take a dictionary,

  • it will take a map, take a table of states,

  • and they're actually going to be in the form of anonymous functions.

  • And we'll see that.

  • That is why we call this here-- this parentheses here,

  • like where you're doing a function call, because each of the self.states

  • is actually a function.

  • [MUSIC PLAYING]

  • It's actually a function object.

  • Mirkyst, thank you very much for following.

  • So if I go to main.Lua here, first of all, go to dependencies.Lua

  • and require source state machine, like so.

  • And in the LOVE.load, this is where I like to initialize the state machine.

  • I'll usually do it after all the setup stuff.

  • And back for another half hour or so says, JPGuy.

  • Thanks for joining again.

  • Just doing some state machine stuff now, so a little bit more meaty,

  • so to speak.

  • It's a simple class, but a very flexible, very useful class.

  • What we're going to do is, I'm going to create a g state machine.

  • Now, this g state machine is global because we

  • might want to change our state from within other states.

  • Well, from outside of main.Lua, we are going

  • to change it from within other states.

  • Each state of our game is going to be able to transition to other states.

  • So the state machine itself needs to be global.

  • So I'm going to do this here.

  • And I'm going to say title is equal to an anonymous function that

  • returns a title state object, which we have not seen yet.

  • Play is going to be equal to a function that returns a play state, which

  • we also have not seen yet.

  • And game over is equal to a function that

  • returns a game over state, which we also have not seen yet.

  • And I'm going to change our state to the title state.

  • It takes no parameters.

  • And we want to make sure that if self.current state, then we

  • want to basically put this in an if statement,

  • because if we're starting the very beginning of the game

  • and we don't have a current state enabled, then this will not work,

  • because self.current state will be equal to nil.

  • And we can't call exit on nil.

  • It has to be something that has an exit method attached

  • to it, which is every state.

  • Every state needs to have enter and exit associated with it.

  • That's part of the interface.

  • That's how we can get this to work.

  • It assumes that any state that we implement will have that interface.

  • This is kind of like an interface, as per what Bhavik mentioned earlier.

  • But again, this is all dynamic.

  • Nothing is going to enforce that these functions exist.

  • It's up to you to enforce that.

  • So I'm going to go into states, here, and create a new file.

  • I'm going to call this base state.

  • Not the most useful comment blocks of all time, but better than nothing,

  • I suppose, arguably.

  • Base state is going to be the state from which every other state inherits.

  • And the reason that we want this to exist--

  • we wouldn't normally need this, but this is

  • to prevent us from having to write empty enter and exit

  • methods for our future states, because not every state needs

  • an enter and an exit method, but our state machine assumes they exist.

  • And because it assumes they exist, we want

  • to basically make sure every state has this interface-- it

  • adopts this interface.

  • And so we get this through inheritance.

  • So base state is then equal to a class.

  • And all base state is a bunch of empty methods.

  • Base state, update, render, enter, which takes parameters, and exit.

  • So this is all that base state is-- just an empty class--

  • well, not a completely empty class, but a class with five empty methods,

  • an init, update, render, enter, and exit-- all functions

  • that the state machine assumes exists on each state that it works with.

  • So now what we can do is, I can say, new file in states.

  • I can call this title state.

  • And title state is going to be equal to a class which

  • includes equals base state, right?

  • And so now this is essentially the same thing

  • as copying over these five methods into title state for free.

  • So if we just saved it like this, titles class

  • is essentially the same thing as titles state--

  • sorry, this should be title state, not title class.

  • Title state will be the same thing as base state, essentially.

  • They have the same methods.

  • We don't want that to be the case, though.

  • We want title state to actually have some behavior.

  • So what I'm going to say is, any function now that I

  • define that has the same name as these, it'll just override that.

  • It will just replace it with our own version

  • while keeping the other ones that we inherit

  • from base state, which is super useful.

  • So we can say it's title state, and then this will be update and render.

  • We only need these two for now, just to demonstrate how it works.

  • So I'm going to say I LOVE.graphics.print, Space Invaders.

  • This is the title screen.

  • Actually, this is going to be printf.

  • Title screen, right, so this is going to be Space Invaders.

  • It's going to start at zero.

  • It's going to be roughly in the middle of the screen, virtual height divided

  • by two minus--

  • I don't remember how tall the font is.

  • I'm going to assume 16, so I'm going to have minus 8.

  • It's going to use the width of the screen as its wrap amount,

  • and I'm going to center it.

  • Oh, sorry, this should be in the draw function.

  • And up here in the update function, I'm going to say,

  • if LOVE.keyboard.was pressed, enter, or LOVE.keyboard.was pressed return,

  • then I'm going to--

  • a bit of a brain fart.

  • g state machine change to play.

  • No parameters.

  • So maybe you see what's happening here.

  • It'll be clear shortly if not.

  • This is just a title screen that's going to say Space

  • Invaders in the middle of the screen.

  • And it's going to check for input when we press enter or return-- return

  • taking into consideration Macbooks, which by default, the enter key is

  • return.

  • It's going to use that g state machine we declared in main.Lua and transition

  • to our play state, a state that we defined as play.

  • And again, the states are all here-- title, play, and game over.

  • Those are the string keys.

  • And they map to anonymous functions.

  • These anonymous functions are being stored in our state machine

  • in self.states.

  • So self.states is just a reference to this table, here.

  • That's what this curly bracket syntax is.

  • Remember, you can instantiate a class with its name and curly brackets

  • if it just takes in a dictionary or table or map--

  • however you want to look at it.

  • And these are anonymous functions that are

  • stored there, assigned to these keys.

  • So key value-- the values are function objects.

  • Those function objects will be called here, right?

  • And remember, all they do is they just return a state-- a title state,

  • of play state, and a game over state-- a fresh state, completely blank,

  • which we so set to self.current state on line 13 here by saying,

  • self.states at state--

  • remember-- change to state.

  • And then, we call these curly brackets.

  • That'll actually call the function object.

  • It'll execute that function's code.

  • And the function's return value is that state--

  • depending on whether it's a play state, a title state, or a game over state--

  • and therefore assign it to current state,

  • so then we have a reference to that state object-- a brand new,

  • clean state object.

  • MetalEagle says, hello CCSA TV.

  • Hello, MetalEagle.

  • Glad to have you with us.

  • Happy new year.

  • So that's how the state machine is working.

  • That's how it's getting the state object and it's

  • keeping track of whatever state is currently active.

  • We still have a few pieces left to implement, though.

  • Why don't we go ahead into--

  • well, we're already implementing the different states, the title screen.

  • Let's add a play state.

  • The play state is essentially going to be all the stuff that we

  • implemented in main.Lua already.

  • Play state equals class, includes equals base state.

  • And so what this is going to do is, all of this stuff here, right,

  • this is all going to be sent to the init function of the state.

  • Play state.

  • Right, so that's what this does.

  • So we're using self now.

  • So we're no longer having these pseudo-global variables

  • that are at the parent level of our main.Lua.

  • Now they're actual member variables--

  • member fields-- of the object itself, these new state objects.

  • We've essentially taken all of our logic--

  • we've found a way to take the game logic itself and put that into a class.

  • And so now we can modularize how our game flow works and not

  • put every bit of important logic into main.Lua, so it's super nice.

  • And that includes also these variables up here.

  • We don't need these in main.Lua.

  • Those can be in the play state, just like that.

  • And they're no longer a local, right?

  • So boom, boom, boom, boom.

  • They're self.

  • And now these-- the [? Aslee ?] and Bhavik variables

  • were just kind of stored temporarily they aren't really

  • ever referenced later on.

  • So we can keep those as locals.

  • We can keep the ship index as local.

  • We're reusing it here and we're putting that

  • into self.ship, which is actual ship variable that we're

  • going to use consistently throughout the rest of the course.

  • Oh, it's JPGuy's birthday.

  • Happy belated birthday, JP.

  • I did not know, as well.

  • I apologize if he mentioned it before.

  • I did not know.

  • December 17th, nice.

  • Happy birthday.

  • So now, notice that self.projectile, self.aliens, self.ship

  • is not necessary here, because we assigned it here.

  • Yeah.

  • We declared it in advance as a local because we

  • wanted to reference it later on.

  • And if you want a local variable that you reference

  • from within a nested scope, you need to define it

  • above that function, which is why we did it before.

  • We don't need to do that anymore, because we can just

  • call it self.ship here and assign it, so super straightforward,

  • saving a lot of code, actually.

  • All of these we can keep in main.Lua.

  • We can keep [? kush, ?] we can keep all the initialization stuff.

  • We can keep the state machine, itself, being initialized.

  • All of this stuff, though-- this is all actually

  • update code for our play state.

  • So copy that, bring it over here to our update function.

  • So this is cool.

  • Now remember, every time we have a reference to ship,

  • it's no longer just a reference to ship or projectiles.

  • We actually have to reference it as self.ship and self.projectiles,

  • so that's important that we keep track of that.

  • Let me make sure that I'm not missing any of those.

  • OK.

  • And tick aliens-- remember, that was a function that was in main.Lua

  • We no longer have access to that from within here.

  • So what we want to do is, we want to take tick aliens probably

  • out, and take that out of main.

  • So again, saving space in main, allocating it to a different module.

  • Can I just copy it here?

  • Right, yeah.

  • OK, and self.aliens, cool.

  • And alien movement timer, that's a self as well, right?

  • Yep.

  • So we want to set alien movement timer as self.alien movement timer.

  • And alien direction, as well?

  • Yep.

  • Alien direction, that also gets adjusted.

  • So alien direction becomes self.alien direction.

  • Whoops.

  • Boom, boom, self.alien direction, perfect.

  • So that should be good.

  • That looks good to me.

  • So our main.Lua is now 68 lines of code, so we've actually

  • reduced it quite a bit.

  • The draw, as well, we can get rid of that, right?

  • So we can do this, get rid of this, go over here to make a draw function.

  • Or render, I should say.

  • And this should also be set to play state tick aliens.

  • And this will be self tick aliens.

  • So here, we do this.

  • So pair is self.projectiles, self.aliens, and self.ship.

  • So everything is kind of coming into plan, here.

  • Code looks very good and modularized now.

  • Yep, thank you.

  • Watched the video about state machines 600--

  • so of course someone has mentioned it before.

  • Yeah, super useful construct in game programming-- ubiquitous, very useful,

  • very helpful.

  • OK.

  • We're going to keep if keys equal to escape in our actual main.Lua

  • so it's accessible from within every single game state.

  • Now, the missing ingredient is, we actually

  • have to call g state machine render and g state machine update, right?

  • Because if we don't do that, then all that code that we put into play state

  • is just kind of lost in space.

  • It still needs to be triggered.

  • And we do that from within main.Lua using

  • the state machine object that we declared earlier

  • and that we initialized.

  • So assuming that I did everything right, and actually I

  • know right now that I did not actually require the states, themselves.

  • Very important that you do this.

  • Title, state.

  • And because I have a reference to it, I am going to require a game over state,

  • and just add it really fast.

  • Game over state.Lua.

  • Game over state class.

  • Not functioning game over state.

  • Game over state is equal to class, includes equals base state.

  • Cool, now assuming that everything is in place,

  • this should at least give us a title screen.

  • And as soon as we hit the title screen, and we press enter or return,

  • we should get transitioned to sort of the game as we saw before.

  • So let's take a look.

  • Cool, pretty nice.

  • We see we have the Space Invaders text in the middle.

  • Because remember we're defaulting to the title screen-- the title state.

  • So starting right off in the middle of the screen, we press Enter.

  • OK, so it's not completely perfect.

  • Play state line 33, bad argument to insert.

  • OK, play state line 33.

  • Let's see what that looks like.

  • Ah, aliens needs to be self.aliens.

  • Let's try that again.

  • Bad argument, two pairs, play state 66.

  • Probably the same bug.

  • Yep, self.aliens.

  • And play state 112.

  • Yep, self.aliens.

  • OK, boom, boom, boom, boom.

  • Remember, aliens by itself is no longer meaningful.

  • It needs to be self.aliens because it's within the play state.

  • So now-- OK, 78.

  • Still have a few that we missed, which is OK.

  • Self.projectiles.

  • Yep.

  • I can't believe we haven't missed any other one.

  • Let's see.

  • Yep, right here, self.projectiles.

  • OK, so hopefully that works.

  • Once again, 16.

  • Oh man.

  • Wait, what did I miss there?

  • What was that?

  • Oh, projectile line 16.

  • OK.

  • So projectile line 16.

  • OK, takes on an entity, which gets an xy within a height.

  • So where are we referencing that for--

  • let's see if we have a stack trace here.

  • OK, that's from line-- oh, play state 56.

  • OK, play state 56.

  • This needs to be self.ship, not ship.

  • OK.

  • And this needs to be self.ship, as well.

  • A lot of these little things you've got to tweak.

  • If you start this off from the beginning in a state--

  • what was the key code to press when changing two of the same names at once?

  • Oh, if you highlight something, you can press Command-D or Control-D,

  • and that will highlight the next thing of the same name in VS Code,

  • and a lot of other text editors like Atom, too.

  • All right.

  • Is this working?

  • Come on.

  • [LASERS BLASTING]

  • OK, that looks pretty good.

  • Let's make sure that I can shoot other ships too.

  • OK, cool.

  • One thing that we didn't implement, actually, which is important

  • is that the ships above the ships that die on the bottom row

  • haven't been given the ability to fire, so we need to also do that.

  • But yeah, we've modularized everything.

  • Everything is cool.

  • We have two different states, now.

  • We have a title state and a play state.

  • And the last one will be a game over state when we die.

  • So as soon as we die, we should just do a change

  • to the game over state, which we can very simply just kind of copy over

  • the title state stuff, here, making sure to rename them to game over state.

  • And then, we want to set this to title.

  • And then, game over like that, which won't do anything,

  • because we're not actually transitioning to it yet.

  • So from within play state, what we want to do is, when we die,

  • we want to g state machine, change to game over.

  • Is that how I named it?

  • Let me make sure.

  • Yep, perfect.

  • So now when we die, we should go to a game over.

  • Let's try it.

  • [EXPLOSIONS]

  • Awesome.

  • So we have a game over.

  • If I press Enter, we go back to the title screen.

  • If I press Enter again, back to a brand new game.

  • Now, do the ships regenerate?

  • OK, let's move firstly the green one and then--

  • yep.

  • So the ships themselves also regenerate.

  • Sorry, that was a lot of sound at once.

  • MetalEagle says, why do you have call stop before play?

  • So we mentioned this before.

  • What it does is, if you're triggering in LOVE 2D a sound effect,

  • and you trigger it fast before it's finished playing,

  • LOVE 2D will not refire it.

  • It will let it the sound effect keep playing until it's finished.

  • And so if you want to have a rapid fire laser,

  • for example, you will kind of just be stuck

  • waiting for the first sound effect to finish,

  • and it will kind of be like a weird sort of--

  • sometimes it'll fire, sometimes it won't, kind of sound.

  • So if you stop the sound effect and then play it immediately afterwards,

  • you will be able to re-trigger it instantly.

  • So you will get the effect of rapidly triggering

  • that sound effect over and over again.

  • Good question.

  • You weren't thinking about separating the entity class,

  • and with modularity, look how far we've come with state machine.

  • Yes.

  • Yes, this is Bhavik And that's a it's a good point.

  • Always try to modularize stuff as best as you can.

  • I mean, I'm sure we could do a better job of modularizing what we have now.

  • It's fairly well-structured, but you can almost always

  • get better at organizing stuff.

  • If you see really large functions, a good heuristic

  • is to kind of break those up and make smaller functions.

  • Separating the states is very good for game development, that sort of thing.

  • Yeah, it's good practice.

  • Always try to make the most elegant code that you can.

  • It's difficult if you're in a rush and you have deadlines to meet and stuff.

  • Sometimes you can't do it.

  • But as best as you can, as much as is realistic for your hobby projects

  • or whatnot, it's good practice, if anything.

  • The other thing that we were going to do is,

  • we were going to make sure that aliens could still

  • fire at us from the upper rows when the lower rows are cleared out.

  • And we also want a score.

  • And we want to display the score when we get a game over.

  • So what we can do is--

  • and also we would also want to trigger a new level each time

  • we clear out the map.

  • I'm not sure if we'll have time to do that,

  • but we could certainly talk about it.

  • But for now, let's do the aliens being able to fire

  • when the lower aliens are cleared out.

  • So what we can do is--

  • where is the code located at?

  • [MUSIC PLAYING]

  • Balthazar_Bratt, thank you so much for following.

  • Appreciate it.

  • In play state-- so in play state where we actually check each projectile

  • against each alien, what we're going to want to do there is,

  • when we do collide with an alien--

  • which is what we do here, when we trigger the code for clearing it out--

  • we want to give the alien above it the ability to fire,

  • give it an interval, that sort of thing.

  • So what we can do is, A key will be the number--

  • the index of the alien every time.

  • That's what we have set to, here.

  • That's why we needed to declare it.

  • So we're going to say, if A key is greater than 11--

  • and the reason that we're setting a greater than 11--

  • and what we should actually do is use a constant, so in this case,

  • aliens wide, right?

  • What this will give us is true if it's anything greater

  • 11, which is the length of an individual alien row.

  • So any alien beyond the top row, right?

  • So if it's the case that that alien is below the top row, then we want to say,

  • aliens at A key minus aliens wide.can fire is equal to true.

  • And we have to do the same thing that we do when we basically

  • figure out if the y level is aliens tall when we initialize the aliens.

  • So we're going to say, can fire is equal to true, fire timer to zero,

  • and fire interval to math.random 10, right?

  • So fire timer equal to zero.

  • And fire interval, math.random 10.

  • And that should work.

  • Let's try it out.

  • [LASERS FIRING]

  • I'm going to clear out this alien.

  • Whoops.

  • Play state 55.

  • I forgot to do self.aliens for all of these.

  • So make sure to do that.

  • OK.

  • Clear that alien out.

  • So pay attention to that.

  • OK, so he killed the other alien, so that was an unfortunate bug.

  • Whoops.

  • Let's try that again.

  • [EXPLOSIONS]

  • So the reason that's doing that is because the movement is fast.

  • It's too fast.

  • So we can maybe tweak that.

  • So if we set constants to the alien movement interval to 0.75--

  • [LASERS FIRING]

  • OK, so it's still killing the aliens below it.

  • So how did I fix this before?

  • I'm trying to remember.

  • I solved this problem once before.

  • The simple thing to do--

  • they're not spawning in the middle of the ship, are they?

  • No, they're not.

  • That is a bug.

  • Yeah, the projectile is spawning right at the alien x.

  • We don't want that to happen.

  • We want it spawn at alien size divided by two--

  • alien x plus alien size divided by two.

  • That should fix it.

  • That should fix it.

  • Now it's right in the middle.

  • [LASERS FIRING]

  • There we go.

  • Perfect.

  • So it was spawning right at the x, right at the edge of the alien,

  • and so it was instantly colliding with the alien right below it

  • if there was an alien right below it.

  • So you don't want that.

  • You want the lasers to spawn right in the middle of the alien.

  • So we fixed that problem.

  • Let's try and figure out a [INAUDIBLE],, see what it looks like.

  • [LASERS FIRING]

  • Whoa.

  • They can still kind of--

  • they can still sort of kill each other.

  • Yeah, because they move in such a way that the movement itself

  • might be slightly off of when the projectiles themselves move.

  • So I think what we should do is set the movement interval to 0.7 and not 0.75,

  • so it's not in between a second, because all the lasers will always

  • fire at a second, I think.

  • Oh, I guess not, because when we kill the alien, it changes the--

  • oh whoa, that was weird.

  • I got an alien to die above another alien, which

  • is probably because I shot a projectile and it moved right into it.

  • That's pretty cool.

  • [EXPLOSIONS]

  • So yeah, lots of fun stuff.

  • To do features where we ask a player named, score, high score,

  • with the player name, levels, et cetera.

  • If the alien should check whether any alien is in front of it

  • or not before shooting.

  • Yeah, that would be one way to solve the problem a little bit more

  • appropriately.

  • The problem isn't that there's an alien front of it.

  • The problem is that it moves into it on the frame

  • after it's shot the projectile.

  • So anticipating that is a little bit trickier.

  • I could just make all the projectiles always spawn at the lowest y-level,

  • but that's not as convincing-looking.

  • We won't spend too much time fine-tuning that, I don't think.

  • We'll kind of consider it a feature for now, just for the sake of time.

  • I think if we had more time, we'd actually implement perfect laser firing

  • from the aliens.

  • It's pretty good, though.

  • It works pretty well.

  • Andrej says, you could do what the other, original game did.

  • With game creation, I found some glitches

  • that you did not initially intend to be a cool and unexpected feature.

  • Yeah, games are so crazy a lot of the time that stuff that you try out

  • and mess up ends up actually being interesting,

  • and demos you create end up being fun.

  • It's pretty cool.

  • And then, Andrej said, you could do what the original game did.

  • But I'm not sure what that is.

  • Andrej, if you wouldn't mind elaborating that'd be awesome.

  • Aliens' lasers would be ignored by aliens.

  • Oh yeah, we could do that too.

  • I could just-- yeah, I could say if projectiles down

  • and it collides with an alien, do that.

  • It's not super important at this point, I think.

  • We'll just let it exist as it is.

  • It works well enough for the demo.

  • It's probably somewhat fairly easy to fix.

  • And I encourage, actually, if you're following along, to try to fix it,

  • because that'd be cool.

  • Let's implement a score mechanic in the play state,

  • and then let's feed that into our game over state.

  • So let's go ahead and set a score to--

  • here, I'm going to set the score to zero.

  • And in the render function, I'm going to draw the score after the ship.

  • So LOVE.graphics.print score, two string, self.score.

  • Just like that.

  • So that'll print a score in our play state, which it does, very top left.

  • Pretty cool.

  • It's not going to change, though, because we don't

  • get a score any time we kill an alien.

  • But that's easy.

  • All we have to do is go to our update function,

  • and whenever we collide with an alien--

  • which is here.

  • Say, self.score equals self.score plus 100.

  • Pretty easy, right?

  • But we're not feeding the score to our game over screen,

  • so we can't see what our score actually was.

  • So we should probably do that.

  • So I'm going to, in our what we call game over, which is-- where is that?

  • That is right here.

  • So game over, remember the change function can take parameters.

  • So what we're going to do is, we can say,

  • we're going to pass in an anonymous table, and I'm going to say,

  • score is equal to self.score.

  • And what this is going to do is, this is going

  • to pass this table to the game over state

  • with a key of score that equals the score as it is right now, self.score.

  • So in game over, I'm going to game over a state, Enter, params,

  • self.score is equal to params.score.

  • So remember, that table is going to get passed in our state machine.

  • This is what params is.

  • It's going to be fed into current state enter, which

  • is the new state that we're getting.

  • And we're just pass in that params as a table.

  • So then, game over state.

  • Here we have params.

  • We have access to it.

  • Self.xcore score is equal to params.score,

  • and then here I can just say, your Score Two string, self.score.

  • So go ahead and get some points.

  • Whoa, that's horrible, because all I did was copy and paste it.

  • So what I'm going to do is, I'm going to say,

  • virtual height divided by two, plus 32, I think.

  • Try that.

  • OK, pretty good, not bad.

  • Not as much as I wanted it to be.

  • Let's do plus eight.

  • Cool.

  • That's nice.

  • That's pretty nice.

  • We can move it up a little bit, probably,

  • to be more vertically centered.

  • We won't worry too much about that kind of detail.

  • That's something that you can just solve by messing around with it.

  • But yeah, that's pretty much it.

  • I guess one more message we could add to the title state

  • would be the press Enter.

  • Just like that.

  • There we go.

  • Space Invaders, press Enter to play.

  • When we press Enter, we play.

  • We do this.

  • [INAUDIBLE] High score of 900.

  • Space Invaders, press Enter to play.

  • Bhavik Knight says, what does the dot dot do in front of two string?

  • I forgot.

  • Dot dot is the string concatenation operator, so what that does

  • is it will just add two things together and just add one directly

  • onto the end of the other one.

  • So in some languages, like Python, you can use use plus.

  • Two string just means convert a non-string value--

  • a numerical value-- to a string, and we're using dot dot

  • to concatenate your score.

  • And then, that string converted value, just adding them together.

  • All right.

  • That was pretty much all I aimed to complete for today's stream.

  • I do want to stick around for any questions,

  • if people have some, before we adjourn.

  • In case I went too fast over something, I would like to do that.

  • Maybe receive any ideas or suggestions for future streams.

  • That would be awesome, as well.

  • I know that [? Aslee ?] did say she wanted a typing game,

  • so I have been thinking about that.

  • We'll probably do something like that.

  • I might do hangman first before I do the typing game.

  • We can use hangman, build a hangman, and then use

  • some of that code to develop the typing game, which would be cool.

  • And we could talk about what kind of typing game--

  • just a generic typing game.

  • I've never made a typing test before.

  • I mean, a simple typing game would be pretty easy,

  • just get the words correct, I suppose.

  • But yeah.

  • [? Aslee ?] says, yes, I'm dying for the typing game.

  • Hangman would also be great.

  • That's also sort of a typing game.

  • I did debug it.

  • Oh, one thing that we do want to do is push this to GitHub, right?

  • LS, where are we at?

  • This is in Pong, so out of there, into streams, Space Invaders,

  • get status, OK.

  • Final stream version, cool.

  • So now, as of right this second, the code that we did today

  • plus the code that we did last stream is now on GitHub.

  • So if you want to clone it, mess with it, try and add some new stuff to it,

  • definitely do so.

  • Definitely try to add new features.

  • It's a very good exercise.

  • Maybe use it to create your own game in the future if you want to.

  • We have a full--

  • it's pretty much a full, I mean, proof of concept, Space Invaders.

  • It doesn't have levels.

  • It doesn't have a lot of bells and whistles,

  • but it will help you if you want to start building a space

  • shooter of some kind, certainly.

  • Did a hangman game in 60 '01, says Bhavik Knight, for a pset.

  • There was a typing game where you got points as you typed correctly,

  • says [? Aslee. ?] Interesting.

  • Couldn't finish the story yet.

  • I'll share it here when I finish.

  • [? Aslee, ?] are you building an adventure game?

  • I don't remember offhand.

  • If we talked about this on a prior stream, I apologize.

  • Any questions at all about the code today, or about anything?

  • Anybody have anything they want to chat about for a couple of minutes?

  • We have a few minutes left, here.

  • Oh, text adventure in Unity.

  • Got it, OK.

  • Nice.

  • There is a Udemy course that teaches how to do something like that.

  • If I'm not sure if you're using that-- going through that Udemy course

  • by Ben Tristem, I believe.

  • That's a good course.

  • No, I got the code because I was late.

  • How was your holiday?

  • Says [? Aslee. ?] Holiday was great.

  • So I was in California.

  • I spent a lot of time with my family.

  • It was super cool.

  • Played games with my dad and my brother and my friends.

  • Played some of the new Super Smash Brothers, which is super fun.

  • Super good game.

  • Like I mentioned it earlier, 900 or something like that--

  • 850 or 900 songs in that game--

  • insane.

  • Yeah, how about your holiday?

  • And anybody else in the stream, what did we do over our holidays?

  • Certainly the weather was a little warmer over there than it is here now.

  • It's in the 20s today.

  • It's a little chilly.

  • And again, more ideas for games much welcomed.

  • I personally want to start getting a little bit more into modern web dev

  • this year a little bit, so if folks are open to more hands-on building

  • of websites using React and other tools, that's

  • something that I think I might want to do and learn.

  • So I wouldn't mind doing it on stream if people are interested in that.

  • I think probably more people care about web dev than they do about games,

  • so I'm not sure, but let me know.

  • Definitely that's what I'm using.

  • It's awesome.

  • I moved past the text adventure to harder things,

  • but I'm a sucker for writing, so I'm working on it.

  • Wow, 900 songs, that sounds like a nice holiday.

  • Yeah.

  • It's a crazy game.

  • And yeah, it's a good course.

  • Hopefully you can make it through.

  • I would definitely stick through it.

  • Ben has a great teaching style.

  • I'm a big fan of his course.

  • [? Aslee ?] said, I care more about games.

  • I personally think I care a little bit more about games, too.

  • But I'd like to get good at web dev.

  • It'd be cool, I think it'd fun.

  • It would be good stuff.

  • Yeah, good stuff.

  • We made Space Invaders, everybody.

  • It took us two streams, but we did it.

  • So future streams-- so probably realistically,

  • the next game stream for me is probably going to be next week.

  • It's probably going to be Monday.

  • It's probably going to be hangman.

  • And then, another day next week, maybe we'll

  • do the typing game, if not the week after that.

  • David's on Friday doing Docker.

  • So that'll be really cool.

  • I'm going to try and get some more people in here.

  • I'm going to try to get Nick in here after a couple of weeks.

  • Nick is still on vacation, but as soon as Nick gets back from vacation,

  • you'll probably get some white hat hacking streams

  • from him on Kali Linux, and maybe some other stuff maybe on Bitcoin

  • and who knows what else.

  • He's a jack of all trades.

  • For me in the future, hopefully React.

  • I have to learn it.

  • That's going to take me time.

  • It's going to be some late nights and weekends in the future

  • before we can do that.

  • But next week, Monday--

  • anticipate Monday-- hangman stream.

  • Friday, David with Docker.

  • Tuesdays and Wednesdays are going to be a little light,

  • because we have a class in this room, but we might shift.

  • We might do a Monday, Thursday, Friday stream schedule.

  • We may or may not still be able to do Tuesday, Wednesday,

  • depending on how things work out.

  • It'll at least be about eight weeks until we

  • resume Tuesdays and Wednesdays.

  • React, CTF, Docker-- yep, that's all stuff that I

  • know that Nick was super interested in.

  • The CTF stuff was super cool.

  • Three days a week sounds great, says [? Aslee. ?] Yeah, three days a week

  • is what we will strive for, if not more.

  • It depends on the room's availability.

  • This room is very popular.

  • Sorry if you have already talked about it,

  • but is it possible to generate an executable file?

  • Says-- I can't read that username--

  • [? CesarCXBR. ?] An executable file--

  • yes it is.

  • So on LOVE 2D's forum, their wiki, Game Distribution

  • is the section you want to look at here on their on their wiki page.

  • And it will tell you in detail how to go about creating

  • Windows executable, for example, if you're on Windows, or a Mac OS app,

  • for example.

  • And also, a .love file.

  • .love a very standard way that people making LOVE applications tend

  • to distribute their games, because it's pretty easy.

  • It's just a zip archive essentially, but it will run with the LOVE framework.

  • Windows and Mac have all their instructions here for making exes.

  • So this is how you would create, using the command line, an exe using a LOVE--

  • sorry, this is how you would use the command line to make an exe with a LOVE

  • zip having been already created.

  • It assumes that you've done that.

  • It assumes you've created a super game.love thing,

  • or whatever your game is .love, which is defined up here.

  • It's part of the process.

  • You have to do that.

  • And then, you need to--

  • essentially what you do is you add love.exe on top of super game,

  • or whatever.

  • They say super game.love.

  • That's just the name of their game.

  • Whatever your game's name is .love, you add the love exe itself--

  • the actual love exe--

  • so that it will run when you double-click it on a Windows computer.

  • And then, it will receive the name, whatever exe afterwards.

  • And they use this forward slash b as well, which

  • I'm not entirely sure what that is.

  • It probably means binary, appending that exe and the .love thing--

  • the .love binary on top of it.

  • And then, you can create a batch.

  • It has instructions for creating a batch file to do it.

  • It's the exact same thing, sort of, for Linus and OSX using the cat command.

  • So I do think that that slash b is just adding them on top of each other.

  • But yeah, fairly straightforward.

  • Creating a Mac OSX application is a little bit more complicated.

  • The Mac OSX application is--

  • yeah, yeah have a little bit more stuff.

  • They have, like, a package with contents and some pllist files and whatnot.

  • But all the information is there on that page, so definitely check it out.

  • One, two streams per week, says Bhavik Knight.

  • Yeah, I don't want to do only one to two.

  • I want to do at least three, so we'll try to figure it out.

  • I have to figure out the scheduling for this room,

  • make sure that we have the time and the bandwidth for it,

  • make sure I can get enough people to contribute,

  • which I know Nick's on board when he gets back.

  • David's usually on board once every week or two.

  • I can do one or two a week.

  • So we should have enough.

  • We'll work on it.

  • We'll get a schedule.

  • Am I teaching this semester in the extension school?

  • Yes, online only, though--

  • online-only version of the GD50 course.

  • Andrej says, any Unity streams in the near future?

  • Probably, yeah.

  • I would love to do some more Unity stuff.

  • I'm a big fan of Unity, and I want to get better at it and practice doing it.

  • So yeah, expect some Unity stuff.

  • I'll have to brainstorm and think of some fun stuff.

  • I really want to make a 3D platformer at some point,

  • as I mentioned on prior streams, but that's complicated.

  • That's, like, a very large project.

  • So something smaller would be nice.

  • I'll think about it.

  • I have no concrete games in mind just yet.

  • A stream to make an app would be nice with React Native,

  • but perhaps [? Brian ?] could help with that.

  • React Native-- that'd be interesting.

  • I'll see if [? Brian's ?] familiar with React Native.

  • I've been asking him to get involved in making some more streams of us.

  • He's doing a sort of mini workshop course on campus,

  • so he's been a little bit bandwidth locked, but maybe

  • after a couple of weeks have passed.

  • Any streams about functional programming in the near future?

  • Says Bhavik.

  • Nothing planned, but I would love to do something

  • like that, because I have also been wanting to get

  • better at functional programming.

  • I'm a big fan of Closure.

  • And I've also wanted to learn Haskell.

  • So it would be fun to do some of that on stream.

  • Solving the Rubik's cube in Unity.

  • Yeah, Bhavik Knight, that's true.

  • I had thought about--

  • brainstormed that briefly, actually, last year after we

  • talked about it on stream.

  • And it would be possible, but it's going to be somewhat difficult,

  • and I don't know exactly offhand how I would do it.

  • So I would need to brainstorm more, I think,

  • before actually committing to that.

  • I don't want to spend too much time kind of umming and ahhing over

  • on the camera.

  • I kind of want to have a game plan before I set up so that it's not

  • too awkward and quiet.

  • But we'll see.

  • I heard from a reliable source that coding your own games

  • is easier than you think.

  • You know, you should take this online Unity course on Udemy.

  • Online Unity course on Udemy.

  • Oh, that that's the Ben Tristem reference.

  • Yeah, because he does that.

  • Did you finish the Udemy course?

  • I did not finish it.

  • I got through, I think, a few of the games,

  • and then I got distracted with something.

  • I have, like, 20 Udemy courses I have to complete,

  • including some React ones that I just bought, so we'll see how that goes.

  • I swear I see that 25 times a day, at least.

  • Last year sounds so weird.

  • Yeah, it was last year, technically speaking.

  • I mean, it's been almost a month since we

  • streamed-- it's been a month since we streamed Space Invaders-- more

  • than a month.

  • The first part was November 26th, it was when I committed.

  • Udemy courses-- plenty in pending there.

  • Yes.

  • It is that.

  • So I use Udemy, too, [? SisterDemure24 ?] I do.

  • Not a lot, to be honest.

  • The last time I really used it was probably two years ago,

  • and I have recently bought some new courses to try-- like I said,

  • I want to get good at web dev, so I bought a couple of Udemy courses

  • on React and modern web dev, so we'll see.

  • And some AI and machine learning courses--

  • that is going to be a little slower-paced for me, I think,

  • because especially if I want to understand everything really well,

  • I think there's going to be more math behind that.

  • We'll figure that out.

  • But AI and machine learning are courses--

  • topics that would be cool to have, like, Nick and someone else

  • talk about more on camera, because I think those topics are really cool.

  • I was trying to finish the Udemy course before Colton.

  • Student surpasses the teacher.

  • Yeah, definitely do so.

  • I'll be very proud of you, [? Aslee, ?] if you do that.

  • I honestly don't know if I'll get back into that Udemy course,

  • because Catlike coding is a resource that I think

  • I would want to work through first, and it goes into more depth

  • than the Udemy course does.

  • The Udemy courses a little bit simpler, I think--

  • not that it's not very easy.

  • Not that it's not very easy.

  • It does teach you a lot and there's a lot of good stuff in it,

  • but Catlike coding I think is, like, pretty next level.

  • So if anybody is not familiar, CatlikeCoding.com--

  • I'll pull it up here.

  • Really good resource for learning Unity.

  • Do it with the tutorial section.

  • I actually haven't checked it recently.

  • They might have some new stuff on here.

  • The person that does this is really, really, really crazy smart--

  • does a lot of really good stuff, really goes into, like,

  • the low-level side of Unity, which is not something that you see a lot.

  • Like, actually has you digging into the rendering engine

  • and doing, like, custom shaders from scratch,

  • and all kinds of really cool stuff.

  • Yeah, really cool.

  • CatlikeCoding is a brilliant website, says [? Andre. ?] I agree.

  • Udacity-- I like Udacity courses more than Udemy.

  • Udacity offers nano degree about some AI and ML courses.

  • Interesting.

  • I haven't looked too much into Udacity, to be honest.

  • Coursera I have seen.

  • I think Andrew--

  • I don't know if it's Andrew--

  • I don't know how to pronounce the last name--

  • Andrew Ng, but he does an ML course, and his is very well-received.

  • And I think he's on Coursera, if I'm not mistaken.

  • His was one that I wanted to get through, but it's a little dense.

  • And I haven't had time to really dig into it,

  • or the motivation to really go all-in on Machine Learning, but sometimes soon.

  • EdX and Coursera are the best because of the affiliation

  • with a great university.

  • Yeah, edX and Coursera are great.

  • Speaking of that, CS50 2018 materials are edX now, FCS50x, 2019.

  • So check those out if you want to see all

  • of the latest lectures and the latest course materials.

  • We got that shipped over the winter break, so super cool.

  • Can everybody else take an even vacation while I catch up on coding?

  • Says [? Aslee. ?] Oh, wouldn't that be nice, an even longer vacation?

  • The landscape is always changing, I guess.

  • But you know, as long as you put in the effort,

  • I think, and you're consistent about it, I

  • don't think you need to worry about everybody surpassing you forever.

  • I think you'll be just fine.

  • Just go at your own pace.

  • Enjoy the process.

  • Enjoy the journey.

  • You don't want to spend years doing something that you hate doing, right?

  • And eventually you learn-- or at least, I learned to really enjoy the journey.

  • The destination is great-- being able to build stuff.

  • But learning stuff-- if you can enjoy learning,

  • then you'll always be on top of things, or at least relatively so.

  • You'll always at least enjoy the process,

  • and I think go farther than a lot of people that are less interested in it,

  • or maybe monetarily influenced.

  • You know, because there's so much to learn.

  • If you don't enjoy it, then it's not going to stick as much, in my opinion.

  • I don't enjoy everything I learned in CS-- not every single thing.

  • But I enjoy.

  • I would say I can appreciate enjoy the vast majority of it.

  • Certain things I don't really enjoy too much.

  • I can't think of examples offhand, but there are

  • certain topics that are less exciting.

  • I definitely agree on almost everything.

  • I wanted to learn stuff like in The Matrix.

  • Yeah, that'd be really cool.

  • Everybody has their own time.

  • Some start a business at 25 but die at 50, and some start life after 50

  • and work 40 years.

  • Yep, very true.

  • Very true.

  • It's all relative.

  • Like CS50 says, it's not relative to other people.

  • It's relative to how you were when you began, right?

  • How you are now relative to how you are at x time ago is what's important,

  • not how good you are relative to your neighbor.

  • Is the new CS50 stuff out?

  • Yes.

  • The new CS50 stuff is on edX now.

  • That's the one thing I love that the professor says.

  • Yep.

  • It's a great, great expression.

  • Very true words of wisdom.

  • OK.

  • I think we're about ready to wrap up.

  • It's been a pleasure talking with all of you, and a pleasure being back in 2019

  • on stream.

  • I'm glad that we were able to get everything set up and working,

  • and everything was good.

  • We learned and completed Space Invaders, so it's a very successful stream,

  • in my opinion.

  • It went smoothly.

  • There weren't too many great hiccups.

  • [? Thalamus, ?] it was great to see you.

  • You rocked at the Harvard Fair.

  • Thank you so much.

  • Good to see you as well.

  • Hope to see you on TV.

  • Timing for Fridays?

  • Says Bhavik.

  • David's going to be on at 3:30 Eastern Standard time, so a little bit later.

  • [? Ichimasio ?] said, thanks for tuning in.

  • And that means I will go.

  • Fun fact.

  • I will go!

  • Exclamation point.

  • Good to have you back, says [? Aslee. ?] Thank you very much.

  • Cool.

  • All right, everybody.

  • I'm going to transition to the bigger screen, here.

  • I'm going to bid all of you a awesome rest of the week

  • until Friday, which I'll be here with David co-hosting.

  • We'll talk about Docker.

  • Docker is super cool, super modern.

  • It ties in well with all of the dev ops stuff

  • that [? Kareem ?] and I have talked about before.

  • [? Kareem-- ?] I'm going to try to get him on stream, as well, if we could.

  • It would be nice.

  • [? Caesar ?] says, it would be nice if you could present a minimum Super Mario

  • game style.

  • For example, where it seems they work with some kind of maps.

  • Yeah.

  • Yeah, we could maybe do something like that.

  • We would do that in my games course, actually.

  • We implement a version of Super Mario.

  • So if you haven't checked that out, go to CS50.gg,

  • actually, should now point to the games course, although it's old content.

  • Yeah, this is the games course for 2019.

  • Table of contents, Super Mario Brothers.

  • Is it still here?

  • No, it's not.

  • The URLs are all--

  • what's it have?

  • Sort of temporary before we actually get the class fleshed out,

  • but if you go to CS50's YouTube channel, our game development playlist, and type

  • in Super Mario Brothers, I show how to implement a sort of basic Super Mario

  • Brothers style game.

  • And we could maybe certainly take a look at doing something

  • like that again in the future.

  • It's a nice, fun demo.

  • Cool.

  • All right, everybody.

  • Thanks so much for tuning in.

  • See you all on Friday.

  • Enjoy the rest of your week.

  • This was-- this is CS50 on Twitch.

  • And I'll see you all next time.

  • Bye-bye.

COLTON OGDEN: Hello, world.

字幕與單字

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

B1 中級

SPACE INVADERS FROM SCRATCH (Part 2) - CS50 on Twitch, EP.22 (SPACE INVADERS FROM SCRATCH (PART 2) - CS50 on Twitch, EP. 22)

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