Get the game now while it’s free! 😉

digitalerr0r's avatarWilhelmsen Studios

The game Binary Man is published as free from today and during the Easter 2012! This game is an ultimate challenge where you have to solve puzzles really fast, as well as maneuvering correctly through each level. More and more challenges is added to the levels as soon as you start getting near your final destination. Winking smile

Get the game from the Windows Phone marketplace today:
http://windowsphone.com/s?appid=0d0eb3b2-4c23-4fd5-9c74-7ae9024a10b5

Here is the making of Binary Man guide, learn how the game was born Smile
https://digitalerr0r.wordpress.com/2012/03/10/the-making-of-binary-man/

And a trailer for those of you who haven’t seen it yet:

View original post

Posted in Uncategorized | Leave a comment

Binary Man 1.1 submitted

Introcomic

Binary Man version 1.1 is submitted to the WIndows Phone marketplace and will, hopefully, be available in a few days.

Try or buy the game here, having a release discount as well! Smile


What’s new in Version 1.1

– Reduced the pass-score on multiple levels making it easier to unlock new levels (but it’s still just as hard as before to get 5-stars)
– Training room/how-to where you can practice taking out enemies
– Show mission objective when level start
– Fixed the timing issues on the ending-scene
– Corrected typos
– Minor bugs

Posted in Binary Man | Leave a comment

Farseer Physics in XNA 4.0 for Windows Phone #1

image

In this tutorial series, we will go though some of the basics with Farseer Physics Engine 3.3.1 to implement physics into you 2D games.

To get started, go to http://farseerphysics.codeplex.com/ and download Farseer Physics Engine 3.3.1 to your computer.

With the download comes the library itself + many cool samples you can learn from.

Getting started

Start by creating a new project in Visual Studio and create a XNA 4.0 Windows Phone Game. When the new project is created, add a reference to the FarseerPhysicsXNA library (the dll is in the folder of Farseer Engine).

image

Also, add these using statements:

using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;

Now we are ready to start implementing physics into our game!

To do this, we must create a Farseer World object that will contain our world of physics.

It’s simply done using the World class from the Farseer library:

World world;

Now, in LoadContent, we will run the constructor for the World object, giving it a gravity of 1, meaning that the objects will get a pull of 1 in the Y-axis.

if (world == null)
{
    world = new World(new Vector2(0, 1));
}
else
{
    world.Clear();
}

Now, having the world instance set up, we need to update it in the mainloop.
We do this by simply running the Step-function of the World object, making the world of physics do one step ahead.

world.Step(Math.Min((float)gameTime.ElapsedGameTime.TotalSeconds, (1f / 30f)));

 

Adding static and dynamic rectangles

