The game
20 seconds to Mars
The next jam I entered was the 20 second game jam. Now this is my kind of theme! It had a clear constraint and - although it was a month long - it was a lot of fun devising different ways to design gameplay lasting a maximum of 20 seconds.
Some of the previous entries are, frankly, incredible with my absolute favourite being Rubblar - a game about a wibbly wobbly rubber arm robbing a bank.
The bar was set quite high, so I really wanted to do something which had some kind of addictive loop which would make that 20 seconds interesting, and something you want to go back to, to do better than the last time.
Mazes
I had been messing about with various maze generation algorithms, as seen in this prototype, and also here in 3D (sidenote, maybe I will come back to this one day - I really like the aesthetic). The idea of generating something as relatively complex as a labyrinth seemed very appealing to me.
Luckily it’s a lot easier than it sounds.
The gist is that you do a recursive backtrack (ie. depth first search) and carve a path through the nodes, breaking down walls as you go to generate the path.
void RecursiveBacktrack(Node currentCell, HashSet<Node> visited)
{
// Get all unvisited neighbors of the current cell
var unvisitedNeighbours = GetNeighbours(currentCell).Where(n => !visited.Contains(n)).ToList();
if (unvisitedNeighbours.Count == 0)
{
deadEnds.Add(currentCell);
}
// While there are still unvisited neighbors
while (unvisitedNeighbours.Count > 0)
{
// Choose a random unvisited neighbor
var nextCell = RandomUtils.GetFromList(unvisitedNeighbours);
// Remove the wall between the current cell and the chosen neighbor
Direction travelDirection = TravelDirection(currentCell, nextCell);
DeleteCorrespondingWall(travelDirection, currentCell, nextCell);
// Mark the neighbor as visited
visited.Add(nextCell);
// Recursively call the function to move to the next cell
RecursiveBacktrack(nextCell, visited);
// After the recursive call, get any remaining unvisited neighbors
unvisitedNeighbours = GetNeighbours(currentCell).Where(n => !visited.Contains(n)).ToList();
}
// If no unvisited neighbors remain, the function will return (backtracking)
}
The gist of the game was basically just:
- Start a 20 second timer
- Plop the player in a simple maze and have them get to the exit - increment their score.
- GOTO 2 until the timer runs out.
After each maze, I would randomly increase the size of the maze grid - starting at 3x3, and sometimes going to 4x3 or 3x4 to keep it sort of interesting. Some of the mazes were super easy and some were actually kind of difficult. So really it was mostly down to the random number generation whether you got a good score or not. Players reported though that it wasn’t frustrating as the game would end and restart quite quickly to give them another chance to improve their high score.
It was fun - what next?
I actually went out and bought a cheap Android phone and created a Google developer account thinking “hey let’s see if this works for mobile”.
It didn’t really. Moving the block with touch was awkward and unintuitive. I tried different methods of movement - one where each swipe moves the player one square, and another where you could trace a path with your finger and neither were really that enjoyable. It ended up not being worth the hassle of mobile development.
I guess some things just work as quirky browser games!
Learnings
Random generation needs to be closely curated and fenced in for it to actually be fun; it’s not enough to just randomly generate levels and hope it all works out. If I had put more time and effort into this, I would have done something like pre-generate 10 mazes and their solutions and then sorted by difficulty ascending.