Overview

This is is writeup for the Ghost vs Monsters re-written in an object-oriented style of programming. This document lists the project files, objects contained within and their purpose.

Thank You

Many thanks to everyone at Beebe Games and Ansca for making the code publicly available!

Download

The code for this new version is available on github, just click on the image below.

github

The code has been updated for Corona G2 !!

Architecture and Design Inspiration

One of the most useful books I've read about object-oriented design is Head First Design Patterns by O'Reilly. One thing that has always remained with me was the simple explanation about why we bother developing with OO principles. In short it is all about encapsulating change. Change is a constant in software development --- changes in characters, changes in levels, changes in platforms --- and OO principles are our defense against it. When used properly, the benefit is more flexibility in the software and fewer unintended consequences when changes are made.

Design Principle - Identify the aspects of your application that vary and separate them from what stays the same. - Head First Design Patterns

So, in that respect, this version of Ghosts vs Monsters is my take on the flexibility necessary for the changes I would expect in this application. Some of the changes were straight-forward like moving the functionality of the Ghost into its own class, but some changes were more hypothetical like wanting to load level data from a JSON file captured from a web service, or perhaps separating as much from the Game Engine as possible so that it could easily be re-used for another "Angry Birds"-type game. This latter aspect was one of the core, driving tenents of the re-design.

Secondly, I wanted to keep the original game intact as much as possible. I guess this means two things:

  1. You'll see many of the same variable names and chunks of code. I think this makes it easier for those comparing the two code bases to see where things were moved. I even continued to use buttons from the UI library instead of using my own button lib.
  2. Except for the rare features I added (like a progress bar on the loading screen), I wanted to make a faithful rendition of the original as this was really an exercise in porting it to OO. So, from a user perspective, you shouldn't notice anything different while playing the two versions.

Changes

The following is the list of changes with some insight into why they were made.

main.lua

This file is similar to the original in that it starts building the application. It takes the concept a little further with the introduction of the app_token and the setup of the different display groups. The display groups make it easy to layer the different components of the app, for instance, to ensure that the loading screen is always above any menu or game scene.

The app_token is a table which is used to pass around values and references between Director scenes. It is one of the ways information can be passed around between screens, but without the use of global variables. The app_token is discussed in more detail below.

game_objects.lua

This file contains all of the items used in the game and is accessible by a Factory Constructor. The constructor can be used to create any game object via a single line of code (not including the import ): {code:language=none}local GameObjectFactory = require( "game_objects" )

local object = GameObjectFactory.create( object_type ){code} It is this construct which allowed me to super-simplify the level data file, which in turn would make it very easy to download and read JSON-formatted data files.

Three of the items contained within this file - cloud, ghost, and monster - had functionality which I wanted to encapsulate, so object classes were created for them. These object classes still reside in the same file.

Clouds

Clouds are the least complex of the new class objects. The main functionality was to "move themselves" across the screen, but they also listened for Game Engine events to know when to pause themselves in the sky.

Monster

The monster object is next in level of complexity. It contains all of the images required for viewing and exploding and it also contains code to control collisions and communicate the necessary collision data back to the Game Engine. The collision data was used in determining the point value of the Ghost hit.

Ghost

The Ghost is the most complex of the characters. The biggest change and most welcome was the addition of the State Pattern to control the various phases the Ghost goes through during the game.

For one, the Ghost is fully responsible for bringing itself on the stage and doing the hover animation when ready to be launched.

After the Ghost is launched into play, the state machine controls the display of they blast image, collisions and the resulting images changes for each of those. The full battery of states for the Ghost is Conceived, Born, Living, Aiming, Flying, Hit, Dying and Dead. I think that using the State Pattern brings a lot of clarity to the functionality and progression of this object. More importantly, it makes it easier to modify (make CHANGES) the Ghost behavior.

hud_objects.lua

This file contains all of the items needed for popup-type windows.