To add a box to our world that will stay still (not being pulled by gravity or affected by dynamic(moving) object, we add a Body object. We need three boxes in this example, one dynamic and two static.

Body rectangle;
Body rectangle2;
Body rectangle3;

And then in LoadContent, we say that the Body should be a rectangle, static and it’s position.

rectangle2 = BodyFactory.CreateRectangle(world, 1f, 1f, 1.0f);
rectangle2.BodyType = BodyType.Static;
rectangle2.Position = new Vector2(2.7f, 2);

rectangle3 = BodyFactory.CreateRectangle(world, 1f, 1f, 1.0f);
rectangle3.BodyType = BodyType.Static;
rectangle3.Position = new Vector2(5.0f, 3);

Now we have two static rectangles in our world. Let’s also add a new object that’s a rectangle and dynamic, meaning it will be affected by gravity and static objects,

rectangle = BodyFactory.CreateRectangle(world, 1f, 1f, 1.0f);
rectangle.BodyType = BodyType.Dynamic;
rectangle.Position = new Vector2(3.5f, 0);

Rendering

We render using a sprite batch in the Draw loop.

What we do is to render a texture at each of the rectangles positions, rotate it and set it’s origin to the middle.

spriteBatch.Begin();
spriteBatch.Draw(rectangleSprite, ConvertUnits.ToDisplayUnits(rectangle.Position),
                                null,
                                Color.White, rectangle.Rotation, new Vector2(rectangleSprite.Width / 2.0f, rectangleSprite.Height / 2.0f), 1f,
                                SpriteEffects.None, 0f);

spriteBatch.Draw(rectangleSprite, ConvertUnits.ToDisplayUnits(rectangle2.Position),
                                null,
                                Color.White, rectangle2.Rotation, new Vector2(rectangleSprite.Width / 2.0f, rectangleSprite.Height / 2.0f), 1f,
                                SpriteEffects.None, 0f);

spriteBatch.Draw(rectangleSprite, ConvertUnits.ToDisplayUnits(rectangle3.Position),
                                null,
                                Color.White, rectangle3.Rotation, new Vector2(rectangleSprite.Width / 2.0f, rectangleSprite.Height / 2.0f), 1f,
                                SpriteEffects.None, 0f);
spriteBatch.End();

As you can see, from both the earlier section and the draw-loop is that the world object uses it’s own coordinate system and not the normal pixel-coordinate system.

private static float _displayUnitsToSimUnitsRatio = 100f;

public static Vector2 ToDisplayUnits(Vector2 simUnits)
{
    return simUnits * _displayUnitsToSimUnitsRatio;
}

 

This function simply converts the physics simulator units to pixel units. We can see that we have chosen each unit in the simulator to be 100 pixels. That means that the resolution in the simulator world goes from 0 to 8 in the X-axis and 0 to 4,8 in the Y-axis.

Applying force

It’s also possible to apply force using normal conditions. In this example, I added a force that will make the objects bounce.

if (rectangle.Position.Y > 3)
{
    rectangle.ApplyForce(new Vector2(0, -20));
}

If the object goes over 3 in the simulator world, or 300 in the phones pixel coordinate system, it will apply a force on the rectangle, pushing it upwards.

image

Source

The entire code can be seen below:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;

using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;

namespace FarseerPhysicsTutorial
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        World world;

        Body rectangle;
        Body rectangle2;
        Body rectangle3;
        Texture2D rectangleSprite;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            // Frame rate is 30 fps by default for Windows Phone.
            TargetElapsedTime = TimeSpan.FromTicks(333333);

            // Extend battery life under lock.
            InactiveSleepTime = TimeSpan.FromSeconds(1);
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            if (world == null)
            {
                world = new World(new Vector2(0, 1));
            }
            else
            {
                world.Clear();
            }

            rectangle = BodyFactory.CreateRectangle(world, 1f, 1f, 1.0f);
            rectangle.BodyType = BodyType.Dynamic;
            rectangle.Position = new Vector2(3.5f, 0);

            rectangle2 = BodyFactory.CreateRectangle(world, 1f, 1f, 1.0f);
            rectangle2.BodyType = BodyType.Static;
            rectangle2.Position = new Vector2(2.7f, 2);

            rectangle3 = BodyFactory.CreateRectangle(world, 1f, 1f, 1.0f);
            rectangle3.BodyType = BodyType.Static;
            rectangle3.Position = new Vector2(5.0f, 3);

            rectangleSprite = Content.Load("box");


            
            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// Provides a snapshot of timing values.
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            
            if (rectangle.Position.Y &gt; 3)
            {
                rectangle.ApplyForce(new Vector2(0, -20));
            }

            world.Step(Math.Min((float)gameTime.ElapsedGameTime.TotalSeconds, (1f / 30f)));
            // TODO: Add your update logic here

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// Provides a snapshot of timing values.
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();
            spriteBatch.Draw(rectangleSprite, ConvertUnits.ToDisplayUnits(rectangle.Position),
                                            null,
                                            Color.White, rectangle.Rotation, new Vector2(rectangleSprite.Width / 2.0f, rectangleSprite.Height / 2.0f), 1f,
                                            SpriteEffects.None, 0f);

            spriteBatch.Draw(rectangleSprite, ConvertUnits.ToDisplayUnits(rectangle2.Position),
                                            null,
                                            Color.White, rectangle2.Rotation, new Vector2(rectangleSprite.Width / 2.0f, rectangleSprite.Height / 2.0f), 1f,
                                            SpriteEffects.None, 0f);

            spriteBatch.Draw(rectangleSprite, ConvertUnits.ToDisplayUnits(rectangle3.Position),
                                            null,
                                            Color.White, rectangle3.Rotation, new Vector2(rectangleSprite.Width / 2.0f, rectangleSprite.Height / 2.0f), 1f,
                                            SpriteEffects.None, 0f);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

Download the project: XNA 4.0 + Farseer Physics Engine 3.3.1

Posted in Farseer Physics Engine, Physics, Windows Phone, XNA | 3 Comments

The Making of Binary Man

This post is about how the Windows Phone game “Binary Man” was created. It will explain how I came up with the idea, how it was made, the levels, the enemies and so on, and also how it was ported to both Xbox 360 and Windows.

Introcomic

1. What is “Binary Man”?

Binary Man is about digital superhero controlled by a scientist, who must be able to recover LarsOtek’s new mainframe system – taken over by hackers.

The game is a 2D side scrolling game, and actually a combination of a action-shooter game and a puzzle game, where each enemy is a puzzle itself. The more effective you can kill the enemy, the more points you will get.

The game let’s you control Binary Man, fight through bugs, chunks and blocks of data in a digital world to reach your destination. It starts quite simple, but gets harder and harder.
Each level is rated with five stars, where one star is bad, and five stars is perfect. The score is stored both offline and online.

 

2. The Making of Binary Man

The concept

Binary Man started as a simple project where I wanted to create and actually finish a game for the new Windows Phone Marketplace.

I wanted to nail down some criteria’s before getting to the actual game idea
– It has to be nerdy
– It has to be simple as I don’t have much time for development
– But the game itself must be hard
– I’m a coder, not an artist – the graphics should be simple

It was time to take a look at different game ideas. I started of with a piece of paper and started to draw a brainstorming map. Lot’s of ideas came out, I wanted it to be something stereotype’ish but with a twist of something original – one idea was worth working with.

Cover art of the PAL Sega Mega Drive releaseIt was inspired from the game Zombies Ate My Neighbors (1993/1994) where you had to run around in a zombie infested world and shoot them with water guns and explosives.

I remember playing this game a lot with my twin-brother back in the 90’s and we loved it. This was what I wanted to make!

I worked on the idea and came up with a top/down shooter where you had to control a nerd with a bazooka. The bazooka was a keyboard, not shooting missiles – but sequences of binary code.

image
Taste the Binary System!!

I named the project “Binary Dude” and the interface should be simple. You use one thumb to move around, and the other to shoot either 0 or 1. Enemies was built up by either ASCII chars – like one Zombie-enemy could be named “Stan” and you had to shoot Stan converted to binary: 01110011011101000110000101101110 to take him out (I know.. Smile with tongue out).

Of course there were helpers, so you didn’t have to know the math (unless in Nightmare mode) but it was too nerdy and hardcore. And the controls would have been hard.

So I decided to move the camera down, thus creating a side scrolled game. I also wasn’t happy with the binary weapon (as I said, too nerdy) so I started to think again. A good friend of mine, Jonas Follesø, had created a flood filler game for Windows Phone named Tile Flood. I loved it.

image

 

“Tile Flood is a fun, colorful, simple and addictive puzzle game. The objective of the game is to flood all the tiles on the board in the same color. You start in the top left corner and flood the tiles by selecting one of the six colors. When selecting a color you change the color of the top left tile and any adjacent tile with the same color. This way you can flood all the tiles on the grid. “

imageimageimage

I remember thinking how I could take the Flood Fill concept in to a 2d side scrolling game – it was simple. All enemies are built up by blocks, and your gun will have 4 modes, each a different color. Then you need to take out the enemy in the shortest amount of shots to get a good score. In this way, you had to take out enemies, but each enemy is a puzzle where each shot counts.

Since I was going to work with blocks, I moved the game from being outside in the real world into a digital world. I had my brother and a friend over to discuss some ideas – the game was going to take place in hardware and digital worlds, like inside a modem, TP-cable, DMZ and so on.

I also decided not to use a human as a character, but instead an astronaut-inspired (I had just watched the movie 2001: A space odyssey) character.

image

I also wanted him to be a superhero, so I changed his name from Binary Dude to Binary Man. Binary Man was to be controlled by a scientist who invented him to take back a mainframe system that was taken over by hostile hackers.

 

3. Creating the game

The implementation of the game was fun. I started with creating a simple game engine using XNA. It was to support Game States like Main Menu and In-Game, a 2d side scrolling level, shooting, enemies and touch-input.

Moving Binary Man

image

Moving Binary Man will be done by the left thumb. If you hold your thumb above Binary Man, he will be moved up and stop at the thumb’s location. If the thumb is below, Binary Man should move down to the thumb’s location. If both thumb and Binary Man is on the same position, Binary Man should stay there.

 

 

image

An interface was also created, giving the player some feedback on where the thumb is pushing.

 

 

 

Shooting

imageShooting must be done by the other thumb. You have 4 different colors. Hitting an enemy will color the hit-block with the color you chose, and all blocks with the same color that is linked with the hit-block (flood fill).

This enables the player to take out the enemies in a number of different ways. You can simply fire away random shots and it will die in time, or you can use a systematic sequence like “green-gray-blue-purple-repeat” and hope for the best. But these methods are nothing compared to using your brain and shooting the correct color in the right place based on your strategy on taking out the enemy. The more shots you use to take out the enemy, the less score you will get by killing it – thus making each shot count!

Flood fill

As mentioned earlier, if an enemy is shot, it should be recolored by a flood fill algorithm. How it works is that when you hit an enemy with a color, it will color that block and the connected nodes in the enemy with the same color to the new color. This algorithm is used in paint programs (the bucket tool) and in games like Minesweeper.

How the algorithm works (from wikipedia):

Flood-fill (node, target-color, replacement-color):

1. If the color of node is not equal to target-color, return.
2. Set the color of node to replacement-color.
3. Perform Flood-fill (one step to the west of node, target-color, replacement-color). Perform Flood-fill (one step to the east of node, target-color, replacement-color). Perform Flood-fill (one step to the north of node, target-color, replacement-color). Perform Flood-fill (one step to the south of node, target-color, replacement-color). 4.
Return.

 

Example:
One first enemies you encounter where you have different kinds of solutions is the Shield Bug.

The Shield Bug is a enemy surrounded by a blue shield. If you do it the right way, you will be able to take it out with 4 shots.

Let’s see how the flood fill algorithm is working to take out the enemy.

image

Approach the Shield Bug by either from the top, or the bottom as he shoots straight forward.

Many tend to start by using the purple color on the gun. This will result in being able to kill the Shield Bug with five or more shots as one of the eyes (the right eye) is not connected to the shield-nodes in any way.

1) However, if you start with Gray shot first, you’re off from a good start.

