Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epoxy
GitHub Repository: epoxy/proj11
Path: blob/master/SLICK_HOME/src/org/newdawn/slick/Animation.java
1456 views
1
package org.newdawn.slick;
2
3
import java.util.ArrayList;
4
5
import org.lwjgl.Sys;
6
import org.newdawn.slick.util.Log;
7
8
/**
9
* A utility to hold and render animations
10
*
11
* @author kevin
12
* @author DeX (speed updates)
13
*/
14
public class Animation implements Renderable {
15
/** The list of frames to render in this animation */
16
private ArrayList frames = new ArrayList();
17
/** The frame currently being displayed */
18
private int currentFrame = -1;
19
/** The time the next frame change should take place */
20
private long nextChange = 0;
21
/** True if the animation is stopped */
22
private boolean stopped = false;
23
/** The time left til the next frame */
24
private long timeLeft;
25
/** The current speed of the animation */
26
private float speed = 1.0f;
27
/** The frame to stop at */
28
private int stopAt = -2;
29
/** The last time the frame was automagically updated */
30
private long lastUpdate;
31
/** True if this is the first update */
32
private boolean firstUpdate = true;
33
/** True if we should auto update the animation - default true */
34
private boolean autoUpdate = true;
35
/** The direction the animation is running */
36
private int direction = 1;
37
/** True if the animation in ping ponging back and forth */
38
private boolean pingPong;
39
/** True if the animation should loop (default) */
40
private boolean loop = true;
41
/** The spriteSheet backing this animation */
42
private SpriteSheet spriteSheet = null;
43
44
/**
45
* Create an empty animation
46
*/
47
public Animation() {
48
this(true);
49
}
50
51
/**
52
* Create a new animation from a set of images
53
*
54
* @param frames The images for the animation frames
55
* @param duration The duration to show each frame
56
*/
57
public Animation(Image[] frames, int duration) {
58
this(frames, duration, true);
59
}
60
61
/**
62
* Create a new animation from a set of images
63
*
64
* @param frames The images for the animation frames
65
* @param durations The duration to show each frame
66
*/
67
public Animation(Image[] frames, int[] durations) {
68
this(frames, durations, true);
69
}
70
71
/**
72
* Create an empty animation
73
*
74
* @param autoUpdate True if this animation should automatically update. This means that the
75
* current frame will be caculated based on the time between renders
76
*/
77
public Animation(boolean autoUpdate) {
78
currentFrame = 0;
79
this.autoUpdate = autoUpdate;
80
}
81
82
/**
83
* Create a new animation from a set of images
84
*
85
* @param frames The images for the animation frames
86
* @param duration The duration to show each frame
87
* @param autoUpdate True if this animation should automatically update. This means that the
88
* current frame will be caculated based on the time between renders
89
*/
90
public Animation(Image[] frames, int duration, boolean autoUpdate) {
91
for (int i=0;i<frames.length;i++) {
92
addFrame(frames[i], duration);
93
}
94
currentFrame = 0;
95
this.autoUpdate = autoUpdate;
96
}
97
98
/**
99
* Create a new animation from a set of images
100
*
101
* @param frames The images for the animation frames
102
* @param durations The duration to show each frame
103
* @param autoUpdate True if this animation should automatically update. This means that the
104
* current frame will be caculated based on the time between renders
105
*/
106
public Animation(Image[] frames, int[] durations, boolean autoUpdate) {
107
this.autoUpdate = autoUpdate;
108
if (frames.length != durations.length) {
109
throw new RuntimeException("There must be one duration per frame");
110
}
111
112
for (int i=0;i<frames.length;i++) {
113
addFrame(frames[i], durations[i]);
114
}
115
currentFrame = 0;
116
}
117
118
/**
119
* Create a new animation based on the sprite from a sheet. It assumed that
120
* the sprites are organised on horizontal scan lines and that every sprite
121
* in the sheet should be used.
122
*
123
* @param frames The sprite sheet containing the frames
124
* @param duration The duration each frame should be displayed for
125
*/
126
public Animation(SpriteSheet frames, int duration) {
127
this(frames, 0,0,frames.getHorizontalCount()-1,frames.getVerticalCount()-1,true,duration,true);
128
}
129
130
/**
131
* Create a new animation based on a selection of sprites from a sheet
132
*
133
* @param frames The sprite sheet containing the frames
134
* @param x1 The x coordinate of the first sprite from the sheet to appear in the animation
135
* @param y1 The y coordinate of the first sprite from the sheet to appear in the animation
136
* @param x2 The x coordinate of the last sprite from the sheet to appear in the animation
137
* @param y2 The y coordinate of the last sprite from the sheet to appear in the animation
138
* @param horizontalScan True if the sprites are arranged in hoizontal scan lines. Otherwise
139
* vertical is assumed
140
* @param duration The duration each frame should be displayed for
141
* @param autoUpdate True if this animation should automatically update based on the render times
142
*/
143
public Animation(SpriteSheet frames, int x1, int y1, int x2, int y2, boolean horizontalScan, int duration, boolean autoUpdate) {
144
this.autoUpdate = autoUpdate;
145
146
if (!horizontalScan) {
147
for (int x=x1;x<=x2;x++) {
148
for (int y=y1;y<=y2;y++) {
149
addFrame(frames.getSprite(x, y), duration);
150
}
151
}
152
} else {
153
for (int y=y1;y<=y2;y++) {
154
for (int x=x1;x<=x2;x++) {
155
addFrame(frames.getSprite(x, y), duration);
156
}
157
}
158
}
159
}
160
161
/**
162
* Creates a new Animation where each frame is a sub-image of <tt>SpriteSheet</tt> ss.
163
* @param ss The <tt>SpriteSheet</tt> backing this animation
164
* @param frames An array of coordinates of sub-image locations for each frame
165
* @param duration The duration each frame should be displayed for
166
*/
167
public Animation(SpriteSheet ss, int[] frames, int[] duration){
168
spriteSheet = ss;
169
int x = -1;
170
int y = -1;
171
172
for(int i = 0; i < frames.length/2; i++){
173
x = frames[i*2];
174
y = frames[i*2 + 1];
175
addFrame(duration[i], x, y);
176
}
177
}
178
179
/**
180
* Add animation frame to the animation.
181
* @param duration The duration to display the frame for
182
* @param x The x location of the frame on the <tt>SpriteSheet</tt>
183
* @param y The y location of the frame on the <tt>spriteSheet</tt>
184
*/
185
public void addFrame(int duration, int x, int y){
186
if (duration == 0) {
187
Log.error("Invalid duration: "+duration);
188
throw new RuntimeException("Invalid duration: "+duration);
189
}
190
191
if (frames.isEmpty()) {
192
nextChange = (int) (duration / speed);
193
}
194
195
frames.add(new Frame(duration, x, y));
196
currentFrame = 0;
197
}
198
199
/**
200
* Indicate if this animation should automatically update based on the
201
* time between renders or if it should need updating via the update()
202
* method.
203
*
204
* @param auto True if this animation should automatically update
205
*/
206
public void setAutoUpdate(boolean auto) {
207
this.autoUpdate = auto;
208
}
209
210
/**
211
* Indicate if this animation should ping pong back and forth
212
*
213
* @param pingPong True if the animation should ping pong
214
*/
215
public void setPingPong(boolean pingPong) {
216
this.pingPong = pingPong;
217
}
218
219
/**
220
* Check if this animation has stopped (either explictly or because it's reached its target frame)
221
*
222
* @see #stopAt
223
* @return True if the animation has stopped
224
*/
225
public boolean isStopped() {
226
return stopped;
227
}
228
229
/**
230
* Adjust the overall speed of the animation.
231
*
232
* @param spd The speed to run the animation. Default: 1.0
233
*/
234
public void setSpeed(float spd) {
235
if (spd > 0) {
236
// Adjust nextChange
237
nextChange = (long) (nextChange * speed / spd);
238
239
speed = spd;
240
}
241
}
242
243
/**
244
* Returns the current speed of the animation.
245
*
246
* @return The speed this animation is being played back at
247
*/
248
public float getSpeed() {
249
return speed;
250
}
251
252
253
/**
254
* Stop the animation
255
*/
256
public void stop() {
257
if (frames.size() == 0) {
258
return;
259
}
260
timeLeft = nextChange;
261
stopped = true;
262
}
263
264
/**
265
* Start the animation playing again
266
*/
267
public void start() {
268
if (!stopped) {
269
return;
270
}
271
if (frames.size() == 0) {
272
return;
273
}
274
stopped = false;
275
nextChange = timeLeft;
276
}
277
278
/**
279
* Restart the animation from the beginning
280
*/
281
public void restart() {
282
if (frames.size() == 0) {
283
return;
284
}
285
stopped = false;
286
currentFrame = 0;
287
nextChange = (int) (((Frame) frames.get(0)).duration / speed);
288
firstUpdate = true;
289
lastUpdate = 0;
290
}
291
292
/**
293
* Add animation frame to the animation
294
*
295
* @param frame The image to display for the frame
296
* @param duration The duration to display the frame for
297
*/
298
public void addFrame(Image frame, int duration) {
299
if (duration == 0) {
300
Log.error("Invalid duration: "+duration);
301
throw new RuntimeException("Invalid duration: "+duration);
302
}
303
304
if (frames.isEmpty()) {
305
nextChange = (int) (duration / speed);
306
}
307
308
frames.add(new Frame(frame, duration));
309
currentFrame = 0;
310
}
311
312
/**
313
* Draw the animation to the screen
314
*/
315
public void draw() {
316
draw(0,0);
317
}
318
319
/**
320
* Draw the animation at a specific location
321
*
322
* @param x The x position to draw the animation at
323
* @param y The y position to draw the animation at
324
*/
325
public void draw(float x,float y) {
326
draw(x,y,getWidth(),getHeight());
327
}
328
329
/**
330
* Draw the animation at a specific location
331
*
332
* @param x The x position to draw the animation at
333
* @param y The y position to draw the animation at
334
* @param filter The filter to apply
335
*/
336
public void draw(float x,float y, Color filter) {
337
draw(x,y,getWidth(),getHeight(), filter);
338
}
339
340
/**
341
* Draw the animation
342
*
343
* @param x The x position to draw the animation at
344
* @param y The y position to draw the animation at
345
* @param width The width to draw the animation at
346
* @param height The height to draw the animation at
347
*/
348
public void draw(float x,float y,float width,float height) {
349
draw(x,y,width,height,Color.white);
350
}
351
352
/**
353
* Draw the animation
354
*
355
* @param x The x position to draw the animation at
356
* @param y The y position to draw the animation at
357
* @param width The width to draw the animation at
358
* @param height The height to draw the animation at
359
* @param col The colour filter to use
360
*/
361
public void draw(float x,float y,float width,float height, Color col) {
362
if (frames.size() == 0) {
363
return;
364
}
365
366
if (autoUpdate) {
367
long now = getTime();
368
long delta = now - lastUpdate;
369
if (firstUpdate) {
370
delta = 0;
371
firstUpdate = false;
372
}
373
lastUpdate = now;
374
nextFrame(delta);
375
}
376
377
Frame frame = (Frame) frames.get(currentFrame);
378
frame.image.draw(x,y,width,height, col);
379
}
380
381
/**
382
* Render the appropriate frame when the spriteSheet backing this Animation is in use.
383
* @param x The x position to draw the animation at
384
* @param y The y position to draw the animation at
385
*/
386
public void renderInUse(int x, int y){
387
if (frames.size() == 0) {
388
return;
389
}
390
391
if (autoUpdate) {
392
long now = getTime();
393
long delta = now - lastUpdate;
394
if (firstUpdate) {
395
delta = 0;
396
firstUpdate = false;
397
}
398
lastUpdate = now;
399
nextFrame(delta);
400
}
401
402
Frame frame = (Frame) frames.get(currentFrame);
403
spriteSheet.renderInUse(x, y, frame.x, frame.y);
404
}
405
406
/**
407
* Get the width of the current frame
408
*
409
* @return The width of the current frame
410
*/
411
public int getWidth() {
412
return ((Frame) frames.get(currentFrame)).image.getWidth();
413
}
414
415
/**
416
* Get the height of the current frame
417
*
418
* @return The height of the current frame
419
*/
420
public int getHeight() {
421
return ((Frame) frames.get(currentFrame)).image.getHeight();
422
}
423
424
/**
425
* Draw the animation
426
*
427
* @param x The x position to draw the animation at
428
* @param y The y position to draw the animation at
429
* @param width The width to draw the animation at
430
* @param height The height to draw the animation at
431
*/
432
public void drawFlash(float x,float y,float width,float height) {
433
drawFlash(x,y,width,height, Color.white);
434
}
435
436
/**
437
* Draw the animation
438
*
439
* @param x The x position to draw the animation at
440
* @param y The y position to draw the animation at
441
* @param width The width to draw the animation at
442
* @param height The height to draw the animation at
443
* @param col The colour for the flash
444
*/
445
public void drawFlash(float x,float y,float width,float height, Color col) {
446
if (frames.size() == 0) {
447
return;
448
}
449
450
if (autoUpdate) {
451
long now = getTime();
452
long delta = now - lastUpdate;
453
if (firstUpdate) {
454
delta = 0;
455
firstUpdate = false;
456
}
457
lastUpdate = now;
458
nextFrame(delta);
459
}
460
461
Frame frame = (Frame) frames.get(currentFrame);
462
frame.image.drawFlash(x,y,width,height,col);
463
}
464
465
/**
466
* Update the animation cycle without draw the image, useful
467
* for keeping two animations in sync
468
*
469
* @deprecated
470
*/
471
public void updateNoDraw() {
472
if (autoUpdate) {
473
long now = getTime();
474
long delta = now - lastUpdate;
475
if (firstUpdate) {
476
delta = 0;
477
firstUpdate = false;
478
}
479
lastUpdate = now;
480
nextFrame(delta);
481
}
482
}
483
484
/**
485
* Update the animation, note that this will have odd effects if auto update
486
* is also turned on
487
*
488
* @see #autoUpdate
489
* @param delta The amount of time thats passed since last update
490
*/
491
public void update(long delta) {
492
nextFrame(delta);
493
}
494
495
/**
496
* Get the index of the current frame
497
*
498
* @return The index of the current frame
499
*/
500
public int getFrame() {
501
return currentFrame;
502
}
503
504
/**
505
* Set the current frame to be rendered
506
*
507
* @param index The index of the frame to rendered
508
*/
509
public void setCurrentFrame(int index) {
510
currentFrame = index;
511
}
512
513
/**
514
* Get the image assocaited with a given frame index
515
*
516
* @param index The index of the frame image to retrieve
517
* @return The image of the specified animation frame
518
*/
519
public Image getImage(int index) {
520
Frame frame = (Frame) frames.get(index);
521
return frame.image;
522
}
523
524
/**
525
* Get the number of frames that are in the animation
526
*
527
* @return The number of frames that are in the animation
528
*/
529
public int getFrameCount() {
530
return frames.size();
531
}
532
533
/**
534
* Get the image associated with the current animation frame
535
*
536
* @return The image associated with the current animation frame
537
*/
538
public Image getCurrentFrame() {
539
Frame frame = (Frame) frames.get(currentFrame);
540
return frame.image;
541
}
542
543
/**
544
* Check if we need to move to the next frame
545
*
546
* @param delta The amount of time thats passed since last update
547
*/
548
private void nextFrame(long delta) {
549
if (stopped) {
550
return;
551
}
552
if (frames.size() == 0) {
553
return;
554
}
555
556
nextChange -= delta;
557
558
while (nextChange < 0 && (!stopped)) {
559
if (currentFrame == stopAt) {
560
stopped = true;
561
break;
562
}
563
if ((currentFrame == frames.size() - 1) && (!loop)) {
564
stopped = true;
565
break;
566
}
567
currentFrame = (currentFrame + direction) % frames.size();
568
569
if (pingPong) {
570
if (currentFrame <= 0) {
571
currentFrame = 0;
572
direction = 1;
573
}
574
if (currentFrame >= frames.size()-1) {
575
currentFrame = frames.size()-1;
576
direction = -1;
577
}
578
}
579
int realDuration = (int) (((Frame) frames.get(currentFrame)).duration / speed);
580
nextChange = nextChange + realDuration;
581
}
582
}
583
584
/**
585
* Indicate if this animation should loop or stop at the last frame
586
*
587
* @param loop True if this animation should loop (true = default)
588
*/
589
public void setLooping(boolean loop) {
590
this.loop = loop;
591
}
592
593
/**
594
* Get the accurate system time
595
*
596
* @return The system time in milliseconds
597
*/
598
private long getTime() {
599
return (Sys.getTime() * 1000) / Sys.getTimerResolution();
600
}
601
602
/**
603
* Indicate the animation should stop when it reaches the specified
604
* frame index (note, not frame number but index in the animation
605
*
606
* @param frameIndex The index of the frame to stop at
607
*/
608
public void stopAt(int frameIndex) {
609
stopAt = frameIndex;
610
}
611
612
/**
613
* Get the duration of a particular frame
614
*
615
* @param index The index of the given frame
616
* @return The duration in (ms) of the given frame
617
*/
618
public int getDuration(int index) {
619
return ((Frame) frames.get(index)).duration;
620
}
621
622
/**
623
* Set the duration of the given frame
624
*
625
* @param index The index of the given frame
626
* @param duration The duration in (ms) for the given frame
627
*/
628
public void setDuration(int index, int duration) {
629
((Frame) frames.get(index)).duration = duration;
630
}
631
632
/**
633
* Get the durations of all the frames in this animation
634
*
635
* @return The durations of all the frames in this animation
636
*/
637
public int[] getDurations() {
638
int[] durations = new int[frames.size()];
639
for (int i=0;i<frames.size();i++) {
640
durations[i] = getDuration(i);
641
}
642
643
return durations;
644
}
645
646
647
/**
648
* @see java.lang.Object#toString()
649
*/
650
public String toString() {
651
String res = "[Animation ("+frames.size()+") ";
652
for (int i=0;i<frames.size();i++) {
653
Frame frame = (Frame) frames.get(i);
654
res += frame.duration+",";
655
}
656
657
res += "]";
658
return res;
659
}
660
661
/**
662
* Create a copy of this animation. Note that the frames
663
* are not duplicated but shared with the original
664
*
665
* @return A copy of this animation
666
*/
667
public Animation copy() {
668
Animation copy = new Animation();
669
670
copy.spriteSheet = spriteSheet;
671
copy.frames = frames;
672
copy.autoUpdate = autoUpdate;
673
copy.direction = direction;
674
copy.loop = loop;
675
copy.pingPong = pingPong;
676
copy.speed = speed;
677
678
return copy;
679
}
680
681
/**
682
* A single frame within the animation
683
*
684
* @author kevin
685
*/
686
private class Frame {
687
/** The image to display for this frame */
688
public Image image;
689
/** The duration to display the image fro */
690
public int duration;
691
/** The x location of this frame on a SpriteSheet*/
692
public int x = -1;
693
/** The y location of this frame on a SpriteSheet*/
694
public int y = -1;
695
696
/**
697
* Create a new animation frame
698
*
699
* @param image The image to display for the frame
700
* @param duration The duration in millisecond to display the image for
701
*/
702
public Frame(Image image, int duration) {
703
this.image = image;
704
this.duration = duration;
705
}
706
707
/**
708
* Creates a new animation frame with the frames image location on a sprite sheet
709
* @param duration The duration in millisecond to display the image for
710
* @param x the x location of the frame on the <tt>SpriteSheet</tt>
711
* @param y the y location of the frame on the <tt>SpriteSheet</tt>
712
*/
713
public Frame(int duration, int x, int y) {
714
this.image = spriteSheet.getSubImage(x, y);
715
this.duration = duration;
716
this.x = x;
717
this.y = y;
718
}
719
}
720
}
721
722