• Coding4Fun

    Building the Laser Graffiti System

    • 0 Comments

    Jeremiah Morrill
    HJT & Associates

    Source Code: Download

    Difficulty: Advanced
    Time Required: 25 hours
    Software Needed: XNA Game Studio 3.1, Visual Basic or Visual C# Express 2008 (once XNA 4.0 is released for PC, this should work in VS 2010 )
    Hardware: Projector, Webcam, Bright green laser

    Introduction

    Last year, Clint Rutkas contacted me about building a project involving lasers, graffiti and code. When you get a request like that, you never decline, especially when lasers are involved. The project requirements sounded quite simple: create an application that can, using a laser pointer as a virtual spray can, draw virtual graffiti on the side of a building. It sounded a little daunting to me at first, but after it was broken up into small digestible pieces, the project ended up not being very complicated.

    Below is the basic hardware setup of the Laser Graffiti System.

    clip_image001[10]

    Building a Laser Tracker Engine

    Giving Sight to the Blind

    The very first thing that needed to be proven was the ability to track a laser point. The most obvious solution was to give our application some vision with a webcam. Since this was going to be a WPF application, I decided to use my open-source project, WPF MediaKit, which comes with a webcam control called the VideoCaptureElement.

    At the time, VideoCaptureElement was not very robust, and it occurred to me that my control wasn’t very useful beyond looking at your webcam in a WPF app! I needed a way to get high-performance access to every pixel of every frame the webcam spat out. This was a good time to add that ability and increase value in my project. Without getting into the gory details of DirectShow and p/invoke, I was able to add a hook to pass me the pixel buffer, where I would wrap it in a Bitmap class and raise an event for each frame.

    The XAML required to get each video sample from the web camera.

    <MediaKit:VideoCaptureElement LoadedBehavior="Play"
        RenderOptions.BitmapScalingMode="LowQuality"
        UnloadedBehavior="Manual"
        DesiredPixelWidth="{Binding DesiredPixelWidth}"
        DesiredPixelHeight="{Binding DesiredPixelHeight}"
        VideoCaptureDevice="{Binding SelectedItem, ElementName=videoCapDevices}"
        FPS="30"
        EnableSampleGrabbing="True"
        NewVideoSample="VideoCaptureElement_NewVideoSample"
        Margin="0"
        x:Name="videoElement"
        Stretch="Fill"
        MinHeight="380"
        MinWidth="500" />

    Did I Mention Lasers?

    clip_image004[5]

    Now that I had the ability to look at every pixel sent to me, I needed to make sense of it all. In order to complete the project, we have to be able to find a laser point shined on a wall. The difficulty lies in the fact that a laser can be of varying size and color. For example, if you take two consecutive video frames with the same green laser point, you will find the point may slightly differ between the two. This is because we are dealing with analog data and slightly changing lighting conditions. Whatever algorithm we choose to find the laser must take this into account.

    There are many advanced ways to use video analytics to find the laser pointer, but this project did have a deadline, and I wanted to keep it fun, so I went with a simple method, which allows a user to filter the video based off ranges of hue, saturation, and luminance. To do this, we make use of the AForge image processing library. AForge is an open-source library that comes with tons of useful utilities for just what we want to do.

    Filtering pixels and counting blobs…

    As previously mentioned, we need to first filter the image based on hue, saturation, and luminance. Luckily, the AForge library comes with such a filter, so there is not much work to be done here:

    /* This AForge class helps us filter out the pixels we do not want */
    var hsl = new HSLFiltering
    {
        Hue = new IntRange(HueMinimum, HueMaximum),
        Saturation = new DoubleRange(SaturationMinimum, SaturationMaximum),
        Luminance = new DoubleRange(LuminanceMinimum, LuminanceMaximum)
    };
    
    var bitmapData = bitmap.LockBits(
        new Rectangle((int)targetSearchArea.X, 
            (int)targetSearchArea.Y, 
            (int)targetSearchArea.Width, 
            (int)targetSearchArea.Height),
        System.Drawing.Imaging.ImageLockMode.ReadWrite, 
        m_lastBitmap.PixelFormat);
    
    
    /* Apply the AForge filter.  Doing it "in place" is more efficient as
     * new bitmap does not have to be allocated and copied */
    hsl.ApplyInPlace(bitmapData);

    So, what needs to be done now that we have a filtered image showing only a laser dot? The answer: Blob counting! A blob, in the context of video analytics, is a set of pixels that are all touching, and AForge comes with a class ready to tackle that too!

    The AForge blob counter takes in a gray-scale image for processing, and then returns all blobs found in the image. The information included on a blob includes the rectangular pixel area where the laser was found in the video frame. The center of that area should be the center of our laser. It’s important to note that if the HSL filter is improperly calibrated, we will get an overabundance of blobs and there will accordingly be no way we can tell if it is the laser point or not.

    /* Create an initialize our blob counter */
    var blobsCounter = new BlobCounter
        {
           FilterBlobs = true,
           ObjectsOrder = ObjectsOrder.Size,
           MinHeight = BlobMinimumHeight,
           MinWidth = BlobMinimumWidth,
           MaxWidth = 25,
           MaxHeight = 25
        };
    
    /* We first let the blob counter process our image */
    blobsCounter.ProcessImage(grayImage);
    
    /* Retrieve a list of blobs that were found */
    Blob[] blobs = blobsCounter.GetObjects(grayImage);

    The early laser tracking prototype. We can literally have it track anything, based off color.clip_image008[5]

    Building the Laser Graffiti Application

    Our Rocket Engine Needs a Rocket

    At this point, I had a working proof of concept and a laser tracker. What I needed now was an application to use it. You know, the laser-graffiti application.

    There are many ways to construct a WPF application and one pattern that’s been getting a little bit of traction is MVVM. I chose to make a composite application using Prism and MVVM. Prism is a great guidance library pack for making composite WPF applications with tons of tools to help in common scenarios.

    Composite What? Huh?

    When developing applications, usually of the larger variety, things can become complex. Complexity turns into a mess. A mess turns into time investment. Time investment turns into money. The more money spent, the fewer bonuses you will receive. To make sure we get that end-of-year pay raise, we want to control complexity from the get-go by making our code and our internal systems loosely coupled. A composite application is just a collection of loosely coupled systems that is composited at runtime.

    The advantages of a composite application are:

    · Components can be developed independently

    · Components can be swapped out more easily than if they were tightly coupled

    · The application remains extendable, meaning you can more easily extend your application

    · Maintenance is much clearer with less chance of breaking other components

    · Unit testing is more feasible

    Composite Application Entrails

    There are many, many excellent resources about Prism available on the internet, so I’d rather not compete with them. I did, however, want to cover the basics, and at least how I’ve used it within the Laser Graffiti application.

    clip_image009[5]

    The Shell Assembly – This contains the main entry point of the application and the initial or main UI container for the application. Before anything else, this assembly will initialize something called a bootstrapper. The bootstrapper simply does any pre-initialization that needs to be done before initializing the rest of the application.

    Module Assemblies – Each module assembly can be considered a component. The bootstrapper initializes each component and then the module spins up any application services and adds any user interface to the shell. For instance, the MediaKit module handles the capture of the video and the AForgeModule handles the vision routines.

    The Infrastructure Assembly – This contains any base classes, well known/shared interfaces or general infrastructure code. Simply put, most if not all modules will have reference to this.

    Rendering the Graffiti

    I needed a way to draw the graffiti to a projector. Overall, this will be accomplished by drawing the graffiti to a window. The window will be set to maximized with no border. I also needed to write the code to draw the graffiti. Initially, I just used the WPF InkCanvas control, but I found it wasn’t well tuned for what I wanted to do with it. So, I decided XNA would give the most performance and flexibility with the level of effects desired for drawing graffiti.

    I had never really done any XNA development, so this project is certainly not built using best practices. The important part to notice is that the project is more or less a regular XNA application with the exception that the executable is added as a library reference. This is because we instantiate the XNA from our WPF application’s process. The following snippet of code accomplishes this:

    private void ShowGraffitiWall(bool show)
    {
        if (m_game != null)
            m_game.Exit();
    
        if (!show) 
            return;
    
        var t = new Thread((ThreadStart)delegate
        {
            m_game = new XnaGraffitiGame();
            m_game.SetScreen(m_lastScreen);
            m_game.Effect = m_lastGraffitiEffect;
            m_game.Run();
            m_game.Dispose();
        });
    
        t.SetApartmentState(ApartmentState.STA);
        t.IsBackground = true;
        t.Start();
    }

    We instantiate the XNA window this way because it allows our WPF code to send the XNA messages of where the laser pointer was detected.

    Drawing with the GPU

    A blanket statement about XNA is that it is a drawing API. XNA applications can get quite complex when you add in things like 3D or shaders, but for this article, I’m only covering the required simple stuff. That said, I do make the assumption that the reader has had at least a few hours of XNA experience.

    Our primary goal is to render virtual graffiti using XNA. This means we need to be able to draw a line. I started by creating a PNG file that would act as a “brush.” In XNA, these 2D graphics are also known as sprites. The brush sprite looks like this:

    clip_image011[5]

    Now we can use XNA’s SpriteBatch class to draw our brush. The first problem I had, though, was that most XNA tutorials show drawing the sprite, continue, clear the screen, draw the sprite, continue, etc. I needed to retain previous brush strokes, which means drawing onto an intermediate drawing surface (RenderTarget2D), then drawing that to the screen.

    The next issue I had was that the line I was trying to draw was very broken. See, the laser detection code runs at a maximum 30 FPS (the limit of my web camera). If I moved the laser fast enough, the screen would just look like a bunch of random points were being drawn. I had to move the laser very slowly to make it appear like a line. I remember reading an excellent article detailing a project by Rick Barraza, which led me to a link to some popular algorithms for creating a line where your input is two, 2D points.

    clip_image012[5]

    So, drawing a line required only a simple, two-step process of A) getting a new point from laser detection, and B) drawing a line from last_laser_coordinate to current_laser_coordinate. After putting that together, we can now produce some useful looking graffiti!

    clip_image014[4]

    Adding Crazy Graffiti Effects

    The excitement of drawing simple lines in XNA wore off pretty fast. After all, the GPU is capable of some pretty amazing things. What about drawing with fire? Fire isn’t boring:

    clip_image016[4]

    The fire effect is done using a method slightly similar to the solid color sprite method described earlier. However, since it uses a particle system, the fire effect is much more complex. Each particle has its own shader to control position and color. With the time constraints of this project, I had modified code from this XNA tutorial. There weren’t many changes from the tutorial’s code, but a big hurdle was that the particles took 3D coordinates instead of the 2D coordinates I needed to draw the graffiti. I found a small snippet to handle that, though I was informed by other XNA gurus that there is an easier way to accomplish this. If you are curious, this is what it looks like (and it’s not for the faint of heart):

    protected Vector3 ScreenPointToVector3(Point coords)
    {
        Matrix viewMatrix = 
            Matrix.CreateTranslation(0, -25, 0) *
            Matrix.CreateRotationY(
                MathHelper.ToRadians(CameraRotation)) *
            Matrix.CreateRotationX(
                MathHelper.ToRadians(CameraArc)) *
            Matrix.CreateLookAt(
                new Vector3(0, 0, -CameraDistance),
                new Vector3(0, 0, 0), 
                Vector3.Up);
    
        float aspectRatio = 
            (float)m_game.GraphicsDevice.Viewport.Width /
                (float)m_game.GraphicsDevice.Viewport.Height;
    
        Matrix projectionMatrix = 
            Matrix.CreatePerspectiveFieldOfView(
                MathHelper.PiOver4,
                aspectRatio,
                1, 10000);
    
        Vector3 nearScreenPoint = new Vector3(coords.X, coords.Y, 0);
        Vector3 farScreenPoint = new Vector3(coords.X, coords.Y, 1);
        Vector3 nearWorldPoint = 
            m_game.GraphicsDevice.Viewport.Unproject(
                nearScreenPoint, 
                projectionMatrix, 
                viewMatrix, 
                Matrix.Identity);
        Vector3 farWorldPoint = 
            m_game.GraphicsDevice.Viewport.Unproject(
                farScreenPoint, 
                projectionMatrix, 
                viewMatrix, 
                Matrix.Identity);
    
        Vector3 direction = farWorldPoint - nearWorldPoint;
    
        float zFactor = -nearWorldPoint.Z / direction.Z;
        Vector3 zeroWorldPoint = nearWorldPoint + direction * zFactor;
    
        return zeroWorldPoint;
    }

    The End Result

    Web-camera device selection:

    clip_image020[4]

    Fine tuning the laser detection:

    clip_image022[4]

    About Jeremiah

    Jeremiah Morrill is a software developer and 2010 MVP living in Las Vegas, Nevada. He owns a software company called HJT, http://www.hjtcentral.com/, with a couple partners. There he focuses on multimedia and rich user interfaces with WPF and Silverlight. Jeremiah spends a lot of his free time learning, listening and helping others in the online development communities. 

  • Coding4Fun

    Loading, iterating, and saving photos with WP7

    • 0 Comments

    medialib_Sample_Screenshot[1] Friend of Coding4Fun, René Schulte, has an full write-up on how to do some simple drawing, loading, and saving with Windows Phone 7.  René even used multi-touch as well!

    He leverages the WriteableBitmapEx class that extends out the WriteableBitmap Silverlight class.

    To do a PhotoChooser, the code is simpler than one may think.  Since you're app will navigate away, it works with events.

    public MainPage()
    {
       // ...
    
       // Init chooser
       photoChooserTask = new PhotoChooserTask();
       photoChooserTask.Completed += PhotoChooserTaskCompleted;
       
       // ...
    }
    
    private void ApplicationBarIconOpenButton_Click(object sender, EventArgs e)
    {
       photoChooserTask.Show();
    }
    
    private void PhotoChooserTaskCompleted(object sender, PhotoResult e)
    {
       if (e.TaskResult == TaskResult.OK)
       {
          // Load original image and invalidate bitmap so it gets newly rendered
          var bitmapImage = new BitmapImage();
          bitmapImage.SetSource(e.ChosenPhoto);
          Viewport.Source = bitmapImage;
          bitmap = null;
       }
    }
  • Coding4Fun

    Natural User Interface library

    • 0 Comments

    image Blake.NUI is a new open-source library for multi-touch NUI (Natural User Interface) development.  It can be found at blakenui.codeplex.com and is under a MS-PL license.

    The library is still in undergoing changes but here are some of the things currently included in Blake.NUI:

    • Controls
      • ZoomCanvas - an infinite canvas control that is the heart of NaturalShow
      • CircleText - layout text along the circumference of a circle
    • Gestures
      • TapGestureTrigger - a configurable Blend Trigger that fires when the user completes a tap gesture
      • DoubleTapGestureTrigger - a configurable Blend Trigger that fires when the user completes a double tap gesture
    • TouchDevices
      • MouseTouchDevice - promotes mouse input to WPF 4 touch events
      • SurfaceTouchDevice - promotes Microsoft Surface vision system input to WPF 4 touch events
    • Common classes and utilities
      • DisplayMatrix
      • AnimateUtility and SurfaceAnimateUtility
      • ScatterMatrixHelper
      • MathUtility
      • VisualUtility
    • NaturalShow
      • Common models, views, factories, and other classes
  • Coding4Fun

    WebMatrix, web development made easy

    • 0 Comments

    webx-hero[1]

    WebMatrix was recently released and helps make creating websites super easy.  WebMatrix is everything you need to build Web sites using Windows. It includes IIS Developer Express (a development Web server), ASP.NET (a Web framework), and SQL Server Compact (an embedded database). It streamlines Web site development and makes it easy to start Web sites from popular open-source apps. The skills and code you develop with WebMatrix transition seamlessly to Visual Studio and SQL Server.

    All you do is create the application, click publish, and a fully functional site is up and working.  How it runs on your local computer is how it will run on the website!

    What is even cooler is it connects you to a world of popular and free open-source Web applications, including DotNetNuke, Umbraco, WordPress, Joomla!, and more!

    If you want to learn on how to get started, head over to http://www.microsoft.com/web/webmatrix/learn/.

  • Coding4Fun

    the world is just a flat database

    • 0 Comments

    4330671070_2edcdcc477_b[1] Clint Pearson created a flat database project over at CodePlex.  Initially I wasn’t sold on this but then after a few pokes at the source code, I realized it is actually fairly powerful.  Clint implemented a table solution for FlatDB which makes it far more powerful than a typical single serializable class.

    Using and saving are dirt simple too!

    var db = FlatDatabase("flat.db");
    db.Submit();

    [ pic via flickr ]

  • Coding4Fun

    Extreme LINQ, building a RayTracer

    • 0 Comments

    800px-BallsRender This is what happens when you give certain people spare time :-).  They decide to do something just because they can.  Luke built functional RayTracer with LINQ.  He openly states this is “just because you could” type of thing.

    How does Luke get around the fact LINQ doesn’t support recursion, something that is required in ray tracing?  He uses Y Combinators, which lets you do recursive lambda expressions.

    [Image by Mimigu]

  • Coding4Fun

    .NET on a bicycle

    • 0 Comments

    image The .NET Micro Framework (NETMF) team has a great example of using low cost, embedded computing to improve their biking experience.

    All the source code can be found at http://netmfbikecomputer.codeplex.com/ and just a reminder, the NETMF is 100% open source as well!

    1. Introduction
    2. Getting the project set up
    3. Sensors and Integration
    4. UI – Font and touch
    5. UI custom controls
    6. Complex sensor integration
    7. Working with emulator
  • Coding4Fun

    Wiimote Virtual Room Designer

    • 1 Comments

    In this article, Thijs Brilleman demonstrates how to create, using C# and XNA, a room designer from a mash-up of cheap components.

    Thijs Brilleman
    University of Twente, The Netherlands

    Code It: Download
    Run It: Download

    Difficulty: Intermediate
    Time Required: more than 10 hours

    Cost: Less than $20 if you already own a Wiimote and a digital projector

    Software Needed: Visual C# Express, XNA Game Studio 3.0, XNA Graphics Device on WinForm hack, Johnny Lee Chung’s WiimoteWhiteboard (includes Brian Peek’s managed library for the Nintendo Wiimote), Mike Mander’s RoundLine class.
    Hardware: Nintendo Wii Remote (Wiimote), a compatible PC Bluetooth adapter and stack, 3 Infrared LEDs, 2 push buttons, thin wiring (1m), 3 AAA batteries, plus some more components you’ll find in every kitchen.

    Introduction

    Last year I had to earn my M.Sc. in engineering and decided to make up my own final graduation project, a part of which is this very cheap design tool. Check out the video below!

    Brian Peek, author of WiimoteLib, saw the video and asked me to write a practical article on how to create something like this yourself. Let’s get started!

    Hardware Setup

    To get this working, you only need a digital projector, a Wiimote, a few cheap components, and, of course, a computer. The latter won’t need a lot of horsepower; I’m even able to run it on my $300 netbook with an acceptable framerate! Let’s take a look at how to prepare these items before we get working on the software.

    clip_image002

    Creating the tabletop display

    First, let’s create the poor man’s version of the Microsoft Surface table. If you are like me and aren’t burdened by a fat bank account, just take some ugly photo-in-a-frame off the wall and lay its protective sheet of glass down on a table-without-tabletop. Trust me: as long as you don’t sit on it, it works like a charm! Now go to a nearby grocery store and buy some white fat-free paper. Tape it on top of the glass to create your DIY projection screen. You may also tape it to the bottom side, but in my experience it will start hanging down after some time has passed. As shown in the video, use an inclined mirror and a projector connected to your computer. Now you have your own backlit tabletop display!

    Making the display interactive

    Now comes the trick: making the display interactive. Enter the brilliant Nintendo Wiimote. Simply put it in a stable position facing the mirror on top of the projector, preferably right above the lens. The Wiimote contains a high-performance camera that can simultaneously track up to 4 infrared light sources. Now, if you fashion a simple infrared pen (or do it the easy way and buy one online), the Wiimote can track its position all across the display!

    Fashioning the point-of-view device

    clip_image004clip_image006

    We’re not finished yet, because for this project we’re also going to need a point-of-view device. This device is nothing more than a small glass turned upside down, with two infrared LEDs in its bottom as shown in the photo. Building this is similar to building 2 IR pens.  Make sure the small glass is large enough to fit an AAA battery in there! It’s best to also incorporate a pushbutton switch right between the LEDs, so that they only emit light when the point-of-view is slightly pressed onto the tabletop display. It may be a bit tricky to get the button position exactly right, but I’m sure you’ll manage with some perseverance.

    Connecting to the Wiimote

    Now it’s time to connect to the Wiimote. For your convenience, I’ve copy-pasted Brian’s short manual here:

    1. Start up your Bluetooth software and have it search for a device.

    2. Hold down the 1 and 2 buttons on the Wiimote. You should see the LEDs at the bottom start flashing. Do not let go of these buttons until this procedure is complete.

    3. Wiimotes should show up in the list of devices found as Nintendo RVL-CNT-01. If it's not there, start over and try again.

    4. Click Next to move your way through the wizard. If at any point you are asked to enter a security code or PIN, leave the number blank or click Skip. Do not enter a number.

    5. You may be asked which service to use from the Wiimote. Select the keyboard/mouse/HID service if prompted (you should only see one service available).

    6. Finish the wizard.

    If you can’t get it to connect, you might try using a Wiimote compatible Bluetooth adapter and stack.

    Inserting Some Third-Party Code

    With the hardware all set up, we’ll continue to the software. Make sure you have installed a (free) version of Visual Studio with C#, and Microsoft XNA Game Studio version 3.0 or higher. XNA integrates neatly into Visual Studio and we’ll use it as an easy way to harness your graphic card’s 3D power. Our home designer relies heavily on work done by the open-source community, so let’s start by inserting these components first.

    XNA Surface As A WinForms Control

    We’re going to use a hack that allows us to use XNA-controlled surfaces on as many forms as we like. There’s no need to know exactly how the hack works—we’ll just use it! Therefore, instead of starting a normal XNA project within Visual Studio, we’ll make use of a sample project that has been posted online at the XNA Creators club. Download and open it. If you run the solution you just downloaded, you’ll see two separate XNA-powered surfaces (the left one contains some text and the right one is a spinning triangle).

    clip_image008

    Both are standard Controls, but why is that important? Because now we can easily create multiple WinForms, each containing its own XNA surface—one for the blueprint and the other for the 3D perspective view! Later on, it will look like this:

    clip_image010

    A Little Housekeeping

    Since we don’t want to do any more work than necessary, we’ll start our project from the this XNA control example project. Let’s do a little housekeeping to keep everything neat and tidy. First, make sure you delete all controls present on the MainForm. In the solution explorer, rename “SpinningTriangleControl.cs” and “SpriteFontControl.cs” to “BluePrintControl.cs” and “_3Dcontrol.cs” respectively, and move all files except “MainForm.cs” to a new directory called “Controls”. By the way, this would also be an appropriate moment to rename both Control classes. We’ll change their functionality later on. Your solution explorer should now look like this:

    clip_image012

    It may also be smart to change the name of our project from “WinFormsGraphicsDevice” to “HomeDesigner” or something (I actually call it “DualDesign”, but choose whatever you like).

    Enabling whiteboard functionality

    Time to honor the Wiimote community’s all-time great: Johnny Lee Chung! He’s the guy that used Brian’s managed Wiimote library to build some amazing applications. We’ll tweak his Whiteboard application to make our blueprint interactive. Check out his video:

    I downloaded the source code for Johnny’s Whiteboard application and then updated to support version 1.7 of WiimoteLib, which is more compatible than the version Johnny used.

    Now, by adding the following code to the Click event handler, let’s add a button on our MainForm that starts Chung’s app inside our own:

    C#

    try
    {
        wiimoteForm = new WiimoteWhiteboard.WiimoteWhiteboardForm();
        wiimoteForm.Visible = true;
    }
    catch (Exception x)
    {
        System.Console.Out.WriteLine("Exception: " + x.Message);
    }

     

    Make sure you have established a Bluetooth connection to the Wiimote before running the code. What Lee’s app basically does is emulate a standard mouse. The operating system (and thus our blueprint control) won’t see the light pen but will receive standard MouseDown, MouseMove, and MouseUp events. This is very convenient, because we don’t need the Wiimote to be connected while debugging: we can simply draw on the form with our mouse since our blueprint won’t be able to tell the difference!

    Pretty Lines

    There’s one last component to insert before we can really start. In the demo, you saw the big, fat white lines I drew on the blueprint. These pretty RoundLines have been coded by Microsoft’s Mike Manders and released into the public domain. Download them and insert the code into our project. Make sure to manually copy all .xnb files to our project’s “bin\ … \debug\Content” directory using the Windows Explorer (not the Solution Explorer), otherwise your code might not run.

    Coding the BluePrintControl

    We’ve got all the third-party code we need, so now it’s time to get our hands dirty. However, the entire solution is pretty comprehensive and much of the code is trivial. Therefore, we won’t be getting into every nitty-gritty detail; instead, I will single out some of the interesting parts. A lot of the code is XNA-related. If you want to read more about it, “Learning XNA 3.0” (O’Reilly) is an excellent starting point, as are Riemer’s online tutorials.

    XNA Basics

    In order to understand the code, you’ll need a grasp of some of the very basics of XNA Game Studio. XNA Game Studio is a comprehensive set of tools that allows the development of entire games, but we’ll only use it for basic visualization. XNA games run in a continuous loop, basically drawing frames to the viewport at as fast a rate as possible. A lot of things happen behind the scenes in each frame, but the most important part is the Draw() method in which all the drawing is done. Here, we draw all elements to the frame buffer, at the end of which the buffer “empties” to the viewport and becomes visible to the user.

    Drawing to the GraphicsDevice

    Because we were lazy and started with a pre-coded demonstration control, we’ll have to remove the unnecessary code in “BluePrintControl.cs” (the drawing of the spinning triangle). After cleaning up, make sure our Draw() method looks like this:

    C#

    /// <summary>
    /// Draws the control.
    /// </summary>
    protected override void Draw()
    {
        // clears the graphicsdevice
        GraphicsDevice.Clear(defaultBackgroundColor);
     
        // draws all elements to the viewport
        this.DrawWalls();
        this.DrawAllSprites();
        this.DrawObjectHandles();
        this.DrawButtons();
    }

     

    The GraphicsDevice is an abstraction of the graphics card and is what we draw to. The Clear() statement clears the entire frame buffer to our default background color on top of which we can draw. I used “Color.DarkBlue” but you can use anything you want here as long as it’s blue (otherwise you should have differently named the BluePrintControl class :) ). Next, we call four methods that draw all elements to the blueprint.

    Drawing the Walls

    Let’s take a look at the DrawWalls() method:

    C#

    private void DrawWalls()
            {
                // Draw lines
                roundLineManager.Draw(
                    ConvertWallListToRoundLineList(wallList), // converts Walls to RoundLines
                    defaultWallThickness,
                    Color.White,
                    camera.View * camera.Projection, // 4x4 matrix to that allows XNA to map 3D coordinates to screen coordinates
                    1f,
                    roundLineManager.TechniqueNames[defaultTechnique]
                    );
            }

     

    This is where we make use of the RoundLine component. The RoundLineManager.Draw() method takes a list of RoundLine objects and draws them to the graphicsdevice in one go. Internally, we store the walls in our own format, so the ConvertWallListToRoundLineList method does exactly what its name implies.

    The fourth argument, camera.View * camera.Projection, tells XNA how to map 3D coordinates to the viewport. The View matrix contains information regarding the position and orientation of the camera, and the Projection matrix can be seen as a matrix describing the lens. Since we want the blueprint to be displayed without any distortion, we previously defined the projection matrix to be orthographic:

    C#

    camera.Projection = Matrix.CreateOrthographic(this.Width, this.Height, 0, 100);

    To get an idea of what an orthographic camera is, imagine a real camera that is hanging 500 feet above our blueprint. In order to see the blueprint, it has to really zoom in. In this case, everything becomes very flat and undistorted—basically, it has become an orthogonal camera.

    Drawing the Objects Using Sprites

    The walls we just drew to the screen are flat lines floating in a true 3D space. However, with XNA it is also possible to just draw to the viewport coordinates; no 3D to 2D mapping has to be done in this case.

    XNA handles this in a very convenient way using Sprites. A sprite is a bitmap drawn into a rectangle that you have specified in advance. It offers a very high performance since your graphics card does all the hard work! The following code draws the furniture to the blueprint using sprites:

    C#

    private void DrawAllSprites()
            {
                spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.BackToFront, SaveStateMode.SaveState);
     
                foreach (ImageTextureObject tempObject in objectList)
                {
                    Rectangle tempBoundingRectangle = this.GetBoundingScreenRectangleFromObject(tempObject);
     
                    spriteBatch.Draw(
                        tempObject.Texture, // the bitmap
                        tempBoundingRectangle, // the bounding rectangle (not yet rotated)
                        null,
                        Color.White,
                        -tempObject.Rotation.Y, // determines the rotation of the sprite
                        new Vector2((float)tempObject.Texture.Width / 2, (float)tempObject.Texture.Height / 2),
                        SpriteEffects.None,
                        1
                    );
                }
     
                spriteBatch.End();
            }

    Note that we created an ImageTextureObject such that it contains all information about a piece of furniture: position, orientation, and size in addition to the image of that particular piece of furniture. Now, we loop through the entire list of furniture and use the SpriteBatch.Draw() method draw to the graphicsdevice. Note that any SpriteBatch.Draw() call has to occur within the SpriteBatch.Begin() and .End() statements. This prepares the graphics device to receive the data and allows some optimization to occur after a group of sprites has been received. We draw the handles that appear (see image below) when clicking an object in exactly the same manner:

    clip_image016

    Receiving User Input

    Now, how do we let the user draw lines and drag furniture around? First, we’ll have to subscribe to the MouseDown, MouseMove, and MouseUp events:

    C#

     
    this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.bluePrintControl_MouseDown);
    this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.bluePrintControl_MouseMove);
    this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.bluePrintControl_MouseUp);

     

     

    Recognizing Button Clicks

    When receiving a MouseDown event, we’ll first check if there is a button pressed. We do this using the ButtonPressed() method:

    C#

    private int ButtonPressed(System.Drawing.Point point)
    {
        foreach (ButtonSprite button in buttonList)
        {
            if (button.Clicked(new Point(point.X, point.Y))) return buttonList.IndexOf(button);
        }
        return -1;
    }

     

    Each Rectangle object comes with a nice Intersects() method that allows us to check if two Rectangles well…intersect J. We exploit this by constructing a new 1x1 rectangle at the cursor location:

    C#

    public bool Clicked(Point location)
    {
        return this.TargetRectangle.Intersects(new Rectangle(location.X, location.Y, 1, 1));
    }

     

    If true, the index of the button is returned by the ButtonPressed() method so that we know what button was pressed. In a similar way, we check if an object was clicked.

    Drawing Walls with the Light Pen

    If it turns out that there is no button, object, or handle at the MouseDown location, we assume that the user wants to draw a wall. The location sent along with the MouseDown event is in screen coordinates (in pixels); however, in order to draw a wall we need it in “world coordinates” (in meters). For that we employ the following method:

    C#

    private Vector3 ConvertLocalMouseToWorldCoors(System.Drawing.Point value)
    {
        Vector3 worldCoors = new Vector3(
            this.camera.Target.X - this.camera.Width / 2 
                + (this.camera.Width / (float)this.ClientSize.Width) * value.X,
            0,
            -this.camera.Target.Y - this.camera.Height / 2 
                + (this.camera.Height / (float)this.ClientSize.Height) * value.Y
            );
        return worldCoors;
    }

     

    This method is called a lot throughout the code, and there is also a method that converts world coordinates to screen coordinates: ConvertWorldCoorsToLocalCoors().

    Reading the Point-of-View Device

    Now comes some good old hackin’! The whiteboard component – the one that uses the light pen to emulate a mouse – only “sees” one infrared dot and ignores any others it may detect. We are going to tweak his code so that it does not discard these dots but instead passes these on to the BluePrintControl in the form of events, so that we can also track the point-of-view.

    Whenever the Wiimote state changes (this may be up to 100 times per second), the mouse emulator will receive an event fired by Brian Peeks WiimoteLib. Instead of immediately passing the first IR dot to the emulator, we’ll tap into the process. No matter how many dots are visible, we’re gonna make sure that the emulator gets only the one dot it needs to “impersonate” a mouse.

    Here’s how to tap into the emulator code. Open WiimoteWhiteboardForm.cs from the solution explorer and change the first part of the event listener as follows:

    C#

    void wm_OnWiimoteChanged(object sender, WiimoteChangedEventArgs args)
    {
     
        mut.WaitOne();
     
        // wiimote state with the real data
        WiimoteState wsReal = args.WiimoteState;
     
        // gets which IR point is mouse pointer and which are POV
        int mouseIR = new int();
        int[] POVIR = new int[2];
        int[,] rawIRCoors = new int[5, 2];
     
        // will contain the raw IR coors in array form for easy access
        this.IdentifyIRpoints(wsReal, ref rawIRCoors, ref mouseIR, ref POVIR);
     
     // REST OF CODE OMITTED
    }

     

    This means that the “hard” work of recognizing point-of-view and light pen is done in the IdentifyIRpoints() method. Now imagine that there are three dots visible. We know that one belongs to the light pen and the others to the point of view. In the case of three visible points we’ll assume that:

    1. the two points that are closest to each other belong to the point of view and

    2. the remaining dot belongs to the light pen

    We are able to make these assumptions since there is only one light pen and one point of view. Because the Wiimote can track up to four points simultaneously, it’s also possible to add another light pen. In this case, the algorithm becomes a little bit more complicated (and probably also less robust) but with some commonsense reasoning it should also be possible. I’ll leave that up to you!

    C#

    /// <summary>
    /// Recognizes which IR points are mouse point and POV points and returns
    /// </summary>
    /// <param name="ws">WiimoteState object</param>
    /// <param name="rawIRCoors">Returns the IR-points in a raw form. Needs an initialized int[5,2]</param>
    /// <param name="mouseIR">Returns the index of the IR mouse dot or 0 if not found</param>
    /// <param name="POVIR">Returns the indices of the IR POV dots or null if not found</param>
    void IdentifyIRpoints(WiimoteState ws, ref int[,] rawIRCoors, ref int mouseIR, ref int[] POVIR)
    {
        // Holds all IR points. Note: @index 0 the default values are stored if no point is found.
        // This is needed for faking Chung's implementation.
        int[] IRState = new int[5];
        IRState[0] = 0;
        IRState[1]=Convert.ToInt16(ws.IRState.IRSensors[0].Found);
     
        // Contains the raw coors in a bidimensional array
        rawIRCoors[0, 0] = 1023; // default value when not found
        rawIRCoors[0, 1] = 1023; // default value when not found
        rawIRCoors[1, 0] = ws.IRState.IRSensors[0].RawPosition.X;
        rawIRCoors[1, 1] = ws.IRState.IRSensors[0].RawPosition.Y;
     
        // Calculates the total number of points seen
        int noOfPointsSeen = 0;
        for (int i = 1; i <= 4; i++) {
            noOfPointsSeen += IRState[i];
        }
     
        // Executes recognition of which dot is what based on
        // the amount of dots seen
        switch (noOfPointsSeen)
        {
            // No points seen
            case 0:
                mouseIR = 0;
                POVIR = null;
                break;
     
            // Only mouse seen
            case 1:
                // passes the dotnumber containing the mouse
                for(int i=1; i<=4; i++) {
                    if(IRState[i]==1)
                    {
                        mouseIR=i;
                        break; //exit for loop
                    }
                }
                POVIR = null;
                break;
     
            // Only POV seen
            case 2:
                mouseIR = 0;
                POVIR = new int[2];
     
                // passes the dotnumbers containing the IR dots
                
                int tempIndex = 0;
                for (int i = 1; i <= 4; i++)
                {
     
                    if ((IRState[i] == 1) && (tempIndex<=1))
                    {
                        POVIR[tempIndex] = i;
                        tempIndex++;
                    }
                }
                break;
     
            // determine which dots are closest. There are the POV dots
            // (so I do not remember points but analyze each frame individually
     
            // Three dots seen
            case 3:
                double smallestDistanceSoFar = 10000000;
                double currentDistance;
                int[] closestTwoPoints = new int[2];
                POVIR = new int[2];
     
                // Algorithm to calculate the points the smallest distance apart.
                // Only checks the first 3 points (which are all visible).
                for (int i = 1; i <= 3; i++)
                {
                    for (int j = i + 1; j <= 3; j++)
                    {
                        // Calculates the distance squared between points (using Pythagoras)
                        currentDistance =
                            (rawIRCoors[i, 0] - rawIRCoors[j, 0]) * (rawIRCoors[i, 0] - rawIRCoors[j, 0]) +
                            (rawIRCoors[i, 1] - rawIRCoors[j, 1]) * (rawIRCoors[i, 1] - rawIRCoors[j, 1]);
     
                        // If this distance is smaller than the previously smallest distance,
                        // the smallest distance so far is updated, as well as the array
                        // containing the indices of this pair
                        if (currentDistance < smallestDistanceSoFar)
                        {
                            smallestDistanceSoFar = currentDistance;
                            POVIR[0] = i; POVIR[1] = j;
                        }
     
                        
                    }
                }
                
                // The mouse index is returned (can be calculated easily
                // based on the fact that the the sum of all three indexes must be 0)
                mouseIR = 6 - (POVIR[0] + POVIR[1]);
                break;
     
            // Four dots seen: not supported so no output
            case 4:
                mouseIR = 0;
                POVIR = null;
                break;
        }
    }

    Consequently, we create a new WiimoteState object only if a light pen is seen. This stripped-down state is then passed on to the emulator code, effectively filtering the information received by the emulator. An event with the point-of-view data is received by the BluePrintControl, which subsequently updates the camera position. I’ll leave checking for that in the source files to you.

    Inserting Images

    At the bottom of the BluePrintControl, there is a strip with images that can be inserted as objects. It is a so-called FlowLayoutPanel (found in the System.Windows.Forms namespace) and displays all .png images (“contained” in PictureBoxes) in a certain directory. Make sure you set the right directory by modifying the defaultImageDirectory private variable in the blueprint form (not the control).

    clip_image018

    I won’t run you through the code to add images to the FlowLayoutPanel because it is quite obvious. However, it’s worth noting that we’ll use a System.IO.FileSystemWatcher to update the list:

    C#

    private bool SetUpFileSystemWatcher()
    {
        try
        {
            fileSystemWatcher = new System.IO.FileSystemWatcher(defaultImageDirectory, defaultSupportedImageMasks);
        }
        catch (Exception ex)
        {
            System.Console.Out.WriteLine("Error: {0}", ex.Message);
            return false;
        }
     
        this.fileSystemWatcher.Changed += new FileSystemEventHandler(fileSystemWatcher_Changed);
        this.fileSystemWatcher.EnableRaisingEvents = true;
     
        return true;
    }
    Drag ‘n’ drop

    The drag procedure is initiated when the user clicks an image:

    C#

    // initiates the drag procedure
    private void pictureBox_MouseDown(object sender, MouseEventArgs e)
    {
        PictureBox pictureBox = (PictureBox)sender;
        pictureBox.DoDragDrop(pictureBox.Tag, DragDropEffects.Copy);
    } 

     

    Now, we’ll have to subscribe to the DragEnter (when the cursor enters the control while dragging is in progress) in the BluePrintControl and DragDrop (when the user drops an object by releasing the button) events:

    C#

    // Subscribes to (drag and) drop user actions
    this.DragEnter += new DragEventHandler(BluePrintControl_DragEnter);
    this.DragDrop += new DragEventHandler(BluePrintControl_DragDrop);

    In the method handling the DragDrop event, we can receive the object containing information regarding the image using the GetData method:

    C#

    // called on a drop (as in drag & drop)
    void BluePrintControl_DragDrop(object sender, DragEventArgs e)
    {
        ImageObject tempImageObject = 
            (ImageObject)e.Data.GetData("DualDesign.Data_containers.ImageObject");
        // code below omitted
    }

     

     

    Coding the 3D Control

    clip_image020

    Thread-safety

    We’ve come a long way by programming the blueprint. Since the 3D view doesn’t have to accept direct user input – all that is handled by the blueprint – all it has to do is display the home as-is. There are three types of data that the 3Dcontrol has to receive:

    1. The walls

    2. The objects

    3. The location and orientation of the point of view

    This introduces some problems related thread-safety: the 3D control and blueprint run asynchronously on separate threads. Imagine that the 3dview is reading an object that is just in the process of being deleted; in this case data corruption may occur and the application may crash with some seemingly unrelated error code.

    We’ll use two different methods to avoid these issues in order to assess performance. The objects are stored in a shared static class that uses locking to prevent data corruption. On the other hand, the walls and the point of view location object are sent in the form of events that have a copy of the data attached. Now I hear you ask: why use two different methods? That’s because I wanted to assess the performance of both methods, which, by the way, turned out to be similar.

    Here, I’ll focus on only the objects because the locking procedure they employ is cleaner and easier to implement. If I’d have to do the project again, I wouldn’t even bother using the data-exchange-through-events method.

    Locking the object list

    First, we’ll have to go back to the BluePrintControl for a moment. Internally, this control keeps track of the list containing all objects that have been placed on the blueprint. Also, we’ll have to make sure we can access this data from the 3D Control by implementing a static property in the BluePrintControl. In order to make it thread-safe, we’ll lock access to the list: simultaneous reads are possible, though simultaneous reads/writes are not.

    ReaderWriterLockSlim, a class written by a programmer who was unhappy by the low-performing ReaderWriterLock class included in .NET, is what we are going to use for locking. This slimmed down version performed so well that Microsoft eventually bought it and included it in subsequent versions of .NET, and so we are now reaping the fruits J :

    C#

    static ReaderWriterLockSlim texturedObjectListLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
     
    static List<ImageTextureObject> objectList = new List<ImageTextureObject>();
     
    /// <summary>
    /// Contains all ImageTextureObjects
    /// </summary>
    public static List<ImageTextureObject> ObjectList
    {
        get 
        {
            texturedObjectListLock.EnterReadLock();
            try { return BluePrintControl.objectList; }
            finally { texturedObjectListLock.ExitReadLock(); }
        }
        private set
        {
            texturedObjectListLock.EnterWriteLock();
            try { BluePrintControl.objectList = value; }
            finally { texturedObjectListLock.ExitWriteLock(); }
        }
    }

    Unfortunately, however, we haven’t achieved complete thread-safety: access to the list is locked, but not to the objects themselves! For this reason the access to properties of the ImageTextureObjects need locking too:

    public Vector3 Position
    {
        get 
        {
            objectLock.EnterReadLock();
            try { return position; }
            finally { objectLock.ExitReadLock(); }
        }
        set 
        {
            objectLock.EnterWriteLock();
            try
            {
                dataChanged = true;
                position = value; 
            }
            finally { objectLock.ExitWriteLock(); }
        }
    }

     

    Always use a try/finally combination to ensure that the writelock is released in all cases, no matter what.

    Drawing to the GraphicsDevice

    Now that we have reliable access to all the data we need, let’s actually draw our house in 3D! Of course, we rely heavily on XNA to get this done. This is what the Draw() method looks like:

    C#

    protected override void Draw()
    {
        // sets up device for new rendering round
        this.GraphicsDevice.Clear(Color.White);
        this.GraphicsDevice.RenderState.CullMode = CullMode.CullClockwiseFace; // hides clockwise faces
        basicEffect.World = Matrix.Identity;
        basicEffect.View = camera.View;
        basicEffect.Projection = camera.Projection;
        // draws elements to screen
        this.drawWalls();
        this.drawSkySphere();
        this.drawImageObjects(); // these should be drawn last due to transparency
    }

    A graphics cards works by rendering large amounts of triangle surfaces. Often, you only need to draw one side of the triangle, as is the case for us: since our walls have thickness, one side of each triangle is facing “into” the wall itself and will never be seen from that side. Therefore, we use the GraphicsDevice.RenderState.CullMode to tell the graphics card to not even bother drawing the triangle when the camera is on the “wrong” side of the triangle. This enhances performance, though not so much in our case since our scenes are very simple, but it is still a small effort to tell the graphics card this.

    Three essential matrices are also set: the Projection matrix determines the camera’s lens, the View matrix the camera’s position, and the World matrix the position of the drawn objects.

    Looking at the drawWalls() method, the walls are drawn using the userfriendly DrawUserIndexedPrimitives method. We basically give two lists. The first contains a set of coordinates in 3D space (the corners of the walls) and the second contains a list of integers that refer to coordinates in the other list. In this way, we can easily construct a series of triangles while only specifying the coordinates of the corners once:

    C#

    GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalColor>
    (
        PrimitiveType.TriangleList,
        this.All3DWallVertices, 0,
        this.All3DWallVertices.Length,
        this.All3DWallIndices, 0,
        this.All3DWallIndices.Length / 3
    );

     

    The images are displayed by drawing textured triangles. In the DrawImageObjectTexturedFaces() method, we use the VertexPositionTexture custom vertex format to this end. This not only defines the texture itself, but also defines texture coordinates: these specify which part of the texture should be drawn onto a triangle:

    C#

    this.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionTexture>
    (
        PrimitiveType.TriangleList,
        imageTextureObject.TexturedFaceVertices,
        0, 4,
        imageTextureObject.TexturedFaceIndices,
        0, 2
    );

     

     

    Conclusion

    The code is quite comprehensive, so I’ve only been able to explain a few parts and ideas. If you want to try this out, the download link for the source code is at the top of the article! And be creative! Why not create a smart Google Earth mash-up in which you can use point-of-view to navigate intuitively though a landscape? Surely there are also some innovative games you can think of. Be inspired by the Nintendo DS games that make use of a similar dual display layout!

    About The Author

    Thijs Brilleman has a Master in Mechanical Engineering but has always been interested in fun, tangible machines that are somewhere in-between computers and mechanical machines. Currently employed at the University of Twente, he designs new interaction methods that can be used in collaborative design. You can reach him at: thijsbrilleman@gmail.com

  • Coding4Fun

    Creating a Shuffleboard Game using Silverlight

    • 5 Comments

    Introduction

    In this walkthrough, we will create a table shuffleboard style game for Windows Phone 7 using Silverlight. Many bars feature these long wooden table games, in which players slide metal pucks down the length of the table, attempting to get as close as possible to the far end without sliding off the edge.

    Andy Beaulieu
    Microsoft Silverlight MVP

    http://www.andybeaulieu.com

    http://www.spritehand.com

    Source Code: http://shuffleboard.codeplex.com
    Difficulty:  Intermediate
    Time Required:  4 hours 
    Cost:  FREE
    Software Needed:  Windows Phone Developer Tools, Expression Blend 4, Expression Blend Add-in Preview 2 for Windows Phone, Expression Blend SDK Preview 2 for Windows Phone

    image Multi-Targeting

    Windows Phone 7 uses a version of Silverlight 3 with a few bonus features. Because this version of Silverlight is so close to the Web version of Silverlight 3, we’ll take the approach of Multitargeting a solution for both platforms. To do so, start with a Silverlight 3 (web) application template, and then add in a Windows Phone project with linked files that re-use the Silverlight 3 solution. This allows us to deploy to many different platforms using the same code base.

    Let’s get started by creating our Solution in this way.

    Creating the Solution and MainPage

    1. In Expression Blend 4, create a new Silverlight Application + Website template named Shuffleboard. Be sure to select “3.0” from the Version dropdown, as this is what is supported by Windows Phone (in a little bit we’ll add in the Windows Phone project template as well).
      image
    2. In the Objects and Timeline Panel, select the main UserControl and set its Width to 480 and its Height to 800. This is the size of a Windows Phone UI in Portrait mode.
    3. Select the LayoutRoot Grid and set its Width to 480 and its Height to 800.
    4. For games, a Canvas layout container is better than a Grid, so let’s change the LayoutRoot container type. Right-click LayoutRoot and select Change Layout Type/Canvas.
      image
    5. Since some parts of our UI can appear outside of the game area, we want to add a Clip to this main canvas so that the user does not see these outside elements. Add the following just under the LayoutRoot Canvas:
      This XAML is snippet “MainPage Clip” inside snippets.txt.
      <Canvas.Clip>
          <RectangleGeometry Rect="0,0,480,800"/>
      </Canvas.Clip>
    6. We’ll be using some pre-built Behaviors that make it easy to introduce physics into Silverlight using the Farseer Physics Library. Right-click the Silverlight project and select Add Reference. Select the following assemblies, located in the sample download:

      \ShuffleBoard\Bin\Debug\FarseerPhysics.dll
      \ShuffleBoard\Bin\Debug\Spritehand.FarseerHelper.dll
      \ShuffleBoard\Bin\Debug\Spritehand.PhysicsBehaviors.dll

      NOTE: To learn more about the Physics Helper Library, visit http://physicshelper.codeplex.com

      Get Microsoft Silverlight

    Creating the Game User Control

    Next we’ll create a separate user control that will contain the core game logic. By creating a separate user control, we can easily target different platforms by placing the user control inside different MainPage elements.

    1. Right-click the Shuffleboard Silverlight project and select Add New Item. Select UserControl and name the control “ucMainGame”.
      image
    2. Change the LayoutRoot element from a Grid to a Canvas.
    3. Set the Width to 480 and the Height to 800 for both the UserControl and LayoutRoot Canvas elements.
    4. Set the Background color to Black for the LayoutRoot Canvas.
    5. Build the project by pressing Ctrl+Shift+B.
    6. Back on MainPage, add an instance of ucMainGame to the page by going to the Assets Panel and expanding Controls/All. Drag an instance of ucMainGame to the artboard.
      image
    7. Rename the ucMainGame control to ucMainGame1.
    8. Set the Left and Top properties of ucMainGame1 to 0.

    Creating the Table

    We’ll use a pre-rendered PNG image for the shuffleboard table.

    1. Create a new subfolder in the Silverlight project named “images”.
    2. Add the following JPG image into the new images subfolder, located in the sample download:
      \ShuffleBoard\images\shuffleboardTable.jpg
    3. Open the ucMainGame user control.
    4. Insert a new Canvas element named cnvTable, and set the following attributes:
      1. Width = 480
      2. Height = 800
      3. Left = 0
      4. Top = 0
    5. In the Assets Panel, expand the Behaviors category and drag a PhysicsControllerBehavior on the cnvTable. This behavior introduces the core physics simulation into the game. Set the properties of the Physics Contoller as follows (this turns of gravity and sets some other physics parms):
      image
    6. Inside the cnvTable Canvas, add a second Canvas named cnvTableInner, which will hold the table image. Set the following properties on cnvTableInner:
      1. Width = 360
      2. Height = 1077
      3. Left = 60
      4. Top = -277
    7. Drag the shuffleboardTable.jpg image from the Projects Panel into cnvTableInner. Set the following properties on the image:
      1. Width = 360
      2. Height = 1077
      3. Left = 0
      4. Top = 0
    8. The Objects and Timeline Panel should look like so:
      image
    9. We will need to detect when a puck hits the edge of the table and “falls off.” Let’s add in some Rectangle elements and add Physics Behaviors to them.
    10. Add a new Rectangle named rectSensorLeft and size it so that it covers the entire left side of the table. Set the following properties:image
      1. Width = 60
      2. Height = 1190
      3. Left = -40
      4. Top = -334
      5. Opacity = 20%
    11. Drag a PhysicsObjectBehavior from the Assets Panel Behaviors onto rectSensorLeft, and then set its IsStatic property to true.
    12. Add three more Rectangles by copying rectSensorLeft, so that they surround the borders of the table:
      1. rectSensorRight
      2. rectSensorTop
      3. rectSensorBottom
    13. Your artboard should look similar to the following:
      image
    14. Now we’ll add in the goal Rectangles at the end of the table. These will be used to determine the number of points a puck receives. Draw out three Rectangles that cover the width of the board and are 100 pixels high. Name these rectPoints3, rectPoints2, and rectPoints1. Set their Stroke to Red and their Fill to No brush.
    15. Add a TextBlock element inside each Rectangle to depict the points. Set the text properties to 3, 2, and 1 so that the artboard looks similar to the following:
      image
    16. We’ll also need a Rectangle representing the “slide zone” where players can legally slide a puck (we don’t want to allow sliding all of the way down the table!). Add a Rectangle named rectInBounds and position it at the lower end of the table:
      1. Width=360
      2. Height=292
      3. Left = 60
      4. Top = 508
      5. Fill = No Brush
      6. Stroke = Red

    Get Microsoft Silverlight

    Adding the Pucks

    For the pucks, we are going to add in an existing control which has the artwork completed.

    1. Right-click the project and select Add Existing Item.
    2. Browse to the following two files in the sample code download:
      \ShuffleBoard\ucPuck.xaml
      \ShuffleBoard\ucPuck.xaml.cs
    3. Open ucPuck.xaml on the artboard and notice the following in the Objects and Timeline Panel:
      image
      1. There is a Canvas named Puck that has a PhysicsObjectBehavior applied to it. This allows each instance of this Canvas to behave like a Physics Object so it animates with velocity and force and participates in collisions. Note that this Behavior has a large MomentOfInertia value. This keeps the object from rotating due to torque and collisions. Also note the RestitutionCoefficient, which gives the object some “bounce.”
        image
      2. There is a “cnvInner” Canvas that defines the look of the Puck.
      3. There is a StoryBoard defined, sbLostPuck, which we will execute when the puck “falls off the edge” of the table.
      4. Open the code-behind file, ucPuck.xaml.cs, and note the get / set property for the color of the puck. This will allow us to create both Red and Blue pucks for each player.
    4. Build the project by hitting Ctrl+Shift+F5 and go back to ucMainGame.
    5. From the Assets Panel, expand Controls/All and find the ucPuck control. Drag an instance of this onto the artboard. Name this bluePuck1.
    6. Copy and paste bluePuck1 twice so that you have three blue pucks. Name the new pucks bluePuck2 and bluePuck3.
    7. Copy and paste a fourth puck and name it redPuck1. In the Properties Panel, go to the Miscellaneous Category and change the ColorHighlight and ColorMain properties to a Red color:
      image
    8. Copy and Paste redPuck1 two times and rename these new pucks redPuck2 and redPuck3.
    9. Let’s test our table. Find the PhysicsControllerBehavior just under the cnvTable Canvas in the Objects and Timeline Panel and set the MousePickEnabled property to true.
    10. Run the project by clicking F5. Try dragging some pucks around with the mouse.

    Get Microsoft Silverlight

    Adding the Scoreboard

    We need a way of tracking players’ scores, so let’s add in a simple scoreboard for Blue vs. Red.

    1. Right-click the project and select Add Existing Item.
    2. Browse to the following two files in the download:
      \ShuffleBoard\ShuffleBoard\ucScoreBoard.xaml
      \ShuffleBoard\ShuffleBoard\ucScoreBoard.xaml.cs
    3. Open ucScoreBoard and notice the following:
      1. It has a TextBlock for Red scores and a TextBlock for Blue scores.
      2. In the code-behind, there are setters and getters to set the score.
    4. Build the project by selecting Ctrl+Shift+B.
    5. Switch back to ucMainGame.
    6. Drag an instance of ucScoreBoard from the Assets Panel onto into LayoutRoot.
    7. Name the element ucScoreBoard1, and position it at the top left of the Canvas like so:

    image

    Adding a Player Up Display

    We’ll need a simple control that displays which player’s turn it is.

    1. Right-click the Silverlight project and select Add/Existing Item.
    2. Browse to and select the following two files in the sample folder:
      \ShuffleBoard\ucPlayerUp.xaml
      \ShuffleBoard\ucPlayerUp.xaml.cs
    3. Note the following about ucPlayerUp:
      1. The code-behind file has a simple property, IsBlueTurn, which shows the appropriate message in the UI.
    4. Build the project by clicking Ctrl+Shift+B
    5. Back on ucMainGame, drag an instance of ucPlayerUp into cnvTableInner.
    6. Name the control ucPlayerUp1 , and set the following properties:
      image

    Adding a 3D Look

    Let’s give our table a bit of a 3D look by adding a Perspective transform so the table appears to go off into the distance.

    1. Create a new StoryBoard named sbRotateTable.
      image
    2. Select the cnvTable Canvas element.
    3. Advance the timeline ahead one second.
    4. In the Properties Panel, under Projection, set the X value to -40.
      image
    5. Your artboard should now look something like this:
      image
    6. Close the Storyboard to end recording.
      image
    7. We want to rotate the table after the PhysicsController is initialized so the Physics Helper can determine the boundaries of the physics objects properly. Start by adding in some Using statements at the top of ucMainGame.xaml.cs:
      This code is snippet “ucMainGame Imports” inside snippets.txt.
      using System.Collections.Generic;
      using Spritehand.FarseerHelper;
      using FarseerGames.FarseerPhysics.Mathematics;
      using System.ComponentModel;
    8. Next, let’s add in some class-level declarations, which will store the physics controller, list of pucks, and some variables used to track shooting and scoring:
      This code is snippet “ucMainGame Declarations” inside snippets.txt.
      PhysicsControllerMain _physicsController;
      List<PhysicsSprite> _pucks = new List<PhysicsSprite>();
      List<PhysicsSprite> _redPucks = new List<PhysicsSprite>();
      Point _ptStartShot;
      bool _draggingPuck;
      int _draggingStartTick;
      
      int _currentPuckToShoot = 0;
      int _currentPuckShot = -1;
      int _gameOverScore = 15;
    9. In the ucMainGame() constructor, add some code to set the MaxFrameRate to 30 (Windows Phone will be limited to 30 FPS) and wire up the Loaded event handler.
      This code is snippet “ucMainGame Constructor” inside snippets.txt.
      this.Loaded += new RoutedEventHandler(ucMainGame_Loaded);
      Application.Current.Host.Settings.MaxFrameRate = 30;
    10. Implement the ucMainGame_Loaded event handler, which gets a reference to the Physics Controller and wires up our event handlers.
      This code is snippet “ucMainGame Loaded” inside snippets.txt.
      void ucMainGame_Loaded(object sender, RoutedEventArgs e)
      {
          if (DesignerProperties.GetIsInDesignMode(this))
              return;
      
          _physicsController = 
              cnvTable.GetValue(
                  PhysicsControllerMain.PhysicsControllerProperty
              ) as PhysicsControllerMain;
          _physicsController.Initialized += _physicsController_Initialized;
          _physicsController.Collision += _physicsController_Collision;
          _physicsController.TimerLoop += _physicsController_TimerLoop;
      }
    11. Next, we’ll add in the event handlers that we just wired up in the loaded event. Note that the Initialized event starts the Rotate Table StoryBoard.
      This code is “ucMainGame Event Handlers” inside snippets.txt.
      void _physicsController_Initialized(object source)
      {
          sbRotateTable.Begin();
      }
      
      void _physicsController_Collision(string sprite1, string sprite2)
      { }
      
      void _physicsController_TimerLoop(object source)
      { }
    12. Run the Project by clicking F5. Note the 3D look and try manipulating the pucks with the mouse.

    Controlling the Pucks

    Next, we’ll add logic to control the player’s turn and control and along with score. Note that, when we implement the Windows Phone version in a bit, we can take advantage of Multitouch Manipulation events. But since these events are not available in the Web version of Silverlight 3, we’ll use a simple mouse input mechanism.

    1. Let’s turn off the default mouse manipulation. Select the PhysicsControllerBehavior just under the cnvTable Canvas and set the MousePickEnabled property to false.
    2. We need to get references to our Pucks, which the Physics Helper has translated into PhysicsSprite objects. A Physics Sprite contains the XAML UI for the Physics Object, plus the Physics Engine attributes for the underlying physics objects–including boundary shape, mass, velocity, etc.
      This code is “ucMainGame Initialized” inside snippets.txt.
    3. Now we need to handle the Mouse events on the pucks in order to control the player’s shot. When the player clicks the mouse down on a puck, we track the position along with time that they clicked. When the player moves the mouse, we update the puck position and also check if the player paused or moved backwards. We do this in case the player is just adjusting the puck position and not yet sliding the puck for a shot. Finally, in the Mouse up event, we release the puck in the specified direction and apply an appropriate amount of force to it.  Copy in the snippet “Puck Mouse Control” from snippets.txt.  Code is not shown here for brevity.
    4. When the Physics Helper Library does its magic, it translates existing UI Elements into PhysicsSprite objects. So what if we want to get at the original controls, perhaps to execute a StoryBoard that we defined on them? We can do this by using the FindName method to get the original user control instance. So, add the code below, which we’ll use to get a reference to the Puck Storyboard “sbLostPuck”.
      ucPuck GetPuckControl(string spriteName)
      {
          ucPuck puck;
          var parent = PhysicsControllerMain.ParentCanvas;
          switch (spriteName)
          {
              case "Puck":
                  puck = parent.FindName("bluePuck1") 
                          as ucPuck;
                  break;
              case "Puck_1":
                  puck = parent.FindName("bluePuck2") 
                          as ucPuck;
                  break;
              case "Puck_2":
                  puck = parent.FindName("bluePuck3") 
                          as ucPuck;
                  break;
              case "Puck_3":
                  puck = parent.FindName("redPuck1") 
                          as ucPuck;
                  break;
              case "Puck_4":
                  puck = parent.FindName("redPuck2") 
                          as ucPuck;
                  break;
              default:
                  puck = parent.FindName("redPuck3") 
                          as ucPuck;
                  break;
          }
      
          return puck;
      }
    5. Finally, to score, we’ll determine if the puck lies between any of the Rectangles in the end zone.
      This code is the snippet “Get Points for Puck” in snippets.txt.
      int GetPointsForPuck(PhysicsSprite puck)
      {
          int score = 0;
          Vector2 puckPos = puck.BodyObject.Position;
      
          double left = Convert.ToDouble(
              rectPoints3.GetValue(Canvas.LeftProperty));
          double top = Convert.ToDouble(
              rectPoints3.GetValue(Canvas.TopProperty));
      
          if ((puckPos.X > left && puckPos.X < left + rectPoints3.Width) 
           && (puckPos.Y > top && puckPos.Y < top + rectPoints3.Height))
              score = 3;
      
          left = Convert.ToDouble(rectPoints2.GetValue(Canvas.LeftProperty));
          top = Convert.ToDouble(rectPoints2.GetValue(Canvas.TopProperty));
          if ((puckPos.X > left && puckPos.X < left + rectPoints2.Width) 
           && (puckPos.Y > top && puckPos.Y < top + rectPoints2.Height))
              score = 2;
      
          left = Convert.ToDouble(rectPoints1.GetValue(Canvas.LeftProperty));
          top = Convert.ToDouble(rectPoints1.GetValue(Canvas.TopProperty));
          if ((puckPos.X > left && puckPos.X < left + rectPoints1.Width) 
           && (puckPos.Y > top && puckPos.Y < top + rectPoints1.Height))
              score = 1;
      
          return score;
      }

    Implement the “Game Loop”

    Most games are controlled by a “Game Loop” that executes many times per second. Within this Game Loop, we can check for collisions, perform enemy AI, and scoring. The PhysicsController fires an event called “TimerLoop” that we can use for this purpose.

    1. Replace the existing TimerLoop and Collision events with the following code snippet, which checks to see if any pucks have been shot and if we are ready for the next shot. Do this by seeing if the puck velocity has slowed down to nearly a stop…
      This code is snippet “Timer Loop” inside snippets.txt.
      void _physicsController_TimerLoop(object source)
      {
          // check to see if the current shot is completed
          if (_currentPuckShot >= 0)
          {
          var puck = _pucks[_currentPuckShot].BodyObject;
      
              if (puck.Enabled == false ||
                Math.Abs(puck.LinearVelocity.X) < 3                                   
                && 
                Math.Abs(puck.LinearVelocity.Y) < 3)
              {
                  // did the shot clear the end zone?
                  if (!PointWithinBounds(
                    new Point(puck.Position.X, 
                             puck.Position.Y)))
                  {
                      _currentPuckShot = -1;
                      _currentPuckToShoot++;
                      SetupThePuck();
                  }
              }
          }
      }
      
      void _physicsController_Collision(string sprite1, string sprite2)
      {
          // check for puck off sides
          if (sprite1.StartsWith("rectSensor") && sprite2.StartsWith("Puck"))
          {
              ucPuck puck = GetPuckControl(sprite2);
      
              _physicsController.PhysicsObjects[sprite2].
                  BodyObject.Enabled = false;
      
              puck.sbLostPuck.Begin();
          }
      }

    Targeting Windows Phone

    So far, we’ve created a Silverlight 3, web-based version of a shuffleboard game. Next we’ll quickly port this to Windows Phone, taking advantage of some of the platform’s capabilities, such as multitouch. We’ll do this by using linked files that point back to our existing Silverlight 3 project.

    1. Right-click the Silverlight solution in Blend and select Add New Project.
    2. Select Windows Phone Application and enter ShuffleBoard.WindowsPhone for the name.
      image
    3. Delete the TitleGrid and ContentGrid elements from the Objects and Timeline Panel.
    4. Convert LayoutRoot to a Canvas by right-clicking it and selecting Change Layout Type/Canvas.
    5. Right-click the project and select Add Reference. Browse to the following assemblies located in the sample download (these are the WindowsPhone versions of the Physics Helper and Farseer):

      \ShuffleBoard.WindowsPhone\Bin\Debug\FarseerPhysics.dll
      \ShuffleBoard.WindowsPhone\Bin\Debug\Spritehand.FarseerHelper.dll
      \ShuffleBoard.WindowsPhone\Bin\Debug\Spritehand.PhysicsBehaviors.dll
    6. We need references to the assemblies used for Behaviors. An easy way to do this is to add a Behavior to an element and then delete it. From the Assets panel, drag a PhysicsControllerBehavior onto LayoutRoot and then delete it. Note that this adds a reference to System.Windows.Interactivity to the project.
    7. Next, we’ll add in the linked files from the existing Silverlight 3 project. Right-click the ShuffleBoard.WindowsPhone project and select Add/Link to Existing Item. Select the following files:
      1. ucMainGame.xaml
      2. ucMainGame.xaml.cs
      3. ucPlayerUp.xaml
      4. ucPlayerUp.xaml.cs
      5. ucPuck.xaml
      6. ucPuck.xaml.cs
      7. ucScoreBoard.xaml
      8. ucScoreBoard.xaml.cs
    8. 8. Add a new folder to the project named images.
    9. 9. Right-click the images folder and select Link to Existing item, then navigate to the following image in the sample folder:
      \ShuffleBoard\images\shuffleboardTable.jpg
    10. Build the project by clicking Ctrl+Shift+B.
    11. From the Assets Panel, under Controls/All, find ucMainGame and drag an instance onto LayoutRoot. Set the Left and Top properties to 0.
    12. Right-click the WindowsPhone project and select “Startup” to set this as the startup project.
    13. Run the project by clicking F5.

    Get Microsoft Silverlight

    Windows Phone Touch Input

    So far, we’ve used a simple mouse-event-based input mechanism for shooting the pucks. But on the Windows Phone 7 platform, we can do better than that by using Multitouch events. These events include an inertia property that will make our puck physics more realistic.

    1. Since we are going to need to support two different platforms now (Web and Windows Phone), we need to introduce a Conditional Compilation Symbol so that the compiler can differentiate between each platform’s code.
    2. Open the solution inside Visual Studio by right-clicking it in the Projects Panel and then select “Edit in Visual Studio”
      image
    3. Right-click the ShuffleBoard.WindowsPhone Project and select Properties.
    4. 4. Add in a new compilation symbol for WINDOWS_PHONE.
      NOTE: Future releases of WP7 Tools will likely define a default compilation symbol, so you may already see one defined here.
      image
    5. Now we’ll add in the Windows Phone-specific events for manipulation using touch. Open ucMainGame.xaml.cs and find the _physicsController_Initialized event handler. Replace the events for the mouse events with the following, which will wire up either the manipulation events (for Windows Phone) or the mouse events (for Silverlight Web):
      This code is snippet “Manipulation Events” inside snippets.txt.
      #if WINDOWS_PHONE
          puck.ManipulationStarted += 
            new EventHandler<ManipulationStartedEventArgs>
            (puck_ManipulationStarted);
          puck.ManipulationCompleted += 
            new EventHandler<ManipulationCompletedEventArgs>
            (puck_ManipulationCompleted);
          puck.ManipulationDelta += 
            new EventHandler<ManipulationDeltaEventArgs>
            (puck_ManipulationDelta);
      #else
          puck.MouseLeftButtonDown += 
            new MouseButtonEventHandler(puck_MouseLeftButtonDown);
          puck.MouseMove += 
            new MouseEventHandler(puck_MouseMove);
          puck.MouseLeftButtonUp += 
            new MouseButtonEventHandler(puck_MouseLeftButtonUp);
      #endif
    6. Now we just need to implement those event handlers for manipulation. The main consideration here is that the ManipulationCompleted event passes in a FinalVelocity.LinearVelocity parameter, which we can use to create realistic physics from the user’s shot:

      Add the code snippet “Manipulation Event Handlers” from snippets.txt

      Vector2 force = new Vector2(
          (float)(e.FinalVelocities.LinearVelocity.X * scalePower), 
          (float)(e.FinalVelocities.LinearVelocity.Y * scalePower));
    7. Run the project by clicking F5, and try sliding some pucks.

    Performance

    Depending on your configuration, you may experience poor performance for the shuffleboard game when running it in the Windows Phone emulator. There are different reasons for this, including video card drivers and virtualization settings. Please refer to this blog post, which details these performance considerations.

    1. 1. Inside the ucMainGame constructor, let’s turn on the Frame Rate counter so we can see our current frame rate. This code is snippet “Frame Rate Counter” inside snippets.txt
      Application.Current.Host.Settings.EnableFrameRateCounter = true;
    2. Run the project by hitting F5, and note the current frame rate.
      NOTE: Your development configuration may prevent you from having an adequate framerate in the Windows Phone Emulator! Please refer to this blog post for tips on emulator performance.

    One performance tweak we can easily make is taking advantage of hardware acceleration. Silverlight enables us to use the video card to render elements, which can greatly increase performance. To do this, we add a Cachemode=”BitmapCache” attribute to any elements we want to hardware accelerate.

    1. Open ucPuck.xaml and note that there is a CacheMode attribute on the Puck Canvas.
    2. Open ucMainGame.xaml and add a CacheMode attribute to the cnvTable Canvas element.
      <Canvas x:Name="cnvTable" Width="480" Height="800" 
      d:LayoutOverrides="Width, Height" CacheMode="BitmapCache">
    3. Run the project and note the frame rate change.

    Get Microsoft Silverlight

    Adding Sound

    Sound is one area where Windows Phone and Web Silverlight differ a bit. Under Silverlight for Web, we can use the MediaElement class to play back WMA and MP3 format audio. Multiple instances of the MediaElement class can be played simultaneously, and the sound output will be automatically mixed.

    However, on Windows Phone we have access to the XNA game libraries, including sound support. This is a much more efficient way to add mixed sound to a Silverlight game on Windows Phone, but it only supports WAV format sound files.

    The Physics Helper Library has a useful class wrapper for sounds, which we’ll use to play a “hit puck” sound on both platforms.

    1. In the ShuffleBoard (Silverlight Web) project, right-click and select Add Folder.
    2. Name the folder sounds.
    3. Add an existing item to the sounds folder from this location:
      \ShuffleBoard\ShuffleBoard\sounds\hitPuck.wma
    4. Set the Build Action of the file to Content.
    5. In the ShuffleBoard.WindowsPhone project, right-click and select Add Folder.
    6. Name the folder sounds.
    7. Add an existing item to the sounds folder from this location:
      \ShuffleBoard.WindowsPhone\sounds\hitPuck.wav
    8. Set the Build Action of the file to Content.
    9. Inside ucMainGame.xaml.cs, add a class-level declaration for a SoundMain class (this is defined in the Physics Helper Library):
      This snippet is “Declare the Sound” inside snippets.txt.
      SoundMain _soundPuckHit;
    10. 10. Inside the ucMainGame_Loaded event, add code to declare the two sounds. Note that Windows Phone uses a WAV format sound without a “/” prefix for the root.
      This snippet is “Declare the Sound” inside snippets.txt.
      #if WINDOWS_PHONE
          _soundPuckHit = new SoundMain(this.LayoutRoot,
                         "sounds/hitPuck.wav", 2, 0);
      #else
          _soundPuckHit = new SoundMain(this.LayoutRoot, 
                         "/sounds/hitPuck.wma", 2, 0);
      #endif
    11. Inside the _physicscontroller_collision event, add code to play the sound if a puck-to-puck collision occurs and the velocity is > 10:
      This snippet is “Play the Sound” inside snippets.txt.
      // check for puck to puck collsion
      if (sprite1.StartsWith("Puck") && sprite2.StartsWith("Puck"))
      {
          PhysicsSprite sprite = _physicsController.PhysicsObjects[sprite1];
      
          if (Math.Abs(sprite.BodyObject.LinearVelocity.X) > 10
              || Math.Abs(sprite.BodyObject.LinearVelocity.Y) > 10)
          {
              _soundPuckHit.Play();
          }
      }
    12. Run the project and try hitting pucks together.

    Get Microsoft Silverlight

    Summary

    Since the 1.1 Alpha release, Silverlight has offered a compelling casual game development environment. With the addition of Windows Phone, along with demos of Silverlight running on other embedded devices, you can bet the future of Silverlight for gaming is bright.

    Here are a few resources for more information:

    Andy’s blog
    http://www.andybeaulieu.com

    Andy’s Silverlight Demos
    http://www.spritehand.com

    The Physics Helper Library
    http://physicshelper.codeplex.com

    Great free resource for learning Expression Blend and Silverlight
    http://www.microsoft.com/design/toolbox/

  • Coding4Fun

    Coding4Fun Cannon – Coding for Windows Phone 7

    • 0 Comments

    Clint Rutkas - Microsoft
    Coding4Fun Ninja

    Source Code: Download

    Difficulty: Software Medium - Hardware Advanced
    Time Required: 2 weeks
    Software Needed: Visual C# 2010 Express, Visual Studio Phone Developer Express
    Hardware: Servo City Pan-Tilt, Robot Marketplace battlebot kit, Phidget 0/0/4 Relay board, Phidget Advance Servo controller, t-shirt cannons, nuts and bolts, polycarbonate, and a lot of coffee / soda.

    In this multipart article series, we’ll cover:

    1. Project overview
    2. Developing the phone application
    3. Developing the server software
    4. Building the robot
    5. Creating the phone user experience
    6. Improving performance
    Get Microsoft Silverlight

    Session WPH 314 – Coding4Fun: Learning Windows Phone 7 Development by creating a robotic t-shirt cannon!

Page 1 of 64 (631 items) 12345»