1 package nl.tudelft.jpacman.sprite;
2
3 import java.awt.Graphics;
4
5 /**
6 * Animated sprite, renders the frame depending on the time of requesting the
7 * draw.
8 *
9 * @author Jeroen Roosen
10 */
11 public class AnimatedSprite implements Sprite {
12
13 /**
14 * Static empty sprite to serve as the end of a non-looping sprite.
15 */
16 private static final Sprite END_OF_LOOP = new EmptySprite();
17
18 /**
19 * The animation itself, in frames.
20 */
21 private final Sprite[] animationFrames;
22
23 /**
24 * The delay between frames.
25 */
26 private final int animationDelay;
27
28 /**
29 * Whether is animation should be looping or not.
30 */
31 private final boolean looping;
32
33 /**
34 * The index of the current frame.
35 */
36 private int current;
37
38 /**
39 * Whether this sprite is currently animating or not.
40 */
41 private boolean animating;
42
43 /**
44 * The {@link System#currentTimeMillis()} stamp of the last update.
45 */
46 private long lastUpdate;
47
48 /**
49 * Creates a new animating sprite that will change frames every interval. By
50 * default the sprite is not animating.
51 *
52 * @param frames
53 * The frames of this animation.
54 * @param delay
55 * The delay between frames.
56 * @param loop
57 * Whether or not this sprite should be looping.
58 */
59 public AnimatedSprite(Sprite[] frames, int delay, boolean loop) {
60 this(frames, delay, loop, false);
61 }
62
63 /**
64 * Creates a new animating sprite that will change frames every interval.
65 *
66 * @param frames
67 * The frames of this animation.
68 * @param delay
69 * The delay between frames.
70 * @param loop
71 * Whether or not this sprite should be looping.
72 * @param isAnimating
73 * Whether or not this sprite is animating from the start.
74 */
75 public AnimatedSprite(Sprite[] frames, int delay, boolean loop, boolean isAnimating) {
76 assert frames.length > 0;
77
78 this.animationFrames = frames.clone();
79 this.animationDelay = delay;
80 this.looping = loop;
81 this.animating = isAnimating;
82
83 this.current = 0;
84 this.lastUpdate = System.currentTimeMillis();
85 }
86
87 /**
88 * @return The frame of the current index.
89 */
90 private Sprite currentSprite() {
91 Sprite result = END_OF_LOOP;
92 if (current < animationFrames.length) {
93 result = animationFrames[current];
94 }
95 assert result != null;
96 return result;
97 }
98
99 /**
100 * Starts or stops the animation of this sprite.
101 *
102 * @param isAnimating
103 * <code>true</code> to animate this sprite or <code>false</code>
104 * to stop animating this sprite.
105 */
106 public void setAnimating(boolean isAnimating) {
107 this.animating = isAnimating;
108 }
109
110 /**
111 * (Re)starts the current animation.
112 */
113 public void restart() {
114 this.current = 0;
115 this.lastUpdate = System.currentTimeMillis();
116 setAnimating(true);
117 }
118
119 @Override
120 public void draw(Graphics graphics, int x, int y, int width, int height) {
121 update();
122 currentSprite().draw(graphics, x, y, width, height);
123 }
124
125 @Override
126 public Sprite split(int x, int y, int width, int height) {
127 update();
128 return currentSprite().split(x, y, width, height);
129 }
130
131 /**
132 * Updates the current frame index depending on the current system time.
133 */
134 private void update() {
135 long now = System.currentTimeMillis();
136 if (animating) {
137 while (lastUpdate < now) {
138 lastUpdate += animationDelay;
139 current++;
140 if (looping) {
141 current %= animationFrames.length;
142 } else if (current == animationFrames.length) {
143 animating = false;
144 }
145 }
146 } else {
147 lastUpdate = now;
148 }
149 }
150
151 @Override
152 public int getWidth() {
153 assert currentSprite() != null;
154 return currentSprite().getWidth();
155 }
156
157 @Override
158 public int getHeight() {
159 assert currentSprite() != null;
160 return currentSprite().getHeight();
161 }
162
163 }