Thread: Animation class not working

  1. #1
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607

    Animation class not working

    Just built an animation class on the same principle as the rest of my resource management classes and it's not working correctly.

    Here is the concept. Basically everything in a game from sounds to textures are all stored in global lists. Each is accessed through the controlling class via an ID number. The ID number is nothing special and is usually the index element into the array or vector.

    So instead of each object storing an IDirect3DTexture9 object, it only needs to store and ID value. This ID value is then used to access the texture list. The list is public in the management class which is not fancy, but eliminates the need for a function call just for the sake of hiding data. There is no benefit gained here to just hiding data. A set texture call will look like this in my engine:

    Code:
    Device->SetTexture(0,TextureSystem->m_pTextures[m_iMyTextureID]);
    Ok now for the animation. Essentially since all images are contained in the Texture System vector, animation comes down to flipping ID numbers. The set texture function then becomes:

    Code:
    if (m_iCurrentTextureID!=m_iCurrentFrameTextureID)
    {
       m_iCurrentTextureID=m_iCurrentFrameTextureID;
       Device->SetTexture(0,TextureSystem->m_pTextures[m_iCurrentTextureID];
    }
    This code will only call set texture if the current ID does not match the current frame's texture ID. So SetTexture() is only called when absolutely necessary - essentially when we have moved forward or backward by (x) frames.

    Now the animation class holds a fixed-size array of TextureIDs that are relative to this animation sequence. It also holds a fixed size array of floats that keep track of how long each frame is supposed to be displayed (in ms). This is all done when the animation sequence is created. Essentially if the animation sequence is ever going to be used in the game, you must create it before the level even loads. This might seem strange, but it's very efficient. No sequences are created on the fly.

    The problem is that I'm using an integer to access the elements of both arrays and MSVC doesn't seem to like it. Now I've used this same approach with the music system and pretty much everything in the engine, the only difference here is that it must access a float array as well.

    Can't you access the elements of a float array using an integer as the index? I thought this was the only way to do it since you cannot use a float as an array index. I've even tried this with vectors, and the value is still getting fragged.

    Here is the code I'm having problems with:

    Code:
    #ifndef CANIMSEQUENCE
    #define CANIMSEQUENCE
    
    #include <d3dx9.h>
    #include "CTimer.h"
    
    class CAnimSeq
    {
      private:
        bool m_bPlaying;
        bool m_bLooping;
    
       //Not used anymore 
       //friend class CAnimSeqContainer;
        
        
        //No need to hold actual texture objects
        //IDirect3DTexture9 *CurrentFrame;
    
        //Current frame hold time
        float         m_fCurFrameTime;
    
        //Current texture number (in local array, not global)
        //This indexes into array of IDs to extract global ID
        //for access to the texture
        unsigned int  m_iCurrentFrame;
        
        //Array of global IDs
        unsigned int  *m_pTextureIDs;
        
        //Max number of frames
        unsigned int  m_iMaxFrames;
    
        //Frame counter - used when creating sequence
        unsigned int  m_iFrameCount;
    
        //Array of frame hold times in ms
        float         *m_pFrameTimes;
        
        //Plays the animation
        unsigned int Play(float _fTimeDelta)
        {
          m_fCurFrameTime+=_fTimeDelta;
          
          //This line is the problem - iCurrentFrame is like 36356678
          //Get switch time from array for current frame
          float SwitchTime=m_pFrameTimes[m_iCurrentFrame];
         
          //If switch time>elapsed time for this frame - we must switch
          if (m_fCurFrameTime>SwitchTime)
          {
            //Reset elapsed frame time and increment frame number
            //Current frame
            m_fCurFrameTime=0.0f;
            m_iCurrentFrame+=1;
            if (m_iCurrentFrame>m_iMaxFrames)
            {
              //Are we a looping animation, if not act accordingly
              if (m_bLooping==false)
              {
                
                m_bPlaying=false;
                m_iCurrentFrame=0;
                return 0xFFFF;     //0xFFFF means this sequence is done
              } 
              m_iCurrentFrame=0;
            }
          } 
          //This is a test - both values are fragged by now      
          //And I don't know why
          char txt[80];
          sprintf(txt,"CAnimSeq::Play(): %u %f",m_iCurrentFrame,m_fCurFrameTime);
          ::MessageBox(0,txt,0,0);
    
          return m_pTextureIDs[m_iCurrentFrame];
    
        }
     
       public:
         //Just a constructor
         CAnimSeq(void) 
         {
            m_iCurrentFrame=0;
            m_fCurFrameTime=0.0f;
            m_bPlaying=false;
            m_iFrameCount=0;
         }
         
        //Just a destructor
        virtual ~CAnimSeq(void)
        {
          
          delete [] m_pTextureIDs;
          delete [] m_pFrameTimes;
    
        }
    
        //Resets the sequence
        void Reset(void)
        {
          m_bPlaying=false;
          m_iCurrentFrame=0;
          m_fCurFrameTime=0.0f;
          m_bLooping=0;
        }
      
        //Sets looping attribute
        void SetLooping(bool bcond)
        {
          m_bLooping=bcond;
         
        }
    
        //Called externally to update animation
        //Class that uses this system needs to render using 
        //the texture ID returned from this function
        unsigned int Update(float fTimeDelta)
        {
         
          unsigned int value=Play(fTimeDelta);
          return value;      
        }
          
        //Create the sequence
        void Create(int iMaxFrames)
        {
          m_pTextureIDs=new unsigned int[iMaxFrames];
          m_pFrameTimes=new float[iMaxFrames];
          m_iMaxFrames=iMaxFrames;
          m_fCurFrameTime=0.0f;
          m_iCurrentFrame=0;
          m_iFrameCount=0;
    
        }
    
        //Add an image to the sequence
        //This in turn adds the image to the global list
        //Calling this populates the global list with the actual texture
        void AddImage(CTextureManager &TextureSys,std::string _strFile,float _fFrameTime)
        {
        
          if (m_iFrameCount<m_iMaxFrames)
          {
    
            //Get our ID from the Texture System
            unsigned int tempID=TextureSys.Add(_strFile.c_str());
    
            m_pTextureIDs[m_iFrameCount]=tempID;
            m_pFrameTimes[m_iFrameCount]=_fFrameTime;
            m_iFrameCount+=1;
            
            //Debug messages
            char txt[80];
            sprintf(txt,"CAnimSeq::Play(): %u %f",m_pTextureIDs[m_iFrameCount],
                                                  m_pFrameTimes[m_iFrameCount]);
            ::MessageBox(0,txt,0,0);
    
          }
                
        }
      
        //Not used - no need
        IDirect3DTexture9 *GetFrame(CTextureManager *TextureSys,unsigned int _ID)
        {
          return TextureSys->m_pTextures[_ID];
        }
          
    };
    
    
    //Not finished
    //Eventually will control all sequences
    //Plan is that all valid animations will play by calling 
    //CAnimSeqContainer::Play()
    class CAnimSeqContainer
    {
      private:
        static std::vector<CAnimSeq> AnimSeqs;
    
      public:
        CAnimSeqContainer(void) {}
        virtual ~CAnimSeqContainer(void) {}
    
        unsigned int AddSeq(CAnimSeq &newSeq)
        {
          AnimSeqs.push_back(newSeq);
          return (AnimSeqs.size()-1);
        }
    
        //void Update(float _fTimeDelta)
        //{
        //  std::vector<CAnimSeq>::iterator iterAnimSeq;
        //  
        //  while (iterAnimSeq!=AnimSeqs.end())
        //  {
        //    iterAnimSeq->Update(_fTimeDelta);
        //  }
        //}
    };
    
    #endif

    I know its a lot of code and you may not like my method, but trust me there is a reason I'm doing it this way. When you start coding Direct3D you will probably understand why I'm limiting calls to SetTexture.

    If you have programmed games you will certainly understand why the timing information for the frames is held separate from the actual textures. Note that with this system you could create thousands of instances of the same sequence, but yet be guaranteed only one copy of each texture involved in the sequences is in memory at one time. You can also be assured that each sequence could hold different timing information.

    Note that this class also does not render anything - this class simply returns a texture ID representing the current texture ID for the current frame in the global list.

  2. #2
    Registered User
    Join Date
    Apr 2002
    Posts
    1,571
    Code:
    	  if (m_iFrameCount<m_iMaxFrames)
    	  {
    
    		//Get our ID from the Texture System
    		unsigned int tempID=TextureSys.Add(_strFile.c_str());
    
    		m_pTextureIDs[m_iFrameCount]=tempID;
    		m_pFrameTimes[m_iFrameCount]=_fFrameTime;
    
     	   m_iFrameCount+=1;
    		
    		//Debug messages
    		char txt[80];
    		sprintf(txt,"CAnimSeq::Play(): %u %f",m_pTextureIDs[m_iFrameCount],
    											 m_pFrameTimes[m_iFrameCount]);
    		::MessageBox(0,txt,0,0);
    
    	  }
    You increment your index too soon. You want to do that after your debug messages, you aren't accessing the correct element.
    "...the results are undefined, and we all know what "undefined" means: it means it works during development, it works during testing, and it blows up in your most important customers' faces." --Scott Meyers

  3. #3
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    I got it working. I was attempting to make a copy of the animation sequence and then pass it to the sprite class. I've changed it. The animation sequence is a member of the sprite class, but you add images via the sprite class, not the sequence. This makes more sense anyways.

    Here is a screenshot of my new class. The explosion in the center is the animation class working. The insane amount of bullets is a test of a new powerup in the game. There are three fire-based powerups in the game so far.

    1. Fast fire - very fast firing
    2. Triple fire - fires 3 projectiles instead of 1 - each separated by about 30 degrees
    3. Insane fire - fires 5 projectiles instead of 1 - each again sep by 30 degrees.

    With #2 and #3 you can really chew through asteroids so I won't make them available until later levels.

  4. #4
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Wow did I really screw up.

    In my setup the timing information is relative to the sequence, not the actual instance of the sprite. So what I need to do is change the sprite class to update itself and keep track of timing as well as frame times. Otherwise every instance of my animation sequence will run at exactly the same speed. Not to mention since they use the same exact time elapsed variable....each instance of the sequence makes all of them play faster.

    What a mess. Time to clean it up. Once this is done all I need to do is add some scoring stuff at the top, some asteroid hit detection, and then a front end.

    Hopefully this will be playable soon. I've been sort of sidetracked by other projects or have been busy updating them and/or learning MFC.

    The problem before was that I was using an instance of CAnimSeq in the setup() function. Since I just passed a pointer to the instance, when I exited the function and got to the render loop, my object was fragged. Essentially a null pointer. The CAnimSeq from Setup() was no longer in scope within Render().
    Duh.
    Last edited by VirtualAce; 03-02-2005 at 05:25 AM.

  5. #5
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Another problem.

    Anyone know how to fix this alpha box problem visible in this shot? I've checked the alphas at the edges of the burst and they are all 0's. Why am I getting this problem when I overlay multiple images?

  6. #6
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    This is with 200 sprites varying in size from 16x16 to 32x32. It seems my video card does not like to render large alpha blended quads - I get this same thing from modern games. Fog volumes and the like just kill the FPS. This is something I don't think I will be able to get around.

    The only optimization left is to place a current texture ID in the Texture System and make it public. I'll check this every time before I call Set Texture in order to avoid calling SetTexture when the current ID is the same as the one we need to render. Since it's a public member, I can also set it w/o using a function call.

    This is a good test of the engine and I like how it is going so far.
    Now after an intense night of coding....it's time for bed.

    Sorry for all the posts, but hey...maybe I could make a tutorial out of all this junk someday.

    Oh and I tried alpha testing instead of alpha blending. Absolutely no change in FPS and it was...ugly.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Getting an error with OpenGL: collect2: ld returned 1 exit status
    By Lorgon Jortle in forum C++ Programming
    Replies: 6
    Last Post: 05-08-2009, 08:18 PM
  2. Class not working!!! HELP
    By swgh in forum C++ Programming
    Replies: 1
    Last Post: 02-16-2006, 06:47 AM
  3. Dikumud
    By maxorator in forum C++ Programming
    Replies: 1
    Last Post: 10-01-2005, 06:39 AM
  4. My Window Class
    By Epo in forum Game Programming
    Replies: 2
    Last Post: 07-10-2005, 02:33 PM
  5. working with a class function
    By Noobie in forum C++ Programming
    Replies: 1
    Last Post: 12-05-2003, 07:07 PM