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.
