Pac[Metro]Man - Recreating PacMan for Metro with a little help from F#
Today's wild Wednesday post is an interesting mix of languages and things, all thrown together as a Metro game (Yeah, I know it's not Metro Monday, but still...)
Today's project mixes Metro style app development, Portable Libraries, C#, F# and the always fun to play PacMan!
Back in January I built a sample Pacman maze script in F# to use at a Pacman Kata evening with the F#unctional Londoners group. Coincidentally there’s another Coding Kata this Thursday 26th July at Skills Matter. Anyway a couple of weeks ago I started playing with the sample again on the train to and from work, filling in some of the gameplay.
You can play the latest version with your cursor keys and 9 lives below:
As the sample runs in Silverlight I thought I’d also try it out on it’s cousin WinRT. WinRT lets you build Metro apps on Windows 8. The transition code wise has been pretty straight forward and I now have a tile for the game appearing on my Windows 8 start page:
Multi-targeting Silverlight and WinRT is the route I decided to go down which allowed me to develop the game on my laptop running Windows 7 with Visual Studio 2010. Then I periodically tested it out on a desktop box running the Windows 8 Preview Release using Visual Studio 2012. Visual Studio 2012 does run on Windows 7, however WinRT does not.
The WinRT version of the app is implemented in F# and C#. The game part is written in F# as a portable library and the plumbing in C#. There’s a great walkthrough on Creating a Portable F# Library over on MSDN which describes this direction in some detail.
The full source code to the game is publicly available on BitBucket:
A single file playable script version is also available on F# Snippets:
One interesting thing was how it scaled. For example, here's the app snapped, where it remained fully playable;
Here's a snap of the Solution, showing off how F# and C# were mixed, using a Portable Library project.
One of the meta items in the project was how F# was used to generate the PacMan logo's/images.
#if INTERACTIVE #r "System.Drawing.dll" #endif open System open System.IO open System.Drawing open System.Drawing.Imaging let drawPacman (bitmap:Bitmap, x:int, y:int, size:int) = let width, height = bitmap.Width, bitmap.Height let graphics = Graphics.FromImage(bitmap) do graphics.Clear(Color.Black) graphics.FillEllipse(Brushes.Yellow,x,y,size,size) let points = let right = x + size [|right,y + size/2 - size/4;right,y + size/2 + size/4; x + size/2, y + size/2|] |> Array.map (fun (x,y) -> PointF(float32 x,float32 y)) graphics.FillPolygon(Brushes.Black,points) open System.Windows.Forms let show (bitmap:Bitmap) = let form = new Form() let box = new PictureBox(Image = bitmap) box.Width <- bitmap.Width box.Height <- bitmap.Height form.Width <- bitmap.Width form.Height <- bitmap.Height + 32 form.Controls.Add(box) form.Show() //let root = @"C:\Users\Moon\Documents\Visual Studio 2010\Projects\pacman\PacMan.Xaml\Assets\" let root = @"C:\Users\Phil\Documents\Visual Studio 2012\Projects\pacman\PacMan.Xaml\Assets\" do // Logo let path =root + "Logo.png" let bitmap = new Bitmap(path) drawPacman(bitmap, 42, 38, 64) bitmap.Save("PacMan_Logo.png", ImageFormat.Png) do // SmallLogo let path = root + "SmallLogo.png" let bitmap = new Bitmap(path) drawPacman(bitmap, 3, 3, 24) bitmap.Save("PacMan_SmallLogo.png", ImageFormat.Png) do // StoreLogo let path = root + "StoreLogo.png" let bitmap = new Bitmap(path) drawPacman(bitmap, 5, 5, 40) bitmap.Save("PacMan_StoreLogo.png", ImageFormat.Png) do // SplashScreen let path = root + "SplashScreen.png" let bitmap = new Bitmap(path) drawPacman(bitmap, 200, 42, 214) bitmap.Save("PacMan_SplashScreen.png", ImageFormat.Png) do // Icon 16x16 let bitmap = new Bitmap(16,16) drawPacman(bitmap, 1, 1, 14) bitmap.Save("Icon16x16.png", ImageFormat.Png) do // Icon 32x32 let bitmap = new Bitmap(32,32) drawPacman(bitmap, 2, 2, 28) bitmap.Save("Icon32x32.png", ImageFormat.Png) do // Icon 48x48 let bitmap = new Bitmap(48,48) drawPacman(bitmap, 3, 3, 42) bitmap.Save("Icon48x48.png", ImageFormat.Png) do // Icon 128x128 let bitmap = new Bitmap(128,128) drawPacman(bitmap, 8, 8, 112) bitmap.Save("Icon128x128.png", ImageFormat.Png) show bitmap
Okay, what about for the non-Metro dev's? Well there's VS2010/SilverLight, Windows Phone and XAML fun for you too!
If you're looking to expand your development language repertoire and looking for fun way to do it, looking for examples of how to mix and match C# and F#, looking for examples of F# and SilverLight or finally F# in a Metro world, this project has all that and more...