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; }
}
}
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();
}
}
}
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>
That is my basic Sprite class, I will post my animated sprite class next.