Placeholder Image

字幕列表 影片播放

  • COLTON OGDEN: All right.

  • Good evening, everybody.

  • Welcome back to GD50.

  • This is lecture six.

  • And today we're going to be venturing out of the 8-bit world

  • and back into a more modern era of gaming.

  • We're talking about Angry Birds today.

  • I have pretty fond memories of Angry Birds, actually.

  • It was the first mobile game that I remember

  • playing where I realized that mobile was actually a viable platform for gaming.

  • I played it, I think, back in 2009, was when it was first released.

  • And I really enjoyed it.

  • It has a very simple formula.

  • The goal, if you're unfamiliar, is you have

  • birds that you control by a sling shot on the left side of the screen.

  • And then these pigs that have stolen your eggs,

  • you're trying to destroy them or kill them

  • by knocking down their fortresses made out of various materials, wood, glass,

  • metal.

  • And you get a few different bird types.

  • But the gist of it is, basically, just sling shot a bird into some structure,

  • knock it down.

  • And the whole entire underlying mechanism

  • for how things work is via a physics engine,

  • which we'll talk about today in lecture, called Box2D Probably

  • the most ubiquitous 2D physics engine.

  • But that's the overall structure of the game.

  • It's literally just throwing things into structures, knocking them down.

  • And then that tactile and fun game play makes for really great mobile gaming.

  • And I enjoyed it a lot back in the day.

  • This is a screenshot of the first level in the first Angry Birds, which

  • most people I think have probably seen.

  • This is a sample from one of their newer games.

  • As you can see, it's taken a whole new layer here.

  • There's stone and a bunch of intricately varying structures and new creatures

  • and stuff like that.

  • The game has changed a lot, but the formula has stayed the same.

  • And today we'll explore the foundation of what makes this game work.

  • And the topics today, a smaller than usual.

  • But Box2D, we can consider as being a pretty large topic.

  • We'll be talking about Box2D, which is the physics

  • engine we'll be using today in lecture.

  • And via Love2D, which has its own wrapper for Box2D.

  • And we'll also talk a little bit about mouse input, which you haven't really

  • done a lot.

  • But it's very apt, especially in the context of mobile gaming.

  • Because mouse input and touch input are synonymous.

  • But first, let's get a lecture demo.

  • If we have a volunteer come up on stage to showcase

  • what I've put together here.

  • So let me make sure I'm in the right directory here.

  • So whenever you're ready, go ahead and press Enter.

  • So this is a little demo I put together to demonstrate the concepts we'll

  • talk about today in class.

  • This is just the Start screen, but it already

  • shows Box2D representing a bunch of these square shaped aliens, which

  • we'll actually be what we're trying to target in the game.

  • But notice that they all fell and had their own collision

  • and their own physics that took place, and I didn't have to manually code

  • rotation and stuff like this.

  • This is all stuff that Box2D takes care of for us.

  • And we'll see it used to great effect soon.

  • If you go ahead and just click anywhere on the screen,

  • we'll go to the main part of the game.

  • So this is a very simple representation of what Angry Birds is.

  • You start on the left side of the screen.

  • You have a bird, in this case an alien, we

  • used a free art pack that uses aliens instead of birds,

  • but it's the same concept.

  • You have an alien that you can click and drag.

  • So if you click it and then drag around, you

  • can see the trajectory that you'll have when you let go of the mouse.

  • So we're simulating where it's going to go via these purple circles.

  • And then the goal in Angry Birds is to throw the bird into,

  • or the alien, into the fortress guarding the pigs,

  • or in this case, square shaped aliens.

  • So if you go ahead and just launch the bird by letting go of the mouse

  • you'll see.

  • Oh, there we go.

  • And we knocked down the.

  • OK.

  • What happened was we shot the alien into the structure.

  • It destroyed one of the wooden blocks guarding the other alien.

  • And then, as soon as that happened, the top box,

  • because these are all being simulated via Box2D's physics engine,

  • that other box was detected as being unsupported.

  • So it fell down, it hit the other alien.

  • And we've coded it so that when a collision occurs

  • between an obstacle and our alien of sufficient speed,

  • it should kill the alien.

  • Which is how it works in Angry Birds.

  • So if you'll try it again.

  • And this time we hit both of those at the same time.

  • So it triggered both of those being deleted.

  • But this guy is still alive.

  • So after it stops moving, which is similar to how

  • it behaves in the original game, it's going to let us try again to shoot.

  • So if we try one more time.

  • And then we launch and hit it, we kill it and then we get a victory.

  • So that's the overall underlying foundation for what Angry Birds is.

  • Obviously we're using a very simple representation.

  • It doesn't have a lot of the frill that Angry Birds does.

  • But we could easily build upon this and create a fully fleshed out

  • game very similar to Angry Birds.

  • So thanks, Steven.

  • I appreciate the demonstration.

  • So that's our goal today.

  • We'll be talking about how to construct a very basic but functional simulation

  • of what Angry Birds is at its core.

  • Which is just flinging things into obstacles, destroying them,

  • and ultimately destroying the things that they're protecting,

  • the aliens, the pigs that are in the base game.

  • So here's a shot of what the different sprite sheets we're going to be using

  • are.

  • There's a really great sprite sheet that I got off of Open Game Art.

  • Kenny is the artist.

  • He makes a lot of great art.

  • If you notice, it's very similar looking to the art

  • that we use in the Mario lecture.

  • Actually, it's the same artist.

  • So if you're ever looking for assets, he's got a ton of awesome assets

  • on open game art.

  • So we have a set of aliens, square and round shaped.

  • I decided just arbitrarily we made the round shaped aliens the birds.

  • So we shoot those into the structures that are

  • protecting the square shaped aliens.

  • The square shape aliens will be the bad guys in this case.

  • And then we have another sprite sheet down here below on the left side.

  • Which just, I used the tile here just to make a ground element.

  • The rest of these you could easily include in the game if you wanted to,

  • but we're only using the ground here.

  • And then notice here, we have this large sprite sheet

  • which has a bunch of different shapes and sizes of materials.

  • The whole sprite sheet comes with metal and explosive and glass sheets as well.

  • But just for simplicity we only use the wood here.

  • But notice that we have entirely whole pieces

  • and then we have pieces that are partially destroyed

  • and then pieces that are hollow.

  • You could easily model all of these in your game and use them.

  • But we only decided to use just a couple of these,

  • which was just the horizontal and the vertical ones that

  • are completely whole.

  • These, unfortunately, don't have quite the same systematic layout

  • as the sprites that we used before.

  • They're not laid out in a grid evenly spaced.

  • So in this case, in util dot Lua, I ended up hard

  • coding the different XY width and height quads

  • for each of these, which is what you have to do in a situation

  • where you're interacting not with tiles per se,

  • but with more organic shaped objects.

  • So it can take a little more time to end up

  • constructing all of the quads for your objects

  • when you have a sprite sheet like this.

  • But, fortunately, you only have to do it once.

  • Here's a few useful links before we get started in talking about what Box2D is

  • and how to use it, and basically how it's called love dot

  • physics, effectively, in love2D.

  • The first two links are documentation for love 2D,

  • what the functions and objects are that we'll be talking about.

  • And a simple tutorial that talks about how to make a ball

  • bounce in love 2D using Box2D.

  • The third is a great resource that I used actually

  • to learn most of what I know about Box2D, especially

  • in the context of this lecture.

  • So it talks about a lot of different concepts.

  • It talks about all the things that we'll be talking about.

  • And then it goes into a lot more detail about how

  • you can go about constructing a lot of really cool, crazy things like tanks

  • and pulleys and a whole bunch of different things

  • that are worth looking into if you're looking into potentially making

  • a physics based game.

  • Obviously we won't be going into things of that complexity here.

  • But you could easily do it with Box2D.

  • It makes it very, very possible.

  • So the very first thing that we should talk

  • about when we want to construct a game, or simulation,

  • or whatever we want to do, with Box2D is we

  • need some sort of system that will actually perform the simulation for us.

  • And the fundamental core of what a Box2D app or game is, the core is the world

  • object.

  • So there is a world that you can think of as sort of being your world,

  • but what it effectively is is a machine that simulates all of the pieces

  • that you've told it interact with each other in Box2D.

  • So Box2D has a set of objects called fixtures and bodies.

  • Those perform the physical interactions.

  • And it's up to the world to update all of those

  • and apply the relevant forces and physics calculations

  • that resolve collisions and do all sorts of things.

  • All of the things that Box2D does for us, the world

  • takes care of this for us.

  • So we don't have to manually go through and update every single object

  • with its velocity that we've done before and check for collisions.

  • The world does this for us, and resolves them

  • based on how we tell it to resolve them.

  • And the world also possesses, like an actual world

  • would, gravity on the x and y-axis.

  • In this case, we have gravity applied on the y-axis going down,

  • so it's a positive value.

  • We set it to 300 in this distro, but you can set it to whatever you want.

  • Setting a lower gravity would have the effect

  • of making it feel like we're on the moon or something.

  • Making it feel like we're on a different planet.

  • So that's what the world is.

  • The world simulates everything.

  • And we'll go through just a few terms here

  • before we look at some source code.

  • But there's a few terms that we need to understand before we can really

  • understand what Box2D is doing.

  • And this is the function that we use to create a new world in love2D.

  • Very simple, love dot physics dot new world.

  • And this love dot physics is just a name space that

  • encapsulates all of the Box2D functions and objects that love2D has access to.

  • So anything that you see in love dot physics is effectively

  • a wrapper for Box2D.

  • And to clarify about Box2D, Box2D is just a library that's written in C++

  • that you can plug-in pretty much anywhere you want to.

  • Unity uses it and, actually, most 2D game engines

  • that I've ever seen including Live GDX, for example, which is a very large Java

  • 2D game framework uses Box2D.

  • You can use it anywhere.

  • In this case we're using love2D's own wrapper for it.

  • So the people that created love 2D, they took Box2D

  • and then they just put a bunch of Lua functions

  • around them, around all the objects and functions

  • to make it possible to use it in the same style

  • that we use the rest of the framework.

  • This is how you create a new world.

  • This is the first step in getting your Box2D simulation working.

  • So any questions so far as to how we can get that going?

  • OK.

  • So beyond the world object, which is the foundation, sort of sets up our stage,

  • you can think of it as our stage, we need bodies

  • to actually interact with each other.

  • So a body is just an abstract container.

  • It basically holds a position and a velocity.

  • And you attach things to it via what are called fixtures

  • that allow you to give the body a shape, and therefore a collision box,

  • and therefore allow it to interact with other things.

  • But a body is essentially all the disparate things in your scene interact

  • with each other and move around.

  • And so to create a new body we just do love dot physics dot new body.

  • We pass in the world, so therefore when we do this,

  • the world has a reference to this body now.

  • And every time we call update on our world, which

  • we'll see in the source code, it will know, OK,

  • I have a reference to this body, perform all of the relevant checks

  • on the collision for that body and all the fixtures that it contains.

  • Update its position, updates velocity, and so forth.

  • And not only a world, but it also gets an x and a y,

  • which will place it in the world on instantiation.

  • The last parameter here, type.

  • There are three fundamental types of bodies

  • which we'll see in love 2D, static, dynamic, and kinematic.

  • And that basically influences how it'll interact with the other objects,

  • the other bodies, in our scene.

  • So we have the world, which encapsulates everything,

  • all the bodies, all the fixtures.

  • And then we have the bodies, which are the entities in our game world

  • that have position and velocity, effectively.

  • The last key ingredient here that will allow

  • us to create interactions between the bodies that we have are fixtures.

  • And fixtures, all a fixture is, is this abstract object

  • that will allow you to attach a shape to a body.

  • So bodies are shapeless by default. They don't have a shape.

  • They are just a container that has position velocity, effectively.

  • But they don't interact with anything else.

  • And they don't know how to interact with anything else

  • until you give them a fixture.

  • And the fixture you will give the body and a shape.

  • So for example, if you want the bird that we

  • were looking at earlier, the alien, the round alien, we create a body for it

  • in our world, which doesn't mean anything yet.

  • But we say, I'm going to attach a fixture to that alien.

  • I'm going to give it a circle shape.

  • And it'll then know whenever it performs any calculations,

  • that that alien should interact with things as if it were round.

  • And therefore trigger collisions based on a circular hitbox,

  • as opposed to a rectangular or polygonal hitbox, as we'll see.

  • Fixtures, in addition to attaching shapes

  • to bodies, which will, as said here, influence

  • how they collide with other bodies, they have density, which we'll see.

  • So that things with higher density obviously

  • will fall faster, or not fall faster, but they will influence things

  • as if they have more weight.

  • They will push things farther when they collide with them.

  • They also have friction and they have restitution.

  • Restitution is bounciness.

  • So if something, if we had our alien, no restitution when it hits the ground

  • it'll just fall flat.

  • But if we give it a higher restitution it

  • will actually bounce when it hits the ground.

  • And therefore interact with the world a little bit differently.

  • So when we want to take a fixture and apply a shape to a body,

  • we have a few different shapes that we can

  • apply to it that are given to us by default in love 2D.

  • So circle shape, rectangle shape, edge shape.

  • These are just, effectively, how we define how

  • our bodies interact with other bodies.

  • How, for example, if it's something that's circular

  • it should roll when it's moving along the ground.

  • Or when it hits something the corner obviously of it

  • won't hit something because it's rounded, as opposed to something

  • that has a square hit box, it'll affect things in a slightly different way.

  • And we can define arbitrarily shaped hit boxes, thanks to the polygon shape.

  • If we want something to be shaped like a pentagon, for example,

  • and have it roll around and behave like such,

  • we can just define a polygon via a set of vertices.

  • And then affix that to a body and it will

  • behave as if it were pentagon shaped.

  • And this is how you would instantiate just as we've

  • seen with love dot physics dot new world and love dot physics dot new body,

  • love dot physics dot new fixture takes in a body and a shape,

  • and will apply that shape to the body.

  • And the world after that will know exactly how to collide with things.

  • And so the last thing, the last slide we'll

  • look at here before we start looking at source code

  • is what the different body types are.

  • So I alluded to having three different body types before, static, dynamic,

  • and kinematic.

  • So a static body will exist in our world but not actually be affected by gravity

  • or the collision of anything else.

  • Things can hit it and bounce off of it and do their own thing,

  • but the static body will never be influenced by something else.

  • It exists as some sort of permanent structure, almost like the ground.

  • You don't really affect the ground by moving into it and bouncing into it,

  • unless you do it with enough force.

  • But in our Box2D world, a static body cannot be influenced by anything else.

  • A dynamic body is the opposite.

  • It has the full simulation of Box2D.

  • Gravity affects it, things collide into it, it will bounce off of them.

  • It'll do what you would expect a normal body to do.

  • If I throw a ball in this room and it hits the wall, it's a dynamic body.

  • The walls are the static bodies in this case.

  • And then a kinematic body is a hybrid between the two.

  • It's something that can move and can rotate and do things,

  • but it's not influenced by other objects colliding with it.

  • So, for example, if I have a platform that's just spinning indefinitely,

  • but it's not being affected by gravity and it doesn't move when I hit it,

  • that's a kinematic body.

  • It's still moving and it's semi-static and it influences other things,

  • but it's not purely static.

  • It does have a little bit of behavior that it can grant it.

  • So let's go ahead and look at a few examples

  • now and see how this actually looks in code.

  • So I'm going to go into an example, if you're looking in the distro,

  • there is an example called static.

  • So we'll take a look here and see what a static body looks like in our scene.

  • And for all of these examples leading up to Angry Birds,

  • we're just going to render everything with shapes for simplicity.

  • But this, as anti-climactic as it is, is a full Box2D world

  • with just a single static body.

  • And it's just this square here, colored white.

  • The static body doesn't move, it doesn't do anything.

  • Nothing can influence how it moves or behaves.

  • But it exists in our world as a permanent fixture.

  • And if we had dynamic bodies and we threw a dynamic body at it,

  • for example, the dynamic body would bounce off,

  • the static body would stay there permanently.

  • So let's go ahead and take a look at what

  • the source code looks like for that.

  • So I'm here in main dot Lua of our static file.

  • And just as we've seen before, we needed to find a world.

  • So I have a world here on line 45.

  • Love dot physics dot new world, no x-able gravity,

  • but we are going to have 300 units of positive gravity on the y-axis, which

  • is going from top to bottom.

  • We need a body for our square that's in our game world, our static square.

  • So we're going to go ahead and define a new body here.

  • Love dot physics dot new body takes in the world,

  • recall, because that's how our world's going to have a reference to that body,

  • doesn't know about it unless we pass it into here, our new body constructor.

  • And then I'm just going to put it right in the middle of the screen.

  • So virtual width divided by 2 and virtual height divided by 2.

  • The difference between Box2D bodies and things

  • that we've drawn before or seen before is

  • that everything is defined by its center point, as opposed to its top left.

  • So I'm able to say virtual width divided by 2

  • and virtual height divided by 2 here.

  • But I don't actually need to say virtual width divided by 2 minus whatever

  • the half of that square is.

  • By default, the center point is the XY of that object.

  • And this last string here in the constructor for our new body, static,

  • tells the constructor that this is going to be a static body specifically,

  • not a dynamic body and not a kinematic body.

  • So we have a body and it's static, but it doesn't have a shape,

  • it doesn't really know how to interact with anything else in our world.

  • So we're going to give it a--

  • we're going to create a new shape first.

  • So love gives us a few functions in the form of new X shape.

  • We have new rectangle shape, new circle shape, new edge shape,

  • a few other ones.

  • We're just going to create a new rectangle of width and height of 10.

  • So that's what the 10 and 10 are here.

  • Then we're going to create a new fixture.

  • We're going to affix the box shape to our body with this function.

  • And then once we do that, all we have to do is then render a polygon with fill

  • and then we get the coordinates for our-- or the vertices for our polygon

  • by saying, body get world points.

  • So that's a function off of any Box2D body

  • that will basically get where it is in the world and all of its vertices.

  • And then you just pass in the shape that you want to get the points for.

  • And that will end up just exploding here into a set of vertices

  • that fill up this love dot graphics dot polygon.

  • And the end result of that is we get a square.

  • And we specifically use polygon instead of love

  • dot graphics dot new rectangle because that's what the get

  • world points function explodes out to.

  • It doesn't explode out to the number of arguments that would satisfy

  • love dot graphics dot rectangle.

  • So not a very exciting example.

  • But this is basically the foundation of a full Box2D application.

  • So any questions as to how this works at all?

  • AUDIENCE: Yeah.

  • I'm wondering.

  • How does it determine the center of a abnormal shape?

  • Not like a polygon, square, rectangle, or circle.

  • COLTON OGDEN: The center of an abnormal shape.

  • I'm not entirely sure.

  • The circle is just--

  • AUDIENCE: [INAUDIBLE]

  • COLTON OGDEN: Yeah.

  • I'm not entirely sure about a polygon though.

  • I haven't looked into that into too much detail.

  • I can explore that and see.

  • That's something that I think Box2D is--

  • the actual library implements that and calculates that.

  • Probably based upon calculating the area of--

  • and to repeat for the camera if I didn't repeat already, it's

  • how does Box2D calculate the center point of something non-symmetrical,

  • like a polygon?

  • And best I can understand is that it would do an area calculation off of it

  • and figure out where all the vertices tend towards, I guess.

  • But, yeah, that's something that's implemented in the library.

  • I'm not entirely sure.

  • I can look into it and see and then I'll post in the Slack.

  • AUDIENCE: It seems like [INAUDIBLE] you did have that,

  • it would be difficult to place in a very precise spot.

  • COLTON OGDEN: Yeah.

  • If it were-- yeah.

  • If we did have an odd shape to get it placed in a exact spot.

  • Yeah, I'm not entirely sure.

  • I'd have to explore that a little bit.

  • Interesting idea though.

  • But, yeah, that's essentially how we get a Box2D world up and running.

  • We have a static body.

  • It's not terribly interesting.

  • But with one change--

  • so I have a separate example called dynamic in the source code distro.

  • But all we need to do to really see the difference

  • between a static and dynamic body is on line 48, just change static to dynamic,

  • save it, and then rerun it.

  • And then we'll immediately see that it's affected by gravity

  • and it moves downwards as we've told the world that our gravity is

  • set to positive 300.

  • And so that behaves how we would expect it to.

  • Now, there's nothing else in the scene so it's not particularly interesting.

  • So I've created a another example called ground.

  • So let's go ahead and look at that.

  • So if we want more interesting behavior, what do we need to do in a nutshell?

  • AUDIENCE: Have more shapes and make a ground.

  • COLTON OGDEN: Some more shapes and make a ground.

  • Exactly.

  • That's a simple way we can start to get instantly

  • a sense of how powerful Box2D is.

  • Just introduce more shapes that interact with each other in different ways.

  • So ground is an example that just introduces a ground into our scene,

  • so that we can see the box fall down and actually collide with something else.

  • And Love2D makes this nice and easy.

  • Excuse me.

  • They have a actual edge shape that will allow us

  • to basically form where the ground is.

  • Anything that collides with this-- it's effectively a line.

  • But no matter how fast anything moves in are scene,

  • the body will not move past that line.

  • So it's a nice, easy way of getting a ground in our scene

  • without having to implement a polygon that maybe has two vertices going

  • left to right on the screen.

  • So that's what we do here.

  • We have a ground body, which is a static body, recall.

  • Because the ground shouldn't move.

  • We're going to change the box into a dynamic body,

  • but the ground shouldn't move.

  • The ground should be unaffected by anything.

  • It's going to be static in our scene.

  • And it's going to have an edge shape.

  • So notice here, we take 0, 0 as the XY.

  • And then a virtual width is zero.

  • So with shapes, when we define them here,

  • it's not going to draw the shape at 0, 0, at virtual width in 0.

  • This is relative to wherever the body is located.

  • So wherever our body is, this shape will be

  • drawn with these coordinates, this X and Y and this width and height,

  • relative to that.

  • And specifically, relative to the center point of wherever we place a body.

  • So if this ground is set to 0, 0 at virtual width and zero,

  • where do we need to place the actual body for it

  • to render the ground appropriately?

  • AUDIENCE: The middle bottom.

  • COLTON OGDEN: Yeah, so we place it towards the middle bottom.

  • So we actually end up placing the body itself here at virtual height minus 30.

  • And when we affix this edge shape to the body, we'll end up,

  • even though it says 0, 0 virtual width zero,

  • it's relative to wherever the body's XY are.

  • So it's actually going to be 0 virtual height minus 30,

  • virtual width zero will be where that edge exists.

  • And then lastly, just as we did with our box,

  • we need a ground fixture so that the ground body knows how

  • it should interact with other things.

  • So we're going to affix the edge shape, which is just a line,

  • to our ground body.

  • And then here we do love dot graphics-- just

  • as we did with the love dot graphics dot polygon for our box,

  • we're going to do love dot graphics dot line for our ground body.

  • And we're going to do the exact same thing, get world points

  • and get points passing the edge shape here.

  • And I'm also setting a line width of 2 just

  • so we can see it a little bit better.

  • And I'm going to color it red.

  • Again, I colored the box up here green.

  • So go ahead take a look at what this looks like.

  • So I'm going to go into ground.

  • And so notice-- and I also added a little bit of restitution

  • to the-- as I said before, restitution is a quality that a fixture can

  • have which gives it bounciness.

  • So rather than just falling flat down onto the ground,

  • it bounces a little bit as well.

  • And we can see the interaction, so I'll play it one more time.

  • It starts in the middle.

  • And then as soon as it hits the ground body

  • that we created before, the edge shape, it bounces a couple of times.

  • But it shows that the box is dynamic, but the ground is static.

  • Nothing influences the position of the ground.

  • It gets hard set and will stay there permanently.

  • So that shows a nice, easy simple demonstration

  • of an interaction between a static and a dynamic body.

  • I'm going to go ahead and pull up another example here.

  • So kinematic, recall, what was the difference between a kinematic

  • and a static or dynamic body?

  • AUDIENCE: Kinematic can move but not influence other shapes.

  • COLTON OGDEN: Correct.

  • So a kinematic body can move.

  • You can ascribe it positional velocity or angular velocity, it's rotation.

  • But when something collides with it, it's not going to be influenced by it.

  • It's going to influence the body that it collides with.

  • It's going to affect it in some way, but nothing

  • colliding with the kinematic body is going

  • to have an effect on its position or its velocity.

  • It's something that exists and does something programmed

  • and will just do that indefinitely, but it

  • will interact with other dynamic bodies as we have programmed.

  • And it will not interact with other kinematic bodies either.

  • They will almost pretend that each other doesn't exist.

  • If you overlap a static and a kinematic body or kinematic and a kinematic body,

  • they render on top of each other, but they don't actually

  • influence each other's position or anything like that.

  • So I'm going to go ahead and run the kinematic example.

  • So here we have a few things going on.

  • We have the box body that we had before, the dynamic box that

  • falls from the middle of the screen.

  • We have the ground on the very bottom to catch it.

  • But we have three kinematic boxes in the very center that

  • are spinning that influence the green box

  • when the green box collides with them.

  • So as you can see, it tosses it around and then

  • the green body falls back to the bottom.

  • And in this example I've taken away its restitution.

  • So as soon as it hits the ground, it just falls flat.

  • Just like that.

  • And so, as you can see, these blue bodies, they're moving.

  • They have angular velocity indefinitely.

  • Specifically 360 degrees per second.

  • And they will stay in that exact position and rotate in that exact way

  • forever.

  • But as soon as they interact with a dynamic body,

  • they actually cause a collision with that dynamic body.

  • And the collision resolves and this green body

  • gets tossed around because it's dynamic.

  • It will basically do whatever it can to interact with the game world

  • as long as it's interacting with other bodies

  • and resolving its collision that way.

  • And so that's the key example between what the three different bodies are

  • fundamentally.

  • And with these three body types you can construct pretty much any scene

  • that you want to.

  • And obviously the bodies and the fixtures can get insanely complex.

  • I mean, we can look back here at this first example.

  • These structures are a composition of many different types of dynamic bodies

  • that have been given anchors and joints and all sorts of other things

  • to make them look as if they're big constructions.

  • But at the end, they're all just a bunch of little bodies

  • that are welded together, fixtures that are welded together.

  • And these fundamental building blocks are how you construct scenes like this.

  • Put a bunch of these blocks together, weld them together with joints,

  • in this case.

  • We won't cover joints in the context of this lecture.

  • But if you're wondering how all of these individual things

  • can be collideable while still constructing these massive scenes

  • and having physics applied to them, they're

  • just jointed together using weld joints or other types of joints, pulley joints

  • depending on what they are.

  • In this case, there is two circle shapes here, circle fixtures,

  • that are the wheels on this cart and then

  • they're welded to the flat constructions here.

  • Allowing those to be dynamic allows the wheels to roll, and therefore

  • carry the other load with it.

  • Same with this bridge.

  • I forget the exact name of the joint, but it's a chain of fixtures together

  • that are welded by a specific kind of joint.

  • And by putting them together in this way,

  • you get a bridge and all sorts of things that you could think of,

  • including tanks, which is in the notes here.

  • This third link, one of them talks about how

  • to implement a tank by having treads going

  • around circles and then a massive body.

  • You could do anything with Box2D, it's an awesome library.

  • In the context of Angry Birds, really we just

  • scratched the surface for what is possible here.

  • So I'm going to demo now.

  • Actually, I enjoyed, I think, this demo, this bit of code,

  • more than I enjoyed the Angry Birds implementation.

  • And it's a program that I wrote called Ball Pit.

  • Oh, by the way, before I get into that.

  • So I'll leave that as a little teaser, I guess.

  • Looking at kinematic, really quickly.

  • We're going to look and see that I've created

  • a table for the kinematic bodies, a table for the fixtures,

  • and then one shape.

  • Because you only need one shape actually,

  • and you can apply one shape to as many bodies

  • as you want to as long as they all have the same shape.

  • I just create three kinematic bodies here.

  • So spacing them out relative to the center with this math here.

  • They get the string kinematic.

  • And that's really the key difference.

  • And then before finishing, I make sure that I

  • set them to have an angular velocity.

  • So this is how you spin something.

  • If you want to set something to spin indefinitely,

  • just set an angular velocity.

  • In this case, 360 times degrees to radians.

  • And that's just a formula written out as a constant, which is just the number--

  • I forget offhand what exactly the formula is.

  • But it's up here.

  • It's degrees to radians 0.01745329, et cetera.

  • But just like pi, it's a number that you can

  • use to multiply a number in degrees, and you'll get a number in radians.

  • And there's the reverse up there as well.

  • We have to do this because Box2D expects, for any types of rotation,

  • it expects it in radians.

  • I prefer thinking in degrees.

  • So I passed in 360 times degrees to radians.

  • And so we render down here, just as we did with the box body.

  • We render the kinematic bodies, polygon fill, kinematic bodies at i

  • get world points kinematic shape get points.

  • Nothing terribly different.

  • The only real key difference is that we've added kinematic as a string

  • to the constructor for the body.

  • And we've added some angular velocity.

  • And recall, this will make it spin indefinitely,

  • but it will never be influenced-- its angular velocity will never

  • be influenced, its position will never be influenced

  • by anything else in our scene.

  • So now with that out of the way, I'm going to pull up Ball Pit.

  • And maybe I'm just a little bit too excited about this.

  • But I enjoyed this a lot.

  • So what it is, is this is like a bigger demonstration

  • of putting all these pieces together.

  • We have a bunch of circle shapes.

  • They're all interacting with each other.

  • They all have physics.

  • And then I have a larger shape here, this square,

  • which has a higher density than everything else.

  • And by pressing Spacebar I can just dive into--

  • throw it into the ball pit, and it'll cause an interaction

  • with everything else.

  • And a bug slash feature that I discovered

  • about this is that if you press Spacebar over and over again,

  • it never resets its velocity.

  • So it just slams down into the ball pit.

  • So it's just kind of fun, and I think there might actually

  • be possibly a game idea in here.

  • But, I mean, well, what are the pieces here?

  • What's different about this?

  • AUDIENCE: They're all dynamic shapes.

  • COLTON OGDEN: Yeah.

  • So they're all dynamic shapes.

  • Except for the ground at the very bottom.

  • And also, hidden from view are actually two more static shapes

  • on the left and right.

  • Because if we didn't have those, all of the balls

  • would fall to the side out of view.

  • But yeah, we have the static delimiters for our scene.

  • But we have a bunch of dynamic bodies, the balls are all dynamic.

  • And then the square is also dynamic.

  • And then, like I said before, the only real difference between them

  • is that obviously the square is a rectangle.

  • But it also just has a higher density.

  • And so by giving it a higher density it pushes everything else-- oh,

  • what happened there?

  • That was weird.

  • It pushes-- I think it went to sleep because we didn't do anything

  • for a while.

  • But it's able to fall through everything else

  • because it knows that it's heavier, and that it should push and apply

  • a larger force to everything else that's around it.

  • And so by using these fundamental building blocks of what Box2D is,

  • you can construct a lot of really cool simulations and other fun programs

  • and actually get interesting game ideas.

  • I'm inclined to believe that Angry Birds started out as somebody messing around

  • with the Box2D, or physics engine like this.

  • And it was inspired by some other game that I should look into a little bit.

  • But the creators of that game, presumably, found this physics engine

  • and were like, oh, this is cool.

  • I'm going to put a tower of blocks here and just throw something at it.

  • And then they realized, oh, we can make a game out of this.

  • And so I encourage you, if you're ever curious to just experiment with things

  • like that.

  • We could probably turn this into a game.

  • I don't know.

  • I like this a lot.

  • It's a good segue from the abstract, for lack of a better word, examples

  • that we used earlier, and merges it more into the realm

  • of how can we make something fun with this?

  • And so that's how we're going to start moving into the distro today.

  • So the main topic of today's lecture is Box2D and how we use it to make a game.

  • Another thing that we should consider is mouse input.

  • We haven't really used it yet.

  • And I believe I've mentioned it before, slightly offhand.

  • But Love2D makes it super easy.

  • It's just like we do with keyboard input.

  • We just have a couple of callbacks that are in main dot Lua,

  • mouse pressed and mouse released.

  • The difference between these and key pressed and key

  • released is that they also get an X and a Y. Because usually,

  • when you click the mouse or release the mouse,

  • you want to know where it happened because that's

  • obviously pertinent to what you're doing when you're using a mouse.

  • So these are fired by Love2D every time you click or release a mouse button.

  • And they get the X the Y and the key, and you

  • can do whatever you want with those.

  • And just as we've done in prior lectures so

  • that we can use mouse input in other modules besides main dot Lua,

  • there's a function called love dot mouse dot key pressed and key

  • released that I implemented in main dot Lua.

  • You can check those out.

  • They're very similar to how we did the input tables for the keyboard before.

  • But they allow us to use this functionality inside

  • of other functions, other modules, besides main dot Lua.

  • So let's go ahead and start looking at--

  • this is where we're going to start looking at the distro for Angry Birds

  • and how we can take all these pieces and form them into an actual game.

  • So the first thing we'll do, we'll take a look at just a couple of things

  • and then we'll take a short break and then we'll

  • get more into the meat of it.

  • But let me go ahead and clear out all of these.

  • And then we're going to pull up-- so the distro is in Angry 50.

  • And so main dot Lua is here.

  • So not a whole lot is different in here.

  • So we have two states in our game.

  • So we had a start and a play state, as we saw before.

  • The start state is like we've done before.

  • The only difference is in this start state, well, for one,

  • it's running a Box2D simulation.

  • And two, we're actually using mouse input.

  • So actually, let's look at main so you can

  • see where I've added this, which is different than before.

  • So we have love dot key pressed, as we've seen before.

  • But we also have mouse pressed and mouse released.

  • And then mouse was pressed and mouse was released.

  • So those are the main differences in main dot Lua this time, as opposed

  • to last prior lectures, which we only had keyboard input.

  • And as you can see here, we have input tables for keys pressed and released

  • on the mouse.

  • And so we initialized those to empty on every update frame

  • just like we've done with the keyboard.

  • And then the input tables get updated in the callback functions

  • as we've done before.

  • And so that's basically all that's different about main dot Lua this time.

  • And that's how we've tied together the new mouse functions that we've just

  • looked at into our game.

  • The states that exist in our game are play state and start state.

  • So very simple.

  • Very similar to last week where we looked at Zelda,

  • we only basically had a start state and a play state.

  • The start state, we can see here, just to tie together

  • the last bit of our usage of the mouse.

  • Love dot mouse dot was pressed 1.

  • Does anybody know what this 1 is?

  • AUDIENCE: Is that left click?

  • COLTON OGDEN: It is left click.

  • So Love2D assigns integer values to all of your mouse buttons.

  • And 1 is traditionally the default value for left click.

  • Some frameworks will use 0.

  • But Lua, 1 index so start with 1 instead of 0.

  • The thing about the start state here that's kind of cool and interesting.

  • So I'm going to go ahead and play it again, the game.

  • So we start off and right off the gate, just to make things interesting

  • rather than just have a static screen that says

  • Anger 50 click to start, we're actually running a Love2D,

  • a Box2D simulation here.

  • It's a world with a bunch of squares.

  • And so what kind of bodies are all of these?

  • AUDIENCE: [INAUDIBLE].

  • COLTON OGDEN: They're all dynamic bodies.

  • And we've encapsulated them all, just as we did before in the ball pit example,

  • with some invisible static bodies on the left, right, and bottom of the screen.

  • Because if we didn't have those static bodies

  • they would just fall all the way down.

  • And the nice thing is we don't actually have to render anything.

  • So if you want maybe an invisible barrier for something in your game,

  • or you want to encapsulate something, something physical,

  • you don't have to render anything.

  • You can just have arbitrarily shaped and positioned static bodies.

  • And that will act as a container.

  • So that's all we're doing there.

  • We have a container set for all of our little square alien guys.

  • And by creating, I think, a hundred of them and just letting them drop,

  • we have this interesting visual start to our game

  • with very, very minimal effort.

  • So we can take a look at this.

  • So in our start state init, as I said before, we have a world.

  • We obviously need a member of new world.

  • Anytime we do any Box2D stuff you have to start

  • by having a love dot physics dot new world,

  • else you won't be able to run any simulations.

  • Going to create a ground, walls, and then a bunch of aliens.

  • So this here, we can see that we have a table that we're inserting aliens into.

  • But we have a class called alien.

  • So anybody want to ballpark what an alien class ultimately

  • encapsulates or ultimately is?

  • AUDIENCE: Probably the way it looks, like the skin.

  • COLTON OGDEN: It is, yeah, that's definitely a part of it.

  • So the way it looks, or it's skin.

  • So it does have a reference to that.

  • And then more functionally, it also possesses a body and a fixture.

  • So rather than having a bunch of bodies and fixtures

  • that are separated out and maybe just in tables

  • at the surface level of whatever, our level, just wrap them in a class.

  • And then we just can maintain a reference

  • to each individual alien's body and fixture that way.

  • So it's a little bit more encapsulated.

  • It's a little bit more object oriented, little bit cleaner in my opinion.

  • The alien class can take square or round as its--

  • well, it can take anything you want to as its type.

  • So this will ultimately decide how it's rendered and what shape it gets.

  • But if it's square, which is the default, so if you just

  • create an alien with no type, it will get

  • a love dot physics dot rectangle shape.

  • So as we saw before, that's just a box.

  • And then if not, we just default to circle shape.

  • But you could program it to take in whatever shape you want,

  • and then just give it that shape.

  • And you don't really have to do much in terms

  • of coding how it interacts with anything else in terms of collision, at least.

  • Because, thankfully, Box2D will know, OK, it's a circle.

  • It should spin around and interact with things like a circle.

  • Or it's a rectangle so it should interact with things like it's a box.

  • Just nice and convenient.

  • And then we'll just create a fixture here.

  • This set user data function here is important

  • because we'll see in the context of how we actually resolve collisions

  • in a customized way, we'll need user data

  • to be able to differentiate what gets collided in our world.

  • But at the moment you can just know that this basically allows us to pass in--

  • to set arbitrary data onto a fixture.

  • So we can say, fixture set user data alien, the string alien.

  • And what that means is that fixture has some customized metadata about it that

  • says, this is an alien.

  • It's whatever we want to do with it.

  • We could give this a table as well.

  • We could just say, the user data is a table

  • and then has a bunch of information that we can then use at collision time

  • to perform different work on it.

  • But this set user data function is how we

  • are able to resolve collisions between obstacles and aliens differently than,

  • say players and aliens, or even the alien and the ground.

  • Because when we do any Box2D collision, right, the world's

  • taking care of the collisions for us.

  • How do we tell the world, OK, when I hit the ground I want to play a sound,

  • but not do anything.

  • If I hit this box at this velocity, I want it to destroy it.

  • If I hit the alien, I want the alien to disappear

  • and I want to show a victory label.

  • How do we do all these different things?

  • We do that with what are called collision callbacks

  • in the context of Box2D.

  • And we'll see how that works.

  • But suffice to say, user data will be very important coming forward.

  • And then this launched false, actually we don't end up

  • using this so this is irrelevant.

  • But it has a render function here, which just takes in the bodies X and Y

  • and will draw it at the angle that it's at.

  • So that's an important thing.

  • When we before, what we were doing is drawing things via shapes.

  • So love dot graphics dot polygon, love dot graphics dot circle,

  • love dot graphics dot line.

  • But if we want to draw a sprite instead, we

  • need to draw it at the right position, first of all, right?

  • And then things also rotate in Box2D.

  • So we need to draw it at the right angle.

  • So what we do is we can actually query the body for its X.

  • We can query the body for its Y. And we can also query the body for its angle.

  • And then we can draw the texture and the quad

  • that we want to using those XY and the angle,

  • and that will have the effect of drawing a sprite in the world that

  • mirrors what's going on in Box2D, rather than just a simple shape.

  • So that's as simple as it is for drawing a sprite instead of a shape.

  • You can see here, the 17.5 17.5.

  • Does anybody know what that's for?

  • AUDIENCE: Not sure.

  • COLTON OGDEN: So 17.5 17.5 at the end is half of the width

  • and half of the height of the aliens.

  • So aliens are 35 by 35 in this game.

  • We pass those in.

  • This is the center of origin.

  • So when we rotate something by center of origin,

  • it basically describes where the rotation is going to take place.

  • So if it's rotated about the top left and we rotate something,

  • it's going to have the effect of the sprite going around

  • in a circle in sort of an odd way.

  • If we rotate the sprite based on the center of origin of the actual sprite

  • itself, that will have the effect of rotating the sprite on its center.

  • So you can set that origin wherever you want to

  • and it'll perform a 360 degree rotation about that point.

  • And we're setting that point to half--

  • to basically the middle of where we're drawing the sprite.

  • So that will have the effect of when we give it this angle here,

  • self dot body to get angle, the rotation will take place in place.

  • It won't take place-- it won't be some sort of weird about the top left corner

  • rotation, which is not what we want.

  • So when you see center of origin being modified like that,

  • you can assume that it's because we have an offset

  • and we're trying to find the center of where we're drawing

  • and rotate about that to do an in place rotation.

  • But not necessarily, you could also draw something.

  • You could also, maybe you want something some sort of magical ball of energy

  • to rotate about a rod or something.

  • And so you want it to rotate around a different center,

  • or whatever you want arbitrarily.

  • But typically, at least mostly that I've seen,

  • this is useful for making sure that your rotations, your in place rotations,

  • are accurately rendered.

  • So any questions about how the alien class works?

  • All right.

  • So that's the alien class.

  • That's the basically the fundamental building block of our game.

  • The other part is the obstacle.

  • We have obstacles and we have aliens.

  • The obstacles and aliens are actually very similar.

  • So what's the difference between--

  • I mean, ultimately, how are they similar?

  • AUDIENCE: They're both dynamic [INAUDIBLE]..

  • COLTON OGDEN: They're both dynamic bodies, yeah.

  • Really, the only thing that's different about a obstacle and an alien

  • is what we do with them in our scene and how they're rendered.

  • But they function very similarly.

  • They're just dynamic bodies that we give a shape to

  • and we render them with that shape.

  • In this case, the obstacle constructor, we've

  • decided to design it such that it could take a shape horizontal or vertical,

  • which is similar to the type that we saw before with the alien,

  • where it could be square or circular.

  • In this case, if we look back at our sprite sheet back here,

  • we can see there's a ton of different shapes.

  • But the only two shapes that we're going to use in the context of this game,

  • just for this demonstration, are the horizontal, clean wooden shape here

  • and the vertical one that's right here.

  • And so in order to find those out I had to open up

  • basically this sprite in my sprite editor,

  • figure out where the XY width and height were, and then create

  • a quad manually off of that.

  • And then I edited the util dot Lua-- or not util dot Lua,

  • the dependencies dot Lua here.

  • Normally we just create gframes and then we use generate quads.

  • And you can do that like with the aliens and with the tiles, the tiles

  • being this sprite sheet that--

  • this one right here.

  • These are 35 by 35.

  • These are 35 by 35.

  • These are not 35 by 35.

  • These are a bunch of different shapes and sizes.

  • So I went through and gframes wood is for manually created quads here.

  • And then there's four because I also added the semi broken shapes as well.

  • But we don't actually use those.

  • But you could decide to turn these into these on collision,

  • perhaps maybe if the velocity isn't strong enough to break it necessarily,

  • but you want to have some sort of feedback that you collided with it.

  • You can just set maybe the frame to 1 or 2 on collision for that object,

  • instead of 2 and render it appropriately.

  • But if you're dealing with a sprite sheet

  • and that sprite sheet has odd distribution of its sprites,

  • sometimes you have to figure out where the offsets are manually

  • and do it that way.

  • The ideal is that you don't have to and that you can do it programmatically.

  • But it really depends on the game, what your domain is

  • and what objects you're interacting with in the scene.

  • Any questions on the why or how as to that?

  • Cool.

  • So back to the obstacle.

  • So they're horizontal or vertical, and what that does

  • is it sets the frame to 2 or 4.

  • And the 2 or 4 being in gframes that would, the 4 quads that I had a hand,

  • figure out the coordinates for.

  • And really, it's not a whole lot different at that point.

  • If it's a horizontal or a vertical shape,

  • then we need to set its width and height appropriately.

  • Because it's going to have a different width and a different height.

  • If it's vertical, obviously the height is higher than the width.

  • And if it's horizontal, the opposite is true.

  • But they're both a rectangle shape.

  • So you can pass in the width and the height

  • after you calculate that, and give it the right shape,

  • and then ascribe it a fixture.

  • And then set user data in this case, we set user data to obstacle.

  • So now that obstacle, the fixture specifically,

  • knows that it's an obstacle, as opposed to being an alien, as opposed

  • to being anything else, as opposed to being the ground.

  • And so when we explore in a few minutes what the custom collision, world

  • collision callbacks, that we can actually

  • define all this interesting collision behavior for,

  • this user data is going to be relevant.

  • And then we render it just like we render the alien as before.

  • So any questions on obstacles and aliens, how they differ,

  • how they're the same?

  • Cool.

  • All right.

  • Let's take a five minute now.

  • When we get back, we're going to actually look at the play state.

  • We're going to look at what makes a level.

  • And we're going to actually look at how we can customize the world

  • to resolve collisions in ways that are relevant to our game behavior.

  • As in, how to make things break when we collide with them,

  • how to make the victory screen pop up when we've destroyed the bird,

  • so on and so forth.

  • All right.

  • Welcome back.

  • So before we took a break, we were talking

  • about the aliens and obstacles that are in our game

  • world that interact with each other.

  • They are the backbone of what makes our games slash Angry Birds work.

  • You throw aliens into the obstacles, obstacles break,

  • the bad pigs slash aliens die and then you score points.

  • But we actually have to model these interactions

  • and we have to tell our game, our world, what

  • to do when these collisions happen in order for things more interesting

  • than just things bouncing off each other to work.

  • That's the default behavior.

  • Box2D's goal, by default, is when two things overlap,

  • assuming that they're dynamic or at least one of them is dynamic,

  • is to push the dynamic bodies away until they no longer overlap

  • via either position or rotation.

  • But that's not the gist or the goal of our game.

  • Because what we want to have happen is different things happen

  • and certain things to disappear and to break and all sorts of other things

  • to happen when different kinds of objects

  • interact with different kinds of objects at differing speeds.

  • So in order to do this, we need to define collision callbacks

  • for our world.

  • So a callback, recall, is a function that gets

  • called back when something happens.

  • It's just something that will get called at a specific time or later on.

  • And we can define these callbacks for our world

  • such that when two things collide with each other,

  • it will execute this call back and then perform the corresponding logic

  • that we've defined therein.

  • And with every collision in Box2D, there are four callbacks that take place.

  • There's begin contact, so when two things begin to overlap

  • or begin to contact one another.

  • End contact, so once that ends, once two objects

  • are pushed away from each other.

  • Presolved, which happens right before the collision actually gets

  • solved in Box2D, meaning that the things get pushed away from each other.

  • And then postsolved, meaning right after they get pushed away from each other.

  • And the postsolved in particular is interesting

  • because it gets the information about how the collision needed to resolve.

  • So how much velocity or rotation needed to happen within that interaction.

  • And we will not be using end contact, presolve, or postsolve.

  • We will only be using begin contact because, really, that's

  • all we need in order to model the behavior that we're looking for.

  • Because anything that happens in our game

  • we can just figure it out as soon as two objects touch each other.

  • And these are things, if you're interested in a tutorial that

  • goes over these in perhaps a little bit more detail,

  • there's a link here in the slides.

  • But I'll show you how to actually implement these callbacks yourself.

  • You do this via a function called world set callbacks,

  • in this case, F1, F2, F3, F4.

  • And recall, because Lua is a dynamic language where functions

  • are first class objects, you can pass in functions as arguments

  • to other functions.

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

  • So this is assuming that we have four functions

  • we've defined called begin contact, end contact. presolve, or postsolve.

  • Their actual names don't matter at all.

  • These are just the de facto names for them.

  • What matters is that you have the logic there and you pass in function objects

  • that perform something.

  • And you can pass in all empty functions and Box2D will still behave as normal.

  • These are only for when you want more complicated behavior out of your game

  • than just things bouncing off of each other

  • and moving relative to one another.

  • So does that make sense?

  • So we'll see how this actually works.

  • We're going to go ahead and open up level dot Lua.

  • So level dot Lua is a container class that

  • basically has our game level in it, including

  • the world and all the entities.

  • And we update it and are able to model, effectively,

  • like a level from Angry Birds.

  • That's really what it is.

  • It has a world.

  • So the level has its own world with 300 positive Y gravity, as we saw before.

  • It has a table called destroyed bodies.

  • And we'll see that in a second.

  • And then here we have four functions.

  • Starting on line 22, we have begin contact, which is a long function.

  • And then we have end contact, presolve, and postsolve.

  • Those are the four callback functions that I

  • alluded to just a few seconds ago.

  • They take in slightly different signatures.

  • The first three take A, B, and collision.

  • And then the last one takes in A, B, collision, and then a normal impulse

  • and a tangent impulse, which are the forces that it needed to apply in order

  • to push apart the two objects.

  • Like I said, we won't be using these three functions.

  • But there might be a situation where you need to use those functions.

  • Maybe you want end contact because, in your game, two objects attached

  • to one another, when they collide maybe they're magnetic or something,

  • and then once they pull apart maybe you want a particle effect

  • or something to show that they've separated or something.

  • And then presolve and postsolve.

  • Presolve, offhand I can't think of a use case,

  • but postsolve could be useful for, depending on your game,

  • whether you need to just figure out maybe the amount of force

  • that they needed to separate.

  • Maybe you multiply that by some amount and cause some sort of dramatic effect.

  • Those are ultimately dependent on the domain of your game.

  • The important function that we'll be using today

  • is the first one, begin contact.

  • And notice, that we've defined these four functions,

  • even if these three are just empty.

  • But we pass in, as I said before, the set callbacks

  • function takes in those four functions.

  • And notice another interesting thing, because of Lua's dynamic nature, line

  • 11 you can see that we have level init, which

  • is the constructor for our level class.

  • Within the constructor we are defining more functions.

  • You can define functions as many layers deep as you want to.

  • And you can even return functions from functions, which

  • are called higher order functions.

  • Really do whatever you want to.

  • In this case we're just defining the collision callbacks here inside

  • of our init.

  • But you could put the most anywhere you want to.

  • You can have them outside of the class, you can have them wherever you want.

  • You can have them be global functions in your main dot Lua,

  • which I don't know how I feel about that.

  • But you can do whatever you want to as long as the functions exist

  • and you can reference their symbols, you can pass them

  • into self dot world set callbacks.

  • And now, whenever a collision happens in the world period,

  • it's going to call all four of those functions

  • at each stage of each collision.

  • So you could see that potentially getting a little bit

  • hairy if you were to scale high enough with all of your logic.

  • If you had a million lines of code in each of these

  • and its executing million lines of code per collision,

  • you could run into trouble.

  • But, fortunately, we're not going anywhere near that.

  • The gist of begin contact, so it takes in an A and a B and a collision.

  • We don't end up using the collision itself, we just use the A and the B

  • because that's all we need for our game world.

  • The A and the B are what?

  • Do we know what the A and the B are?

  • AUDIENCE: Probably the two objects [INAUDIBLE]..

  • COLTON OGDEN: The two objects.

  • Do you know whether it's a body or a fixture?

  • AUDIENCE: It would the fixture.

  • COLTON OGDEN: It would be the fixture.

  • The fixtures collide with each other, not the bodies.

  • So when you have a body, recall that the fixture attaches the shape to a body.

  • The body is just a position and velocity container for a bunch of fixtures,

  • the body is.

  • Each individual fixture collides with other things,

  • other fixtures in your game world.

  • And so, recall that before we had fixture

  • set user data because that's what we ended up

  • needing to have data on when we do the collisions.

  • We check to see via get user data what something is

  • or what metadata we've given to that fixture.

  • And then we can then separate different classes of objects this way.

  • We can say, oh, this object was an alien, or this object was an obstacle,

  • or this object was the ground.

  • And then we can say, oh, did we collide with an alien?

  • Was the collision between an alien and the ground?

  • If it was, OK, let's play a bounce sound effect.

  • And if it was between an alien and the other alien, the player

  • and the other alien, and the velocity was fast enough,

  • OK, the alien should die.

  • Right?

  • We can do arbitrary things.

  • And so the way that I've programmed it here such that we

  • can see what two things interacted with each other,

  • and this is a very simple use of user data.

  • All we we're doing in this code base is just assigning strings to fixtures,

  • but you could assign tables to fixtures with arbitrary amounts of data

  • and do all sorts of things.

  • In this case, we're only using strings.

  • So I create a table, an empty table, and then I just

  • assign at table, at that string, true.

  • And then I can just query that table.

  • Do I have a key player and a key alien?

  • Do I have a key obstacle and a key obstacle?

  • This is how you can figure out what your two objects were.

  • Because A could be a player and B can be an obstacle.

  • A can be an obstacle and B could be the player.

  • So you have to take both of that into consideration.

  • So it allows us to do if types obstacle and types player, so

  • a collision between the player and an obstacle, if it's fast enough, then we

  • can destroy the obstacle.

  • This is what we do here.

  • So I take the absolute value of the velocity on the X and Y.

  • So I do vel X vel Y gets the body's linear velocity.

  • So linear velocity is just where it's moving in the world.

  • And it returns two values because velocity has an X and Y component.

  • And then we sum it here by taking its absolute value of both parts

  • and adding it together.

  • So if it's moving fast on the x-axis, but not fast on the y-axis,

  • or if it's not moving fast on either, or if it's moving fast on both,

  • we have a sense, in general, what's the average velocity of our object.

  • If it's moving fast on any of the axes, we

  • can assume that that's sufficient force to cause an object

  • to get destroyed, right?

  • So we do if the velocity is greater than 20, just an arbitrary value

  • that I came up with that seemed appropriate,

  • then we're going to do this.

  • Table dot insert self dot destroyed bodies, which we saw earlier.

  • And then the obstacles body.

  • Now, why are we inserting that value here as

  • opposed to, maybe just destroying it inside this function?

  • AUDIENCE: Destroying the fixture.

  • COLTON OGDEN: Destroying the body.

  • The fixture, yeah, in this case.

  • AUDIENCE: I mean the fixture, sorry.

  • Because you're still referencing it later in the code.

  • COLTON OGDEN: You're still referencing later in the code.

  • Box2D maintains a reference to all of the bodies, all

  • of the fixtures in your world, regardless of

  • whether you've deleted them or not.

  • But if you delete them while it's in the middle of a,

  • like checking for collision, it'll try to do another collision

  • with that destroyed body and you'll get a crash or a stack overflow error.

  • I found I experienced both of those.

  • You don't want to ever delete or destroy anything while inside a collision

  • callback for your world.

  • It will cause horrible things to happen.

  • So what we do is we maintain a reference to everything that we're

  • going to destroy by just inserting it.

  • So if we do table dot insert the body of whatever we want into destroyed bodies,

  • we can then loop over that after the world updates.

  • And then just destroy them one by one outside of the update function.

  • And the reason that we are passing in the body and not the fixture

  • is when we destroy a body, it destroys all the fixtures

  • associated with that body as well.

  • So we're just destroying the top level container here.

  • In this case, it doesn't matter too much whether we destroy a fixture or a body

  • because it's a one to one relationship.

  • But if you had, let's say, a body that has five fixtures on it.

  • And if that entire thing collides with something else

  • and you want to destroy that entire thing,

  • you want to destroy the body, not an individual fixture.

  • Because when you destroy the body, it destroys all the fixtures, not just

  • the one fixture.

  • So that's we're deleting the body, adding the body to destroyed bodies,

  • and then later performing a delete off of that.

  • The function is destroy here.

  • So on line 157--

  • well, 155 to 159, this is where we actually

  • iterate over everything that we wanted to flag,

  • or that we've flagged as destroyed, and we destroy it.

  • So if not body is destroyed, destroy it.

  • And then once we destroy it, we're going to go down here and end up

  • actually removing the obstacle and the alien class

  • from our list of aliens and obstacles.

  • Because that maintains a reference to what we're drawing

  • and we want to also delete that.

  • So not only do we want to delete the object from the world,

  • we want to delete the objects that we've created

  • that are a wrapper for the bodies and fixtures

  • and also the drawing of our aliens so it no longer gets drawn to the scene,

  • basically.

  • So yeah, don't ever delete a body or a fixture inside your callbacks.

  • Always flag them and delete them afterwards.

  • Basically, don't delete in the middle of a world update function

  • call, which we see here.

  • Notice that this takes place, 152, were doing self world update.

  • And then on 155 to 159, we've populated destroyed bodies via the collision

  • callback that we defined up above.

  • So in here, this is where we can actually destroy everything.

  • This is outside of the update function, here, the world update function.

  • We don't need to worry about stack overflow or a segfault, which

  • we get by deleting something while it's in the middle

  • of processing its collisions.

  • So unfortunate bug.

  • If you ever find yourself running into stack overflows or segfaults

  • in your collision callbacks, make sure you're not deleting anything.

  • But we can see here, it's very similar, the behavior

  • we defined between obstacles and the player, between obstacles and aliens,

  • and between the player and alien.

  • Ultimately, it's check to see whether the average of its velocity

  • is greater than a certain number, in this case, 20.

  • And if it is, flag it as destroyed.

  • So if the player hits the alien, destroy it.

  • If a obstacle hits the alien, destroy it.

  • And it's similar to how it works in Angry Birds.

  • When you throw something at a structure in Angry Birds and a piece of debris

  • falls off of it and hits the pig, usually kills the pig too.

  • And if your bird hits the pig, that usually kills the pig.

  • But if you're not moving fast enough or if a piece of debris

  • isn't moving fast enough, it'll just nudge the pig,

  • it won't actually kill the pig.

  • So that's why we're taking all of this into consideration.

  • We're not just doing a blind delete off of the body's in our code,

  • we're actually making sure, is it also moving fast enough, i.e.

  • does it have enough force?

  • And if it does, then perform the code, then perform the deletion or flag it

  • as being deleted.

  • And so once again, that's why user data is important.

  • Because that's how we're able to-- because notice in the callback

  • we just get an A and a B. And those are always going to be fixtures.

  • Fixtures, in order for it to know what kind of a fixture

  • it is, whether it belongs to a player or an alien,

  • we need to give it some information.

  • So the set user data flags the fixtures as being of a specific type.

  • And then we can fetch it here with get user data

  • and then actually perform the relevant game logic.

  • Any questions as to how this works?

  • AUDIENCE: Are you checking for if two obstacles knock into each other?

  • COLTON OGDEN: Am I checking with two obstacles collide with each other?

  • I might not be.

  • In that case-- you should.

  • In that case, since we're not, they'll just bump into each other.

  • But, yeah, if we wanted two wooden obstacles

  • to destroy each other if they hit fast enough,

  • you would just do the same thing here.

  • If types obstacle, I guess.

  • But in this case, because they're both the same key,

  • you would have to do if types--

  • let's see how we're doing it again.

  • So types obstacles is true, types obstacles is true.

  • You would say if types obstacle and not types alien, not types player, not--

  • there's a cleaner way to do it.

  • AUDIENCE: Like a series of [INAUDIBLE] statements.

  • COLTON OGDEN: Yeah.

  • AUDIENCE: [INAUDIBLE] statements.

  • COLTON OGDEN: Yeah.

  • That's true.

  • Yeah, there's a lot of ways.

  • And if I were to re-engineer this, I would also abstract out this code

  • and make it a function.

  • Because it's pretty much the same code between all three of these.

  • But just to illustrate and just for simplicity

  • because it's pretty similar interactions,

  • didn't really put too much engineering forethought into it.

  • Definitely if you expand upon it, I would recommend doing that.

  • But that's the gist of making our world behave beyond just resolving

  • collisions and pushing obstacles away from each other, which

  • is the default behavior.

  • So we set the callbacks.

  • We're good.

  • Now things, when we interact with each other, they'll behave differently,

  • they'll trigger different behavior.

  • We have this thing called a launch marker, an alien launch marker.

  • Anybody know what that might be?

  • AUDIENCE: Is that the little dots that show the trajectory?

  • COLTON OGDEN: Yeah, so it's the dots that show the trajectory.

  • It's one, the alien being rendered on the left side of the screen

  • without any physics applied to it, that's click and dragable.

  • And it also renders a trajectory.

  • And when you release the mouse, it launches an actual Box2D alien

  • traveling in the direction that that trajectory foretells.

  • If we look at alien launch marker here, it basically maintains a reference to

  • whether we're aiming or not.

  • So it's got a couple of states.

  • It's got a launch state and an aiming state.

  • An alien that we'll have a reference to eventually, which

  • will spawn and will give it an impulse.

  • So an impulse is effectively setting its velocity immediately to some value,

  • as opposed to something over time.

  • We can apply force to an object, which would

  • be like you driving your car up against something

  • and then gradually accelerating, that's applying force.

  • And we can also apply impulse by going full speed with our car

  • and hitting an object, and that will have

  • the effect of applying an impulse at a certain velocity.

  • When we drag our alien and then we release it,

  • we want to apply an impulse in the opposite direction of where we're

  • dragging based on a certain amount.

  • I scaled it by 10, but you can have it be arbitrary.

  • And then the trajectory models where it's going.

  • And the trajectory is calculated via these lines here.

  • So from line 90 to 104.

  • There is a formula for, in that Box2D set

  • of tutorials, that actually shows you how

  • to calculate an estimated trajectory given a starting impulse and a starting

  • position.

  • Which is this formula here.

  • It's semi-complicated.

  • The article goes into detail as to how it works,

  • but it effectively calculates 1/60 of a second,

  • assuming that we're running our simulation at 1/60 of a second,

  • it will, over 90 iterations here, 1 to 90,

  • calculate each individual step of that simulation.

  • And then I only render every 5 here.

  • So if i is mod 5 s 0, then I'll actually end up

  • drawing a circle at trajectory X, trajectory

  • Y. Trajectory X and Y being here.

  • Shifted X, shifted Y being the starting location.

  • And then we multiply i by 1/60 of a second, which will give us

  • the scalar for this impulse here.

  • And then with gravity we have to do this i squared plus

  • i times 1/2 times the gravity on the y-axis times 1/60 squared.

  • The article goes into a little bit more detail as to how it works.

  • But that's it converted into source code.

  • But it's effectively a gravity simulation

  • and a velocity simulation over time.

  • And by rendering it based on 90 iterations, which

  • is one and a half seconds, at 1/60 of a second

  • we can forecast where exactly we're going.

  • And then when we apply this impulse, X and Y, the ball will actually travel,

  • the alien will actually travel in that direction at that exact trajectory.

  • That's the complicated part of the launch marker.

  • The other part is that it has a couple of states, like I said before.

  • So when we click and we're not launched, it should go into aiming mode.

  • And so if we're aiming then we're going to set a rotation to--

  • actually, rotation is not relevant because this was before I ended up

  • using the predictive trajectory method.

  • The shift at X and Y though, those are relevant because that's the starting

  • location for your trajectory.

  • That's wherever your mouse is.

  • And we clamp it so that it doesn't go past a certain limit

  • on the left or the right, so that it stays within a box area.

  • But this will be whenever you let go of the mouse, that

  • will be where we spawn the Box2D alien and apply

  • an impulse in a negative direction relative to where we move the mouse.

  • So if we move the mouse to the left and down,

  • it's going to negate that with an impulse going up

  • and to the right, if that makes sense.

  • And that's what's shown by the trajectory.

  • And then, aside from that, it renders different things

  • depending on whether or not--

  • what state we're in.

  • So if we haven't launched, it will render just the alien.

  • If we're in aiming mode then it should actually render and calculate

  • the trajectory.

  • Otherwise, it would just render the alien.

  • And so once we release the mouse, so was released 1 and were aiming,

  • launched is true, spawn an alien.

  • So we create a new alien with self world, it's round.

  • We started at shifted X and shifted Y. We

  • set its linear velocity to the same values that we calculated before.

  • So it's base X minus shifted X times 10.

  • So the times 10 is a scalar amount.

  • And then the base X is where we've moved it, effectively.

  • Or, no.

  • Base X is where it starts and shifted X is where we've moved it.

  • And so by subtracting shifted from the base,

  • we get the negative direction that we want to effect the impulse.

  • And then the impulse is set here with linear velocity.

  • And then we also set it to have a restitution of 0.4.

  • Recall, restitution is bounciness.

  • So our alien bounces a little bit when it hits the ground.

  • And then anybody know what angular damping might be?

  • Any guesses?

  • Angular damping is when it rotates, basically, friction on its rotation.

  • So that when it rotates on the ground, it doesn't roll indefinitely.

  • If we don't set that, it'll just roll forever and ever and ever and ever.

  • Which is not what we want.

  • We want it to stop at a certain point because once it stops,

  • we know, OK, now we can get the next alien ready to launch.

  • And that's the gist behind the launch marker.

  • How we render trajectory.

  • For the math on that, a little bit more in detail, I would explore that URL.

  • It goes into it into pretty good detail.

  • I use that as a reference for creating this bit of code here.

  • But yeah, effectively, is it's just rendering

  • a bunch of circles with that trajectory and calculating it

  • over 90 ticks, 90 frame iterations.

  • Back to the level.

  • Sorry, any questions overall as to how the launch marker works?

  • AUDIENCE: No.

  • COLTON OGDEN: Cool.

  • All right.

  • So then we have an alien's table, an obstacles table,

  • edge shape for the ground.

  • And then we just create an alien to destroy.

  • Spawn a few obstacles here.

  • So in this case, two vertical obstacles and a horizontal one.

  • Positioned such that the horizontal one is over the vertical ones

  • and they're spaced apart such that the aliens are in the middle of them.

  • Then the ground here, we give the ground some friction, 0.5.

  • And that's pretty much it for setting up our level.

  • So if we wanted to, after this point, we have the foundation

  • necessary to really spawn arbitrary levels with admittedly simple obstacles

  • at this point.

  • But we could set--

  • because all we're doing here is just simple insertions to our aliens

  • and obstacles table, we could create pretty much any level just by, maybe

  • in data, specifying level could be like a table

  • and then aliens could be another table.

  • And then maybe all it is just a like X equals some value,

  • and then Y equals some value.

  • And then obstacles is the same thing.

  • And then all we do is just we iterate over this level definition

  • and we just say, new alien for every table here.

  • And then new obstacle for every table in here.

  • And then now your levels are data driven.

  • It's easy just to make levels.

  • You don't have to code, really, much.

  • And you can put this in a separate file.

  • Be like, levels dot Lua, and then just load individual levels at a time.

  • Level 1 equals-- levels would be the top level container.

  • And then you would have 1 equals all of this, and then 2

  • equals another one, 3 equals another one.

  • And then you're not really programming as much

  • as you are just laying things out in data.

  • Super nice and concise.

  • That's a nice thing about a language like Lua, is that you can,

  • and it's the same thing in JavaScript with JSON,

  • you can just define things as data and then write

  • a script to go over it and construct your actual relevant data structures

  • that way--

  • in your code that way.

  • When you have the foundation like we have now

  • where you can think in terms of obstacles and aliens,

  • you can construct levels like so.

  • And obviously you could go a lot more complicated with this.

  • All we're doing is having very simple almost boring static obstacles.

  • They're not static in a technical sense because they're dynamic objects,

  • but all they really do is just stand there and then fall over.

  • But if you wanted a pulley system or maybe

  • something that's shaped in a giant head or something,

  • you can create arbitrarily complex objects that way using joints.

  • And if you're curious, I recommend to look into the documentation for Love2D

  • a little bit more.

  • Especially their weld joints are what you would use

  • to combine pieces in arbitrary shapes.

  • But you could easily take this to the next level

  • and start to create in that same level definition, arbitrarily shaped,

  • welded together obstacles.

  • But that would be, I think, a next step if you're looking

  • to take this beyond just one level.

  • I would say, think in terms of, how can I get my game world represented

  • in a very simple data like way?

  • Because not only does it make it easier for you to create content,

  • it allows you to shift that burden to somebody else

  • and allow you to give the task of creating levels

  • less to a programmer and maybe more to somebody

  • who has just a design background who isn't as comfortable writing code.

  • And allow you to create the engine that constructs the game world based off

  • of this data.

  • Any questions as to how we've set things up here?

  • OK.

  • So we have the ground, we have a background.

  • A background is just a simple class that renders a static image

  • that you can scroll the image with left or right,

  • but we don't end up using it much.

  • It's relevant in Angry Birds because in Angry Birds they have a camera,

  • and the camera pans left to right depending on how far away

  • the fortress is from your sling shot.

  • So that's in there if you want to experiment with it at all,

  • and experiment with a moving camera.

  • Maybe use timer dot tween to tween the camera

  • or just to have it track the alien if you want larger worlds.

  • But we don't end up using it in the distro as much.

  • Our update function is simple.

  • We update the launch marker, update the world,

  • and then we process all the bodies that we flagged as being destroyed,

  • which we've already seen.

  • We reset destroyed bodies to an empty table

  • because we've processed all of them.

  • We actually remove from our level the obstacle objects

  • so that they're no longer rendered, and we no longer

  • try to reference the bodies that are destroyed.

  • And then notice here too, when we destroy the obstacle,

  • we're playing a sound effect inside this bit of code.

  • So we can just do g sounds.

  • I put five wooden sound effects in there just for variability.

  • It'll pick a random one.

  • And then using to string at the number, just

  • create break one, two, three, four, five here.

  • And then stop and play it.

  • Same thing here.

  • I have a sound called kill for when we destroy an alien.

  • So when we flag an alien as destroyed and we remove it from the scene,

  • we should also call that sound effect.

  • And then if the alien stops moving in our scene, the player,

  • we can get a reference to it here, self dot launch marker dot alien,

  • as opposed to any aliens in our scene.

  • When it stops moving, so we get the average of its velocity

  • and if it's less than 1.5, so not perfectly

  • still, because it's tedious to wait that long, but almost still,

  • we destroy the alien and then we create a new launch marker.

  • We destroy the alien so that the world doesn't keep a reference to it

  • and then we just create a brand new marker,

  • which has the effect of instantiating a new alien there when we relaunch.

  • And then here, if there are no more aliens in our scene,

  • if we've destroyed all of the aliens, in this case there's only ever one,

  • but there could easily, with a little more work,

  • be a few more aliens in the scene, if it's set to zero,

  • then go back to the start.

  • Which we saw before when we finally killed the alien.

  • And then all we're doing here is deferring

  • rendering to the individual objects we want to render, the launch marker,

  • the alien, the obstacle.

  • We render ground tiles.

  • So recall, from our ground example before we were just using a line,

  • an edge shape to represent the ground.

  • But if we look at our game, it's a little hard to see because I'm in 720,

  • but there's actually a ground tile here at the very bottom, a bunch of them.

  • And even though we have all of those tiles,

  • all we're doing to detect collision is just an edge shape.

  • So what we're doing is just beyond having the edge shape in our scene,

  • we draw that tile, which is frame 12 in our sprite sheet

  • from negative virtual width to virtual times 2.

  • So three screen widths total.

  • And then we just do it in increments of 35 pixels

  • because that's how wide the tile is.

  • And that'll just create a bunch of the same tile

  • at the very bottom of our screen.

  • So just a graphical thing, not really necessarily functional.

  • We have the edge shape already in our scene, but just to make the ground

  • look a little larger than one pixel tall.

  • And then if we haven't launched anything,

  • we should display some instructions here.

  • And then if we're at no aliens left, then we

  • should display the victory screen.

  • And the victory thing will last for just a little bit of time,

  • because even though self dot aliens is zero, this bit of code

  • doesn't register until after the velocity of our moving alien

  • slows down sufficiently.

  • So this will get called when we're just about to finally stop moving,

  • and then we check to see, oh, OK, do we indeed have no aliens left?

  • If not, time to go back to the start.

  • We've completed a level.

  • And then you obviously would just change this

  • to go to be next level if you ended up implementing more

  • levels in your game besides just one.

  • Beyond that, that's pretty much all of the code that's in this example.

  • It's fairly, I would say, unsophisticated relative

  • to prior examples.

  • But mainly, the burden is learning how to use

  • the physics engine, the Box2D physics engine, which

  • itself is quite a few functions.

  • It's some of the longest documentation on Love2D's wiki, I think.

  • But the principles are pretty simple, in my opinion.

  • I think it's actually quite easy to get rolling with a lot of cool features.

  • The vast majority of which I don't think we even really cover,

  • at least in terms of what you can do with compound objects and joints

  • and things like that, which really start to go

  • into the world of more sophisticated and interesting physical simulations.

  • Things like I alluded to before, pulleys and tanks and other things like that.

  • Some features that we could look at potentially expanding upon

  • if we wanted to make our game more interesting,

  • is more shapes for our objects.

  • So if we look at our sprite sheet here, we

  • can see there are things like roofs and circles and things like that.

  • So more interesting obstacles beyond just square rectangular

  • shaped obstacles.

  • Go back to-- like I said before, compound obstacles.

  • So a bunch of things put together via joints.

  • So pulleys, there's motors, weld joints, which you can use to-- you

  • can affix a roof to a square and then have two pieces that are tied together.

  • And you can make arbitrarily complex and shaped things

  • and just be really interesting.

  • And that big thing, that body with all these fixtures,

  • will collide just as any other thing would, thanks to Box2D.

  • As I alluded to before, instead of having levels to be hard coded

  • into our level class, maybe define them in a Lua file called levels dot Lua.

  • And then just have aliens be a table, obstacles

  • be a table, and then whatever else, however

  • more complicated you want to get with your game.

  • You can add more things.

  • But just have it be represented as simple data structures.

  • So aliens that maybe have different shooting mechanics.

  • So if you've played Angry Birds you're familiar with the fact

  • that some birds will split off into multiple birds.

  • Some birds will dive and go super fast and break through all the obstacles

  • in their path.

  • Some birds will explode, and then their explosion

  • affects all of the obstacles around them.

  • So there's a lot of different game play mechanics

  • you can implement using different types of birds or aliens.

  • And then different obstacle materials is another direction we could go.

  • And it's supported out of the box with the sprite sheet

  • that I provided because it has sheets for metal

  • and glass and explosive material.

  • So there's a lot of different interesting things

  • you could do just by changing up what materials you're using in the game.

  • And obviously those will have different densities and behave in different ways.

  • So assignment five is a fairly simple assignment.

  • So this is just as I alluded to before.

  • The task here is to split the bird that you

  • shoot by pressing Spacebar when it's in the bird-- the alien, when

  • it's in the air, press Spacebar and have it split into three.

  • So you have your one that you're shooting already.

  • So it should just shoot off two more.

  • One that's angled higher, one that's angled lower.

  • And all of those should be interactable with your game world.

  • And that's really it.

  • So if you can do that then it'll show that you know

  • how to effect the Box2D game world.

  • Next time, next lecture, we'll be talking about--

  • the theme is going to be Pokemon, but we'll

  • be talking about more generally RPGs and turn-based games of that nature.

  • It won't maybe necessarily look quite as pretty as this.

  • But we'll be striving for a similar over world

  • that we can walk around and then engage in fairly simple turn-based battles

  • and stuff like that.

  • And also, we'll be talking about user interfaces and things

  • like dialogue and stuff like that.

  • But that was it for lecture six, Angry Birds.

  • I'll see you next time.

  • Thank you.

COLTON OGDEN: All right.

字幕與單字

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

B1 中級

憤怒的小鳥編碼教程--CS50的遊戲開發入門篇 (Angry Birds Coding Tutorial - CS50's Intro to Game Development)

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