Last few days I’ve been relaxing a bit. For one, I’ve been playing Baldur’s Gate again. I would love to create an RPG like Baldur’s Gate myself some day, but the work involved is massive. Between chilling out and playing Baldur’s Gate, I did find some time to work on Bomberman.

What Is Finished?

Sounds can now be played. Like animations, sounds are to be configured in the JSON file of an entity. For each state, 1 sound can be played. So a monster can play a sound when it attacks or when it’s killed.

Today I started the game on my AppleTV and performance seems to be smooth; at least when nothing interesting happens. Sadly the controllers don’t work yet, so I don’t know what performance will be when lots of explosions show up on the screen. I will be looking to fix the controller issue by tomorrow as I can’t wait to test this game with my girlfriend :)

Bomberman Demo (1)

I’ve cleaned up the codebase. I feel I’m still not well-versed in Swift, so I might often use an Objective-C approach (“When all you have is an hammer, everything looks like a nail”). But occasionally I find Swiftified approaches on internet and I try to adjust my code accordingly.

One simple example would be nested ifs that show up often in my current codebase:

override func keyUp(theEvent: NSEvent) {
    if let delegate = self.gameSceneDelegate {
        let playerAction = playerActionForKeyCode(theEvent.keyCode)
        
        if let player = playerAction.player {
            if let action = playerAction.action {
                if action != .DropBomb {
                    delegate.gameScenePlayerDidStopAction(self, player: player, action: action)
                }
            }
        }
    }
}

The deeply nested if constructions look ugly to me, but in Swift this can be avoided. In Swift if/else statements can check several optionals simultaneously and add constraints. Thus the code can be Swiftified as follows:

override func keyUp(theEvent: NSEvent) {
    if let delegate = self.gameSceneDelegate {
        let playerAction = playerActionForKeyCode(theEvent.keyCode)
        
        // We check for 2 optionals and add a contraint in a single if-statement.
        if let player = playerAction.player, let action = playerAction.action
            where action != .DropBomb {
            delegate.gameScenePlayerDidStopAction(self, player: player, action: action)
        }
    }
}

Work In Progress

As stated before, I will do additional work on controller support for the AppleTV.

When trying to run the app on the AppleTV, I noticed I could not save the assets ZIP file in the documents directory. Instead I have to save this data in the caches directory, which of course can be cleaned-up by tvOS whenever it’s low on disk space. So I might have to refactor some of the asset loading code to make it work smoothly with the caches directory.

I’ve added basic support for power-ups (e.g. shield, extra bomb, explosion range increase, etc…), but there’s still work that remains to be done here. At this moment a power-up is just a different entity that is loaded from the file system. I have yet to implement the effects of each power-up.

To get a good overview of the power-up status, I will probably start to work on the hud as well.

Planned Work

The following work remains to be done:

Features:

  • Add level transitions.
  • Implement power-ups.
  • Add music.
  • Improve points handling when monster and walls are destroyed. The score textures should not be part of the assets ZIP file, but generated based on a score value in a config file.
  • Create a main menu.
  • Create a settings menu.
  • Create a proper HUD for display of lives, power-ups, etc…
  • Create more interesting monsters by adding more states, e.g. a Chase state so monsters can chase the player.
  • Create custom graphics, sounds & music.
  • Create a marketing website.

Performance (if needed):

  • Cache data like images and audio. Currently every time an entity is created, files are read from the filesystem.
  • Pre-load audio. Currently the first time a sound is played, there’s some visual lag.