Building a simple game using XAML/C#: Part 4 – Make it fun

At this point, you have a simple version of a “Simon” game that works: you can start, restart, win, and lose. That said, the game still lacks the pizazz that really could make it stand out as something fresh and the game still needs to keep the player’s score.

Who is keeping score?

There’s plenty of places that we could logically keep the score, but I’m going to opt for keeping it in the Game object. Make sure that you have added an integer for storing the score in the Game class. Next, we’ll have to figure out a way to increment it. First, we’ll add two simple accessor methods, addScore and getScore for incrementing and retrieving the score.

//Game.cs
//------------
// score accessors       
public void addScore(int _score){
    score += _score;       
}
    public int getScore(){
            return score;       
}

Next, we’ll have to figure out when we’ll be giving the player points. I like using the clicks on the game squares as the source for updating the score and also incrementing the score when the user completes a level.

 

Change your button click handlers to increment the score when the user clicks them and also add tons of points when the user completes a level.

//Game.cs
//-----------
        void redRect_PointerPressed(object sender, Windows.UI.Xaml.Input.PointerEventArgs e)
        {
            if (theState != GameState.responseUser) return;

            triggerOnRed();
            if (!theLevel.solve(GameSwitches.red))
            {
                theState = GameState.levelFailed;
            }
            else
            {
                addScore(theLevel.getDifficulty());
                if (theLevel.isComplete())
                {
                    addScore(theLevel.getDifficulty() * 100);
                    doLevelComplete();
                }
            }
        }

Finally, we’ll have to update the score to show while the game is running. We’ll also update the current game level when we do this. The following methods will update the score and level text in the XAML.

MainPage.xaml.cs
------------------
        void updateScore()
        {
            scoreBox.Text = "Score: " + theGame.getScore().ToString();
        }

        void updateLevel()
        {
            // restart on first entry
            if (theGame.getLevel() == null) return;

            // normal operation
            levelBox.Text = "Level: " + theGame.getLevel().getDifficulty().ToString();
        }

The following changes to the Game loop will update the score while the player plays:

MainPage.xaml.cs: GameLoop method
------------------------------------
            // TODO, use a proper timer, wth is up with the new threading?
            switch (theGame.getState())
            {
                case GameState.challengeUser:
                    statusBox.Text = "Simon is telling you the secret code.";

                    if (theGame.getLevel().isCreated())
                    {
                        theGame.setState(GameState.responseUser);
                    }
                    else
                    {
                        // The following condition will ensure we don't pump too fast
                        if ((count > initialWait) && ((count % currentSpeed) == 0))
                        {
                            theGame.addLevel();
                        }
                    }
                    break;

                // start gameloop changes for score and level snippet
                case GameState.levelComplete:
                    statusBox.Text = "Nice work homeslice!  Reward is harder level.";

                    updateScore();
                    // TODO: using the status box reference is hacky
                    theGame.LevelUp(ref status2Box);
                    updateLevel();

                    // TODO: move into game logic
                    if (currentSpeed > minSpeedValue) currentSpeed -= 5;
                    status2Box.Text = "";
                    break;
                case GameState.levelFailed:
                    statusBox.Text = "You fail. Game over man.";
                    _gameLoop.Stop();
                    //AppBar.IsOpen = true;
                    break;
                case GameState.responseUser:
                    statusBox.Text = "Simon is waiting for you to enter the code.";
                    updateScore();
                    break;
                // end gameloop changes for score and level snippet
                case GameState.notRunning:
                    statusBox.Text = "Shall we play a game?";
                    break;
                default:
                    break;

            }

If you build and run the game now, you will notice the score and level updates in the UI.

Where did I put that trap door…
To add a new twist to the game, let’s make to board spin around once the user reaches the 5th level. Before we get ahead of ourselves, let’s first figure out how to spin the board.

We will be adding a new method to the Game class which will let us rotate the board. First, add a variable that will be used to hold the current board rotation.

Game.cs ---------------  
double boardRotation = 0.0;

Next, we’ll add a new method, rotateBoard that will take in a value for the number of degrees to rotate. The way that the rotation will have to work is that all of the squares will rotate about the middle of the board, like a pinwheel. The following image shows how the rotation will work for each of the squares:

An image showing the gameboard spinning.
In this rotation, the x and y center positions stay the same and the squares all are rotated about that center point. To perform the transformation, there is a convenient object, the CompositeTransform, that lets us easily transform the shapes about the gameboard’s center. The following code shows how this comes together:

        // Rotate the entire board
        public void rotateBoard(double degrees)
        {
            boardRotation += degrees;

            Rectangle current;

            foreach (GameSwitches gameSwitch in Enum.GetValues(typeof(GameSwitches)))
            {
                switch (gameSwitch)
                {
                    // bottom left triangle
                    case GameSwitches.blue:
                        current = blueRect;
                        centerX = blueRect.ActualWidth;
                        centerY = 0;
                        break;
                    // top right triangle
                    case GameSwitches.green:
                        current = greenRect;
                        centerX = 0;
                        centerY = greenRect.ActualHeight;
                        break;
                    // top right triangle
                    case GameSwitches.red:
                        current = redRect;
                        centerX = redRect.ActualWidth;
                        centerY = redRect.ActualHeight;
                        break;
                    // bottom right triangle
                    case GameSwitches.yellow:
                        current = yellowRect;
                        centerX = 0;
                        centerY = 0;
                        break;
                    default:
                        // impossible?
                        current = null;
                        break;
                }
                CompositeTransform transform = new CompositeTransform();
                transform.CenterX = centerX;
                transform.CenterY = centerY;
                transform.Rotation = boardRotation;

                current.RenderTransform = transform;
            }
        }

What this code does is just iterate through all of the game switches and rotates them about the game board center, pretty simple…

Now, that we have the ability to rotate the board, we should test it! You may have noticed that I added an extra button to the AppBar for turning on and off test features. Clicking this button will flip the enableTestFeature Boolean in the MainPage.xaml.cs partial class. In this partial, there is a section at the top of the GameLoop method that gets enabled or disabled when that button gets clicked. While we create and debug the rotation code, it is convenient to put the code into that section.

To test the rotation code, I added the following code to the enableTestFeature section of the game loop:

MainPage.xaml.cs
----------------------
            // start test code feature snippet
            if (enableTestFeature)
            {
                // insert fun weird tweaks in here
                // I used to have the rotation code here...
                theGame.rotateBoard(.2);
            }
            // end test code feature

Note that the value should be pretty small because the rotation code will be called frequently by the GameLoop. If you build and run, you can turn on and off the rotation feature by clicking the ??? button on the App Bar. Now that we have verified that the rotation code works, it’s time to insert some logic into the game that will enable the rotation code when the user reaches a certain level and will “unrotate” the board when the game is reset. For the first case, making the board rotate when the user reaches a certain level, I added the following code:

            // If we're Level 5+, start spinning 🙂
            if (theGame.getLevel() != null && theGame.getLevel().getDifficulty() > 5)
            {
                theGame.rotateBoard(.01 * theGame.getLevel().getDifficulty());
            }
            // end rotation gameplay section snippet

For resetting the board position, I added the following code to the resetGame function in the Game class:

MainPage.xaml.cs
----------------------
public void resetGame()
…
boardRotation = 0.0;
rotateBoard(0);
…

Now the board will rotate after reaching level 5 and resetting the game will place the board back to its original position. The following screenshot shows the game running with the board turning:

 

A screenshot of the game running with the board turning.

Also, hax!

I’m a huge fan of Easter eggs, and so… I thought it would be fun to add a nifty little cheat to show the current pattern while you’re playing. This is actually pretty helpful when you’re testing gameplay into higher levels and aren’t the greatest “Simon” player. The way that the cheat will work is that we’ll have an invisible rectangle that when clicked, will write the queue of moves to the “status” text area box.

First, we’ll add and initialize a rectangle for the invisible button when we set up the rest of the game board:

            cheatRect = new Rectangle();
            Canvas.SetLeft(cheatRect, 0);
            Canvas.SetTop(cheatRect, 0);
            cheatRect.Width = 50;
            cheatRect.Height = 50;
            cheatRect.Fill = new SolidColorBrush(Colors.Transparent);
            cheatRect.PointerPressed += new Windows.UI.Xaml.Input.PointerEventHandler(cheatRect_PointerPressed);
            gameCanvas.Children.Add(cheatRect);

 

Next, we’ll add the handler for when the button is clicked that will trigger cheat mode on the level:

void cheatRect_PointerPressed(object sender, Windows.UI.Xaml.Input.PointerEventArgs e)
        {
            if (theLevel != null) theLevel.toggleCheat();
        }

Finally, we’ll implement the “cheat mode” on the actual level. This is the code that will show the last played while the colors are presented.

First, update the add section to ensure that it will present the played colors into the status box when the cheat mode is enabled. The following code shows how this works:

Level.cs
-----------
        public Level(int _difficulty, ref TextBlock _statusBlock)
        {
            chimesAdded = 0;
            lastQueued = -1;
            difficulty = _difficulty;
            statusBlock = _statusBlock;

            if (cheat)
            {
                statusBlock.Text = "Cheating: ";
            }

...

Next, add an accessor method that will enable cheating:

public void toggleCheat()
        {
            if (cheat)
            {
                cheat = false;
            }
            else
            {
                cheat = true;
            }
        }

Toggle a break point to your application in the toggleCheat block, build and run your game and then click around until you find the “cheat” area of the game (if you used my code, clicking the top left of the screen will trigger the cheat mode). Clicking it will bring you to the toggleCheat breakpoint. Continue and run and see how easy the game is with cheating enabled .

In the following screenshot you can see the pattern being displayed to the player to make their life way too easy.

A screenshot of the game running with the moves shown in the top left.

 

Conclusions

 

So that’s pretty much it for this game, we’ll be working on another one soon. Hope you had fun!

 

You can download the source for the Metro Simon C#/XAML app which should have the complete working sample as well as a few other minor improvements like programming Windows 8 orientation changes in C#.

 

This post is the fourth in a series. The previous post in the series was Building a simple game for Windows 8 using XAML/C#: Part 3 – Make it a game