image

2) Now fire of a Purple shot to remove the face.

image

3) Then Green to remove the eyebrows

image

4) And lastly, fire off a blue shot to kill it.

 

4. Creating enemies

I created a tool using WPF to create enemies. In this tool you get a very simple interface, and you use this to draw the enemy.

image

The enemy editor starts with giving the user a popup menu. Here you can set the size of the enemy. Clicking create will give you a new interface with the desired enemy-size.

imageimage

Clicking export will export the data to XML, and can then be loaded in the game. The data is simply a long string with the id of each block-type (color).

Also, many of the in-game enemies are random generated, making the levels different each time you run it.

 

5. Creating the levels

I also created a custom tool in WPF for creating levels.

image

This tool let me draw levels (areas that will get random generated blocks). It is very simple and I didn’t put a lot of time into the editor. But it still get’s the job done and is quite powerful. I can place out enemies and blocks, scroll around on the playfield and draw the areas that will be blockers. I can either add blocks manually, or go into draw mode (add mode) that lets me add blocks as I click around on the field.

Clicking export will generate an XML file with the level-data ready to imported as an asset in XNA.

 

6. Putting it all together

Putting the game together was fun. I did it in a agile way, creating new functionality and creating levels side-by-side. I got my brother to draw me the intro comic so I could focus on the code (and I don’t know how to draw).

The intro comic

 

I wrote down a simple story that I parted into 26 different sections, one for each level.

The world map is based on a tile engine that renders different tiles for each world. When adding a new level, I can place the level on the world map and it will get connected with last level in the list (previous level).

The main menu is a big picture and a viewport. I move the viewport around based on where the user clicks, opening up for different screens for the different parts of the menu.

After each level is complete, it will measure the total score and rank it based on stars. The high scores will be stored on the local app-storage, and if the device is connected, it will upload the result to an online service.

When navigating the world map, the player with the best score for that level is displayed on the level.

image

The complete game can be seen in this video

Playing Binary Man

 

 

7. Other platforms (Xbox 360 and Windows)

Since the game was made using XNA, it’s a very simple job to convert the app to both a Windows and a Xbox360 version.

How I did it was to Right-click the project in Visual Studio and select “Create Copy of Project for Xbox 360”:

image

I used about 15 minutes to get the game running on both Xbox 360 and Windows using the Xbox 360 Game Pad (mostly input/controller changes, and some resolution)

image
Me playing “Binary Man” on the Xbox 360

It worked just like on the phone but of course I’d like to change the interface to a more gamepad friendly style, as well as the main menu and world map so its more console-looking.

 

8. Download the game

imageThanks for reading this. Now, it’s time for you to try it out!

http://windowsphone.com/s?appid=0d0eb3b2-4c23-4fd5-9c74-7ae9024a10b5

Posted in Tutorial, XNA | 4 Comments

Binary Man is released

I’m happy to announce that Binary Man is now available in the Windows Phone Marketplace.

Note: A new update is currently being worked on where a few typos in the intro comic and the story will be fixed.

Introcomic

You are a digital superhero controlled by a scientist, who must be able to recover LarsOtek’s new mainframe system – taken over by hackers.
 
The game is a 2D side scrolling game, and actually a combination of a action-shooter game and a puzzle game, where each enemy is a puzzle itself. The more effective you can kill the enemy, the more points you will get.

The game let’s you control Binary Man, fight through bugs, chunks and blocks of data in a digital world to reach your destination. It starts quite simple, but gets harder and harder.
 
Each level is rated with five stars, where a one star is bad, and five stars is perfect and stored both offline and online (if connected).

The trial mode gives you access to the first four levels for free.

Binary Man gives you
– Each level is a part of the story
– World map with 6 worlds
– Total of 26 levels
– Many enemies, each a puzzle on it’s own
– Bosses
– Rated performance with starts, both local and online high scores for each level

Binary Man is a game by Wilhelmsen Studios
– Created by Petri Wilhelmsen
– Comic is created by Paul Wilhelmsen
– Music is produced by Egil Hasting and Andreas Skaarung

Check the trailer

The trailer for Binary Man : Attack of NonyMou5

Get the game here
http://windowsphone.com/s?appid=0d0eb3b2-4c23-4fd5-9c74-7ae9024a10b5

qrcode

Posted in Windows Phone, XNA | 2 Comments

Project Binary Man–Trailer 2

Introcomic

Project Binary Man is a 2D-side scrolling game for Windows Phone. You are a digital-superhero controlled by a scientist who must be able to recover LarsOtek’s new mainframe system.
 
The game is actually a combination of an action-shooter game and a puzzle game, where each enemy is a puzzle itself. The more effective you can kill an enemy, the more points you will get. There is both a local offline high score list, and a online high score for each level.
 
Each level is rated with five stars, where a one star is bad, and five stars is perfect and better 😉

Get the best score to get your name on the level selection for every Binary Man player on Windows Phone – until someone else beats you!

This is the 2nd trailer for Binary Man, enjoy!

Posted in Binary Man, Windows Phone, XNA | Leave a comment

Xbox 360 game with source: Escape

GameThumbnailI just quickly ported the game Escape (created in this tutorial) to Xbox 360. The code is quite similar with two exceptions:

– I had to handle the safe limit zone and various screen resolutions
– I changed the controller from being touch-based to GamePad-based, and also considering that the player might use a gamepad that is either player one, two, three of four.

I also removed the online high score system since when it comes to the Xbox 360, we have to use a completely different approach when it comes to online storage.

mainmenu

Anyways, please read the tutorial if you got some questions regarding the game logic.

Download source for Xbox360 here

Enjoy!

Posted in Tutorial, Xbox360, XNA | 2 Comments

Tutorial: Creating a XNA 4.0 game for Windows Phone 7.5

clip_image001

Note: This tutorial is still a WIP, but I decided to release it just in case anyone needs it. Enjoy! clip_image002

In this tutorial we are going through the process of creating a simple game from A to Z using Visual Studio 2010 and the Windows Phone 7.1 SDK – Targeting Windows Phone 7.5.

This game was created during a presentation and took about 45 minutes, and patched as the session went on by feedback from the audience. I just documented the process and shared it on this blog. clip_image002[1]

First of all, if you haven’t done so yet – you will need to download the Windows Phone SDK.

clip_image003

1. Designing the game

The first thing you should do when creating a game is to come up with an idea, story and how you should go through that story. It’s important to be able to explain the game in one sentence – if you can’t, the players/gamers/consumers will have trouble understanding the game. If you can, it will be much easier to market and sell the game!

This game will be quite simple: I want a player to move around on a field and avoid the enemies as long as possible. If hit, it’s game over and you have to restart. The longer you stay alive, the higher will the score be.

The setting
You are inside and lost in a hostile ghost house.

One-Sentence Marketing Description
”Run around on the playfield without getting caught by evil ghosts and survive as long as possible.”

 

2. Creating the graphics

Most games require some graphics. In this game I want to have a playfield that represents the floor in a ghost house, a ghost, a player, some text that display high scores and some dot’s that show where I just walked (path).

Note: The textures and the source for the game can be downloaded here:

Source for Windows Phone 7.1 and XNA 4.0

Texture: Floor

clip_image004

Texture: Ghost

clip_image005

Texture: Player

