Jump to content
  • Advertisement
Sign in to follow this  
harmless

My CAnimation Class Design. Please Advise on Possible Improvement

This topic is 3909 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

The past few days i have been thinking how i should handle animation in my project and after some careful thought, i come up with this design. Design Consideration: ------------------------------- 1. The animation would be time-based, to make animation sequence independent of application's frame rate. 2. The animation class is composed of the following: 2.1 list of key frames (FRAME) 2.2 list of animation sequences (SEQUENCE) 2.3 current key frame index 2.4 current animation sequence index 3. An animation sequence will be composed of key frames defined by structure FRAME. The FRAME structure will consist of a key frame value and time delay. the time delay is the time spent by the key frame while key frame value is generic. it can be a integer representing an index of an image array, or a matrix structure representing a mesh' rotation.
	struct FRAME
	{
		TYPE Data;
		unsigned long m_dwDelay;	
	};



4. An animation sequence is defined by a structure SEQUENCE. This structure will consist of values representing the starting, ending and current index of animation. These indexes are reference values of FRAMES in the animation class' FRAME list. Say FRAME list has 16 frames and a SEQUENCE has start index = 5, and end index = 10. When animation is simulated, it will start at FRAME list[5] and end at list[10]. If loop flag is enabled, animation key will just keep on looping back after the last frame index of the sequence has been simulated.
	struct SEQUENCER
	{
		int nStart;
		int nEnd;
		int nCurrent;
		bool m_bLoop;
	};



How it works: -------------------------- 1. So basically you load key frame values by a call to Add() function below. This function creates a FRAME object and loads it into the FRAME list. The function then returns the reference index of the frame in the list.
int Add( TYPE Data, unsigned long dwDelay )
{
	FRAME Frame = { Data, dwDelay };
	m_FrameList.push_back( Frame );
	return (int) m_FrameList.size() - 1;
}



2. Then once you finished loading key frames in your animation object, you now load animation sequences. This function creates SEQUENCE object and loads it into the SEQUENCE list. The function then returns the reference index of the sequence in the list.
int Add( int nStart, int nEnd, int nCurrent, bool bLoop )
{
	SEQUENCER Sequencer = { nStart, nEnd, nCurrent, bLoop };
	m_SequencerList.push_back( Sequencer );
	return (int) m_SequencerList.size() - 1;
}



3. The return value of the above function is important. Say you want to define a button object's transparency animation sequence. So your animation object will have a list of colorkeys from 0-255 (full transparency to full opaque). You will then have to define 4 animation sequences - idle, on mouse over, on mouse leave, and pressed state. The return value of the above function will serve as you SEQUENCE identifier. 4. Once animation sequence lists are defined, the active sequence will be set with a call to Set() function as shown below:
void Set( int nSequence, int bUseCurrentFrame = false )
{
	m_nCurrentSequence = nSequence;
	if ( !bUseCurrentFrame ) m_nCurrentFrame = m_SequencerList[ m_nCurrentSequence ].nCurrent;
}



The nSequence parameter will be the reference for which animation sequence you want to activate. 5. Lastly, on game loop, call Update() to simulate animation. Animation is time-based. This function then updates the value of current frame index. If you notice, because each frame can have different time delay, to get the next animation frame, i still have to sort thru frame list in sequence despite the fact that i use stl vector in which i could have just used [] operator to get the next frame.
void Update( unsigned long dwTimeElapsed )
{
	m_dwTime += dwTimeElapsed;
	
	while ( m_dwTime > m_FrameList[ m_nCurrentFrame ].m_dwDelay )
	{
		m_dwTime -= m_FrameList[ m_nCurrentFrame ].m_dwDelay;
		m_nCurrentFrame++;
		if ( m_nCurrentFrame > (int)m_SequencerList[ m_nCurrentSequence ].nEnd ) 
		{ 
			if ( m_SequencerList[ m_nCurrentSequence ].m_bLoop ) m_nCurrentFrame = m_SequencerList[ m_nCurrentSequence ].nStart; 
			else m_nCurrentFrame--;
		}
	}
}



6. And lastly, a call to GetCurrent() returns the frame value of the current key frame.
TYPE GetCurrent()
{
	return m_FrameList[ m_nCurrentFrame ].Data;
}



There you have it. The full source code of animation class is shown below:
#ifndef __CAnimation__
#define __CAnimation__

//-----------------------------------------------------------------------------------------------------------------------------
// include files
//-----------------------------------------------------------------------------------------------------------------------------
#include <list>
#include <vector>
#include "..\Utility\TLinkedList.h"
#include "..\CGraphics_Framework\CGraphics.h"

//-----------------------------------------------------------------------------------------------------------------------------
// class declaration
//-----------------------------------------------------------------------------------------------------------------------------
template < typename TYPE >
class CAnimation
{
private:
	// key frame structure
	struct FRAME
	{
		TYPE Data;
		unsigned long m_dwDelay;	
	};

	// sequencer structure
	struct SEQUENCER
	{
		int nStart;
		int nEnd;
		int nCurrent;
		bool m_bLoop;
	};

