Word War III – Dev Diary – 03: Some Structure

Next up is some “boring” housekeeping work on the code. Eventually the game will consist of four screens. If I keep up with my current approach of putting everything in a single script things will get pretty messy, pretty fast.

Luckily there is a better way which is highlighted in the Futile videos and in the Banana Demo project . The idea is to subclass the FContainer for each screen and then add and remove these objects from the Futile stage.

First off I create an enumeration defining each of the screens:

public enum ScreenType
{
    TitleScreen,
    GameScreen,
    ScoreScreen,
    HelpScreen
}

Then I created a base class which, extends FContainer, for my screens:

public abstract class Screen : FContainer
{       
    override public void HandleAddedToStage()
    {
        Futile.instance.SignalUpdate += Update;
        base.HandleAddedToStage();
    }

    override public void HandleRemovedFromStage()
    {
        Futile.instance.SignalUpdate -= Update;
        base.HandleRemovedFromStage();
    }

    public abstract ScreenType Type
    {
        get;
    }

    abstract public void Start();

    abstract protected void Update();
}

The Start() method will be called just after the screen object is created. This is where we will create all our FSprites etc for the screen. The Update() method is where we can stick any logic that needs to be updated every frame. I overrode HandleAddedToStage() and HandleRemovedFromStage() to clue Futile into the fact that it needs to call (or stop calling) the Update() method. Lastly I created the Type property so that the main script can figure out what type a screen is at run time.

Here is the (redacted) code for the TitleScreen:

public class TitleScreen : Screen
{
    private FButton startButton;
    private FSprite bg;
    private FSprite nuke;
    private FSprite skyline;
    private FSprite title;

    override public void Start()
    {
        bg = MakeSprite("Background.png", 0f, 0f);      
        AddChild(bg);               

        nuke = MakeSpriteWithBaseLine("nuke.png", 0f, -Futile.screen.halfHeight + 50);      
        AddChild(nuke);

        skyline = MakeSpriteWithBaseLine("skyline.png", 0f, -Futile.screen.halfHeight);     
        AddChild(skyline);      

        title = MakeSprite("Title.png", 0f, 180f);      
        AddChild(title);

        startButton = MakeButton(0f, 0f, "START");
        AddChild(startButton);      
        startButton.SignalRelease += HandleStartButtonRelease;
    }

    private FSprite MakeSprite(string name, float xPos, float yPos)
    {
        FSprite sprite = new FSprite (name);
        sprite.x = xPos;
        sprite.y = yPos;

        return sprite;
    }

    private FSprite MakeSpriteWithBaseLine(string name, float xPos, float yBaseline)
    {
        //...
    }

    private FButton MakeButton(float xPos, float yBaseline, string buttonText)
    {
        //...
    }

    public override ScreenType Type
    {
        get { return ScreenType.TitleScreen; }
    }

    override protected void Update()
    {
        // do nothing
    }

    private void HandleStartButtonRelease(FButton button)
    {
        Main.instance.GoToScreen(ScreenType.GameScreen);
    }
}

The MakeSprite(), MakeSpriteWithBaseLine() and MakeButton() methods are some helper methods to simplify the code. As you can see the Start() method is where all the components of the title screen are created. Since the title screen doesn’t have any frame based animation the Update() method is empty.

Lastly HandleStartButtonRelease() is set up as the method that is called when the start button is pressed. It calls the GoToScreen() method on the Main class to switch to the GameScreen. I’ll cover this in more detail below.

Main is a subclass of MonoBehaviour and is the script that is attached to the “Futile” object in the games’s scene. It looks something like this:

public class Main : MonoBehaviour
{
    public static Main instance;
    private Screen currentScreen = null;
    private FStage stage;

    // Initialise Futile and get the ball rolling
    void Start()
    {
        instance = this;

        FutileParams fparams = new FutileParams (true, true, false, false);

        //...

        Futile.instance.Init(fparams);

        //...

        stage = Futile.stage;       

        //...       

        // and lastly show the title screen
        GoToScreen(ScreenType.TitleScreen);
    }

    public void GoToScreen(ScreenType screenType)
    {
        if (currentScreen != null && currentScreen.Type == screenType)
        {
            return; //we're already on the same page, so don't bother doing anything
        }

        Screen screenToCreate = null;

        switch (screenType)
        {
            case ScreenType.TitleScreen:
                {
                    screenToCreate = new TitleScreen ();
                    break;
                }
            case ScreenType.GameScreen:
                {
                    screenToCreate = new GameScreen ();
                    break;
                }
        }

        if (screenToCreate != null)
        {
            //destroy the old page and create a new one
            if (currentScreen != null)
            {
                stage.RemoveChild(currentScreen);
            }

            currentScreen = screenToCreate;
            stage.AddChild(currentScreen);
            currentScreen.Start();
        }

    }

    // This is called by unity every frame
    void Update()
    {
        // check if back key was pressed
        if (Input.GetKeyDown(KeyCode.Escape))
        {           
            switch (currentScreen.Type)
            {
                case ScreenType.TitleScreen:
                    {
                        Application.Quit();
                        break;
                    }
                case ScreenType.GameScreen:
                    {
                        GoToScreen(ScreenType.TitleScreen);
                        break;
                    }
            }
        }
    }
}

The Start() method here sets up Futile, sets up a static reference to the instance of Main for easy access by the screens and lastly calls the GoToScreen(ScreenType.TitleScreen) method to get the ball rolling and show the title screen.

GoToScreen() first checks that we aren’t already on the screen that has been requested. Next up it creates an instance of new screen. If there is an existing screen it removes it from the stage (so it gets garbage collected). It then adds the new screen to the stage and calls it’s Start() method so the screen can set itself up.

One thing to note is that I currently only have the TitleScreen and GameScreen plumbed in. The switch statement will need to be expanded for the help and score screens.

Lastly the bit of code in the Update() method is there to allow the back button to work for Android. If I ever port this game to iOS I will need to add some on screen buttons to achieve the same thing.

Here is a clip of this all in action:

YouTube Preview Image

 

The next post in this series can be found here