clip_image006

Texture: Footstep

clip_image007

(added shadow as the image is white, just so you can see it’s a round thing)

Texture: Main menu screen

clip_image008

 

3. Game logic

Next we should think how we should play the game, what’s the logic, how is the enemies moving, how should the player move and so on.

Player movment
clip_image009Tap position

The player should move towards a point on the screen. The point is set each time the player taps on the screen. This enables the player to both move by tapping on spots, or hold the finger on the screen to move.

Enemy movment
clip_image010

All the enemies active in the game should move towards the players location. If the player moves, the target to where the enemies are moving should be updated. This will make the enemies follow the player.

Flow
Every second:
– Add new enemy to the game
– Increase the score with the number of active enemies

If the player is hit by an enemy
– Initiate Game Over process
– Set high score if better than previous
– Reset and restart the game

I don’t want the player to be able to get a resting-pause when game over (thus, not going back to the main menu or “tap to restart” or anything), but instead just let the player continue from where he died, just reset the score and remove “living” ghosts. I also want to motivate the player by showing him/her that he got a new record, or that “Almost there, just X points away from high score, you can do it!!”

Player
The player will have a class that contains a target-positions. In the update loop, the player will move towards this target. Target is set by tapping on the screen.

Enemy
The enemy is the same as the player, but the target will be set to the players location instead of a tap on the screen.

Enemy Handler
There will be many enemies, so we create a List that will contain all the enemies and place it in a Enemy Handler. The handler will loop through each enemy, update and draw then, and also check for collisions with the player.

clip_image011

Offline and Online High score
There will be both an offline local high score and a online high score. The local high score is stored to the App’s Isolated Storage. The online high score is stored to a free service that hosts online high scores for both free and commercial games; Mogade. You can read more about Modage here, and sign up for a free account.

http://www.mogade.com/

The highscores will show when you start the game and when it’s game over. After playing a few seconds, the high score will not be visible.

Walking steps
A path of steps will follow the player, showing the walked path. Each step should slowly fade out after a while. This is done by adding steps to a list with the players position, and in the update, fade them out and remove them from the list.

The game screen
This is how the game will look in the end:
clip_image012

Game icons for the phone app-menus
The games need a logo and app tile image. They will have alpha as background, making the tile for the app use the same background color as the phones theme. The red below is the color of the Theme.

Little icon:

clip_image013

Large icon:
clip_image014

 

4. Implementing the game

clip_image015

The games classes we will implement

Be sure do download the source for the applications so you can follow the implementations. Or you can follow the steps below and create the game as we go, but not all details will be covered.

Download source here: Source for Windows Phone 7.1 and XNA 4.0

Next, we need to create a new project. Make sure you select XNA Game Studio 4.0 and Windows Phone Game (4.0)

clip_image016

Now you should have a empty game project. Don’t add anything yet, I will explain some logic first. We will add the Game1 logic last, so just read until we start adding the Player class. clip_image002[2]

Main Menu State

When starting the app, the player should see the main menu. All it will do is to wait for some input and start the game. When the player taps the screen, the game should ask for a name and start the game.

In this case the scenes will be very simple. It’s either in-game or main menu. So to handle the scene state we simply use a boolean variable. We also create a boolean variable to check if the player has typed in his/her name yet.

bool inMainMenu = true;
bool nameTyped = false;

In the Draw() function of the game we check if these indicate if we should show the game or draw the main menu, and in Update() we use these to update the game accordingly.

Draw()

if (inMainMenu || !nameTyped)
{
    spriteBatch.Begin();
    spriteBatch.Draw(mainMenu, new Vector2(), Color.White);
    spriteBatch.End();
}
else
{
    spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied);
    spriteBatch.Draw(floor, new Vector2(), Color.White);
    enemyHandler.Draw(gameTime, spriteBatch);
    player.Draw(gameTime, spriteBatch);
    spriteBatch.DrawString(font, "Current score: " + player.Score.ToString(), new Vector2(80, 10), Color.White);
    spriteBatch.DrawString(font, "Last score: " + player.LastScore, new Vector2(80, 35), Color.White);
    
     // and so on....

Update()

if (inMainMenu)
{
    TouchCollection touchStateMain = TouchPanel.GetState();
    if (touchStateMain.Count > 0)
    {
        object stateObj;
        try
        {
            Guide.BeginShowKeyboardInput(PlayerIndex.One, "Enter name", "Please enter a name for the scoreboard (Minimum 3 and no longer than 20 characters)", username, GetText, stateObj = (object)"GetText for Input PlayerOne");
        }
        catch (Exception ex)
        {
        }

        inMainMenu = false;
    }
    return;
}

// Don't update the name if we have moved away from menu, but not typed username yet
if (!nameTyped)
    return;

We check for a touch on the screen. If we touch, we set the first boolean to false making the game only wait for the player to have completely set the name. We do this by using the built in function Guide.BeginShowKeybaordInput(..). This will call the function GetText when the player clicks OK/Cancel.

private void GetText(IAsyncResult result)
{
    try
    {
        string resultString = Guide.EndShowKeyboardInput(result); ;

        if (resultString != null)
        {

            if (resultString.Length > 20)
            {
                resultString = resultString.Remove(20);
            }

            if (resultString.Length < 3)
            {
                inMainMenu = true;
                return;
            }
            username = resultString;
        }
        else
        {
            username = "Player";
            inMainMenu = true;
        }
    }
    catch (Exception ex)
    {
        username = "Player";
        inMainMenu = true;
        return;
    }

    try
    {
        IsolatedStorageSettings.ApplicationSettings["Username"] = username;
    }
    catch
    {
        IsolatedStorageSettings.ApplicationSettings.Add("Username", username);
    }

    nameTyped = true;

    //reset scoreboard list, since it will be loaded again when starting the game
    sblist.Clear();
}

This function gets the written name and stores it both in memory, and in the IsolatedStorage for later use. Once the name is set, we set the final boolean that the name is typed. The next time we visit update and draw, it will execute the code that represent “in-game” – you start playing the game.

In-Game

This is where all the fun is happening. Let’s just take a look at the logic from section 3.

Every second:
– Add new enemy to the game
– Increase the score with the number of active enemies

 

The Update() function will now bypass the main menu and instead start updating the game logic. It will increase the total score each second by using a scoreTimer. If the scoreTimer is above 1000 (ms), it should be set to 0 (restarting the timer) and also increase the score and add a new ghost (logic soon to be implemented).

scoreTimer += gameTime.ElapsedGameTime.Milliseconds;

if (scoreTimer >= 1000)
{
    scoreTimer = 0;
    player.Score += enemyHandler.NumberOfEnemies();
    enemyHandler.AddEnemy();
}

We also check for a touch by the player. If the player touches the screen, we should update the players target so the player moves towards the new location.

TouchCollection touchState = TouchPanel.GetState();
foreach (TouchLocation t in touchState)
{
    player.Target = t.Position-(new Vector2(32,32));
}

Starting the development!

Let’s move out from the main Update() for a while and instead create the Player Game Component class, Enemy Game Component, the Enemy Handler and the rest of the classes we want to have in the game.

In other words, it’s time to start writing some code. For each of the classes below, add a new class to your game. I created a folder named GameClasses (just to show the audience that it’s possible) and added the classes into it. Feel free to make a better architecture once you are comfortable with the logic and the implementation, refactor and revisit the code where needed.

