AnimatedSprite.java

Any community contributions to libgdx go here! Some may get included in the core API when permission is granted.

AnimatedSprite.java

Postby Wavesonics » Fri Jan 14, 2011 1:35 am

I know this is on ur to do list, but I needed it now, so here is my Animated Sprite class, use it if you like :)

Code: Select all
import java.util.Iterator;
import java.util.Vector;

import com.badlogic.gdx.graphics.Sprite;
import com.badlogic.gdx.graphics.TextureAtlas;
import com.badlogic.gdx.graphics.TextureRegion;

public class AnimatedSprite extends Sprite
{
   protected final TextureAtlas mTextureAtlas;
   
   protected Vector<TextureRegion> mFrame;
   protected Iterator<TextureRegion> mCurFrame;
   
   protected float mAnimationRateInSeconds;
   protected float mFrameTimeCounter;
   protected boolean mIsPlay = false;
   
   public AnimatedSprite(final TextureAtlas textureAtlas, final String regionName, final int frameCount)
   {
      super( textureAtlas.findRegion( regionName, 0 ) );
      mTextureAtlas = textureAtlas;
     
      mAnimationRateInSeconds = 1.0f;
     
      mFrame = new Vector<TextureRegion>( frameCount );
      for( int ii=0; ii<frameCount; ++ii )
      {
         mFrame.add( mTextureAtlas.findRegion( regionName, ii ) );
      }
     
      mCurFrame = mFrame.iterator();
   }
   
   public void setAnimationRate(final float animationRateInSeconds)
   {
      mAnimationRateInSeconds = animationRateInSeconds;
   }
   
   public void play()
   {
      mIsPlay = true;
      mFrameTimeCounter = 0;
   }
   
   public void pause()
   {
      mIsPlay = false;
      mFrameTimeCounter = 0;
   }
   
   public void update(final float secondsElapsed)
   {
      if( mIsPlay )
      {
         mFrameTimeCounter += secondsElapsed;
         if(mFrameTimeCounter > mAnimationRateInSeconds)
         {
            mFrameTimeCounter = 0;
           
            // Back to first
            if( !mCurFrame.hasNext() )
            {
               mCurFrame = mFrame.iterator();
            }
           
            setRegion( mCurFrame.next() );
         }
      }
   }
}
Wavesonics
 
Posts: 102
Joined: Sun Jul 11, 2010 5:10 am

Re: AnimatedSprite.java

Postby mzechner » Fri Jan 14, 2011 2:33 am

Looks good, will need to add support for AtlasRegions since they need offsetting. Also, don't use Vector :)
mzechner
Site Admin
 
Posts: 4879
Joined: Sat Jul 10, 2010 3:50 pm

Re: AnimatedSprite.java

Postby Wavesonics » Fri Jan 14, 2011 3:25 am

Replaced Vector with an array, TextureRegion with AtlasRegion. Added stop() in addition to the existing start() & pause(), and added the ability to control if it loops or not.

Ya know what might be really handy? A registrable callback when the animation finishes, what do you think?

Also side note, do you really think declaring final on primitives is worth while? I know in C++ declaring them const is not worth it.

Code: Select all
package com.darkrockstudios.games;

import com.badlogic.gdx.graphics.Sprite;
import com.badlogic.gdx.graphics.TextureAtlas;
import com.badlogic.gdx.graphics.TextureAtlas.AtlasRegion;

public class AnimatedSprite extends Sprite
{
   protected final TextureAtlas mTextureAtlas;
   
   protected AtlasRegion mFrame[];
   protected int mCurFrame;
   
   protected float mAnimationRateInSeconds;
   protected float mFrameTimeCounter;
   protected boolean mIsPlaying;
   protected boolean mLoop;
   
   public AnimatedSprite(final TextureAtlas textureAtlas, final String regionName, final int frameCount)
   {
      super( textureAtlas.findRegion( regionName, 0 ) );
      mTextureAtlas = textureAtlas;
     
      mAnimationRateInSeconds = 1.0f;
      mIsPlaying = false;
      mLoop = true;
     
      mFrame = new AtlasRegion[ frameCount ];
      for( int ii=0; ii<frameCount; ++ii )
      {
         mFrame[ii] = mTextureAtlas.findRegion( regionName, ii );
      }
     
      mCurFrame = 0;
   }
   
   public void setAnimationRate(final float animationRateInSeconds)
   {
      mAnimationRateInSeconds = animationRateInSeconds;
   }
   
   public void loop( boolean doLoop )
   {
      mLoop = doLoop;
   }
   public final boolean looping()
   {
      return mLoop;
   }
   
   public void play()
   {
      mIsPlaying = true;
      mFrameTimeCounter = 0;
   }
   
