#Stencyl:
Maze generation (Part I)
Difficulty:
(7/10)
Estimated
time of work: 4-5 hours
Maze
generation problem
In
some games you move from room to room while encountering various
types of monsters along the way. Classic examples involves dungeon
crawlers RPGs, Metroid or Binding of Issac. Today we will be
generating a maze.
Binding of Issac as an example of game using room map
(map of rooms at the top left)
Analysis
of problem:
While
playing this type of game you usually start at some starting point
while looking for the exit. All of the generated rooms should be
connected. Otherwise the map would be made of islands and it would be
impossible for player to visit some of the generated rooms. What
would be even worse is the case where the starting point is on one
island and exit point is on another. This situation makes for player
impossible to proceed.
Example
of properly generated maze - all rooms connected (red - start, blue -
exit)
Example
of badly generated maze - player won't be able to get to the exit
Follow the
link to get to the finished game:
http://www.filedropper.com/metroidmap
Link to
video of the game:
https://www.youtube.com/watch?v=MbXKNUlxxi0
Algorithm:
- Create matrix with width and height of your choice representing map with no rooms
- Declare number of rooms you want to create (no bigger than number of cells in matrix)
- Create empty list(room list), we will keep there coordinates of rooms
- Pick a cell at random, add it to the list
- Pick one room from room list, with some probability if neighbouring cell is empty try to create a room
- If 5) succeed in creating a room add its coordinates to the room list
- Repeat 5) and 6) until you reach desired number of rooms on the map (as defined in 2))
Problems while creating match game involves:
- Matrix in Stencyl - creation, adding value
- Generating map
- Not getting caught in endless or inefficient loop while generating map
- Updating matrix while moving to another room
Working
example:
In my Stencyl
game there are green squares representing exits from the current
room. You click on the square to move to the other room. The map of
rooms is generated in console(2- current room, 1- room, 0 - empty
space). The map is updated after each switching of the room.
The
solution was made for problem where you can move from room in 4-way
fashion(up, down, left, right), no diagonal movement is allowed.
Creating
matrix:
Stencyl does
not support matrix so we need to handle it on our own. If you ask on
forum about "how do I make matrix in Stencyl?" the answer
you are most likely to get is "create list of lists". It
does work however we will present different approach. Using width of
matrix we can write matrix as list and use it as matrix by properly
translating the (x,y) into list index.
Input Matrix (M)
Matrix written as list(List)
- yellow numbers are equal to the index in list
Let's assume
in our matrix we start counting from 0 in both rows and columns so
M(0,0)=10. In order to get M(x,y) from M represented as list we take
List(width of matrix*y+x). For example we want to get M(1,2)=12. We
have our matrix written as list and we know that the width of matrix
is 5. We get list(5*2+1)=list(11)=12. Similar thinking can be done in
order to put value into Matrix.
Generating
map:
Map
generation goes according to algorithm.
Start
- upon entering the room
<do
after> is important because we will have plenty of custom events
along the way and Stencyl can't see them in "When created"
moment so we give him some time - as little as possible. We set width
and height of maze. If there is no map generated we go to generating
the map. Then we create exits based on current room we are in.
Generate
map - part I
We
set numbers of rooms we want to create. Then we check if there is
enough place in the matrix for the rooms. We create empty list filled
with 0s; 0 marks place in matrix where is no room. (As check) we
print empty list to console. We pick our starting cell for building
maze. We move to actual map generation: <generate_maze2>.
Generate
map - part II
We
mark with 1 our starting cell. (As check) we print matrix to console
with 1 in place cell where we start.
Generate
map - part III
We
add x and y to the list of rooms. In while loop(Generate
map - part IV)
we generate rooms until numbers of generated room will be equal to
number of rooms that we wanted to create. <helper_2> is used to
move through the list of already created rooms.
Once
we are out of loop we print generated map to console. We set it as
game attribute <map matrix> so that we won't lose it while
switching between rooms. We set <2> in matrix in order to mark
our starting position.
Generate
map - part IV; pick the already created room and check neighbours
around the picked room, if any of them is empty try to create
room(using room_check)
<helper_3>
stores the index under which there is x coordinate of already created
room. List is created in such way that under index <helper_3>+1
there is y coordinate of already created room. <Remainder+length
of list> construction makes sure we won't ask indexes out of list.
The
rest of the code says for each of the neighbouring cells(top,down,
left, right) try creating the room(Generate
map - part V).
Generate
map - part V; For current neighbour if its empty space (0 in matrix)
try to create room from it
Two
ifs at the beginning check if input values(coordinates of matrix
cell) are ok = are they corresponding to cell inside the matrix. Code
then asks about value inside the cell. If it's zero(= there is no
room there) and there was no error while asking about the value we
try to make it from free space into room.
There
is a random chance that current free space will become a room. If the
probability test is passed empty space is changed into room(Generate
map - part VI).
Generate
map - part VI; change empty space into room
Increase
number of already created room by one. Add coordinates of newly
created room to the list of rooms. Change value inside matrix from 0
to 1(in the coordinates of newly created room).
Generate
exits upon entering the room
Generate
exits based on your coordinates of the current room
This
code triggers upon entering the room. Once we have our map generated
we ask cells in map that are neighbours of the current one if they
are rooms(=1) or empty spaces(=0). If they are rooms we create exits
actors that lead to them. We set attributes of the exits so that each
exit knows where it leads. We will need that in formation to move to
another room when we click on exit.
Change
of room upon clicking exit
Change
rooms when you click on exit
Firstly
we check if we clicked on exit
actor(using
2 ifs). If we clicked on exit
actor
we change to 1 value in map as we are living the current room. Exit
actor
knows to which room it leads using X
and Y
attributes. We update the current room based on information from exit
actor,
we change the value in map to 2. Then we reload the scene with new
X_room
and Y_room.
It
may seems strange why we are reloading the scene instead of switching
it. In my solution things specific to the current room(such as
monsters or items) are generated on room creation based on value of
variables X_room
and Y_room.
Further
development
The
present algorithm creates a map that has all rooms connected to the
neighbours. It's good because there are no islands which can crush
the game. On the other hand to make things more interesting it would
nice to add walls between rooms, so that even if there are two rooms
next to each other there can be wall between which would prevent
creation of exit and making things to easy for player.
Tags:
Stencyl, maze, Metroid, Zelda, RPG, room