So, go ahead an add a new folder named GameClasses to your project, and then we will add the classes one-by-one as we follow the tutorial. in the end, the project might look something similar to below.

clip_image017

Player
The player class is a small and simple class. Add a new class to your project by rightclicking the GameClasses foler and select Add new item, make sure it’s a GameComponent and name it Player.

image

An empty Game Component class named Player is generated and added to your project. Now, go to the class and change the inheritance from being GameComponent to DrawableGameComponent. This should be done for each class except the FotStep (more on this later) as it will be a GameComponent since the player-class will draw them directly.

So change this:

image

to this:

image

clip_image018

The code can be seen below, but let me first explain how the Player class works.

The player class contains the current score for the player, the previous score if any, a position and a target. It also contains a texture that represents the player, a footstep texture and a footstep list.

The constructor just sets the target to 0,0 and the position to 0,0. The player will then start in the upper corner of the screen.

LoadContent will load both the player texture and the footStepTexture.

We have a foot step timer that will add a new footstep to the footstep list once it hit the timer. Each footstep will be added at the center of the player. Also, for each Update() we remove each footstep that is inactive.

The key part of Update() is that the position of the player must move towards the target position. We simply do this by checking the position of the player and the target, and it they don’t match with an offset of 5 pixels, we should move the player. This can be done in a smarter way but hey, I only had 45 minutes to create what the audience wanted clip_image019

The Draw() will take a spriteBatch as an input (so I don’t have to create a new one for the player and each enemy, and also use the same spriteBatch only once for each frame), and render all the footsteps – using the alpha channel as the fadeout for a footstep, and also draw the player.

The entire player class can be seen below:

public class Player : Microsoft.Xna.Framework.DrawableGameComponent
{
    Texture2D fotStepTexture;
    Texture2D texture;
    Vector2 position;
    Vector2 target;

    int addNewFotstepTimer = 200;

    List<FotStep> fotstepList = new List<FotStep>();

    int score = 0;
    int lastScore = 0;

    public int LastScore
    {
        get { return lastScore; }
        set { lastScore = value; }
    }

    public int Score
    {
        get { return score; }
        set { score = value; }
    }

    public Vector2 Target
    {
        get { return target; }
        set { target = value; }
    }

    public Vector2 Position
    {
        get { return position; }
        set { position = value; }
    }

    public Player(Game game)
        : base(game)
    {
        // TODO: Construct any child components here
        position = new Vector2();
        target = new Vector2();
    }

    /// <summary>
    /// Allows the game component to perform any initialization it needs to before starting
    /// to run.  This is where it can query for any required services and load content.
    /// </summary>
    public override void Initialize()
    {
        // TODO: Add your initialization code here
            
        base.Initialize();
    }

    public void LoadContent()
    {
        texture = Game.Content.Load<Texture2D>("player");
        fotStepTexture = Game.Content.Load<Texture2D>("fotstep");

        base.LoadContent();
    }

    /// <summary>
    /// Allows the game component to update itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    public override void Update(GameTime gameTime)
    {
        addNewFotstepTimer -= gameTime.ElapsedGameTime.Milliseconds;
        if (addNewFotstepTimer < 0)
        {
            Vector2 p = this.Position;
            p.X += texture.Width/2;
            p.Y += texture.Height/2;
            fotstepList.Add(new FotStep(Game) { Position = p });
            addNewFotstepTimer = 1;
        }


        List<int> deleteInactiveFotsteps = new List<int>();
        int index = 0;
        foreach (FotStep step in fotstepList)
        {
            step.Update(gameTime);
            if (!step.IsActive)
            {
                deleteInactiveFotsteps.Add(index);
            }
            index++;
        }

        foreach (int delInd in deleteInactiveFotsteps)
        {
            fotstepList.RemoveAt(delInd);
        }

        if(position.X < target.X-5)
        {
            position.X += gameTime.ElapsedGameTime.Milliseconds * 0.3f;
        }
        else if(position.X > target.X+5)
        {
            position.X -= gameTime.ElapsedGameTime.Milliseconds * 0.3f;
        }
        else
        {
            position.X = target.X;
        }

        if (position.Y < target.Y-5)
        {
            position.Y += gameTime.ElapsedGameTime.Milliseconds * 0.3f;
        }
        else if (position.Y > target.Y+5)
        {
            position.Y -= gameTime.ElapsedGameTime.Milliseconds * 0.3f;
        }
        else
        {
            position.Y = target.Y;
        }

        if (position.Y >= 480 - texture.Width)
        {
            position.Y = 480 - 64;
        }

        if (position.X >= 800 - 64)
        {
            position.X = 800 - 64;
        }


        base.Update(gameTime);
    }

    public override void Draw(GameTime gameTime)
    {
        base.Draw(gameTime);
    }

    public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
    {
        foreach (FotStep step in fotstepList)
        {
            Color c = new Color(255, 255, 255, step.Alpha);

            spriteBatch.Draw(fotStepTexture, step.Position, c);
        }

        spriteBatch.Draw(texture, position, Color.White);

        base.Draw(gameTime);
    }
}

Enemy
The enemy class is very similar to the player class.

clip_image020

 

If contains a texture that represents the enemy, a position and a target.

The constructor generates a initial position to the player somewhere outside the play area (so he won’t spawn on the player if unlucky). Using random numbers, we place the enemies between the coordinates –1000,-1000 and 1000,1000. If the player is within 0,0 and 800,480 (the playfield), we place the enemy outside to –32, –32.

In other words, if the player is spawned in the green area below, proceed. If it’s in the red area, put the enemy at –32,-32. This will make a more flow of enemies come from the top-left corner, implementing possibilities for strategies when the player is playing the game clip_image019[1]

clip_image021

The Update() will only move the player to the Target-position and Draw() simply draws the ghost-texture at it’s position.

The listing below is the entire code for the enemy-class:

public class Enemy : Microsoft.Xna.Framework.DrawableGameComponent
{
    Texture2D texture;
    Vector2 position;
    Vector2 target;

    public Vector2 Target
    {
        get { return target; }
        set { target = value; }
    }

    public Vector2 Position
    {
        get { return position; }
        set { position = value; }
    }

    public Enemy(Game game)
        : base(game)
    {
        // TODO: Construct any child components here
        Random rnd = new Random();

        int posX = rnd.Next(-1000, 1000);
        int posY = rnd.Next(-1000, 1000);

        if (posX >= 0 && posX <= 800)
        {
            posX = -32;
        }

        if (posY >= 0 && posY <= 480)
        {
            posY = -32;
        }

        position = new Vector2(posX, posY);
        target = new Vector2();

        LoadContent();
    }

    /// <summary>
    /// Allows the game component to perform any initialization it needs to before starting
    /// to run.  This is where it can query for any required services and load content.
    /// </summary>
    public override void Initialize()
    {
        // TODO: Add your initialization code here

        base.Initialize();
    }

    public void LoadContent()
    {
        texture = Game.Content.Load<Texture2D>("ghost");
        base.LoadContent();
    }