   public void pause()
   {
      mIsPlaying = false;
      mFrameTimeCounter = 0;
   }
   
   public void stop()
   {
      pause();
      mCurFrame = 0;
   }
   
   public void update(final float secondsElapsed)
   {
      if( mIsPlaying )
      {
         mFrameTimeCounter += secondsElapsed;
         if(mFrameTimeCounter > mAnimationRateInSeconds)
         {
            mFrameTimeCounter = 0;
            ++mCurFrame;
           
            // Back to first
            if( mCurFrame >= mFrame.length )
            {
               // If not looping, stop
               if( !mLoop )
               {
                  stop();
               }
               // Other wise just reset
               else
               {
                  mCurFrame = 0;
               }
            }
           
            setRegion( mFrame[ mCurFrame ] );
         }
      }
   }
}
Wavesonics
 
Posts: 102
Joined: Sun Jul 11, 2010 5:10 am

Re: AnimatedSprite.java

Postby Obli » Fri Jan 14, 2011 8:28 am

Obli
 
Posts: 616
Joined: Mon Jan 10, 2011 6:18 pm
Location: Bordeaux, France

Re: AnimatedSprite.java

Postby Wavesonics » Fri Jan 14, 2011 10:32 pm

Very good article, thanks!
Wavesonics
 
Posts: 102
Joined: Sun Jul 11, 2010 5:10 am

Re: AnimatedSprite.java

Postby Wavesonics » Tue Feb 01, 2011 4:01 pm

In using my AnimatedSprite class some I noticed a problem with flip. Since flip is normally accomplished using setRegion(), and I'm doing the the frame changes with set region, there was a conflict.

So this isn't the best solution, but I now keep state-full which dimensions are flipped, and reapply the flip after each frame change.

Also some other small changes that make the class nicer. Any ideas on improving this?

Code: Select all
package com.darkrockstudios.games;

import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;

public class AnimatedSprite extends Sprite
{
   protected final TextureAtlas mTextureAtlas;
   
   protected AtlasRegion mFrame[];
   protected int mCurFrame;
   
   protected float mAnimationRateInSeconds;
   protected float mFrameTimeCounter;
   protected boolean mIsPlaying;
   protected boolean mLoop;
   
   protected boolean mFlipX;
   protected boolean mFlipY;
   
   public AnimatedSprite(final TextureAtlas textureAtlas, final String regionName, final int frameCount)
   {
      super( textureAtlas.findRegion( regionName, 0 ) );
      mTextureAtlas = textureAtlas;
     
      mAnimationRateInSeconds = 1.0f;
      mIsPlaying = false;
      mLoop = true;
     
      mFlipX = false;
      mFlipY = false;
     
      mFrame = new AtlasRegion[ frameCount ];
      for( int ii=0; ii<frameCount; ++ii )
      {
         mFrame[ii] = mTextureAtlas.findRegion( regionName, ii );
      }
     
      mCurFrame = 0;
   }
   
   public void setAnimationRate(final int fps)
   {
      mAnimationRateInSeconds = 1.0f / (float)fps;
   }
   
   public void loop( boolean doLoop )
   {
      mLoop = doLoop;
   }
   public final boolean looping()
   {
      return mLoop;
   }
   
   public void restart()
   {
      mIsPlaying = true;
      mFrameTimeCounter = 0;
      mCurFrame = 0;
   }
   
   public void play()
   {
      mIsPlaying = true;
      mFrameTimeCounter = 0;
   }
   
   public void pause()
   {
      mIsPlaying = false;
      mFrameTimeCounter = 0;
   }
   
   public void stop()
   {
      pause();
      mCurFrame = 0;
   }
   
   public void update(final float secondsElapsed)
   {
      if( mIsPlaying )
      {
         mFrameTimeCounter += secondsElapsed;
         if(mFrameTimeCounter > mAnimationRateInSeconds)
         {
            mFrameTimeCounter = 0;
            ++mCurFrame;
           
            // Back to first
            if( mCurFrame >= mFrame.length )
            {
               // If not looping, stop
               if( !mLoop )
               {
                  stop();
               }
               // Other wise just reset
               else
               {
                  mCurFrame = 0;
               }
            }
           
            setRegion( mFrame[ mCurFrame ] );
         }
      }
   }
   
   public void setRegion (TextureRegion region)
   {
      super.setRegion( region );
      super.flip( mFlipX, mFlipY );
   }
   
   public void flip( boolean x, boolean y )
   {
      mFlipX = x;
      mFlipY = y;
      super.flip( mFlipX, mFlipY );
   }
}
Wavesonics
 
Posts: 102
Joined: Sun Jul 11, 2010 5:10 am

Re: AnimatedSprite.java

Postby mzechner » Tue Feb 01, 2011 5:27 pm