	unsigned long m_dwTime;

	std::vector< FRAME > m_FrameList;
	std::vector< SEQUENCER > m_SequencerList;
	int m_nCurrentSequence;
	int m_nCurrentFrame;

protected:
public:
	CAnimation();
	virtual ~CAnimation();

	void Reset();
	void End();
	int Add( TYPE Data, unsigned long dwDelay );
	int Add( int nStart, int nEnd, int nCurrent, bool bLoop );
	TYPE GetCurrent();
	void Set( int nSequence, int bUseCurrentFrame = false );
	int Length() { return (int)m_FrameList.size(); }
	int Length( int nSequence ) { return m_SequencerList[ m_nCurrentSequence ].size(); }

	void Update( unsigned long dwTimeElapsed );
};

//-----------------------------------------------------------------------------------------------------------------------------
// constructor
//-----------------------------------------------------------------------------------------------------------------------------
template < typename TYPE >
CAnimation< typename TYPE >::CAnimation()
{
	m_dwTime = 0;
	m_nCurrentFrame = 0;
	m_nCurrentSequence = 0;
}

//-----------------------------------------------------------------------------------------------------------------------------
// destructor
//-----------------------------------------------------------------------------------------------------------------------------
template < typename TYPE >
CAnimation< typename TYPE >::~CAnimation()
{
	// remove all the items in list and call their destructors (delete them)
	m_FrameList.clear();
	m_SequencerList.clear();
}

//-----------------------------------------------------------------------------------------------------------------------------
// add key frame to bottom of animation frame list
//-----------------------------------------------------------------------------------------------------------------------------
template < typename TYPE >
int CAnimation< typename TYPE >::Add( TYPE Data, unsigned long dwDelay )
{
	FRAME Frame = { Data, dwDelay };
	m_FrameList.push_back( Frame );
	return (int) m_FrameList.size() - 1;
}

//-----------------------------------------------------------------------------------------------------------------------------
// run animation in real-time
//-----------------------------------------------------------------------------------------------------------------------------
template < typename TYPE >
void CAnimation< typename TYPE >::Update( unsigned long dwTimeElapsed )
{
	m_dwTime += dwTimeElapsed;
	
	while ( m_dwTime > m_FrameList[ m_nCurrentFrame ].m_dwDelay )
	{
		m_dwTime -= m_FrameList[ m_nCurrentFrame ].m_dwDelay;
		m_nCurrentFrame++;
		if ( m_nCurrentFrame > (int)m_SequencerList[ m_nCurrentSequence ].nEnd ) 
		{ 
			if ( m_SequencerList[ m_nCurrentSequence ].m_bLoop ) m_nCurrentFrame = m_SequencerList[ m_nCurrentSequence ].nStart; 
			else m_nCurrentFrame--;
		}
	}
}

//-----------------------------------------------------------------------------------------------------------------------------
// get current frame
//-----------------------------------------------------------------------------------------------------------------------------
template < typename TYPE >
TYPE CAnimation< typename TYPE >::GetCurrent()
{
	return m_FrameList[ m_nCurrentFrame ].Data;
}

//-----------------------------------------------------------------------------------------------------------------------------
// reset animation. set the current frame to first frame
//-----------------------------------------------------------------------------------------------------------------------------
template < typename TYPE >
void CAnimation< typename TYPE >::Reset()
{
	m_nCurrentFrame = m_SequencerList[ m_nCurrentSequence ].nStart;
}

//-----------------------------------------------------------------------------------------------------------------------------
// end animation. set the current frame to last frame
//-----------------------------------------------------------------------------------------------------------------------------
template < typename TYPE >
void CAnimation< typename TYPE >::End()
{
	m_nCurrentFrame = m_SequencerList[ m_nCurrentSequence ].nEnd;
}

//-----------------------------------------------------------------------------------------------------------------------------
// add sequence to animation list
//-----------------------------------------------------------------------------------------------------------------------------
template < typename TYPE >
int CAnimation< typename TYPE >::Add( int nStart, int nEnd, int nCurrent, bool bLoop )
{
	SEQUENCER Sequencer = { nStart, nEnd, nCurrent, bLoop };
	m_SequencerList.push_back( Sequencer );
	return (int) m_SequencerList.size() - 1;
}

//-----------------------------------------------------------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------------------------------------------------------
template < typename TYPE >
void CAnimation< typename TYPE >::Set( int nSequence, int bUseCurrentFrame )
{
	m_nCurrentSequence = nSequence;
	if ( !bUseCurrentFrame ) m_nCurrentFrame = m_SequencerList[ m_nCurrentSequence ].nCurrent;
}


#endif


My sincere apologies for such a lengthy post. I do hope someone finds the time to read it and an experienced comment(i don't mind criticism) and possibly an advise on a better approach is very much welcome. Thanks a lot!

Share this post


Link to post
Share on other sites
Advertisement
Generally, not bad. I won't comment on stylistic issues, but here are just 2 things that I spotted:

1) Try to use initialisation lists instead of assignment.
2) You don't need to call clear() on the vector in the destructor. The destructor will automatically be called for all member variables.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!