Click here to Skip to main content
15,885,757 members
Articles / Multimedia / DirectX

Endogine sprite engine

Rate me:
Please Sign up or sign in to vote.
4.84/5 (53 votes)
17 Jul 200615 min read 715.5K   22.1K   216  
Sprite engine for D3D and GDI+ (with several game examples).
using System;
using System.Collections.Generic;

namespace Endogine.Animation
{
	/// <summary>
	/// Animates an object property
	/// </summary>

	public class Animator
	{
		public enum Modes
		{
			Once,
			Loop,
			PingPong
		}
		private Modes m_mode = Modes.Once;

		private bool m_bActive = true;

		private float m_fTime;
		private float m_fStep;
		private SortedList<float,AnimationKey> _slKeys;

		private System.Reflection.PropertyInfo m_pi = null;
		private object m_obj;

		private int m_nFrameInterval = 0;
		private int m_nTimeInterval = 0;

		private System.Timers.Timer m_timer;

		public Animator(object a_obj, string a_sProp)
		{
			m_obj = a_obj;
			System.Type type = a_obj.GetType();
			m_pi = type.GetProperty(a_sProp);

            _slKeys = new SortedList<float, AnimationKey>();

			if (m_pi == null)
				throw new Exception("Property "+a_sProp+" not found for animating "+a_obj.ToString());

			m_fTime = 0.0f;
			m_fStep = 1.0f;
			
			this.FrameInterval = 1;
		}

		public Modes Mode
		{
			get {return m_mode;}
			set {m_mode = value;}
		}

		public bool Active
		{
			get {return this.m_bActive;}
			set {this.m_bActive = value;}
		}

		public int FrameInterval
		{
			get {return this.m_nFrameInterval;}
			set
			{
				if (this.m_timer!=null)
				{
					this.m_timer.Dispose();
					this.m_timer = null;
					this.m_nTimeInterval = 0;
				}
				if (this.m_nFrameInterval == 0)
					EH.Instance.EnterFrameEvent+=new Endogine.EnterFrame(m_endogine_EnterFrameEvent);
				this.m_nFrameInterval = value;
			}
		}

		public int TimeInterval
		{
			get {return this.m_nTimeInterval;}
			set
			{
				if (this.m_nFrameInterval > 0)
				{
					EH.Instance.EnterFrameEvent-=new Endogine.EnterFrame(m_endogine_EnterFrameEvent);
					this.m_nFrameInterval = 0;
				}
				this.m_nTimeInterval = value;
				this.m_timer = new System.Timers.Timer(value);
				this.m_timer.Elapsed+=new System.Timers.ElapsedEventHandler(m_timer_Elapsed);
				this.m_timer.Start();
			}
		}

		public void Dispose()
		{
			//TODO: not sure how delegates work... When object is disposed, will the delegates that point to it be cleaned automatically?
            if (this.m_timer!=null)
                this.m_timer.Dispose();
			EH.Instance.EnterFrameEvent-=new Endogine.EnterFrame(m_endogine_EnterFrameEvent);
		}

		public float StepSize
		{
			get {return m_fStep;}
			set
			{
				m_fStep = value;
			}
		}

		public float GetValueAtTime(float a_fTime)
		{
			if (_slKeys.Count > 1)
			{
				//TODO: use interpolation strategies. Right now it's linear.
				//http://www.tinaja.com/cubic01.asp
				AnimationKey key1 = this.GetKeyNearTime(a_fTime, 0, true);
				AnimationKey key2 = this.GetKeyNearTime(a_fTime, 1, true);

				if (key1.Time == key2.Time) //TODO: shouldn't really happen? key2 should be null instead?
					return key1.Value;

				float fWhereInbetween = (a_fTime-key1.Time)/(key2.Time-key1.Time);
				float fVal = (key2.Value-key1.Value)*fWhereInbetween + key1.Value;
				return fVal;
			}
			else
				return a_fTime;
		}