I think the way you do it is fine. The only thing that is lacking is supported for AtlasRegions that are referencing whitestripped areas in the atlas. I guess Nate can comment on that since he wrote that part. Other than that i think the class is totally fine as is. If you put in proper Javadocs and a license header i'd include it in libgdx if you allow us to do so :) Then i can strike out another item on my list of things todo.
mzechner
Site Admin
 
Posts: 4879
Joined: Sat Jul 10, 2010 3:50 pm

Re: AnimatedSprite.java

Postby Wavesonics » Tue Feb 01, 2011 10:06 pm

Hell ya :D

I can maybe do this tonight, if not I can definitely do it tomorrow.

When ever nates fills me in on AtlasRegions I will get that in.
Wavesonics
 
Posts: 102
Joined: Sun Jul 11, 2010 5:10 am

Re: AnimatedSprite.java

Postby NateS » Wed Feb 02, 2011 8:10 am

Eh, it's tricky. See what extra stuff the TextureAtlas.AtlasSprite class does...
NateS
 
Posts: 1980
Joined: Fri Nov 12, 2010 11:08 am

Re: AnimatedSprite.java

Postby hovox » Thu Feb 03, 2011 7:56 am

Here is my version of animated actor. I use it , and it work's for me.
Code: Select all
package com.socialin.android.game.birdland.actor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import com.badlogic.gdx.graphics.SpriteBatch;
import com.badlogic.gdx.graphics.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.socialin.android.L;
import com.socialin.android.game.birdland.BirdGame;

public class AnimatedActor2 extends Actor {
   protected float mAnimationRateInSeconds = 1f/25f;
   protected boolean mIsPlay = false;
   protected float mFrameTimeCounter;
   private int mCurFrameIndex = 0;
   
   protected HashMap<Integer, KeyFrame> mKeyFrames = new HashMap<Integer, AnimatedActor2.KeyFrame>(5);
   protected ArrayList<AtlasRegion> mTextureRegions = new ArrayList<AtlasRegion>();
   protected AtlasRegion curRegion;
   
   public static final String FRAME_START = "start";
   public static final String FRAME_END = "end";
   
   protected boolean mIsFlipped = false;
   private float mPauseSeconds;

   public AnimatedActor2(final String pName, final List<AtlasRegion> textureRegions) {
      super(pName);
      
      mTextureRegions = (ArrayList<AtlasRegion>) textureRegions;
      originX = mTextureRegions.get(0).packedWidth / 2.0f;
      originY = mTextureRegions.get(0).packedHeight / 2.0f;
      width   = mTextureRegions.get(0).packedWidth;
      height  = mTextureRegions.get(0).packedHeight;
      mKeyFrames.put(0, new KeyFrame(0, FRAME_START, null));
      mKeyFrames.put(mTextureRegions.size() - 1, new KeyFrame(mTextureRegions.size() - 1, FRAME_END, null));
      
      curRegion = mTextureRegions.get(mCurFrameIndex);
   }
   
   public void flip(boolean isFlip){
      if(isFlip == mIsFlipped)
         return;
      mIsFlipped = isFlip;
      for(TextureRegion cur : mTextureRegions){
         cur.flip(true, false);
      }
   }
   
   public float getX(){
      return x + curRegion.offsetX;
   }
   
   public float getY(){
      return y + (curRegion.originalHeight - curRegion.offsetY - curRegion.packedHeight);
   }
   
   public float getDeltaY(){
      return (mTextureRegions.get(0).originalHeight - mTextureRegions.get(0).offsetY - mTextureRegions.get(0).packedHeight);
   }
   
   public float getDelataX(){
      return mTextureRegions.get(mCurFrameIndex).offsetX;
   }
   
   public void setX(float val){
      x = val - mTextureRegions.get(mCurFrameIndex).offsetX;
   }
   
   public void setY(float val){
      y = val - (curRegion.originalHeight - curRegion.offsetY - curRegion.packedHeight);
   }
   
   public float getWidth(){
      return curRegion.packedWidth;
   }
   
   public float getHeight(){
      return curRegion.packedHeight;
   }
   
   public boolean isFlepped(){
      return mIsFlipped;
   }
   
   public void addKeyframe(final KeyFrame pFrame){
         mKeyFrames.put(pFrame.mIndex, pFrame);
   }
   
   public void addKeyframes(final ArrayList<KeyFrame> pFrames){
      for(KeyFrame cur : pFrames){
         mKeyFrames.put(cur.mIndex, cur);
      }
   }
   
   public void addKeyframes(final KeyFrame ...pFrames){
      for(KeyFrame cur : pFrames){
         mKeyFrames.put(cur.mIndex, cur);
      }
   }
   
   public void gotoAndStop(final int pFrameIndex){
      mCurFrameIndex = pFrameIndex;
      curRegion = mTextureRegions.get(mCurFrameIndex);
      mIsPlay = false;
   }
   
