I recently watched the film Indie game. Which is a documentary that follows two indie games for xbox 360 being made (Super Meat Boy & Fez). The film is very good and I would highly recommend watching it (especially if you are interested in these blog posts). During the film the guys were saying that the key to a great game is one that is easy to learn and difficult to master. After watching the film I went back and played on rotation and thought that the concept of rotating to make words, although an interesting idea is fiendishly difficult in practice and would’ve definitely put off potential players of the game.
Instead of rotating your selection to make words I have changed the game so that you rotate colours instead. Much simpler! The idea is to match at four squares together in a square (2 rows of 2) of the same colour. When you successfully create a ‘block’ then that disappears and all of the blocks fall down from the top of the screen to fill in the hole created by the block.
This is where you really see the value of TDD and good use of the single responsibility principle. It took me a little under an hour to make the change from letters to colours, I had to alter the applicable tests to make them work with colours. I updated the code so that all of the tests passed. When I ran the game it worked first time! For anyone that doubts the value of TDD (or having tests in general) that is a key example of why they are so good.
I want to talk a little about the problem I had to solve to make blocks falling animation work smoothly. Workflow of what happens when a block is created:
- Board changed event gets fired when the player rotates a selection
- In the board changed event handler a check is done to see if any blocks are created, If there are new blocks a blocks created event is raised
- In the blocks created event handler a new animation is started to colour in the block that is created
- When the animation that colours in the blocks finishes it raises a remove blocks found event (more on this later)
- The remove blocks found event handler remaps the board in memory and then starts the blocks falling animation
- When the blocks falling animation finishes it raises a board changed event
- Notice that the whole workflow is decoupled into reusable chunks. By doing it this way I achieve complete code separation where no part of the system has to know a lot about the other parts. Each unit of the system has a specific job to do. The reason that I raise a remove blocks event and not put that logic in the start of the blocks found animation is to achieve this separation. In the future I might want to animation the falling blocks differently but I probably still will wanted the blocks to be removed. I have got that segregation.
I want to explain how the blocks falling animation works as I think the logic for that is quite interesting. Below is the psuedo code of how the whole process works with the classes the code is in in brackets:
Colour in the blocks so that the user knows they have made a block (BlocksFoundAnimation)
Set the offsets for all of the squares that need to fall (RemoveFoundBlocksEventHandler)
Reorganise all of the squares to their correct positions as if they have fallen down (RemoveFoundBlocksEventHandler)
Replace squares that are in blocks with new colours (RemoveFoundBlocksEventHandler)
Start a falling blocks animation (RemoveFoundBlocksEventHandler)
Decrease the y offset of each square until it reaches 0 (RemoveFoundBlocksEventHandler)
Check to see if the falling squares have created any new blocks (BoardChangedEventHandler)
The important thing to note is that the positions of the squares never actually change. All that I do is swap the tiles between them and set a y offset on each of them to give them the illusion that they are falling. The last piece of the puzzle was to update the square drawer or to be more precise the SquareOriginCalculator to take the y offset of the square into account.
That’s it for this post. As always you can get the code from the part16 branch on github. Check out the instruction page if you are unsure how to do this.