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 }