Monkey Tap

From TRCCompSci - AQA Computer Science
Revision as of 16:54, 21 October 2017 by Admin (talk | contribs) (Anatomy of a Game)
Jump to: navigation, search

File -> New Game

To get started, we first need to make sure we have both Xamarin and the latest development build of MonoGame installed. For this tutorial, we will be using Xamarin Studio, though the exact same steps apply when building games with MonoGame in Visual Studio as well.

First, we need to create a shared project where our game logic will reside that can also be shared with all target platforms we want our game to run on. Within Xamarin Studio, choose File -> New Solution -> MonoGame -> Library -> MonoGame Shared Library and name the project MonkeyTap. We must add projects for all of the platforms we wish to target.

Add a “MonoGame for Android Application” project to the solution and name it MonkeyTap.Android. If our game needed to take advantage of native Android features, such as NFC, the platform-specific project is the place to do so. Because our game doesn’t utilize any of these components, we can delete the Game1 class created in the Android project and add a reference to the shared project we just created earlier.

Hit F5 or Cmd+Enter to run the app on your selected emulator or the Xamarin Android Player. You should see a lovely blue screen. Now that our solution is properly configured, it’s time to get started writing our game!

Anatomy of a Game

Many different components work together to create a game. The Game1 class contains the main logic for our game, and is made up five main methods. Each serves its own purpose in making sure a game functions properly, from displaying art and playing sound effects to responding to user input and executing game logic. Game1 is made up of five main methods:

  • Constructor
  • Initialize
  • LoadContent
  • Update
  • Draw

The constructor and Initialize methods are used to perform any initializations the game needs before starting to run. LoadContent is for loading up any game content, such as textures, sounds, shaders, and other graphical or audio components. Update is used to update any game logic you have while your game executes (gathering user input or updating the world), while Draw should exclusively be used to draw any graphics that need displaying.

Adding Game Assets

No game is complete without textures and sound effects, known in game development as “content” or “assets”. Most gaming frameworks have a content pipeline, which is just used to take raw assets and turn them into an optimized format for your game. The Content.mgcb file in the Content folder is MonoGame’s content pipeline. Anything added to this file will be optimized and included in your final application package.

All game assets should be shared between any target platforms. Drag the Content directory from the Android project to the shared project. Ensure the Build Action of the Content.mgcb file is set to MonoGameContentReference. If this build action does not appear in the drop down, right-click the Content.mcgb file, select Properties, and manually enter the text to MonoGameContentReference. Download the MonkeyTap assets and extract them into the Content folder in the Shared Project.

MonoGame has a special Pipeline Editor to make it super easy to work with game assets. Double-click the Content.mgcb file to open the Pipeline Editor. Next, add the asset files you just downloaded as seen below:

Add textures and sounds to your game with the MonoGame Pipeline Editor.

Now that our game content is optimized for use in our application, we can use the assets in our game.

Creating MonkeyTap User Interface

Loading game assets is done through a ContentManager, which is exposed by default via the “Content” property of the Game class. First, let’s import some necessary namespaces and declare fields to store our game assets:

using System.Collections.Generic;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
public class Game1 : Game
{
    
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    Texture2D monkey;
    Texture2D background;
    Texture2D logo;
    SpriteFont font;
    SoundEffect hit;
    Song title;
}

Next, we need to load our assets. Add the following code to your LoadContent method, which is where all assets should be loaded in MonoGame:

monkey = Content.Load<Texture2D> ("monkey");
background = Content.Load<Texture2D>  ("background");
logo = Content.Load<Texture2D> ("logo");
font = Content.Load<SpriteFont> ("font");
hit = Content.Load<SoundEffect> ("hit");
title = Content.Load<Song> ("title");
MediaPlayer.IsRepeating = true;
MediaPlayer.Play (title);

All of our content is now loaded, from textures to audio. Now that we have loaded our content, it’s time to draw the user interface on the screen. The SpriteBatch class is used to draw 2D images and text. To make rendering as efficient as possible, drawing is batched together and sprites must be drawn between the Begin and End methods of SpriteBatch. Update the Draw method to draw our monkey texture on the screen using the spriteBatch field we just created:

protected override void Draw (GameTime gameTime)
{
    graphics.GraphicsDevice.Clear (Color.CornflowerBlue);
		
    spriteBatch.Begin ();
    spriteBatch.Draw (monkey, Vector2.Zero);
    spriteBatch.End ();
            
    base.Draw (gameTime);
}

Hit F5 or Cmd+Enter to run the application. You should see the monkey smiling with his banana and hear the music from Jason Farmer we set to play in LoadContent. Now that our game assets are loading, let’s build the rest of the user interface for playing MonkeyTap!

Building the MonkeyTap User Interface

Traditional Whack-a-Mole style games have moles that randomly appear on screen and must be tapped to disappear. Rather than randomly rendering monkeys on the screen, we can use a grid to help ensure that the monkeys don’t overlap to provide a consistent user experience. The grid is made up of multiple cells, each of which contains a rectangle, color, countdown timer, and transition value that will be used to fade the monkey in. Copy and paste the following GridCell class into the Game1.cs file:

public class GridCell 
{
    public Rectangle DisplayRectangle;
    public Color Color;
    public TimeSpan CountDown;
    public float Transition;
 
    public GridCell ()
    {
        Reset ();
    }
 
    public bool Update(GameTime gameTime) 
    {
        if (Color == Color.White) {
            Transition += (float)gameTime.ElapsedGameTime.TotalMilliseconds / 100f;
            CountDown -= gameTime.ElapsedGameTime;
            if (CountDown.TotalMilliseconds <= 0) {
                return true;
	    }
	}
	return false;
    }
 
    public void Reset ()
    {
        Color = Color.TransparentBlack;
        CountDown = TimeSpan.FromSeconds (5);
        Transition = 0f;
    }
 
    public void Show ()
    {
        Color = Color.White;
        CountDown = TimeSpan.FromSeconds (5);
    }
}

The Reset method is used to reset the cell back to its default state where the monkey is hidden. This is called when the user taps a monkey. is used to set the timer to five seconds when the monkey appears onscreen. The Update method is called each frame to update the countdown timer, and helps us to figure out if a user has not tapped the monkey within the given five second timeframe.

Now that cells are defined, let’s define the grid. Create a new List field called grid:

List grid = new List();

Add the following code to the LoadContent method to calculate the display rectangles for the cell:

var viewport = graphics.GraphicsDevice.Viewport;
var padding = (viewport.Width / 100);
var gridWidth = (viewport.Width - (padding * 5)) / 4;
var gridHeight = gridWidth;
 
for (int y = padding; y < gridHeight*5; y+=gridHeight+padding) {
    for (int x = padding; x < viewport.Width-gridWidth; x+=gridWidth+padding) {
        grid.Add (new GridCell () {
            DisplayRectangle = new Rectangle (x, y, gridWidth, gridHeight)
        });
    }
}

If you want your game to look good on all form factors, you need to take screen size into account, rather than hardcode positional values. GraphicsDevice.ViewPort provides us with a dynamic way to work with different form factors. In the code above, we add 10% of the screen width as padding to between the cells and calculate a width and height for the grid using the same property. We then loop through the (x,y) coordinates for each row and column and calculate the display rectangle.

Replace the Draw method with the code to draw our monkeys:

protected override void Draw (GameTime gameTime)
{
    graphics.GraphicsDevice.Clear (Color.SaddleBrown);
    spriteBatch.Begin ();
    foreach (var square in grid)
        spriteBatch.Draw (monkey, destinationRectangle: square.DisplayRectangle, color: Color.White);
    spriteBatch.End ();
    base.Draw (gameTime);
}

Finally, we want our game to run only in Portrait, so add the following code to the constructor:

graphics.SupportedOrientations = DisplayOrientation.Portrait;

Run the app, and you should see a grid full of monkeys!

The UI for MonkeyTap built with MonoGame and the Pipeline Editor.

With that, our user interface for our game built with MonoGame is complete. Follow our next post on adding game logic to complete your first game!