    /// <summary>
    /// Allows the game component to update itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    public override void Update(GameTime gameTime)
    {
        // TODO: Add your update code here
            
        if(position.X < target.X-5)
        {
            position.X += gameTime.ElapsedGameTime.Milliseconds * 0.04f;
        }
        else if(position.X > target.X+5)
        {
            position.X -= gameTime.ElapsedGameTime.Milliseconds * 0.04f;
        }
        else
        {
            position.X = target.X;
        }

        if (position.Y < target.Y-5)
        {
            position.Y += gameTime.ElapsedGameTime.Milliseconds * 0.04f;
        }
        else if (position.Y > target.Y+5)
        {
            position.Y -= gameTime.ElapsedGameTime.Milliseconds * 0.04f;
        }
        else
        {
            position.Y = target.Y;
        }


        base.Update(gameTime);
    }

    public override void Draw(GameTime gameTime)
    {
        base.Draw(gameTime);
    }

    public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(texture, position, Color.White);

        base.Draw(gameTime);
    }
}

Enemy Handler
The enemy handler will handle all the enemies that are active in the game.

clip_image022

It contains a list over the active enemies, if it’s game over or not and a function for Adding new enemies to the list, Clearing the list and checking how many active enemies we have.

The AddEnemy() function adds a new enemy to the list. The Enemy’s constructor randomly places the enemy on the screen, making it easy for us to just add a new enemy to the list, and it will magically get a correct position, start getting updated and drawn.

The Clear() function will remove all enemies in the list, and also set gameOver to false as the Clear() function is called when it’s game over.

The Update()-loop will check if one enemy hits the player. This is done by creating a rectangle around each enemy, and around the player and then checking if any of the enemy-rectangles intersects with the players rectangle. If they do, it’s game over.

The Draw()-loop will simply just iterate through all enemies and draw them.

The listing below is the code for the enemy handler:

public class EnemyHandler : Microsoft.Xna.Framework.DrawableGameComponent
{
    List<Enemy> enemies = new List<Enemy>();
    bool gameOver = false;

    public bool GameOver
    {
        get { return gameOver; }
        set { gameOver = value; }
    }

    public EnemyHandler(Game game)
        : base(game)
    {
        // TODO: Construct any child components here
    }

    public int NumberOfEnemies()
    {
        return enemies.Count;
    }

    public void AddEnemy()
    {
        enemies.Add(new Enemy(Game));
    }
    /// <summary>
    /// Allows the game component to perform any initialization it needs to before starting
    /// to run.  This is where it can query for any required services and load content.
    /// </summary>
    public override void Initialize()
    {
        // TODO: Add your initialization code here

        base.Initialize();
    }

    public void Clear()
    {
        gameOver = false;
        enemies.Clear();
    }

    /// <summary>
    /// Allows the game component to update itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    public void Update(GameTime gameTime, Vector2 target)
    {
        // TODO: Add your update code here
        Rectangle playerRect = new Rectangle((int)target.X, (int)target.Y, 64, 64);

        foreach (Enemy e in enemies)
        {
            e.Target = target;
            e.Update(gameTime);
            Rectangle enemyRect = new Rectangle((int)e.Position.X+7, (int)e.Position.Y+7, 32-7, 32-7);

            if (playerRect.Intersects(enemyRect))
            {
                gameOver = true;
            }
        }

        base.Update(gameTime);
    }

    public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
    {
        foreach (Enemy e in enemies)
        {
            e.Draw(gameTime, spriteBatch);
        }

        base.Draw(gameTime);
    }
}

FotStep
The FotStep represents one footstep for the player class.

clip_image023

 

It contains a position, the alpha channel and if it’s active or not. The alpha value starts at 255 and is slowly reduced to 0. If it hit’s 0, it’s isActive flag is set to false and will be removed the next time the player.Update() function is called.

The Update()-function handles the alpha calculations.

The FotStep does not have a Draw()-function. This is because I didn’t want to load a texture for each foot step. Instead, the texture is loaded at the players class, and just rendered multiple times during the players Draw()-loop.

The code for the FotStep class can be seen below:

public class FotStep : Microsoft.Xna.Framework.GameComponent
{
    Vector2 position;

    public Vector2 Position
    {
        get { return position; }
        set { position = value; }
    }

    int alpha;

    public int Alpha
    {
        get { return alpha; }
        set { alpha = value; }
    }
    bool isActive;

    public bool IsActive
    {
        get { return isActive; }
        set { isActive = value; }
    }

    public FotStep(Game game)
        : base(game)
    {
        position = new Vector2();
        alpha = 255;
        isActive = true;
    }

    /// <summary>
    /// Allows the game component to perform any initialization it needs to before starting
    /// to run.  This is where it can query for any required services and load content.
    /// </summary>
    public override void Initialize()
    {
        // TODO: Add your initialization code here

        base.Initialize();
    }

    /// <summary>
    /// Allows the game component to update itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    public override void Update(GameTime gameTime)
    {
        // TODO: Add your update code here

        alpha -= gameTime.ElapsedGameTime.Milliseconds / 2;
        if (alpha <= 0)
            isActive = false;

        base.Update(gameTime);
    }
}


HighscoreEntry
The HighscoreEntry class contains one entry to the Online high score system in Mogade. It requires you to have added a reference to the Mogade-library:

clip_image024

clip_image025

 

The class is very simple. It contains the level name (we just have one here), the score and the username. These fields are required by mogade.

Remember to add the correct using statements (Mogade, Mogade.WindowsPhone) in this class as the property Score in the constructor is a class in Mogade.

The complete listing for the ScoreboardEntry can be seen below:

public class ScoreboardEntry
{
    public string username;
    public string level;
    public int points;

    public ScoreboardEntry(Score score)
    {
        username = score.UserName;
        level = score.Data;
        points = score.Points;
    }
}

MogadeHelper
The MogadeHelper-class is a class that is used to simplify the implementation of Mogade for online highscore.

clip_image026

It contains an Enum that is used to select what highscore-list will be used by Mogade. You can have more than one, but in this game, we simply just use one: Main.

It contains a gameKey and a secret key that is received when you add a new game on your account at the Mogade website. It also creates an instance of the MogadeClient and contains a list of our ScoardboardEntry. Each leaderboard got a unique key, so when creating a new game at Mogade, make sure to add a new leaderboard to the game, name it (follow the enum), in this case Main, and copy-paste the generated Leaderboard key.

The complete code can be seen below:

public enum Leaderboards
{
    Main = 1,
}
public class MogadeHelper
{
    //Your game key and game secret
    private const string _gameKey = "4f30358d563d8a4acf00000d";
    private const string _secret = "PnW5:VdK=ENl]gpb?nS3q[]rIdyCL[]nluw";
    private static readonly IDictionary<Leaderboards, string> _leaderboardLookup = new Dictionary<Leaderboards, string>
    {
        {Leaderboards.Main, "4f3036d5563d8a4c25000010"}
    };

    public static string LeaderboardId(Leaderboards leaderboard)
    {
        return _leaderboardLookup[leaderboard];
    }

    public static IMogadeClient CreateInstance()
    {
        //In your own game, when you are ready, REMOVE the ContectToTest to hit the production mogade server (instead of testing.mogade.com)
        //Also, if you are upgrading from the v1 library and you were using UserId (or not doing anything), you *must* change the UniqueIdStrategy to LegacyUserId
        MogadeConfiguration.Configuration(c => c.UsingUniqueIdStrategy(UniqueIdStrategy.UserId));
        return MogadeClient.Initialize(_gameKey, _secret);
    }
}

 

Making it all work together, revisiting the main-game class
Let’s go back to the main-class of the game and look at the logic.

clip_image027

 

The game starts by Initialize() – the enemy handler and the player is initialized, three new enemies are added(so the game starts with 3 active enemies) and Loads the Loaderboards.

