COLTON OGDEN: So in Mario 2, we took a look
at allowing us to control Mario, well, control the screen, rather, ourselves--
the camera, so to speak-- using love.graphics.translate as opposed
to making it an autoscroller.
In this update we're going to explore a little bit of what's
called procedural generation, or you might see it
as ProcGen, which is why this is the ProcGen update.
But essentially, it allows us to generate
levels that look a little something like this randomly.
Using an algorithm, we can iterate sort of column
by column throughout our game map.
Instead of just setting the first half to the sky and the second half
to bricks, we can essentially, column by column, say, OK, in this column,
I want it just to be normal.
I want to generate just sky and then bricks [INAUDIBLE]
map divided map height divided by 2.
Maybe in this column I want there to be a cloud that begins here.
And so I want to set this tile and the tile to the right of it to cloud tiles.
And then maybe on this one I want a column,
or maybe both a cloud and a column.
So when we get to the ground level, create the column tiles,
the bottom one, and then the top one two tiles above where the ground begins.
And this sort of way lends itself very well to side scrolling platformers.
Because we can generate the world as it goes left to right.
Just because the world does function in a sort of left to right way,
the flow of how obstacles generate just sort of works well in this model,
as opposed to generating everything sort of scan line by scan line.
Because in that sense, you would have to generate the entire terrain one
sort of x one row iteration.
And it's not impossible to do it.
Certainly you could do it this way.
But at least in my mind, this way makes a little bit more sense.
So we'll take a look at doing it that way.
Now, instead of coding this one from scratch--
because it's a little bit of leg work.
And it's not necessarily germane, I think, to a lot of conceptual learning.
It's really iterating upon what we've already done with the first Mario 0.
We'll explore the code as it's prewritten in the Mario 3 distro.
Now, up here at the very top, you'll see sort of at the header
where we have all of the tile declarations.
We have cloud left and cloud right in addition
to bush left, bush right, mushroom top, mushroom bottom, and then jump block.
And these are just numbers that map exactly to that sprite sheet
that I showed you earlier in the slides and here in the project.
If I come down to where we actually begin-- and this
is, again, in map.lua specifically.
If we go down to where we normally have the map generating
the empty tiles throughout the whole thing,
we see that right after that, we begin at x1.
And we start saying, well, x is less than self.mapWidth do.
And then we're iterating throughout the entire map width in this case.
And this is a little bit different than how we usually
iterate through nested for loops, where we go y by y,
and then we're exploring each.
Basically, we're going an entire row at a time as opposed
to a column at a time.
In this case, we're going one column at a time.
Now, what we're doing is we're leveraging math.random,
which we actually explored in the Pong distro
when we implemented the ball moving in random directions.
Here we're essentially saying, if it's the case that x is less than
self.mapWidth minus 2-- which just means make sure that we're away from
the right side of the screen all the way at the very edge--
we have a 1 in 20 chance, which is why this math.random 20 is equal to 1,
of creating a cloud.
And so what we do is we say the cloud start is
going to be some random value on the y-axis before six
tiles from the bottom, just so that it's not right at the bottom of the screen
or right where the tiles begin.
And then we just set the tile of cloudStart at x in cloudStart
and x plus 1 in cloudStart to CLOUD_LEFT and CLOUD_RIGHT.
And what that has the effect of doing is, per the example here,
it will have a chance to generate this cloud right here.
This is the left tile.
And then this is the right tile.
So it goes down from the top.
It picks a random y height if 1 in 20 is equal to 1.
And then it just sets both tiles at once.
OK, so we'll go over here.
And then here we see we have a 5% chance to generate a mushroom.
So I have it written as pipe here.
But it's actually a mushroom.
So it's literally the same thing as the last one.
Well, not literally the same thing.
But it's conceptually the same thing.
We're setting two tiles.
Only this time, we're actually setting two tiles vertically, the mushroom top
and the mushroom bottom.
If I rerun this, you'll see that we do have the mushroom top right here
and the mushroom bottom when put together.
It looked a little bit weird in the sprite sheet.
But here we can see that, visually, they look
very nice when they are drawn together.
And then also, an important thing to do, we
draw a column of tiles going from the bottom
all the way down to the screen when we get to y divided by 2, essentially.
Which is important.
Because if we have a column of mushrooms,
it's requisite that we should also have tiles to build that mushroom on top of.
So it's an important step.
And you also have to, of course, make sure you increment
x so we do eventually reach x being equal to mapWidth.
There's also a 10% chance to generate a bush, same thing.
A 10% chance to not generate anything at all, which means we
actually create a gap in the tiles.
So we're not creating a column of tiles.
And then we also have a chance to generate a block.
So 1 in 15 chance, in this case, to generate a yellow tile block.
Now, if we run this over and over again, we'll
see that it actually is the same exact output each time.
And to influence this, what we can do in main.lua--
at the very top, roughly--
we can say math.randomseed os.time, which we aren't already doing.
Well, the map is actually being generated
before love.load in this case.
So what I'm actually going to do is I'm going to copy this.
And I'm going to put this underneath math.randomseed.
Since the map was generating before math.randomseed was even called,
it was always still getting the same random generation.
I'm going to go ahead now and rerun it.
And we do indeed see a brand new map in this case.
And as I was hoping, we can see the yellow block here,
which illustrates the logic there.
So perfect, everything works just according to plan.
And that's an overview roughly of procedural generation,
at least as it applies to a platforming game.
You can obviously take this as far as you want to go.
You can implement all kinds of things.
And actually, part of the assignment is going
to be to generate the Mario pyramid.
Using this exact sort of setup here, you can just
sort of plug in that functionality and get it working.
But that will be part of the assignment for the Mario end of the track.
So join me in Mario 4.
We're going to step away from procedural generation,
and actually add our sort of Mario avatar and alien to the scene,
and work towards having a character we can control.
So see you soon in Mario 4.