Micro Framework Invaders...

Back in Feb this year I was presenting at TechDays in Paris/France with Olivier Bloch and Dave Baker - we had a number of embedded sessions which covered .NET Micro Framework, Windows CE, Windows XP Embedded, Windows Embedded for Point of Service, and even an introduction to Microsoft Robotics Studio!

The .NET Micro Framework v2.0 SDK was being released the following week at Embedded World in Germany, so we wanted to give a sneak peek at how cool the .NET Micro Framework is, one way to do that is to create a code demo that shows something like a video game (kinda) running on a Micro Framework reference board. This is an invaders 'like' game, using the D-Pad buttons on the NETMF reference board to control the movement of the base and fire missiles, and using the Micro Framework graphics APIs to display the bases, aliens, missiles and explosions (which actually look more like cheese Pizza's that massive deadly explosions in deep space!). The application only took about two hours to get up and running on a flight from Seattle to Paris (through CPH/Denmark), which was perfect given that I was using a Dell M1710 laptop which only had about two hours of battery, and the laptop tripped the in-seat power whenever I plugged it in (Doh!). So I only had about two hours of coding time - I used a combination of emulator (runs on the desktop) and Freescale iMX reference board (connects over USB) to write and test the application.

Invaders

Here's a screen shot of the application running in the .NET Micro Framework emulator, screen resolution of 320x240. Yes, the sprites are too big, and there aren't any bases to hide under, but this was done in two hours, so what do you expect! smile_tongue

So, what are the elements needed for the game?

  • Graphics - how do we get bitmaps into the application and display them on the LCD/Emulator
  • Timer - we need a timer tick to update the display surface with the new position of the base, invaders, missiles, and scrolling star field (no SetTimer and WM_TIMER handlers here!)
  • Key pad input - we need interrupt handlers for the D-Pad buttons to handle "base left", "base right", and FIRE! (anyone remember using Inkey$ back in the Gee Whizz Basic days? to intercept keyboard input and at the same time keep the live action game running)
  • Random Number - we need to generate random bombs for our invaders to drop!

That's about all we need to get a game up and running - of course we need the game logic, I may leave this up to the reader to implement </g>

Let's jump in and look at each of the elements...

1. Graphics

The .NET Micro Framework doesn't expose Win32 API or Forms based programming, so there's a learning bump to get started, the principles are actually pretty straight forward.

Bitmaps: I'm using GIF files for the images in the game, these are included in the application resources.

Here's how I'm loading the background starfield image into the application.

First I have a static Bitmap called Stars

 static Bitmap Stars;

I have a function in the application called LoadImages() which is called from the CreateWindow() function of the application, I use this function to load all of the images needed in the game - here's how I'm loading the Stars image.

Stars = Resources.GetBitmap(Resources.BitmapResources.Stars);

Once we have a bitmap we need some way to display it. Let's take a look at how the application gets initialized, how we determine the size of our display surface and then how we push an image out to the display.

Here's the entry point for the application, Main, this is where we create our application window.

     public class Program : Microsoft.SPOT.Application
    {
        public static void Main()
        {
            Program myApplication = new Program();

            Window mainWindow = myApplication.CreateWindow();
  
 Here's the CreateWindow function - there are a number of interesting things to see here...
     public Window CreateWindow()
    {
        // Create a window object and set its size to the
        // size of the display.
        mainWindow = new Window();
        mainWindow.Height = SystemMetrics.ScreenHeight;
        mainWindow.Width = SystemMetrics.ScreenWidth;
        LCD = new Bitmap(mainWindow.Width, mainWindow.Height);

        LoadImages();
        CreateInvaders();

        AutoResetEvent autoEvent = new AutoResetEvent(false);
        TimerCallback timerDelegate = new TimerCallback(TimerTick);

        Timer stateTimer = new Timer(timerDelegate, autoEvent, 1000, 40);

        // Set the window visibility to visible.
        mainWindow.Visibility = Visibility.Visible;

        // Attach the button focus to the window.
        Buttons.Focus(mainWindow);

        return mainWindow;
    }

First, we create a new Window, and then use SystemMetrics to get the width and height of the window, then we create a bitmap (which I've called LCD) to be the width and height of the window we've just created - the rest of the code in the CreateWindow function creates a timer, sets the window to be visible and then returns - The interesting thing here is the creation of the Bitmap, "LCD", which we will use to Blt images from our game.

So, once we have a screen sized bitmap we need to push images to it and then force the bitmap to be drawn on the physical screen (you can think of this as being an off screen bitmap, similar to the way you would create a double buffered bitmap when using BitBlt in Win32 programming).

Here's the clever bit (or clever BitBlt </g>) - we can just draw to the Bitmap using LCD.DrawImage(), or LCD.DrawLine() (see below) and then when we're ready to push the image to the display we can use LCD.Flush().

Here's some sample code to show how to draw the "Base Ship" at the bottom of the LCD display - notice that BaseShipX is the X-Offset for the position of the Base Ship on the bottom of the screen.

If this was the end of my drawing routine I could then call LCD.Flush(); to push the bitmap to the display.

 

             LCD.DrawImage(BaseShipX, mainWindow.Height - BaseShip.Height, BaseShip, 0, 0, BaseShip.Width, BaseShip.Height);
            LCD.Flush();

2. Timer

Setting up a timer is very straight forward,  here's the code needed to setup the timer, we first setup an AutoResetEvent and set the initial state to false, we then create a delegate function (called TimerTick) which will be called on each tick event, and then create the Timer, passing in the delegate, the AutoResetEvent, and the timer frequency - the final step is to add the timer handler function - simple!

         {
            AutoResetEvent autoEvent = new AutoResetEvent(false);
            TimerCallback timerDelegate = new TimerCallback(TimerTick);

            Timer stateTimer = new Timer(timerDelegate, autoEvent, 1000, 40);
        }

        public void TimerTick(Object stateInfo)
        {
            // TODO: Do Timer Tick stuff here...
        }

 

3. Key pad input

Setting up keypad input is almost as easy as setting up the timer - the board I'm using (the Freescale i.MXS board) has a D-Pad configuration of buttons at the bottom of the board - this can of course be used to move pieces in a game left, right, up, down, and FIRE (sounds like Galaxian might be the next game to write for the Micro Framework board!).

Using the Freescale reference documentation we can get the following GPIO port mapping for the buttons on the board.

             // center button is GPIO_PORT_B_8
            // down   button is GPIO_PORT_B_10
            // up     button is GPIO_PORT_B_11
            // right  button is GPIO_PORT_B_12
            // left   button is GPIO_PORT_B_16
            // bottom right is  GPIO_PORT_B_15
            

Once we have this information we simply need to setup an interrupt handler (just like adding a delegate function handler).

Here's how to setup the interrupt handler, and the skeleton code for the delegate function.

     {
        InterruptPort LeftButton = new InterruptPort(Pins.GPIO_PORT_B_16, true, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeBoth);
        LeftButton.OnInterrupt += new GPIOInterruptEventHandler(LeftButtonPush);
    }


    public static void LeftButtonPush(Cpu.Pin port, Boolean state, TimeSpan time)
    {
        Debug.Print("Left Button Pushed");
    }

 

That's the button handler taken care of...

4. Random Number

ok, this is simple, but it took me a few minutes to discover the function name - in the game I use the Random function to generate a number which is then used to determine whether I should create a bomb for one of the aliens - even though there are possibly a number of rows of aliens only the bottom (exposed) row of aliens can drop a bomb, note that it's possible that the lowest alien in column might not be on the bottom row - so there's some checking that's needed to determine which alien (if any) is going to drop a bomb.

             int iBombRand = Microsoft.SPOT.Math.Random(10);

 

That's about all that's needed to get the basics up and running, now it's over to you to go build something interesting! - you can get started using Visual Studio 2005 and the .NET Micro Framework 2.0 SDK (which includes a Micro Framework emulator).

- Mike