001 package com.croftsoft.apps.sprite;
002
003 import java.awt.*;
004 import java.awt.image.*;
005 import java.io.*;
006 import java.util.*;
007 import javax.swing.*;
008
009 import com.croftsoft.core.animation.*;
010 import com.croftsoft.core.animation.animator.*;
011 import com.croftsoft.core.animation.clock.*;
012 import com.croftsoft.core.animation.collector.*;
013 import com.croftsoft.core.animation.component.*;
014 import com.croftsoft.core.animation.factory.DefaultAnimationFactory;
015 import com.croftsoft.core.animation.painter.*;
016 import com.croftsoft.core.animation.sprite.*;
017 import com.croftsoft.core.animation.updater.*;
018 import com.croftsoft.core.awt.image.ImageLib;
019 import com.croftsoft.core.util.loop.FixedDelayLoopGovernor;
020 import com.croftsoft.core.util.loop.NanoTimeLoopGovernor;
021
022 // Direct control of imageCache when sprite removed for last time?
023
024 // MemoryCacheInputStream
025 // Java 1.4 upgrade: Component.createVolatileImage(int,int)
026
027 // garbage collection settings?
028
029 // transparent background Color for fun
030
031 /*********************************************************************
032 * Directs the AnimatedComponent.
033 *
034 * @version
035 * $Date: 2008/04/19 21:31:00 $
036 * @since
037 * 2002-02-15
038 * @author
039 * <a href="https://www.croftsoft.com/">David Wallace Croft</a>
040 *********************************************************************/
041
042 public final class SpriteAnimator
043 implements Clock, ComponentAnimator, SpriteConstants
044 //////////////////////////////////////////////////////////////////////
045 //////////////////////////////////////////////////////////////////////
046 {
047
048 private final AnimatedComponent animatedComponent;
049
050 private final Map iconMap;
051
052 // get rid of Random
053 private final Random random;
054
055 private final Rectangle bounds;
056
057 private final Rectangle spriteBounds;
058
059 //
060
061 private Clock clock;
062
063 private long updateTimeNanos;
064
065 // Animators
066
067 private TileAnimator brickTileAnimator;
068
069 private Sprite [ ] headSprites;
070
071 private Sprite [ ] rateSprites;
072
073 private Sprite [ ] sprites;
074
075 private TileAnimator cloudTileAnimator;
076
077 // Updaters
078
079 private FogNightUpdater fogNightUpdater;
080
081 private ComponentUpdater updateRateSampler;
082
083 // Painters
084
085 private ColorPainter brickColorPainter;
086
087 private CursorAnimator cursorAnimator;
088
089 private ColorPainter fogNightColorPainter;
090
091 private ComponentPainter paintRateSampler;
092
093 //
094
095 private boolean initialized;
096
097 private boolean paintBackground;
098
099 private boolean paintBricks;
100
101 private boolean paintSprites;
102
103 private boolean paintClouds;
104
105 private boolean paintFogNight;
106
107 private boolean onlySpriteUpdates;
108
109 private boolean onlySpriteRegions;
110
111 //////////////////////////////////////////////////////////////////////
112 //////////////////////////////////////////////////////////////////////
113
114 public SpriteAnimator ( BufferStrategy bufferStrategy )
115 //////////////////////////////////////////////////////////////////////
116 {
117 //javax.swing.RepaintManager.setCurrentManager ( new SpriteRepaintManager ( ) );
118
119 iconMap = new HashMap ( );
120
121 random = new Random ( );
122
123 bounds = new Rectangle ( );
124
125 spriteBounds = new Rectangle ( );
126
127 if ( bufferStrategy == null )
128 {
129 animatedComponent = new AnimatedComponent (
130 this,
131 new CoalescingRepaintCollector ( ),
132 new NanoTimeLoopGovernor ( DEFAULT_FRAME_RATE ) );
133 }
134 else
135 {
136 animatedComponent = new BufferStrategyAnimatedComponent (
137 this,
138 new CoalescingRepaintCollector ( ),
139 new NanoTimeLoopGovernor ( DEFAULT_FRAME_RATE ),
140 bufferStrategy );
141 }
142
143 animatedComponent.setBackground ( Color.MAGENTA );
144
145 animatedComponent.setForeground ( Color.GREEN );
146
147 animatedComponent.setFont (
148 new Font ( "Times New Roman", Font.BOLD, 12 ) );
149
150 paintBackground = true;
151
152 paintBricks = true;
153
154 paintSprites = true;
155
156 paintClouds = true;
157
158 paintFogNight = false;
159
160 clock = new HiResClock ( );
161 }
162
163 public void initialize ( )
164 throws IOException
165 //////////////////////////////////////////////////////////////////////
166 {
167 IconSprite sprite1
168 = new IconSprite ( validateIcon ( NORMAL_IMAGE_FILENAME ) );
169
170 sprite1.setComponentUpdater (
171 new ArrayComponentUpdater (
172 new ComponentUpdater [ ] {
173 new BounceUpdater ( sprite1, bounds, this ),
174 new ImpactUpdater (
175 sprite1,
176 validateIcon ( NORMAL_IMAGE_FILENAME ),
177 validateIcon ( IMPACT_IMAGE_FILENAME ),
178 250000000,
179 this ) } ) );
180
181 sprite1.setHeading ( random.nextDouble ( ) * 2.0 * Math.PI );
182
183 IconSprite sprite2
184 = new IconSprite ( validateIcon ( LOOK_LEFT_IMAGE_FILENAME ) );
185
186 sprite2.setY ( 10.0 );
187
188 sprite2.setComponentUpdater (
189 new ArrayComponentUpdater (
190 new ComponentUpdater [ ] {
191 new BounceUpdater ( sprite2, bounds, this ),
192 new LeftRightUpdater (
193 sprite2,
194 validateIcon ( LOOK_LEFT_IMAGE_FILENAME ),
195 validateIcon ( LOOK_RIGHT_IMAGE_FILENAME ) ) } ) );
196
197 TextSprite updateRateTextSprite = new TextSprite ( "*" );
198
199 TextSprite paintRateTextSprite = new TextSprite ( "*" );
200
201 updateRateTextSprite.setColor ( Color.GREEN );
202
203 paintRateTextSprite .setColor ( Color.BLUE );
204
205 updateRateTextSprite.setComponentUpdater (
206 new TextWrapUpdater ( updateRateTextSprite, -1, 1, bounds ) );
207
208 paintRateTextSprite .setComponentUpdater (
209 new TextWrapUpdater ( paintRateTextSprite , -1, -1, bounds ) );
210
211 updateRateSampler = new FrameRateSampler (
212 updateRateTextSprite, 3000, "Updates per second: " );
213
214 paintRateSampler = new FrameRateSampler (
215 paintRateTextSprite , 3000, "Paints per second: " );
216
217 headSprites = new Sprite [ ] {
218 sprite1,
219 sprite2 };
220
221 rateSprites = new Sprite [ ] {
222 paintRateTextSprite,
223 updateRateTextSprite };
224
225 sprites = new Sprite [ ] {
226 sprite1,
227 sprite2,
228 paintRateTextSprite,
229 updateRateTextSprite };
230
231 setSpriteVelocity ( DEFAULT_SPRITE_VELOCITY );
232
233 // We need a background color since the background image contains
234 // some transparent pixels.
235
236 brickColorPainter = new ColorPainter ( Color.RED, bounds );
237
238 brickTileAnimator = new TileAnimator (
239 0, 0, validateIcon ( BACKGROUND_IMAGE_FILENAME ), null, 1, 1 );
240
241 cloudTileAnimator = new TileAnimator (
242 0, 0, validateIcon ( CLOUD_IMAGE_FILENAME ), null, -1, 1 );
243
244 fogNightColorPainter = new ColorPainter ( Color.BLACK, bounds );
245
246 fogNightUpdater = new FogNightUpdater (
247 fogNightColorPainter, brickColorPainter, 60, this );
248
249 // show a sprite with more than one updater (multimage animation)
250
251 // also show alpha transparency baloons
252
253 // show orientation and mirroring
254
255 // show animated GIFs and other animation
256
257 cursorAnimator = new CursorAnimator (
258 validateIcon ( MOUSE_IMAGE_FILENAME ),
259 validateIcon ( MOUSE_PRESSED_IMAGE_FILENAME ),
260 ( Point ) null, // hotSpot
261 animatedComponent );
262
263 initialized = true;
264 }
265
266 //////////////////////////////////////////////////////////////////////
267 // accessor methods
268 //////////////////////////////////////////////////////////////////////
269
270 public AnimatedComponent getAnimatedComponent ( )
271 //////////////////////////////////////////////////////////////////////
272 {
273 return animatedComponent;
274 }
275
276 //////////////////////////////////////////////////////////////////////
277 // mutator methods
278 //////////////////////////////////////////////////////////////////////
279
280 public void setSpriteVelocity ( double spriteVelocity )
281 //////////////////////////////////////////////////////////////////////
282 {
283 for ( int i = 0; i < sprites.length; i++ )
284 {
285 sprites [ i ].setVelocity ( spriteVelocity );
286 }
287 }
288
289 public void setPaintBackground ( boolean paintBackground )
290 //////////////////////////////////////////////////////////////////////
291 {
292 this.paintBackground = paintBackground;
293 }
294
295 public void setPaintBricks ( boolean paintBricks )
296 //////////////////////////////////////////////////////////////////////
297 {
298 this.paintBricks = paintBricks;
299 }
300
301 public void setPaintSprites ( boolean paintSprites )
302 //////////////////////////////////////////////////////////////////////
303 {
304 this.paintSprites = paintSprites;
305 }
306
307 public void setPaintClouds ( boolean paintClouds )
308 //////////////////////////////////////////////////////////////////////
309 {
310 this.paintClouds = paintClouds;
311 }
312
313 public void setPaintFogNight ( boolean paintFogNight )
314 //////////////////////////////////////////////////////////////////////
315 {
316 this.paintFogNight = paintFogNight;
317 }
318
319 public void setOnlySpriteUpdates ( boolean onlySpriteUpdates )
320 //////////////////////////////////////////////////////////////////////
321 {
322 this.onlySpriteUpdates = onlySpriteUpdates;
323 }
324
325 public void setOnlySpriteRegions ( boolean onlySpriteRegions )
326 //////////////////////////////////////////////////////////////////////
327 {
328 this.onlySpriteRegions = onlySpriteRegions;
329 }
330
331 public void setUseSystemClock ( boolean useSystemClock )
332 //////////////////////////////////////////////////////////////////////
333 {
334 if ( useSystemClock )
335 {
336 clock = SystemClock.INSTANCE;
337 }
338 else
339 {
340 clock = new HiResClock ( );
341 }
342 }
343
344 public void setLoopGovernor (
345 boolean useFixedDelay,
346 double frequency )
347 //////////////////////////////////////////////////////////////////////
348 {
349 if ( useFixedDelay )
350 {
351 animatedComponent.setLoopGovernor (
352 new FixedDelayLoopGovernor ( frequency ) );
353 }
354 else
355 {
356 animatedComponent.setLoopGovernor (
357 new NanoTimeLoopGovernor ( frequency ) );
358 }
359 }
360
361 public void setUseSwingRepaintCollector (
362 boolean useSwingRepaintCollector )
363 //////////////////////////////////////////////////////////////////////
364 {
365 if ( useSwingRepaintCollector )
366 {
367 animatedComponent.setRepaintCollector (
368 new SwingRepaintCollector ( animatedComponent ) );
369 }
370 else
371 {
372 animatedComponent.setRepaintCollector (
373 DefaultAnimationFactory.INSTANCE.createRepaintCollector ( ) );
374 }
375 }
376
377 public void setUseDoubleBuffering ( boolean useDoubleBuffering )
378 //////////////////////////////////////////////////////////////////////
379 {
380 RepaintManager.currentManager ( animatedComponent )
381 .setDoubleBufferingEnabled ( useDoubleBuffering );
382 }
383
384 //////////////////////////////////////////////////////////////////////
385 // interface Clock method
386 //////////////////////////////////////////////////////////////////////
387
388 public long currentTimeNanos ( )
389 //////////////////////////////////////////////////////////////////////
390 {
391 return updateTimeNanos;
392 }
393
394 //////////////////////////////////////////////////////////////////////
395 // interface ComponentAnimator methods
396 //////////////////////////////////////////////////////////////////////
397
398 public void update ( JComponent component )
399 //////////////////////////////////////////////////////////////////////
400 {
401 if ( !initialized )
402 {
403 return;
404 }
405
406 updateTimeNanos = clock.currentTimeNanos ( );
407
408 // Set the bounds here to prevent them from changing during the
409 // update.
410
411 component.getBounds ( bounds );
412
413 if ( paintSprites )
414 {
415 for ( int i = 0; i < headSprites.length; i++ )
416 {
417 headSprites [ i ].update ( component );
418 }
419 }
420
421 if ( !onlySpriteUpdates )
422 {
423 RepaintCollector oldRepaintCollector
424 = animatedComponent.setRepaintCollector (
425 NullRepaintCollector.INSTANCE );
426
427 if ( paintBricks )
428 {
429 brickTileAnimator.update ( component );
430 }
431
432 if ( paintClouds )
433 {
434 cloudTileAnimator.update ( component );
435 }
436
437 if ( paintFogNight )
438 {
439 fogNightUpdater .update ( component );
440 }
441
442 animatedComponent.setRepaintCollector ( oldRepaintCollector );
443 }
444
445 cursorAnimator.update ( component );
446
447 for ( int i = 0; i < rateSprites.length; i++ )
448 {
449 rateSprites [ i ].update ( component );
450 }
451
452 updateRateSampler.update ( component );
453
454 if ( !onlySpriteRegions )
455 {
456 component.repaint ( );
457 }
458 }
459
460 public void paint (
461 JComponent component,
462 Graphics2D graphics )
463 //////////////////////////////////////////////////////////////////////
464 {
465 if ( !initialized )
466 {
467 try
468 {
469 initialize ( );
470 }
471 catch ( IOException ex )
472 {
473 ex.printStackTrace ( );
474 }
475 }
476
477 if ( paintBackground )
478 {
479 brickColorPainter.paint ( component, graphics );
480 }
481
482 if ( paintBricks )
483 {
484 brickTileAnimator.paint ( component, graphics );
485 }
486
487 if ( paintSprites )
488 {
489 for ( int i = 0; i < headSprites.length; i++ )
490 {
491 headSprites [ i ].paint ( component, graphics );
492 }
493 }
494
495 cursorAnimator.paint ( component, graphics );
496
497 if ( paintClouds )
498 {
499 cloudTileAnimator.paint ( component, graphics );
500 }
501
502 if ( paintFogNight )
503 {
504 fogNightColorPainter.paint ( component, graphics );
505 }
506
507 paintRateSampler.paint ( component, graphics );
508
509 for ( int i = 0; i < rateSprites.length; i++ )
510 {
511 rateSprites [ i ].paint ( component, graphics );
512 }
513 }
514
515 //////////////////////////////////////////////////////////////////////
516 // private methods
517 //////////////////////////////////////////////////////////////////////
518
519 private Icon validateIcon ( String imageFilename )
520 throws IOException
521 //////////////////////////////////////////////////////////////////////
522 {
523 Icon icon = ( Icon ) iconMap.get ( imageFilename );
524
525 if ( icon == null )
526 {
527 Image image
528 = loadAutomaticImage ( imageFilename, Transparency.BITMASK );
529
530 if ( image == null )
531 {
532 return null;
533 }
534
535 icon = new ImageIcon ( image );
536
537 iconMap.put ( imageFilename, icon );
538 }
539
540 return icon;
541 }
542
543 private BufferedImage loadAutomaticImage (
544 String imageFilename,
545 int transparency )
546 throws IOException
547 //////////////////////////////////////////////////////////////////////
548 {
549 return ImageLib.loadAutomaticImage (
550 imageFilename,
551 transparency,
552 animatedComponent,
553 getClass ( ).getClassLoader ( ),
554 null );
555 }
556
557 //////////////////////////////////////////////////////////////////////
558 //////////////////////////////////////////////////////////////////////
559 }