		public void Step()
		{
			if (!m_bActive)
				return;

            if (this.m_fStep == 0)
                return;

			this.m_fTime+=this.m_fStep;
			if (this._slKeys.Count >= 1)
			{
                //TODO: optimize - should cache last key's time.
				AnimationKey finalKey = this._slKeys.Values[this._slKeys.Count-1];
				if (this.m_fTime > finalKey.Time)
				{
					if (m_mode == Modes.Once)
					{
						m_bActive = false;
						return;
					}
					else if (m_mode == Modes.Loop)
						m_fTime = 0;
				}
			}

            //TODO: optimize - should cache next key's time and only proceed if it's been passed.
			float fVal = this.GetValueAtTime(m_fTime);

			if (m_pi != null)
			{
				if (m_pi.PropertyType == typeof(int))
					m_pi.SetValue(m_obj, (int)fVal, null);
				else if (m_pi.PropertyType == typeof(float))
					m_pi.SetValue(m_obj, fVal, null);
				else if (m_pi.PropertyType == typeof(double))
					m_pi.SetValue(m_obj, (double)fVal, null);
			}
		}

		private void m_endogine_EnterFrameEvent()
		{
			this.Step();
		}

		private void m_timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
		{
			this.Step();
		}

		public void AddKey(AnimationKey key)
		{
			this._slKeys.Add(key.Time, key);
		}
		public void RemoveKey(AnimationKey key)
		{
			this._slKeys.Remove(key.Time);
		}
		public void RemoveKeyAtTime(float a_fTime)
		{
			this._slKeys.Remove(a_fTime);
		}
		public void RemoveKeyAtIndex(int a_nIndex)
		{
			this._slKeys.RemoveAt(a_nIndex);
		}


		/// <summary>
		/// Get an animation key near the specified time
		/// </summary>
		/// <param name="a_fTime"></param>
		/// <param name="a_nOffset">0=last key (or, if a_fTime coincides with a key, the current key), 1=next key. Other values are also allowed</param>
		/// <param name="a_bIfOutsideReturnExtreme">If the specified key doesn't exist, should it return the nearest</param>
		/// <returns>The requested key</returns>
		public AnimationKey GetKeyNearTime(float a_fTime, int a_nOffset, bool a_bIfOutsideReturnExtreme)
		{
			int nIndex = this._slKeys.IndexOfKey(a_fTime);
			if (nIndex < 0)
			{
				AnimationKey keyTemp = new AnimationKey(0,0);
				this._slKeys.Add(a_fTime, keyTemp);
				nIndex = this._slKeys.IndexOfKey(a_fTime);
				this._slKeys.RemoveAt(nIndex);
				nIndex--;
			}
			nIndex+=a_nOffset;
			if (nIndex < 0)
			{
				if (a_bIfOutsideReturnExtreme)
					nIndex = 0;
				else
					return null;
			}
			if (nIndex >= this._slKeys.Count)
			{
				if (a_bIfOutsideReturnExtreme)
					nIndex = this._slKeys.Count-1;
				else
					return null;
			}
			return this._slKeys.Values[nIndex];
		}

        public void SetAnimationList(List<float> vals)
        {
            SortedList<float, float> sl = new SortedList<float, float>();
            for (int i = 0; i < vals.Count; i++)
                sl.Add(i, vals[i]);
            this.SetAnimationList(sl);
        }

        public void SetAnimationList(SortedList<float, float> vals)
        {
            SortedList<float, AnimationKey> sl = new SortedList<float, AnimationKey>();
            for (int i = 0; i < vals.Count; i++)
                sl.Add(vals.Keys[i], new AnimationKey(vals.Keys[i], vals.Values[i]));
            this.AnimationList = sl;
        }

		public SortedList<float,AnimationKey> AnimationList
		{
            get { return this._slKeys; }
            set
            {
                //TODO: dispose of keys?
                this._slKeys = value;
                if (this.m_fTime > this._slKeys.Keys[this._slKeys.Count - 1])
                    this.m_fTime = 0;
            }
		}
		public float Position
		{
			set{this.m_fTime = value;}
			get{return this.m_fTime;}
		}

//		public void Load(System.Xml.XmlNode node)
//		{
//
//		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Sweden Sweden
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions