-
In this video we're going to create our
-
board manager script, which is going to
-
procedurally generate our levels
-
so that they're different each time the player plays.
-
We're going to start by creating two empty scripts,
-
our board manager and game manager scripts.
-
We'll choose Create - C# Script.
-
Call the first one BoardManager.
-
Call the second GameManager.
-
We'll also create an empty game object
-
called GameManager.
-
Let's start by adding our level generation code
-
to BoardManager.
-
We'll open it in MonoDevelop.
-
The BoardManager script is going
-
to lay out our randomly
-
generated levels for us each time
-
the player starts a new level
-
based on the current level number.
-
The first thing that we're going to add is a
-
namespace declaration using System.
-
We're going to do this so that we can use the
-
serialisable attribute.
-
Using serialisable just allows us to
-
modify how variables will appear
-
in the inspector and in the editor
-
and to show and hide them using a fold out.
-
We're also going to add
-
using Systems.Collections.Generic
-
so that we can use lists.
-
Finally we're going to include
-
using Random and we're going to
-
set that to equal UnityEngine.Random.
-
We need to specify this because there's a class
-
called Random in both the system
-
and UnityEngine namespaces.
-
In our class we're going to start by
-
declaring a serialisable public class called Count.
-
In Count we're going to declare two public
-
variables called Minimum and Maximum of the type int.
-
We're also going to include an assignment
-
instructor for Count so that we can
-
set the values of minimum and maximum
-
when we declare a new count.
-
We're going to give it parameters Min and Max,
-
which we're going to use to set the values of
-
minimum and maximum.
-
Next we're going to declare our variables.
-
We'll start with our public variables,
-
the first two will be an integer for columns,
-
and an integer for rose
-
These will delineate the dimensions of our game board
-
and will allow us to make our game board
-
larger or smaller by changing them
-
if we want to.
-
In this case we're going to initialise them each to 8
-
meaning we're going to have an 8 by 8 game board.
-
Next we're going to use Count to specify
-
a random range for how many walls
-
we want to spawn in each level.
-
In this case this means that we'll have
-
a minimum of 5 walls per level,
-
and a maximum of 9 walls per level.
-
We're going to do the same for our Food items.
-
Next we're going to declare some variables
-
to hold the prefabs that we're going to spawn
-
to make up our game board.
-
We're going to have a single game object called
-
Exit because there's only one exit object.
-
For other objects we're going to use
-
a raise so that we can parse in
-
multiple objects and then choose
-
one of them that we want to spawn
-
among the variations.
-
Let's start with floor tiles.
-
We'll also do the same for wallTiles,
-
foodTiles, enemyTiles and our outerWallTiles.
-
We're going to fill each of these arrays
-
with our different prefabs to choose between
-
in the inspector.
-
Next we're going to declare a couple of private variables
-
including a transform called boardHolder.
-
BoardHolder is just something that we're going to use
-
to keep the hierarchy clean because we're going to be
-
spawning a lot of game objects we're going to child
-
them all to Board holder
-
so that we can collapse them in the hierarchy
-
and not fill our hierarchy with objects.
-
We're also going to declare a private list
-
of vector3s called 'gridPos'.
-
We're going to use this to track
-
all of the different possible positions
-
on our game board and to keep track of
-
whether an object has been spawned in that
-
position or not.
-
Next we're going to declare a function called
-
InitialiseList that's going to return Void.
-
In InitialiseList we're going to start by
-
clearing our list of grid positions
-
by calling the Clear function on our list.
-
Next we're going to use a pair of nested For loops
-
to fill our list
-
with each of the positions on our game board
-
as a vector3, we'll start with the X axis.
-
So this first loop is going to run
-
for as long as X is less than our number of columns.
-
And then we're going to do the same along the Y axis inside that.
-
Inside that loop we're going to add
-
a new vector3 with our X and our Y values
-
to our List grid positions.
-
What we're doing here is we're creating
-
a list of possible positions
-
to place Walls, Enemies or Pickups.
-
The reason that we're looping from one
-
to columns -1
-
is so that we can leave a border of floor tiles
-
directly within the outer walls,
-
this is going to mean that we're not going to create
-
a completely impassable level.
-
Next we're going to declare a new
-
private function that's going to return void
-
called BoardSetup.
-
We're going to use BoardSetup to
-
setup the outer wall and
-
the floor of our game board.
-
We'll start by setting boardHolder to
-
equal the transform of a new
-
game object called Board.
-
Next we're going to use the same type
-
of loop pattern to lay out the floor
-
and the outer wall tiles.
-
So we're going to use another for loop along the
-
X axis and along the Y axis.
-
The reason that each of these loops
-
is going to go from -1 to columns 1 or to rows 1
-
is because we're building an edge
-
around the active portion of the game board
-
using the outer wall objects.
-
Next we're going to choose a floor tile at random
-
from our array floor tiles
-
and prepare to instantiate it.
-
So what we're doing here is we're declaring a
-
variable of the type GameObject
-
called toInstantiate
-
and setting it to equal
-
an index in our array of game objects called floorTiles
-
which we're choosing randomly
-
between 0 and the length of the array floor tiles.
-
This means that we don't have to pre-specify the length,
-
we can just take the length and choose
-
a number within that array.
-
Next we're going to check
-
if we're in one of the outer wall positions,
-
and if so we're going to choose an
-
outer wall tile to instantiate.
-
So we're going to check if X is either
-
equal to -1 or our value for columns,
-
or Y is equal to -1 or our value for rows
-
and if so we're going to set to instantiate
-
to a randomly chosen tile from
-
our outer wall tiles array.
-
Once we've chosen what tile we want to
-
instantiate we're actually going to instantiate it.
-
So we're going to declare a variable of the type
-
GameObject called instance and then we're going to assign that
-
to the object that we're instantiating.
-
So we're going to call Instantiate,
-
parse in toInstantiate, the prefab that we chose,
-
at a new vector3
-
which is going to be based on our current
-
X and Y coordinates in the loop
-
and we're going to parse in 0 for the Z axis
-
because we're working in 2D.
-
Quaternion.identity just means it's going to be
-
instantiated with no rotation
-
and we're going to cast it to a game object.
-
With that done we're going to set the parent
-
of our newly instantiated game object
-
to boardHolder.
-
BoardSetup if going to lay out our
-
outer wall tiles and our background of floor tiles.
-
What we're going to do next is we're going to
-
start writing some of the functions which are going to
-
place the random objects on the game board
-
like Walls, Enemies and Power ups.
-
We're going to decal re a new function
-
that returns a vector3 called RandomPosition.
-
In RandomPosition we're going to declare
-
an integer called randomIndex
-
and generate a random number within a range for that.
-
The range that we're going to generate the number within
-
is going to be between 0
-
and the number of positions
-
stored in our gridPositions list,
-
which we're accessing using gridPositions.Count.
-
Next we're going to declare a vector3
-
called randomPositions and we're going to
-
set it to equal the gridPositions
-
stored in our gridPositions list
-
at our randomly selected index.
-
To make sure that we don't spawn two objects
-
at the same location we're going to remove
-
that grid position from our list.
-
We're doing this by using the RemoveAt command
-
and providing the index randomIndex.
-
Next we're going to return the value of
-
randomPosition so that we can use it
-
to spawn our object in a random location.
-
Now that we've generated a random position
-
from our list and made sure that it's not a duplicate,
-
we're going to write a function to actually
-
spawn our tiles at the random position that we've chosen.
-
This is going to be called LayoutObjectAtRandom
-
and it's going to take 3 parameters.
-
An array of game objects called tileArray,
-
a minimum integer called and a maximum integer.
-
The first thing that we're going to do is that we're going to
-
declare an integer called objectCount
-
and initialise it with a random value between
-
minimum and maximum + 1.
-
ObjectCount is going to control
-
how many of a given object
-
we're going to spawn, for example the number of walls in a level.
-
Next we're going to write a for loop.
-
We're going to repeat the for loop for as long as
-
i is less than our object count,
-
meaning we're going to spawn the number of objects
-
specified by objectCount.
-
We're going to start by choosing a random position
-
by calling our RandomPosition function.
-
Next we're going to choose a random tile
-
from our array of game objects tileArray to spawn.
-
We're going to do this by generating a
-
random number by using Random.Range between 0 and tileArray.length.
-
We're going to instantiate the tile that
-
we've chosen at our random position.
-
We can delete our start and update functions.
-
And declare a new public function that returns void
-
called SetupScene, which takes a parameter of the type int
-
called Level.
-
And notice that SetupScene is the single public
-
function in this class.
-
This is the one that is going to be called by the game
-
manager when it's time to actually setup the board.
-
Within SetupScene the first thing that we're going to do
-
is call BoardSetup.
-
Next we're going to call initialiseList.
-
We're going to call LayoutObjectAtRandom
-
and we're going to parse in our array of wallTiles
-
and also our minimum and our maximum wallCount values.
-
Next we're going to do the same for our foodTiles.
-
Instead of generating a random number of enemies
-
we're going to generate a number of enemies
-
based on the level number
-
using MathF.Log
-
to great a logarithmic difficulty progression.
-
MathF.Log returns a float
-
and so we're going to cast that to an integer.
-
This means that we'll have one enemy at level two,
-
two enemies at level four,
-
three enemies at level eight,
-
and that the difficulty will continually scale up
-
as the player ascends in level.
-
Now that we've got the number of enemies we want to spawn
-
we're going to lay them out using LayoutObjectAtRandom.
-
Note that the minimum and maximum values in this
-
case are the same because we're not specifying a random range.
-
Finally we're going to instantiate the exit.
-
The exit is always going to be placed in the same
-
place and is always going to be the same object
-
so we're just going to call
-
Instantiate and parse in the prefab Exit.
-
The exit is always going to be in the upper right corner
-
of the level, which is why we're using
-
columns -1 and rows -1
-
and this means that if we choose to resize
-
our game board the exit will still be placed correctly.
-
Let's save our script.
-
Now that we've got our level generation code
-
written in our Board Manager
-
in the next video we're going to start writing
-
our Game Manager and setup the Game Manager prefab.