1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17 package com.badlogic.gdx.graphics.g2d; 18 19 import com.badlogic.gdx.math.MathUtils; 20 import com.badlogic.gdx.utils.Array; 21 22 /** <p> 23 * An Animation stores a list of {@link TextureRegion}s representing an animated sequence, e.g. for running or jumping. Each 24 * region of an Animation is called a key frame, multiple key frames make up the animation. 25 * </p> 26 * 27 * @author mzechner */ 28 public class Animation { 29 30 /** Defines possible playback modes for an {@link Animation}. */ 31 public enum PlayMode { 32 NORMAL, 33 REVERSED, 34 LOOP, 35 LOOP_REVERSED, 36 LOOP_PINGPONG, 37 LOOP_RANDOM, 38 } 39 40 final TextureRegion[] keyFrames; 41 private float frameDuration; 42 private float animationDuration; 43 private int lastFrameNumber; 44 private float lastStateTime; 45 46 private PlayMode playMode = PlayMode.NORMAL; 47 48 /** Constructor, storing the frame duration and key frames. 49 * 50 * @param frameDuration the time between frames in seconds. 51 * @param keyFrames the {@link TextureRegion}s representing the frames. */ 52 public Animation (float frameDuration, Array<? extends TextureRegion> keyFrames) { 53 this.frameDuration = frameDuration; 54 this.animationDuration = keyFrames.size * frameDuration; 55 this.keyFrames = new TextureRegion[keyFrames.size]; 56 for (int i = 0, n = keyFrames.size; i < n; i++) { 57 this.keyFrames[i] = keyFrames.get(i); 58 } 59 60 this.playMode = PlayMode.NORMAL; 61 } 62 63 /** Constructor, storing the frame duration, key frames and play type. 64 * 65 * @param frameDuration the time between frames in seconds. 66 * @param keyFrames the {@link TextureRegion}s representing the frames. 67 * @param playMode the animation playback mode. */ 68 public Animation (float frameDuration, Array<? extends TextureRegion> keyFrames, PlayMode playMode) { 69 70 this.frameDuration = frameDuration; 71 this.animationDuration = keyFrames.size * frameDuration; 72 this.keyFrames = new TextureRegion[keyFrames.size]; 73 for (int i = 0, n = keyFrames.size; i < n; i++) { 74 this.keyFrames[i] = keyFrames.get(i); 75 } 76 77 this.playMode = playMode; 78 } 79 80 /** Constructor, storing the frame duration and key frames. 81 * 82 * @param frameDuration the time between frames in seconds. 83 * @param keyFrames the {@link TextureRegion}s representing the frames. */ 84 public Animation (float frameDuration, TextureRegion... keyFrames) { 85 this.frameDuration = frameDuration; 86 this.animationDuration = keyFrames.length * frameDuration; 87 this.keyFrames = keyFrames; 88 this.playMode = PlayMode.NORMAL; 89 } 90 91 /** Returns a {@link TextureRegion} based on the so called state time. This is the amount of seconds an object has spent in the 92 * state this Animation instance represents, e.g. running, jumping and so on. The mode specifies whether the animation is 93 * looping or not. 94 * 95 * @param stateTime the time spent in the state represented by this animation. 96 * @param looping whether the animation is looping or not. 97 * @return the TextureRegion representing the frame of animation for the given state time. */ 98 public TextureRegion getKeyFrame (float stateTime, boolean looping) { 99 // we set the play mode by overriding the previous mode based on looping 100 // parameter value 101 PlayMode oldPlayMode = playMode; 102 if (looping && (playMode == PlayMode.NORMAL || playMode == PlayMode.REVERSED)) { 103 if (playMode == PlayMode.NORMAL) 104 playMode = PlayMode.LOOP; 105 else 106 playMode = PlayMode.LOOP_REVERSED; 107 } else if (!looping && !(playMode == PlayMode.NORMAL || playMode == PlayMode.REVERSED)) { 108 if (playMode == PlayMode.LOOP_REVERSED) 109 playMode = PlayMode.REVERSED; 110 else 111 playMode = PlayMode.LOOP; 112 } 113 114 TextureRegion frame = getKeyFrame(stateTime); 115 playMode = oldPlayMode; 116 return frame; 117 } 118 119 /** Returns a {@link TextureRegion} based on the so called state time. This is the amount of seconds an object has spent in the 120 * state this Animation instance represents, e.g. running, jumping and so on using the mode specified by 121 * {@link #setPlayMode(PlayMode)} method. 122 * 123 * @param stateTime 124 * @return the TextureRegion representing the frame of animation for the given state time. */ 125 public TextureRegion getKeyFrame (float stateTime) { 126 int frameNumber = getKeyFrameIndex(stateTime); 127 return keyFrames[frameNumber]; 128 } 129 130 /** Returns the current frame number. 131 * @param stateTime 132 * @return current frame number */ 133 public int getKeyFrameIndex (float stateTime) { 134 if (keyFrames.length == 1) return 0; 135 136 int frameNumber = (int)(stateTime / frameDuration); 137 switch (playMode) { 138 case NORMAL: 139 frameNumber = Math.min(keyFrames.length - 1, frameNumber); 140 break; 141 case LOOP: 142 frameNumber = frameNumber % keyFrames.length; 143 break; 144 case LOOP_PINGPONG: 145 frameNumber = frameNumber % ((keyFrames.length * 2) - 2); 146 if (frameNumber >= keyFrames.length) frameNumber = keyFrames.length - 2 - (frameNumber - keyFrames.length); 147 break; 148 case LOOP_RANDOM: 149 int lastFrameNumber = (int) ((lastStateTime) / frameDuration); 150 if (lastFrameNumber != frameNumber) { 151 frameNumber = MathUtils.random(keyFrames.length - 1); 152 } else { 153 frameNumber = this.lastFrameNumber; 154 } 155 break; 156 case REVERSED: 157 frameNumber = Math.max(keyFrames.length - frameNumber - 1, 0); 158 break; 159 case LOOP_REVERSED: 160 frameNumber = frameNumber % keyFrames.length; 161 frameNumber = keyFrames.length - frameNumber - 1; 162 break; 163 } 164 165 lastFrameNumber = frameNumber; 166 lastStateTime = stateTime; 167 168 return frameNumber; 169 } 170 171 /** Returns the keyFrames[] array where all the TextureRegions of the animation are stored. 172 * @return keyFrames[] field */ 173 public TextureRegion[] getKeyFrames () { 174 return keyFrames; 175 } 176 177 /** Returns the animation play mode. */ 178 public PlayMode getPlayMode () { 179 return playMode; 180 } 181 182 /** Sets the animation play mode. 183 * 184 * @param playMode The animation {@link PlayMode} to use. */ 185 public void setPlayMode (PlayMode playMode) { 186 this.playMode = playMode; 187 } 188 189 /** Whether the animation would be finished if played without looping (PlayMode#NORMAL), given the state time. 190 * @param stateTime 191 * @return whether the animation is finished. */ 192 public boolean isAnimationFinished (float stateTime) { 193 int frameNumber = (int)(stateTime / frameDuration); 194 return keyFrames.length - 1 < frameNumber; 195 } 196 197 /** Sets duration a frame will be displayed. 198 * @param frameDuration in seconds */ 199 public void setFrameDuration (float frameDuration) { 200 this.frameDuration = frameDuration; 201 this.animationDuration = keyFrames.length * frameDuration; 202 } 203 204 /** @return the duration of a frame in seconds */ 205 public float getFrameDuration () { 206 return frameDuration; 207 } 208 209 /** @return the duration of the entire animation, number of frames times frame duration, in seconds */ 210 public float getAnimationDuration () { 211 return animationDuration; 212 } 213 } 214