COLTON OGDEN: So with mario8, we got our hands
dirty with a little bit of collision detection-- some very easy collusion
detection, just checking above Mario's head such
that now he can jump, and hit blocks, and change their appearance.
In this update, we are going to actually make certain objects in our scene
collidable, which otherwise were not.
For example, the column there, as you can see,
Mario is actually in the walk animation.
I say Mario lightly.
It's not really Mario.
But Mario is walking towards the right and colliding with the column.
So we're going to make the column collidable.
We're going to make the tiles collidable below Mario.
So basically, we have a whitelist now of titles that we can collide with.
And in addition, we need to not only check what's above Mario, to the left
and to the right of the corners, but also, we
need to look for what's below Mario.
So if they're walking and moving left and right,
we will always want to be checking what's
below them so that we can determine whether they should fall,
or also when they're jumping.
Same thing with the left and the right.
So if we're jumping and we hit a pillar or the like,
we should check to make sure that we are not bumping into it.
So for example, we can fall down into the walls
and not sort of move back and forth and do some weird stuff that way.
So that's really it for the preface to mario9.
Let's get into the actual code here.
So we're going to have a lot of the same stuff
that we saw in the last section more or less.
We added a few functions here.
So the one thing that has remained is that our update function still
has this bit of code here for when we're jumping and detecting a block above us.
Additionally, in all of the states, we have now added some additional code.
So idle doesn't have any additional collision detection code
because really, we don't need to check if we're colliding with something
or standing perfectly still.
But walking, for example, does have a couple of new bits and pieces.
So we have two functions, checkRightCollision
and checkLeftCollision.
And they do exactly as their name-- they check
the right side and the left side of Mario,
assuming that we are going in that direction.
And we also have to see, when we're in the walking state,
whether or not there are two tiles below us.
And you'll look at this and actually realize
that the code here is the exact same, more or less, as it was before,
in that we are checking to see if there's a tile at some value.
Only this time, we're checking below Mario,
as by adding self.height to the y value here.
So we're checking to see if there's a tile at self.x, self.y
plus self.height.
And additionally, we're also using this function self.map:collides.
So let's take a look at that.
So in Map, we're going to go over down to the collides function.
So all this really does is it just has a set of tiles
here, so it's just a table of the constants of the tiles
that we have determined are solid--
ones that we can collide with.
And what we're doing here is actually using some interesting syntax
we haven't looked at yet, which is how to iterate over a collection, a table,
in Lua.
And so what we're doing is we're saying, for every key-value pair in ipairs--
so ipairs is short for iterate pairs.
It's a way of iterating over all of the key-value pairs in a table,
but you're also given an index, as well, if you want that index.
And that's what you get here as the underscore.
Doesn't really matter for this particular example.
We're not using the key in our loop here.
All we're checking to see is if that value has an ID that is equal to v.
And what we're doing is we're essentially
going over all of the collidables, given this tile here.
And if tile.id is equal to v, which is this value,
then we're going to return true.
And if it's the case that all of those collidables
do not match what tile is here, then we're just going to return false,
so we have not actually collided with anything.
That's the addition to Map that we've made in this section.
I'm going to go back to Player.
And so what we're doing is we're just checking if map collides
with the tile at right below our feet and to the right of our character,
because remember, everything is relative to the left side.
So we're also checking the tile that's below the right side and one pixel
down.
Just in case we're between two tiles, we want to make sure of that.
And just to illustrate that again, this is
a perfect example where we have two tiles here
where Mario might be between them.
So we want to check not only the tile below this corner,
but also the tile below this corner.
And we know that we don't span more than two tiles
max because our width is only 16 pixels, which it would be impossible to span
three tiles in that situation.
So I'm going to go back over here.
Now, if we do end up falling and we're in the walking animation,
the walking state, what we want to do is set our state
to jumping, which might be a little bit unintuitive.
But really, just because we have that falling behavior
and gravity is going to apply to us, it makes sense
that our state should become jumping.
But we don't have to apply an initial accelerated gravity
value of negative whatever because we're not actually jumping.
We're just going straight to the gravity hitting us
and making us accelerate downwards.
Additionally, when we jump, when we're in the jumping state,
we want to check to see if there are any tiles below us.
In this case, we are checking the tile map,
or we're doing the exact same collision as we did before.
We're checking self.y plus self.height and self.y plus self.height
with x plus self.width minus 1.
So we're checking both corners of the bottom.
And in that case, if we do hit the ground or some collidable tile--
it could be a mushroom or a block in this case, as well--
we're going to set our dy to 0, set our state to idle,
our animation to idle, and also reset our position, taking into consideration
the tile that's where our feet are, basically
shifting ourselves up by a height as evidenced by this minus self.height.
And we do the same thing elsewhere.
For example, if we do checkRightCollision
and checkLeftCollision, it's sort of the same idea more or less.
I'm going to scroll down here.
So checkLeftCollision checks for our left and our right side.
So LeftCollision checks to see if our dx is less than 0.
If it is, we're essentially checking our top left corner and bottom left corner,
and the tiles that are there.
You're going to look one pixel to the left of them, which
is what this is right here.
And if it's a collidable tile in that direction,
we're going to set our dx to 0, so we stop moving in that direction,
and then make sure that we don't clip into the tile
by resetting our position to the tile to the right of that's exposition, which
will put us right at that position to the right of the tile
that we just tried to go to the left of.
Same thing for checkRightCollision, but it is the exact opposite, in which case
we are moving to the right direction.
So if our dx is greater than 0, we're going
to check the two tiles that are to our top right and bottom right coordinates.
So for you, that's this side, top right and bottom right coordinates--
to the tile that's 1 pixel that way.
And we're going to, again, set our dx to 0
and then make sure that we set our character's position to the left, which
is minus self.width.
We want to set it to the width of our character
minus from that tile's position so that we don't clip into it at all.
And that's essentially it for this example.
And that takes care of the top, the sides, and the bottom.
I'm going to just go ahead and run a little example here.
So if I jump, I should be able to land on top of the tile
just like that, which is pretty cool.
I can still jump and hit that.
And in addition, let's see if I can land on this column.
Yep, I landed on the column, which is great.
And I jumped down here.
And then eventually, I can finally jump--
if I try-- my speed is a little fast.
Let's try doing it this way.
There we go, jumped down into the pit below.
So that's it for collision.
That's really the trickiest part.
And it's a lot of numbers, but if you read through it,
it's actually not terribly complex.
It's very easy to mess this up live coding, though,
which is why we didn't do it live.
But everything is written here, so do study this and understand it,
and it'll make, I think, perfect sense once you get more in tune with it.
So that's it for mario9, the collision update.
The last bit is a lot simpler. mario10 is
sort of callback to Pong, where we looked at adding sound effects.
This time, we're going to add sound effects,
but we're also going to add music and we'll fly through that one.
It'll be a breeze.
So looking forward to seeing you in mario10.