   public void gotoAndPlay(final int pFrameIndex){
      mCurFrameIndex = pFrameIndex;
      curRegion = mTextureRegions.get(mCurFrameIndex);
      mIsPlay = true;
   }
   
   public void nextFrame(){
      mCurFrameIndex = ++mCurFrameIndex%mTextureRegions.size();
      curRegion = mTextureRegions.get(mCurFrameIndex);
   }
   
   public void prevFrame(){
      if(mCurFrameIndex == 0)
         mCurFrameIndex = mTextureRegions.size() - 1;
      else
         mCurFrameIndex--;
      curRegion = mTextureRegions.get(mCurFrameIndex);
   }
   
   public void goTo(final int pFrameIndex){
      mCurFrameIndex = pFrameIndex;
      curRegion = mTextureRegions.get(mCurFrameIndex);
      if( mKeyFrames.containsKey(mCurFrameIndex)  ){
         if(mKeyFrames.get(mCurFrameIndex).mAnimationListener != null){
            mKeyFrames.get(mCurFrameIndex).mAnimationListener.onStart(this, mKeyFrames.get(mCurFrameIndex));
         }else {
            play();
         }
      }
      
   }
   
   public void play(){
      L.d("play");
      mIsPlay = true;
   }
   
   public void stop(){
      mIsPlay = false;
   }
   
   public void pause(float pSeconds){
      mPauseSeconds = pSeconds;
   }
   
   @Override
   protected void act(float delta) {
      if(mIsPlay){
         if(mPauseSeconds > 0)
            mPauseSeconds -= delta;
         if(mPauseSeconds <= 0){
            mPauseSeconds = 0;
            mFrameTimeCounter += delta;
            if(mFrameTimeCounter > mAnimationRateInSeconds){
               mFrameTimeCounter = 0;//mFrameTimeCounter - mAnimationRateInSeconds;
               mCurFrameIndex = ++mCurFrameIndex%mTextureRegions.size();
               curRegion = mTextureRegions.get(mCurFrameIndex);
            }
         }
         
      }
      super.act(delta);
   }

   @Override
   protected void render(SpriteBatch batch) {
      if (mTextureRegions != null) {
         batch.setColor(color);
         if (scaleX == 0 && scaleY == 0 && rotation == 0)
            batch.draw(curRegion, x + curRegion.offsetX, y + (curRegion.originalHeight - curRegion.offsetY - curRegion.packedHeight), curRegion.packedWidth, curRegion.packedHeight);
         else
            batch.draw(curRegion, x  + curRegion.offsetX, y + (curRegion.originalHeight - curRegion.offsetY - curRegion.packedHeight), originX, originY, curRegion.packedWidth, curRegion.packedHeight, scaleX, scaleY, rotation);
         if( mKeyFrames.containsKey(mCurFrameIndex) && mPauseSeconds == 0  ){
            if(mKeyFrames.get(mCurFrameIndex).mAnimationListener != null){
               mKeyFrames.get(mCurFrameIndex).mAnimationListener.onStart(this, mKeyFrames.get(mCurFrameIndex));
            }
         }
      }
   }

   @Override
   protected boolean touchDown(float x, float y, int pointer) {
      return false;
   }

   @Override
   protected boolean touchUp(float x, float y, int pointer) {
      return false;
   }

   @Override
   protected boolean touchDragged(float x, float y, int pointer) {
      return false;
   }

   @Override
   public Actor hit(float x, float y) {
      if (x  > curRegion.offsetX && x < curRegion.offsetX +  curRegion.packedWidth)
         if (y > (curRegion.originalHeight - curRegion.offsetY - curRegion.packedHeight) && y < (curRegion.originalHeight - curRegion.offsetY)) return this;
      return null;
   }
   
   public float getAnimationRateInSeconds(){
      return mAnimationRateInSeconds;
   }
   
   public void setAnimationRateInSeconds(float pAnimationRateInSeconds){
      mAnimationRateInSeconds = pAnimationRateInSeconds;
   }
   
   public static class KeyFrame {
      public int mIndex;
      public String mFrameName;
      public AnimationListener mAnimationListener;
      
      public KeyFrame(final int pIndex, final String pFrameName, final AnimationListener pAnimationListener){
         mIndex = pIndex;
         mFrameName = pFrameName;
         mAnimationListener = pAnimationListener;
      }
   }
   
   public interface AnimationListener {
      public void onStart(AnimatedActor2 pActor, KeyFrame pFrame);
   }
}
hovox
 
Posts: 105
Joined: Mon Nov 29, 2010 11:56 am

Next

Return to Libgdx Contributions

Who is online

Users browsing this forum: No registered users and 1 guest

cron