Silverlight and Windows Phone 7 Game

With the release of Silverlight 4 RC the Windows Phone 7 developer tools I wanted to take a stab at building a Silverlight application for both the web and phone to see what kind of differences there are between the two. Except for the inability to use the ChildWindow I was able to build out controls and share them between the two applications. The main differences were in the MainPage.xaml that is created, along with the default styles, when you create a new Silverlight application for the web and Windows Phone 7. Of course,

I decided to create a game (called ShapeAttack) to see how it would perform on the phone emulator. Sad to say the performance on the emulator is very poor but I would imagine that it would be better on the physical device but as I do not own a Windows Phone 7 yet the emulator has to do for now. For that reason I would recommend doing this parallel type of development so you can actually test your application.

What I did was create all of the game code in UserControls, including the main game surface, then I linked the files from the standard Silverlight project into the phone project.

The game is very simple and kind of cheesy :D, just click on the shapes to destroy them. And of course you can download the source code for ShapeAttack(2.8MB) or play ShapeAttack online.

ShapeAttack

3/27/2010


Performance Tips for Silverlight on Windows Phone 7

Andy Beaulieu has some helpful performance tips for Windows Phone 7 and Silverlight primarily focused on games of course.

3/25/2010


Silverlight Community

Jeff Weber, the guy behind the Farseer Physics Engine, has posted an An Open Letter To Microsoft Regarding The Silverlight Game Development Community. I fully agree and want to add my voice in the request for an XNA-like community site!

From Jeff’s post:

“I hereby request, on behalf of all the future and present Silverlight game developers,  an awesome Silverlight game development portal along the lines of what exists for the XNA Creators Club Online community.”

3/24/2010


Bug in Windows Phone 7 Emulator for Multi-Threading

Laurent Bugnion has discovered a bug in the Windows Phone 7 emulator when multi-threading and has posted a workaround:

3/20/2010


MIX 10 Sessions

Frank LaVigne has posted a very useful tool to download all of the MIX 10 session content.

3/17/2010


Pirates Video

Here is a video of the Pirates game:

[youtube http://www.youtube.com/watch?v=b9LHFS1dKWg&hl=en_US&fs=1&color1=0x2b405b&color2=0x6b8ab6]

3/11/2010


General Purpose Sprite Class

On the heels of some great posts by Bill Reiss on Sprites Part 1 and Sprites Part 2 in Silverlight I wanted to post some general base sprite classes that I use. The classes are intended to be used with the SilverSprite framework.

These classes all exist in an assembly I lovingly call the “Shady Engine” (to explain the namespaces)

The base class I used is ingeniously called Sprite. It implements an interface called ISprite. I added the interface in order to create an interface called IPlayer that the main Game class uses.

ISprite.cs

using System.Windows;using Microsoft.Xna.Framework;namespace Shady.Sprites{    public interface ISprite    {        ISprite Owner { get; set; }        Vector2 Position { get; set; }        double Rotation { get; set; }        System.Windows.Point Scale { get; set; }        double Width { get; set; }        double Height { get; set; }        Rect Bounds { get; }        bool IsActive { get; set; }    }}
.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt{ background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }

And here is the Sprite.cs file:

using System;using System.Windows;using System.Windows.Controls;using System.Windows.Markup;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Shapes;using Microsoft.Xna.Framework;namespace Shady.Sprites{    [TemplatePart(Name = PART_RootElement, Type = typeof(Canvas))]    [TemplatePart(Name = PART_ContentElement, Type = typeof(ContentControl))]    [TemplatePart(Name = PART_DebugCenter, Type = typeof(Ellipse))]    [ContentProperty("Content")]    public class Sprite : Control, ISprite    {        public const string PART_RootElement = "PART_RootElement";        public const string PART_ContentElement = "PART_ContentElement";        public const string PART_DebugCenter = "PART_DebugCenter";        protected Canvas RootElement { get; set; }        protected ContentControl ContentElement { get; set; }        protected Ellipse DebugCenterElement { get; set; }        protected TranslateTransform TranslateTransform { get; set; }        protected RotateTransform RotateTransform { get; set; }        protected ScaleTransform ScaleTransform { get; set; }        protected double HalfWidth = 0;        protected double HalfHeight = 0;        public ISprite Owner { get; set; }        public object Content        {            get { return (object)GetValue(ContentProperty); }            set { SetValue(ContentProperty, value); }        }        public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(object), typeof(Sprite), new PropertyMetadata(null));        public bool Debug        {            get { return (bool)GetValue(DebugProperty); }            set { SetValue(DebugProperty, value); }        }        public static readonly DependencyProperty DebugProperty = DependencyProperty.Register("Debug", typeof(bool), typeof(Sprite), new PropertyMetadata(false, new PropertyChangedCallback(Sprite.OnDebugPropertyChanged)));        private static void OnDebugPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)        {            var sprite = obj as Sprite;            if (sprite == null)                return;            if (sprite.DebugCenterElement != null)                sprite.DebugCenterElement.Visibility = (bool)e.NewValue ? Visibility.Visible : Visibility.Collapsed;        }        public Vector2 Position        {            get            {                var x = (double)GetValue(Canvas.LeftProperty);                var y = (double)GetValue(Canvas.TopProperty);                return new Vector2((float)x, (float)y);            }            set            {                SetValue(Canvas.LeftProperty, (double)value.X);                SetValue(Canvas.TopProperty, (double)value.Y);            }        }        public virtual double Rotation        {            get { return this.RotateTransform.Angle; }            set { this.RotateTransform.Angle = value; }        }        public System.Windows.Point Scale        {            get { return new System.Windows.Point(this.ScaleTransform.ScaleX, this.ScaleTransform.ScaleY); }            set            {                this.ScaleTransform.ScaleX = value.X;                this.ScaleTransform.ScaleY = value.Y;            }        }        public new double Width        {            get { return base.Width; }            set            {                base.Width = value;                HalfWidth = Width * 0.5;                TranslateTransform.X = -HalfWidth;                if (this.DebugCenterElement != null)                    Canvas.SetLeft(this.DebugCenterElement, HalfWidth);            }        }        public new double Height        {            get { return base.Height; }            set            {                base.Height = value;                HalfHeight = Height * 0.5;                TranslateTransform.Y = -HalfHeight;                if (this.DebugCenterElement != null)                    Canvas.SetTop(this.DebugCenterElement, HalfHeight);            }        }        public Rect Bounds        {            get            {                Vector2 position = this.Position;                return new Rect(position.X - HalfWidth, position.Y - HalfHeight, this.Width, this.Height);            }        }        private WriteableBitmap _bitmap;        protected internal virtual WriteableBitmap Bitmap        {            get            {                if (_bitmap == null && this.ContentElement != null)                {                    var content = this.ContentElement.Content;                    if (content != null && content is Image)                    {                        _bitmap = new WriteableBitmap((int)this.Width, (int)this.Height);                        _bitmap.Render((content as Image), new TranslateTransform());                        _bitmap.Invalidate();                    }                }                return _bitmap;            }        }        private bool _isActive = true;        public bool IsActive        {            get { return _isActive; }            set            {                _isActive = value;                this.Visibility = _isActive ? Visibility.Visible : Visibility.Collapsed;            }        }        public Sprite()        {            this.DefaultStyleKey = typeof(Sprite);            this.TranslateTransform = new TranslateTransform();            this.RotateTransform = new RotateTransform();            this.ScaleTransform = new ScaleTransform();        }        public override void OnApplyTemplate()        {            base.OnApplyTemplate();            this.RootElement = GetTemplateChild(PART_RootElement) as Canvas;            this.ContentElement = GetTemplateChild(PART_ContentElement) as ContentControl;            this.DebugCenterElement = GetTemplateChild(PART_DebugCenter) as Ellipse;            if (DebugCenterElement != null && !Double.IsNaN(this.Width) && !Double.IsNaN(this.Height))            {                Canvas.SetLeft(DebugCenterElement, HalfWidth - 1.5);                Canvas.SetTop(DebugCenterElement, HalfHeight - 1.5);            }            if (this.RootElement != null)            {                var group = new TransformGroup();                group.Children.Add(TranslateTransform);                group.Children.Add(RotateTransform);                group.Children.Add(ScaleTransform);                this.RootElement.RenderTransform = group;                this.RootElement.RenderTransformOrigin = new System.Windows.Point(0, 0); // At 0,0 because the translate transform positions the sprite.            }            this.Initialize();        }        public virtual void Initialize()        {        }        public virtual void Update(GameTime gameTime)        {        }        public virtual void Draw(GameTime gameTime)        {        }        /// <summary>        /// Re-initializes the sprite.        /// </summary>        public virtual void Reset()        {            this.IsActive = true;            this.Owner = null;        }        protected static void OnDependencyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)        {            var sprite = obj as Sprite;            if (sprite == null) return;            sprite.Initialize();        }    }}
.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt{ background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }

Because Sprite is a templated control there is also some XAML to go along with it (You will need to place this in a themes/generic.xaml file):

<Style TargetType="sprites:Sprite">        <Setter Property="Background" Value="{x:Null}"></Setter>        <Setter Property="Foreground" Value="{x:Null}"></Setter>        <Setter Property="Template">            <Setter.Value>                <ControlTemplate TargetType="sprites:Sprite">                    <Canvas x:Name="PART_RootElement" Background="{TemplateBinding Background}">                        <ContentControl x:Name="PART_ContentElement"/>                        <Ellipse x:Name="PART_DebugCenter" Width="3" Height="3" Fill="Red" Visibility="Collapsed"/>                    </Canvas>                </ControlTemplate>            </Setter.Value>        </Setter>    </Style>
.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt{ background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }

That is my basic Sprite class, I will post my animated sprite class next.

3/10/2010


Yar, I do be re-visiting Pirates!

One of the first games I started building in Silverlight I called Pirates! Since working on Perenthia and various other tasks I have not re-visited the game for a long time. I really would like to get this game finished so I have to decided to spend some time working on it. I hope to include some videos soon that show the game in varied stages of development. The first screen shot displays what currently exists after implementing some path finding and the SilverSprite library:

Here is the post from the game blog: Pirates Game in Silverlight

3/10/2010