The Update()-loop constantly checks it the player is pressing the backbutton – if so, return to the main menu if in-game, or exit if you are in the main-menu.

It also keeps track of the highscore timer, reducing it every Update(). If it’s above 0, show the highscore, else, hide it.

Then it checks for input, if yes, update the players position.

It also checks if it’s game over by checking the enemy handlers game over property. If it’s game over, save the score, set the highscore timer to 6000 (the high scores will then be visible for 6 seconds). It then clears the enemy list in the enemy hanlder (and setting the game over variable to false), adds 3 new starter enemies and updates the local highscore and saves it to the disc if it’s a new high score… aaand resets the players score to 0.

Mogade got an async function SaveScore. It tries to save the score and calls the ScoreResponseHandler when done.

Mogade.SaveScore(MogadeHelper.LeaderboardId(Leaderboards.Main), userscore, ScoreResponseHandler);

It also contains a GetLarderboard function, also async, that tries to load a Leaderboard and calls LoaderboardReceived when done.

Mogade.GetLeaderboard(MogadeHelper.LeaderboardId(Leaderboards.Main), scope, page, r => LeaderboardReceived(r));

The Draw()-loop draws the floor, player, enemies, renders text and shows the highscore.

The entire Game1 class can be seen below:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
using System.IO.IsolatedStorage;
using WindowsPhoneGame135.GameClasses;
using Mogade.WindowsPhone;
using Mogade;

namespace WindowsPhoneGame135
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Player player;
        EnemyHandler enemyHandler;
        int scoreTimer = 0;
        int highScore = 0;
        bool inMainMenu = true;
        bool nameTyped = false;
        bool showHighscore = true;
        int showHighscoreTimer = 6000;

        bool lastRoundWasHighscore = false;

        Texture2D floor;
        Texture2D mainMenu;

        SpriteFont font;

        List<ScoreboardEntry> sblist = new List<ScoreboardEntry>();


        public IMogadeClient Mogade { get; private set; }


        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            // Frame rate is 30 fps by default for Windows Phone.
            TargetElapsedTime = TimeSpan.FromTicks(333333);

            // Extend battery life under lock.
            InactiveSleepTime = TimeSpan.FromSeconds(1);

            player = new Player(this);
            enemyHandler = new EnemyHandler(this);

            try
            {
                highScore = (int)IsolatedStorageSettings.ApplicationSettings["HighScore"];
            }
            catch
            {
                IsolatedStorageSettings.ApplicationSettings.Add("HighScore", highScore);
            }

            try
            {
                username = (string)IsolatedStorageSettings.ApplicationSettings["Username"];
            }
            catch
            {
                IsolatedStorageSettings.ApplicationSettings.Add("Username", username);
            }

            Mogade = MogadeHelper.CreateInstance();
            Mogade.LogApplicationStart();
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            enemyHandler.Initialize();
            player.Initialize();

            // Add 3 players to start with
            enemyHandler.AddEnemy();
            enemyHandler.AddEnemy();
            enemyHandler.AddEnemy();

            LoadLeaderboard(LeaderboardScope.Overall, 1);

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            player.LoadContent();
            font = Content.Load<SpriteFont>("SpriteFont1");

            floor = Content.Load<Texture2D>("floor");

            mainMenu = Content.Load<Texture2D>("mainmenu");

            // TODO: use this.Content to load your game content here
        }

        bool IsGameOver()
        {
            return enemyHandler.GameOver;
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            {
                if (inMainMenu)
                {
                    this.Exit();
                }
                else
                {
                    enemyHandler.Clear();
                    enemyHandler.AddEnemy();
                    enemyHandler.AddEnemy();
                    enemyHandler.AddEnemy();

                    player.Score = 0;
                    inMainMenu = true;
                }
            }


            if (inMainMenu)
            {
                TouchCollection touchStateMain = TouchPanel.GetState();
                if (touchStateMain.Count > 0)
                {
                    object stateObj;
                    try
                    {
                        Guide.BeginShowKeyboardInput(PlayerIndex.One, "Enter name", "Please enter a name for the scoreboard (Minimum 3 and no longer than 20 characters)", username, GetText, stateObj = (object)"GetText for Input PlayerOne");
                    }
                    catch (Exception ex)
                    {
                    }

                    inMainMenu = false;
                }
                return;
            }

            if (!nameTyped)
                return;

            showHighscoreTimer -= gameTime.ElapsedGameTime.Milliseconds;
            if (showHighscoreTimer < 0)
            {
                showHighscore = false;
            }
            else showHighscore = true;

            scoreTimer += gameTime.ElapsedGameTime.Milliseconds;

            if (scoreTimer >= 1000)
            {
                scoreTimer = 0;
                player.Score += enemyHandler.NumberOfEnemies();
                enemyHandler.AddEnemy();
            }
            
            TouchCollection touchState = TouchPanel.GetState();
            foreach (TouchLocation t in touchState)
            {
                player.Target = t.Position-(new Vector2(32,32));
            }

            enemyHandler.Update(gameTime, player.Position);
            player.Update(gameTime);

            if (IsGameOver())
            {
                SaveScore();

                showHighscoreTimer = 6000;
                player.LastScore = player.Score;

                enemyHandler.Clear();
                enemyHandler.AddEnemy();
                enemyHandler.AddEnemy();
                enemyHandler.AddEnemy();

                if (highScore < player.Score)
                {
                    lastRoundWasHighscore = true;
                    highScore = player.Score;
                    try
                    {
                        IsolatedStorageSettings.ApplicationSettings["HighScore"] = highScore;
                    }
                    catch
                    {
                        IsolatedStorageSettings.ApplicationSettings.Add("HighScore", highScore);
                    }
                }
                else lastRoundWasHighscore = false;

                player.Score = 0;
            }

            base.Update(gameTime);
        }

        string username = "Player";
        private void GetText(IAsyncResult result)
        {
            try
            {
                string resultString = Guide.EndShowKeyboardInput(result); ;

                if (resultString != null)
                {

                    if (resultString.Length > 20)
                    {
                        resultString = resultString.Remove(20);
                    }

                    if (resultString.Length < 3)
                    {
                        inMainMenu = true;
                        return;
                    }
                    username = resultString;
                }
                else
                {
                    username = "Player";
                    inMainMenu = true;
                }
            }
            catch (Exception ex)
            {
                username = "Player";
                inMainMenu = true;
                return;
            }

            try
            {
                IsolatedStorageSettings.ApplicationSettings["Username"] = username;
            }
            catch
            {
                IsolatedStorageSettings.ApplicationSettings.Add("Username", username);
            }

            nameTyped = true;

            sblist.Clear();
        }

        public void SaveScore()
        {
            var userscore = new Score { Data = "1", Points = player.Score, UserName = username };
            Mogade.SaveScore(MogadeHelper.LeaderboardId(Leaderboards.Main), userscore, ScoreResponseHandler);
        }


        private void ScoreResponseHandler(Response<SavedScore> r)
        {
            //scoreboardRanks = string.Format("Daily Rank: {0}\nWeely Rank: {1}\nOverall Rank {2}", 0, 0, 0);
            if (!r.Success)
            {
                if (Guide.IsVisible == false)
                {
                    //Guide.BeginShowMessageBox("Error", "Unable to retreive data from the server please check your network connection", new string[] { "OK" }, 0, MessageBoxIcon.Error, null, null);
                    return;
                }
            }
            else
            {
                //scoreboardRanks = string.Format("Daily Rank: {0}\nWeely Rank: {1}\nOverall Rank {2}", r.Data.Ranks.Daily, r.Data.Ranks.Weekly, r.Data.Ranks.Overall);
            }

            LoadLeaderboard(LeaderboardScope.Overall, 1);

        }

        private void LoadLeaderboard(LeaderboardScope scope, int page)
        {
            sblist.Clear();
            Mogade.GetLeaderboard(MogadeHelper.LeaderboardId(Leaderboards.Main), scope, page, r => LeaderboardReceived(r));
        }

        private void LeaderboardReceived(Response<LeaderboardScores> response)
        {
            if (!response.Success)
            {
                if (Guide.IsVisible == false)
                {
                    //Guide.BeginShowMessageBox("Error", "Unable to retreive data from the server, please check your network connection", new string[] { "OK" }, 0, MessageBoxIcon.Error, null, null);
                    return;
                }
            }
            else
            {
                for (var i = 0; i < response.Data.Scores.Count; ++i)
                {
                    sblist.Add(new ScoreboardEntry(response.Data.Scores[i]));
                }
            }
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            if (inMainMenu || !nameTyped)
            {
                spriteBatch.Begin();
                spriteBatch.Draw(mainMenu, new Vector2(), Color.White);
                spriteBatch.End();
            }
            else
            {
                spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied);
                spriteBatch.Draw(floor, new Vector2(), Color.White);
                enemyHandler.Draw(gameTime, spriteBatch);
                player.Draw(gameTime, spriteBatch);
                spriteBatch.DrawString(font, "Current score: " + player.Score.ToString(), new Vector2(80, 10), Color.White);
                spriteBatch.DrawString(font, "Last score: " + player.LastScore, new Vector2(80, 35), Color.White);

                if (showHighscore)
                {
                    spriteBatch.DrawString(font, "-- Local highscore ----", new Vector2(80, 60), Color.White);
                    spriteBatch.DrawString(font, "  " + highScore.ToString(), new Vector2(80, 85), Color.White);

                    if (sblist.Count > 0)
                    {
                        spriteBatch.DrawString(font, "-- Online highscore ----", new Vector2(80, 110), Color.White);
                        spriteBatch.DrawString(font, "  " + sblist[0].username + " / " + sblist[0].points, new Vector2(80, 135), Color.White);
                    }

                    if (player.LastScore > 0)
                    {
                        if (lastRoundWasHighscore)
                        {
                            spriteBatch.DrawString(font, "  " + "NEW HIGHSCORE!", new Vector2(100, 160), Color.Green);
                        }
                        else
                        {
                            spriteBatch.DrawString(font, "Only " + (highScore - player.LastScore) + " points left, you can do it!", new Vector2(80, 160), Color.Red);
                        }
                    }
                }
                spriteBatch.End();
            }

            base.Draw(gameTime);
        }
    }
}

