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