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