pause screen HUD

This is the simplest of HUD objects. The only instantiation of this class is embedded inside of and controlled by the Game Engine.

It easy to see that the pause screen might be different for another game. For one, the buttons would be styled differently. Having a separate class allowed for that change to be made easily.

load screen HUD

This version of GvsM includes a working version of the load screen. That is, the modifications to the screen include a loading bar which represents the tasks being processed and their completion. Most of the loading within the code is just placeholder, but it serves as an example of how actual loading could be handled.

Also, the way the visibility is controlled by the app allows it to span different Director scenes. In other words, Director Scene #1 could popup up the load screen HUD, Director would switch to Scene #2 underneath of it, and then Scene #2 could continue to control the HUD by updating it with the items it needs to load.

The loading screen in the original didn't have any real functionality - it was just a simple screen shown before the next scene with a timer in place to remove it. Additionally, each level had a separate Director Scene even though they did the same thing. So, if anything were to be changed, updates would have to be made for all of the separate load scenes for each level in the game.

The reference to the HUD is passed via the app_token.

game results HUD

This is a complex item with many display objects, buttons, transitions, sounds, etc. Unlike the other HUD objects, this one is instantiated at the end of every game and is controlled outside of the game engine. This is because the Director class is being used to navigate between the Menu and Game Play.

This is another item which would change in design if the Game Engine were to be re-used for another game with a different look and feel, so it got its own class.

level_manager.lua / level_data.lua

The Level Manager is an interesting object in that it has both roles with a graphical interface and those without one. For instance, from the Menu it is used to select one of two levels. It is also queried after the Game Results HUD if the Next Level button is chosen, because it is this one, single object which knows "what level should be played next".

The Level Manager would be the place to put in other functionality like locking levels or pulling other JSON game levels from the Internet.

To facilitate the latter piece of functionality, the format chosen for the level data file is JSON-friendly, even though the current file is pure Lua (ie, we didn't use any Lua constructs even though we could have).

scene-menu.lua / scene-game.lua

Taking into account that there are only two different screens to the application - one for the menu and another for game play - the other scene files were removed. (The original had game play screens for each level.) For the rest of the view items, class objects were created to contain their functionality - eg, the loading screen.

One of the main things to note within the scene Lua file is that all of the scene is not defined in the single Director function new(), but split up into other functions, variables, etc. This shows that a Director scene is just a namespace like any other Lua file and constants, functions, etc can be defined and reused as normal.

Each of the scenes has functions for button handlers, code to control the loading screen, etc.

game_engine.lua

Even with all of the functionality moved into other files and objects, this object is still the largest part of the application. Some of the changes / refactoring were in an attempt to extract all of the specifics to Ghost vs Monsters, so that it could be re-used as a game engine for Angry-bird type games. Though that goal isn't complete, it's definitely far along.

Misc

app_token

The app_token is a data structure which is passed around by Director from scene to scene. Its purpose is to hold information used by each scene without needing to use global variables. It uses functionality found in Director version 1.3.

Challenges

Physics Engine

I wish that I could say this version was super stable. The game is definitely playable, however, periodically the physics world will glitch at the beginning of a level, then it will be ok for awhile. Being that this was my first experience with the physics engine, I don't have the battle-field knowledge of the absolute dos and don'ts to make it work 100% of the time. Though this is not 100% stable, the original would crash sometimes as well. It really seems tied to the integration of the Physics Engine with the Corona environment.

If you're interested, I started to put together some notes during my research about the physics engine and its dealings. They can be found in the page Physics Engine Insanity. It didn't help matters that there were differing opinions, solutions and examples on a majority of what I read concerning making the physics engine stable. While having the the physics engine is a good idea, the current integration seems finicky and buggy at best. Unfortunately, it's like a tempermental black box that can turn on you at any moment without warning. There's no way to make ammends because you don't know what you did wrong. Good Luck !