Monday, August 23, 2010

A Developer Game Console plugin for XNA Games

This is a project I was recently working on and it's a developer console for XNA games. It can easily be added to any XNA game and modified

Screenshots


Quick Start

Add the compiled XnaGameConsole dll as a reference and initialize the GameConsole:

GameConsole console = new GameConsole(this, spriteBatch); // where `this` is your `Game` class

By default, the console is opened with the ` (OemTilde key) but this, amongst other settings, can be changed via the Options property.

You can then add commands and customize the console.

Features


Source and Downloads

You can checkout a copy of the source at the Google Code page: http://code.google.com/p/xnagameconsole/

The compiled dll of XNAGameConsole can also be downloaded from here: http://code.google.com/p/xnagameconsole/downloads/list

Notes

Clipboard Support

The console supports pasting via CTRL+V but this requires the game to be running in a Single Threaded Apartment.

To do this, add the STAThread attribute to the entry point of your game:
static class Program
{
    /// 
    /// The main entry point for the application.
    /// 
    [STAThread]
    static void Main(string[] args)
    {
        using (Game1 game = new Game1())
        {
            game.Run();
        }
    }
}

Adding Commands

There are currently 2 ways how to add custom commands to the console:

1. Using IConsoleCommand

Console commands can be built via the IConsoleCommand interface.
The following is an example of a command:
class MovePlayerCommand:IConsoleCommand
{
    public string Name
    {
        get { return "move"; }
    }

    public string Description
    {
        get { return "Moves the player"; }
    }

    private Player player;
    public MovePlayerCommand(Player player)
    {
        this.player = player;
    }

    public string Execute(string[] arguments)
    {
        var newPosition = new Vector2(float.Parse(arguments[0]), float.Parse(arguments[1]));
        player.Position = newPosition;
        return "Moved player to " + newPosition;
    }
}

Such a command can then be added to the console like such:
console.AddCommand(new MovePlayerCommand(player));

2. Via a Func<string, string>

Commands can also be added without using IConsoleCommand, like the following:

console.AddCommand("rotRad", a =>
                                 {
                                     var angle = float.Parse(a[0]);
                                     player.Angle = angle;
                                     return String.Format("Rotated the player to {0} radians", angle);
                                 }, "Rotates the player");
The advantage of adding a command like this is that in the Func<> you have access to the variables outside the closure.

Writing to the Console

You can dynamically write to the console via the WriteLine method:
console.WriteLine("Writing from the code");

Console Options

The console can be customized with a number of options. These options can be accessed through the GameConsoleOptions class.

To set the initial options, instantiate a new GameConsoleOptions class, set your desired options and pass it through one of the GameConsole's overloaded constructors:

GameConsoleOptions options = new GameConsoleOptions { FontColor = Color.Crimson, Prompt = ">", BackgroundColor = Color.DarkGreen };
GameConsole console = new GameConsole(this, spriteBatch, options);

The options can also be changed dynamically through the Options property of GameConsole:
console.Options.Height = 200;

The following are the current available options:

ToggleKey (Microsoft.Xna.Framework.Input.Keys)

Gets or sets the key that is used to show / hide the console

Default: Keys.OemTilde

BackgroundColor (Microsoft.Xna.Framework.Graphics.Color)

Gets or sets the color for the background of the console

Default: new Color(0, 0, 0, 125)

FontColor (Microsoft.Xna.Framework.Graphics.Color)

Sets the same specified color for all of the following properties:
  • BufferColor
  • PastCommandColor
  • PastCommandOutputColor
  • PromptColor
  • CursorColor

Default: Color.White

BufferColor (Microsoft.Xna.Framework.Graphics.Color)

Gets or sets the color of the text in the buffer

Default: Color.White

PastCommandColor (Microsoft.Xna.Framework.Graphics.Color)

Gets or sets the color of the past commands

Default: Color.White

PastCommandOutputColor (Microsoft.Xna.Framework.Graphics.Color)

Gets or sets the color of the past command outputs

Default: Color.White

PromptColor (Microsoft.Xna.Framework.Graphics.Color)

Gets or sets the color of the prompt

Default: Color.White

CursorColor (Microsoft.Xna.Framework.Graphics.Color)

Gets or sets the color of the cursor

Default: Color.White

AnimationSpeed (float)

Gets or sets the speed by which the console opens/closes

Default: 1

CursorBlinkSpeed (float)

Gets or sets the speed by which the cursor blinks

Default: 0.5f

Height (int)

Gets or sets the height of the console, in pixels

Default: 300

Prompt (string)

Gets or sets the prompt

Default: $

Cursor (char)

Gets or sets the cursor display character

Default: _

Padding (int)

Gets or sets the padding, in pixels, between the border of the console and the inner text

Default: 30

Margin (int)

Gets or sets the margin, in pixels, between the left and right side of the screen to the console border

Default: 30

OpenOnWrite (bool)

Gets or sets a boolean value that indicates whether to open the console, if it's closed, when writing to the console

Default: true

Font (Microsoft.Xna.Framework.Graphics.SpriteFont)

Gets or sets the font that is used in the console

Default: Consolas, Regular, 11pt


Friday, August 20, 2010

Drawing lines and rectangles in XNA

In my last project, I needed a way to draw a grid with lines in XNA but I had some trouble in actually finding a way how to draw a simple line.  In this post, I will explain how to accomplish this.

First, create a new Texture2D with size: 1x1

Texture2D pixel = new Texture2D(GraphicsDevice, 1, 1, 1, TextureUsage.None, SurfaceFormat.Color);

You can access the GraphicsDevice from your Game (game.GraphicsDevice)

We then set the colour of this 1x1 texture to White. This allows us to choose what colour it should be drawn later on.

pixel.SetData(new[] { Color.White });

Now we can draw this texture by specifying a Rectangle holding the position and size:

spriteBatch.Draw(pixel, new Rectangle(10, 20, 100, 50), Color.DarkGreen);

The above snippet will draw a dark green 100x50 rectangle at {X: 10, Y: 20}

Saturday, August 14, 2010

Handling single keyboard strokes in XNA

In a game, there are times where you need to handle single key strokes, rather than repeating ones. Examples are of a Pause key or a 'Press any key to start' scenario.

Unfortunately, XNA does not directly offer this functionality.

Therefore I wrote this very simple event-driven class that does exactly that:

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace XNASingleStroke
{
    public class SingleKeyStroke : GameComponent
    {
        public event EventHandler<KeyDownEventArgs> KeyDown = delegate { };

        private int pressedKeys;
        public SingleKeyStroke(Game game) : base(game) { }

        public override void Update(GameTime gameTime)
        {
            var pressed = Keyboard.GetState().GetPressedKeys();
            var keys = 0;
            foreach (var key in pressed)
            {
                var num = 1 << (int)key;
                keys |= num;
                if ((pressedKeys & num) != num)
                {
                    KeyDown(this, new KeyDownEventArgs(key));
                }
            }
            pressedKeys = keys;
            base.Update(gameTime);
        }
    }
}

And this is KeyDownEventArgs that's used in the event handler:

using System;
using Microsoft.Xna.Framework.Input;

namespace XNASingleStroke
{
    public class KeyDownEventArgs:EventArgs
    {
        public Keys Key { get; set; }
        public KeyDownEventArgs(Keys key)
        {
            Key = key;
        }
    }
}

Usage is also very simple:

protected override void Initialize()
{
    // ...
    SingleKeyStroke singleStroke = new SingleKeyStroke(this); //where 'this' is your Game
    singleStroke.KeyDown += (o, e) => {
        switch (e.Key)
        {
            case Keys.Space:Console.WriteLine("Space was pressed"); break;
            case Keys.Enter:Console.WriteLine("Enter was pressed"); break;
        }
   };
   Components.Add(singleStroke);
}

Friday, August 6, 2010

A simple menu for a Gosu game in ruby

For The Lost Battle, I wrote a very simple menu system that would allow me to add in-game menus. This allowed me to instantiate a menu and then add items to it and let the menu take care of the drawing.

The menu also takes care of notifying me when an item has been clicked via a callback, and also offsets the position of the item on hover.

The following is the basic gist of how it can be used:

@menu = Menu.new(self) #instantiate the menu, passing the Window in the constructor

@menu.add_item(Gosu::Image.new(self, "item.png", false), 100, 200, 1, lambda { self.close }, Gosu::Image.new(self, "item_hover.png", false))
@menu.add_item(Gosu::Image.new(self, "item2.png", false), 100, 250, 1, lambda { puts "something" }, Gosu::Image.new(self, "item2_hover.png", false))

The arguments of add_item are the image showed when the item is in its normal state, the x position, y position, z-order, the callback that will be invoked when that item is clicked and finally an optional parameter of an image that is used when the item is hovered upon.

Then hook to the button_down event of your Window, and inform the menu that the mouse has been clicked:

def button_down (id)
    if id == Gosu::MsLeft then
        @menu.clicked
    end
end

Finally, just draw and update the menu in the hooked methods:

def update
    @menu.update
end

def draw
    @menu.draw
end

I made a simple demo of this and you can download it from here.


The following is the source of the two aforementioned classes:

class Menu
    def initialize (window)
        @window = window
        @items = Array.new
    end

    def add_item (image, x, y, z, callback, hover_image = nil)
        item = MenuItem.new(@window, image, x, y, z, callback, hover_image)
        @items << item
        self
    end

    def draw
        @items.each do |i|
            i.draw
        end
    end

    def update
        @items.each do |i|
            i.update
        end
    end

    def clicked
        @items.each do |i|
            i.clicked
        end
    end
end

class MenuItem
    HOVER_OFFSET = 3
    def initialize (window, image, x, y, z, callback, hover_image = nil)
        @window = window
        @main_image = image
        @hover_image = hover_image
        @original_x = @x = x
        @original_y = @y = y
        @z = z
        @callback = callback
        @active_image = @main_image
    end

    def draw
        @active_image.draw(@x, @y, @z)
    end

    def update
        if is_mouse_hovering then
            if !@hover_image.nil? then
                @active_image = @hover_image
            end

            @x = @original_x + HOVER_OFFSET
            @y = @original_y + HOVER_OFFSET
        else 
            @active_image = @main_image
            @x = @original_x
            @y = @original_y
        end
    end

    def is_mouse_hovering
        mx = @window.mouse_x
        my = @window.mouse_y

        (mx >= @x and my >= @y) and (mx <= @x + @active_image.width) and (my <= @y + @active_image.height)
    end

    def clicked
        if is_mouse_hovering then
            @callback.call
        end
    end
end

Note that both the code and the method in general can be greatly improved and refactored. Just take a look at the code and modify it as you see fit :)

The Lost Battle - a ruby multiplayer game with Gosu and Chipmunk

This is a game I recently built to learn ruby in conjunction with the Gosu game engine and the Chipmunk physics engine.

Screenshots


Source

The source of the game can be found at it's google code page here: http://code.google.com/p/the-lost-battle/

You can checkout using Mercurial with: hg clone https://the-lost-battle.googlecode.com/hg/ the-lost-battle

Prerequisites

For the game to run, it requires the following:

Running the game

To launch the game, fire up your terminal, navigate to the game's directory and run main.rb with the ruby interpreter: ruby main.rb
Or ruby1.9 main.rb if you have multiple versions of ruby installed.

After you click start game on the main screen, you will see the player selection, similar to the second screenshot above. If you do not have any joysticks connected, you will only be shown 1 player slot and the game will be unable to start since it requires 1 < players <= 4. Therefore, with a single joystick connected, you will be able to play a 2 player game...one player with the keyboard+mouse and the other player with the controller. Make sure that the joysticks are connected before you go into the player selection screen because the amount of player slots that the screen will display depends on the number of connected controllers.


Additional Notes

Joystick support
The game requires at least one joystick to be connected. Since I had very limited time in building this game, I only pre-set configuration for PS3 controllers. You can try and play it with other controllers but it's unlikely that the usable buttons will be comfortable to use, and the axis may not even be configured correctly.

If you want to use a different controller, you need to add a new configuration in the gamestate.rb file, similar to the PS3 one:
PS3_CONTROLLER  = GamepadConfig.new({horizontal: 0, vertical: 1}, {:horizontal => 2, :vertical => 3}, 11, 9)
Then use your new configuration instead of PS3_CONTROLLER when adding a new player to the player list:
@player_list.add(p.name, rand(window.width), rand(window.height), p.controller_type == :joystick ? GamePad.new(p.joystick_path, PS3_CONTROLLER) : Mouse.new(window), p.vehicle, p.crosshair)

To find the correct button-mappings, use jstest.

Further info
I now plan to make other individual posts explaining some of the stuff I did, like Gosu Joystick support on linux, Health Bars with RMagick, Parallax scrolling, the Menu system and others.


Wednesday, July 28, 2010

Typocalypse - A typing game in XNA

Just a couple of weeks ago, Marc Tanti and I decided to check out Microsoft's XNA and what better way to do it than building a game in about 24 hours of sleepless coding and caffeine intake ?

Screenshots




Source

You can view the source here: http://code.google.com/p/typocalypse/

At the Google Code page, there is both the source and an executable installation file (compiled with WiX XNA Installer)

You can checkout with: hg clone https://typocalypse.googlecode.com/hg/ typocalypse

Some notes...

Audio
The in-game music is the theme from the Amiga game The Great Giana Sisters, overlayed with some sound effects recorded by Marc Tanti. All of the other sound effects in the game where also recorded by Marc Tanti.

Text Input in XNA
Since XNA does not provide native support for text input, I used global hooks with these set of classes developed by George Mamaladze to capture keystrokes.

Prefix Matching
Prefix matching was done via a Trie and this is described in more detail at Marc Tanti's post.


Wednesday, July 14, 2010

Managing a Google Code project with Mercurial

As a recent convert to Mercurial, I started my last Google Code Project using Mercurial instead of Subversion. In this post I won't go into details as regards SVN vs. Mercurial, but to SVN users I suggest you check out Mercurial; starting with Joel Spolsky's tutorial and then moving on to the Mercurial: The Definitive Guide online book.

Prerequisites

First you need to have Mercurial installed. To download, visit the Mercurial Downloads page and follow the instructions for your respective Operating System.

After installing Mercurial, fire up your terminal and type: hg version
This should display something similar to the following:

Mercurial Distributed SCM (version 1.4.3)

Copyright (C) 2005-2010 Matt Mackall  and others
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

If you see that on your terminal, then Mercurial has been installed successfully and you're ready to continue.

Setting up a default user

Thanks to Scott Willeke who reminded me about this in the comments

Once Mercurial is successfully installed, you need to setup a default user that will be responsible for commiting changes to the repository. If you're on Windows, create a Mercurial.ini file or a .hgrc file in your %USERPROFILE% folder and if you're on a Linux create an .hgrc file in your $HOME directory.

On my windows box, I created a Mercurial.ini file in C:\Users\Andreas\Mercurial.ini.

Once you create this config file, add the following in it:

[ui]
username = Your Name <your_email_address>

You can add many other options to this config file (more details can be found here) but for the moment, this is sufficient.

Initiating the Project


To create the project on the hosting page, visit Google Code's Create Project page. When choosing a Version Control system, make sure that you choose Mercurial:


For the purposes of this demonstration, let's assume that our project name is myhgproject.

After setting all the required project details, you will be taken to your project's home page; your url takes this form: http://code.google.com/p/myhgproject/

Now we need to set up our local repository on the machine and push everything to the online repository. To do this, first visit the Source page of your project (http://code.google.com/p/myhgproject/source/checkout) and copy the clone command: hg clone https://myhgproject.googlecode.com/hg/ myhgproject

This command will make a clone of the online repository on your machine:


So at the moment, we have our project directory (not revisioned) ~/myproject and a new directory ~/myhgproject that Mercurial created for us.

Now we need to move our project files from the directory we used to work in to the new folder ~/myhgproject:


With this, we now have everything in our new folder and are now ready to add these new files to our local repository:


The hg add command adds the files to the repository on the next commit, so the next step is to commit these files to our local repository:


Note that up till now, we still haven't pushed our files to the online repository. We have only committed our files to our local repository online. Uploading these files is the next step.

To do this, you must first retrieve your GoogleCode.com password from this page: https://code.google.com/hosting/settings. Now using your google username and that password, we can push our files to the online repository:

For the purposes of this demonstration, let's assume my password is: AbCdEfGHiJ12


The files have now been pushed to the online repository.

Working on the project

Now that everything has been pushed to the online repository, you are now ready to start working normally on your project.

Your average routine will be working on some files, committing to the local repository with hg com -m "your commit message" and then when you have a working copy, push to the online repository with hg push.

Pushing without entering the url

Note that to avoid typing in your login credentials and the url every time you want to push, you can add these details in the hgrc file located in the .hg directory of your project.

Open up .hg/hgrc and add the following lines (obviously change according to your project):

[paths]
default = https://andreas:AbCdEfGHiJ12@myhgproject.googlecode.com/hg/

If you don't have the hgrc file, you can manually add it yourself.

Note that now you must now make sure that your hgrc file is well protected since it contains your login details in plain text.