And there you go, a fully working complete game. The game is on the Windows Phone Marketplace and can be seen here:

http://www.windowsphone.com/nb-no/apps/d2115469-3e6a-4bcd-9071-b7c1cf6b78b3

Screenshots of the game we just made

clip_image028

clip_image029clip_image030

Download the source

Download: Source for Windows Phone 7.1 and XNA 4.0

Posted in Tutorial, Windows Phone, XNA | 12 Comments

XNA 4.0 Game with Source: Mage Defender Deluxe

Mage Defender Deluxe was created as a remake during a workshop/presentation I had. It was made pretty fast but I decided to share the source anyways.

In the game, you will need to defend your castle at all cost. Shoot the enemies that move towards you with multiple spells and strategies to take them out one by one. After each wave, you will be able to visit the shop to upgrade abilities, buy new spells, mana/health potions and upgrade your castle (Gameplay video can be found below).

The original game was a DOS game made by Dark Codex in 1999 and looked like this:

The new version contains more enemies, spells, upgradable castle, skill points, attributes, homing spells, controllable spells, many enemies with basic AI and much more.
The game works with the XBox360 controller, just connect it to your PC and play.

You can find the source for the game at GitHub:

https://github.com/petriw/MageDefenderDeluxe/downloads

Click the “Download as zip” button to download the latest source.

Posted in Gaming, XNA | 5 Comments

XNA 4.0 Shader Programming #6–Simple Ocean

image

Welcome back to my tutorial. Today, we are not going to learn anything new (almost), but instead put together a scene that uses some different shaders in order to see how powerful shaders can be.

The island

The island is a 3D model created by a friend of mine, Espen Nordahl. The island use a normal map and the same shader covered in tutorial 4, to make the model look more detailed.

The ocean

The ocean is a plane built up by many vertexes. We use the technique described in tutorial 5 (Deform shader) to create the ocean waves, and using normal map from tutorial 4 to make tinier waves on top of this. I used one normal maps, but sampling in in two different locations and moving each in different directions to make small detail waves ripple on the waves:

Normal = (Normal + Normal2)/2;

Here I take the two normals, adding them together and using the avg. of these normals when calculating lighting, diffuse and specular( Tutorial 3 ).

I also move the texture coordinates on the color map, making it look like there is some stream in the water.

Implementing the shader

Shouldn’t be anything new. The ocean is very large so I started the shader by multiplying the texture-coordinate with 20, making the texture repeat 20 times in both the X and Y direction. I then sample the color for the given pixel.

Next I move the texture-coordinate a bit before sampling a normal, and store it in normalMap. I do the same again after sampling in the other direction and store it in normalMap2.

Then I take the average normal and use that as the normal when calculating the rest of the lighting.

I also hacked a bit with the specular-calculations so it will be visible on small waves.

Remember, this is a quick and dirty way to create ocean. It’s quite cheap and easy to implement and looks OK when water is not in focus. In a later tutorial I will show how you can create a much better, but a lot more expensive water/ocean shader.

Let’s look at the code..

// The Pixel Shader
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
	input.TexCoord.x = input.TexCoord.x*20.0f + sin(TotalTime*3+10)/256;
	input.TexCoord.y = input.TexCoord.y*20.0f;

	float4 color = tex2D(ColorMapSampler, input.TexCoord);

	input.TexCoord.y += (sin(TotalTime*3+10)/256)+(TotalTime/16);
	float3 normalMap = 2.0 *(tex2D(NormalMapSampler, input.TexCoord)) - 1.0;

	input.TexCoord.y -= ((sin(TotalTime*3+10)/256)+(TotalTime/16))*2;
    float3 normalMap2 =(2 * (tex2D(NormalMapSampler, input.TexCoord))) - 1.0; 

	normalMap = (normalMap + normalMap2) / 2;

	normalMap = normalize(mul(normalMap, input.WorldToTangentSpace));
	float4 normal = float4(normalMap,1.0);

	float4 diffuse = saturate(dot(-LightDirection,normal));
	float4 reflect = normalize(2*diffuse*normal-float4(LightDirection,1.0));
	float4 specular = pow(saturate(dot(reflect,input.View)),28);

    float4 finalColor =  color * AmbientIntensity + 
			color * DiffuseIntensity * diffuse + 
			specular*250;

	finalColor.a = 0.3f;
	return finalColor;
}

image

download Download Source (XNA 4.0)

Posted in Tutorial, XNA, XNA Shader Tutorial | 5 Comments