/org/flixel/FlxCamera.as
ActionScript | 706 lines | 376 code | 47 blank | 283 comment | 67 complexity | 79ccb9cb77d38b4e946be8c674cb495d MD5 | raw file
1package org.flixel
2{
3 import flash.display.Bitmap;
4 import flash.display.BitmapData;
5 import flash.display.Sprite;
6 import flash.geom.ColorTransform;
7 import flash.geom.Point;
8 import flash.geom.Rectangle;
9
10 /**
11 * The camera class is used to display the game's visuals in the Flash player.
12 * By default one camera is created automatically, that is the same size as the Flash player.
13 * You can add more cameras or even replace the main camera using utilities in <code>FlxG</code>.
14 *
15 * @author Adam Atomic
16 */
17 public class FlxCamera extends FlxBasic
18 {
19 /**
20 * Camera "follow" style preset: camera has no deadzone, just tracks the focus object directly.
21 */
22 static public const STYLE_LOCKON:uint = 0;
23 /**
24 * Camera "follow" style preset: camera deadzone is narrow but tall.
25 */
26 static public const STYLE_PLATFORMER:uint = 1;
27 /**
28 * Camera "follow" style preset: camera deadzone is a medium-size square around the focus object.
29 */
30 static public const STYLE_TOPDOWN:uint = 2;
31 /**
32 * Camera "follow" style preset: camera deadzone is a small square around the focus object.
33 */
34 static public const STYLE_TOPDOWN_TIGHT:uint = 3;
35
36 /**
37 * Camera "shake" effect preset: shake camera on both the X and Y axes.
38 */
39 static public const SHAKE_BOTH_AXES:uint = 0;
40 /**
41 * Camera "shake" effect preset: shake camera on the X axis only.
42 */
43 static public const SHAKE_HORIZONTAL_ONLY:uint = 1;
44 /**
45 * Camera "shake" effect preset: shake camera on the Y axis only.
46 */
47 static public const SHAKE_VERTICAL_ONLY:uint = 2;
48
49 /**
50 * While you can alter the zoom of each camera after the fact,
51 * this variable determines what value the camera will start at when created.
52 */
53 static public var defaultZoom:Number;
54
55 /**
56 * The X position of this camera's display. Zoom does NOT affect this number.
57 * Measured in pixels from the left side of the flash window.
58 */
59 public var x:Number;
60 /**
61 * The Y position of this camera's display. Zoom does NOT affect this number.
62 * Measured in pixels from the top of the flash window.
63 */
64 public var y:Number;
65 /**
66 * How wide the camera display is, in game pixels.
67 */
68 public var width:uint;
69 /**
70 * How tall the camera display is, in game pixels.
71 */
72 public var height:uint;
73 /**
74 * Tells the camera to follow this <code>FlxObject</code> object around.
75 */
76 public var target:FlxObject;
77 /**
78 * You can assign a "dead zone" to the camera in order to better control its movement.
79 * The camera will always keep the focus object inside the dead zone,
80 * unless it is bumping up against the bounds rectangle's edges.
81 * The deadzone's coordinates are measured from the camera's upper left corner in game pixels.
82 * For rapid prototyping, you can use the preset deadzones (e.g. <code>STYLE_PLATFORMER</code>) with <code>follow()</code>.
83 */
84 public var deadzone:FlxRect;
85 /**
86 * The edges of the camera's range, i.e. where to stop scrolling.
87 * Measured in game pixels and world coordinates.
88 */
89 public var bounds:FlxRect;
90
91 /**
92 * Stores the basic parallax scrolling values.
93 */
94 public var scroll:FlxPoint;
95 /**
96 * The actual bitmap data of the camera display itself.
97 */
98 public var buffer:BitmapData;
99 /**
100 * The natural background color of the camera. Defaults to FlxG.bgColor.
101 * NOTE: can be transparent for crazy FX!
102 */
103 public var bgColor:uint;
104 /**
105 * Sometimes it's easier to just work with a <code>FlxSprite</code> than it is to work
106 * directly with the <code>BitmapData</code> buffer. This sprite reference will
107 * allow you to do exactly that.
108 */
109 public var screen:FlxSprite;
110
111 /**
112 * Indicates how far the camera is zoomed in.
113 */
114 protected var _zoom:Number;
115 /**
116 * Internal, to help avoid costly allocations.
117 */
118 protected var _point:FlxPoint;
119 /**
120 * Internal, help with color transforming the flash bitmap.
121 */
122 protected var _color:uint;
123
124 /**
125 * Internal, used to render buffer to screen space.
126 */
127 protected var _flashBitmap:Bitmap;
128 /**
129 * Internal, used to render buffer to screen space.
130 */
131 internal var _flashSprite:Sprite;
132 /**
133 * Internal, used to render buffer to screen space.
134 */
135 internal var _flashOffsetX:Number;
136 /**
137 * Internal, used to render buffer to screen space.
138 */
139 internal var _flashOffsetY:Number;
140 /**
141 * Internal, used to render buffer to screen space.
142 */
143 protected var _flashRect:Rectangle;
144 /**
145 * Internal, used to render buffer to screen space.
146 */
147 protected var _flashPoint:Point;
148 /**
149 * Internal, used to control the "flash" special effect.
150 */
151 protected var _fxFlashColor:uint;
152 /**
153 * Internal, used to control the "flash" special effect.
154 */
155 protected var _fxFlashDuration:Number;
156 /**
157 * Internal, used to control the "flash" special effect.
158 */
159 protected var _fxFlashComplete:Function;
160 /**
161 * Internal, used to control the "flash" special effect.
162 */
163 protected var _fxFlashAlpha:Number;
164 /**
165 * Internal, used to control the "fade" special effect.
166 */
167 protected var _fxFadeColor:uint;
168 /**
169 * Internal, used to control the "fade" special effect.
170 */
171 protected var _fxFadeDuration:Number;
172 /**
173 * Internal, used to control the "fade" special effect.
174 */
175 protected var _fxFadeComplete:Function;
176 /**
177 * Internal, used to control the "fade" special effect.
178 */
179 protected var _fxFadeAlpha:Number;
180 /**
181 * Internal, used to control the "shake" special effect.
182 */
183 protected var _fxShakeIntensity:Number;
184 /**
185 * Internal, used to control the "shake" special effect.
186 */
187 protected var _fxShakeDuration:Number;
188 /**
189 * Internal, used to control the "shake" special effect.
190 */
191 protected var _fxShakeComplete:Function;
192 /**
193 * Internal, used to control the "shake" special effect.
194 */
195 protected var _fxShakeOffset:FlxPoint;
196 /**
197 * Internal, used to control the "shake" special effect.
198 */
199 protected var _fxShakeDirection:uint;
200 /**
201 * Internal helper variable for doing better wipes/fills between renders.
202 */
203 protected var _fill:BitmapData;
204
205 /**
206 * Instantiates a new camera at the specified location, with the specified size and zoom level.
207 *
208 * @param X X location of the camera's display in pixels. Uses native, 1:1 resolution, ignores zoom.
209 * @param Y Y location of the camera's display in pixels. Uses native, 1:1 resolution, ignores zoom.
210 * @param Width The width of the camera display in pixels.
211 * @param Height The height of the camera display in pixels.
212 * @param Zoom The initial zoom level of the camera. A zoom level of 2 will make all pixels display at 2x resolution.
213 */
214 public function FlxCamera(X:int,Y:int,Width:int,Height:int,Zoom:Number=0)
215 {
216 x = X;
217 y = Y;
218 width = Width;
219 height = Height;
220 target = null;
221 deadzone = null;
222 scroll = new FlxPoint();
223 _point = new FlxPoint();
224 bounds = null;
225 screen = new FlxSprite();
226 screen.makeGraphic(width,height,0,true);
227 screen.setOriginToCorner();
228 buffer = screen.pixels;
229 bgColor = FlxG.bgColor;
230 _color = 0xffffff;
231
232 _flashBitmap = new Bitmap(buffer);
233 _flashBitmap.x = -width*0.5;
234 _flashBitmap.y = -height*0.5;
235 _flashSprite = new Sprite();
236 zoom = Zoom; //sets the scale of flash sprite, which in turn loads flashoffset values
237 _flashOffsetX = width*0.5*zoom;
238 _flashOffsetY = height*0.5*zoom;
239 _flashSprite.x = x + _flashOffsetX;
240 _flashSprite.y = y + _flashOffsetY;
241 _flashSprite.addChild(_flashBitmap);
242 _flashRect = new Rectangle(0,0,width,height);
243 _flashPoint = new Point();
244
245 _fxFlashColor = 0;
246 _fxFlashDuration = 0.0;
247 _fxFlashComplete = null;
248 _fxFlashAlpha = 0.0;
249
250 _fxFadeColor = 0;
251 _fxFadeDuration = 0.0;
252 _fxFadeComplete = null;
253 _fxFadeAlpha = 0.0;
254
255 _fxShakeIntensity = 0.0;
256 _fxShakeDuration = 0.0;
257 _fxShakeComplete = null;
258 _fxShakeOffset = new FlxPoint();
259 _fxShakeDirection = 0;
260
261 _fill = new BitmapData(width,height,true,0);
262 }
263
264 /**
265 * Clean up memory.
266 */
267 override public function destroy():void
268 {
269 screen.destroy();
270 screen = null;
271 target = null;
272 scroll = null;
273 deadzone = null;
274 bounds = null;
275 buffer = null;
276 _flashBitmap = null;
277 _flashRect = null;
278 _flashPoint = null;
279 _fxFlashComplete = null;
280 _fxFadeComplete = null;
281 _fxShakeComplete = null;
282 _fxShakeOffset = null;
283 _fill = null;
284 }
285
286 /**
287 * Updates the camera scroll as well as special effects like screen-shake or fades.
288 */
289 override public function update():void
290 {
291 //Either follow the object closely,
292 //or doublecheck our deadzone and update accordingly.
293 if(target != null)
294 {
295 if(deadzone == null)
296 focusOn(target.getMidpoint(_point));
297 else
298 {
299 var edge:Number;
300 var targetX:Number = target.x + ((target.x > 0)?0.0000001:-0.0000001);
301 var targetY:Number = target.y + ((target.y > 0)?0.0000001:-0.0000001);
302
303 edge = targetX - deadzone.x;
304 if(scroll.x > edge)
305 scroll.x = edge;
306 edge = targetX + target.width - deadzone.x - deadzone.width;
307 if(scroll.x < edge)
308 scroll.x = edge;
309
310 edge = targetY - deadzone.y;
311 if(scroll.y > edge)
312 scroll.y = edge;
313 edge = targetY + target.height - deadzone.y - deadzone.height;
314 if(scroll.y < edge)
315 scroll.y = edge;
316 }
317 }
318
319 //Make sure we didn't go outside the camera's bounds
320 if(bounds != null)
321 {
322 if(scroll.x < bounds.left)
323 scroll.x = bounds.left;
324 if(scroll.x > bounds.right - width)
325 scroll.x = bounds.right - width;
326 if(scroll.y < bounds.top)
327 scroll.y = bounds.top;
328 if(scroll.y > bounds.bottom - height)
329 scroll.y = bounds.bottom - height;
330 }
331
332 //Update the "flash" special effect
333 if(_fxFlashAlpha > 0.0)
334 {
335 _fxFlashAlpha -= FlxG.elapsed/_fxFlashDuration;
336 if((_fxFlashAlpha <= 0) && (_fxFlashComplete != null))
337 _fxFlashComplete();
338 }
339
340 //Update the "fade" special effect
341 if((_fxFadeAlpha > 0.0) && (_fxFadeAlpha < 1.0))
342 {
343 _fxFadeAlpha += FlxG.elapsed/_fxFadeDuration;
344 if(_fxFadeAlpha >= 1.0)
345 {
346 _fxFadeAlpha = 1.0;
347 if(_fxFadeComplete != null)
348 _fxFadeComplete();
349 }
350 }
351
352 //Update the "shake" special effect
353 if(_fxShakeDuration > 0)
354 {
355 _fxShakeDuration -= FlxG.elapsed;
356 if(_fxShakeDuration <= 0)
357 {
358 _fxShakeOffset.make();
359 if(_fxShakeComplete != null)
360 _fxShakeComplete();
361 }
362 else
363 {
364 if((_fxShakeDirection == SHAKE_BOTH_AXES) || (_fxShakeDirection == SHAKE_HORIZONTAL_ONLY))
365 _fxShakeOffset.x = (FlxG.random()*_fxShakeIntensity*width*2-_fxShakeIntensity*width)*_zoom;
366 if((_fxShakeDirection == SHAKE_BOTH_AXES) || (_fxShakeDirection == SHAKE_VERTICAL_ONLY))
367 _fxShakeOffset.y = (FlxG.random()*_fxShakeIntensity*height*2-_fxShakeIntensity*height)*_zoom;
368 }
369 }
370 }
371
372 /**
373 * Tells this camera object what <code>FlxObject</code> to track.
374 *
375 * @param Target The object you want the camera to track. Set to null to not follow anything.
376 * @param Style Leverage one of the existing "deadzone" presets. If you use a custom deadzone, ignore this parameter and manually specify the deadzone after calling <code>follow()</code>.
377 */
378 public function follow(Target:FlxObject, Style:uint=STYLE_LOCKON):void
379 {
380 target = Target;
381 var helper:Number;
382 switch(Style)
383 {
384 case STYLE_PLATFORMER:
385 var w:Number = width/8;
386 var h:Number = height/3;
387 deadzone = new FlxRect((width-w)/2,(height-h)/2 - h*0.25,w,h);
388 break;
389 case STYLE_TOPDOWN:
390 helper = FlxU.max(width,height)/4;
391 deadzone = new FlxRect((width-helper)/2,(height-helper)/2,helper,helper);
392 break;
393 case STYLE_TOPDOWN_TIGHT:
394 helper = FlxU.max(width,height)/8;
395 deadzone = new FlxRect((width-helper)/2,(height-helper)/2,helper,helper);
396 break;
397 case STYLE_LOCKON:
398 default:
399 deadzone = null;
400 break;
401 }
402 }
403
404 /**
405 * Move the camera focus to this location instantly.
406 *
407 * @param Point Where you want the camera to focus.
408 */
409 public function focusOn(Point:FlxPoint):void
410 {
411 Point.x += (Point.x > 0)?0.0000001:-0.0000001;
412 Point.y += (Point.y > 0)?0.0000001:-0.0000001;
413 scroll.make(Point.x - width*0.5,Point.y - height*0.5);
414 }
415
416 /**
417 * Specify the boundaries of the level or where the camera is allowed to move.
418 *
419 * @param X The smallest X value of your level (usually 0).
420 * @param Y The smallest Y value of your level (usually 0).
421 * @param Width The largest X value of your level (usually the level width).
422 * @param Height The largest Y value of your level (usually the level height).
423 * @param UpdateWorld Whether the global quad-tree's dimensions should be updated to match (default: false).
424 */
425 public function setBounds(X:Number=0, Y:Number=0, Width:Number=0, Height:Number=0, UpdateWorld:Boolean=false):void
426 {
427 if(bounds == null)
428 bounds = new FlxRect();
429 bounds.make(X,Y,Width,Height);
430 if(UpdateWorld)
431 FlxG.worldBounds.copyFrom(bounds);
432 update();
433 }
434
435 /**
436 * The screen is filled with this color and gradually returns to normal.
437 *
438 * @param Color The color you want to use.
439 * @param Duration How long it takes for the flash to fade.
440 * @param OnComplete A function you want to run when the flash finishes.
441 * @param Force Force the effect to reset.
442 */
443 public function flash(Color:uint=0xffffffff, Duration:Number=1, OnComplete:Function=null, Force:Boolean=false):void
444 {
445 if(!Force && (_fxFlashAlpha > 0.0))
446 return;
447 _fxFlashColor = Color;
448 if(Duration <= 0)
449 Duration = Number.MIN_VALUE;
450 _fxFlashDuration = Duration;
451 _fxFlashComplete = OnComplete;
452 _fxFlashAlpha = 1.0;
453 }
454
455 /**
456 * The screen is gradually filled with this color.
457 *
458 * @param Color The color you want to use.
459 * @param Duration How long it takes for the fade to finish.
460 * @param OnComplete A function you want to run when the fade finishes.
461 * @param Force Force the effect to reset.
462 */
463 public function fade(Color:uint=0xff000000, Duration:Number=1, OnComplete:Function=null, Force:Boolean=false):void
464 {
465 if(!Force && (_fxFadeAlpha > 0.0))
466 return;
467 _fxFadeColor = Color;
468 if(Duration <= 0)
469 Duration = Number.MIN_VALUE;
470 _fxFadeDuration = Duration;
471 _fxFadeComplete = OnComplete;
472 _fxFadeAlpha = Number.MIN_VALUE;
473 }
474
475 /**
476 * A simple screen-shake effect.
477 *
478 * @param Intensity Percentage of screen size representing the maximum distance that the screen can move while shaking.
479 * @param Duration The length in seconds that the shaking effect should last.
480 * @param OnComplete A function you want to run when the shake effect finishes.
481 * @param Force Force the effect to reset (default = true, unlike flash() and fade()!).
482 * @param Direction Whether to shake on both axes, just up and down, or just side to side (use class constants SHAKE_BOTH_AXES, SHAKE_VERTICAL_ONLY, or SHAKE_HORIZONTAL_ONLY).
483 */
484 public function shake(Intensity:Number=0.05, Duration:Number=0.5, OnComplete:Function=null, Force:Boolean=true, Direction:uint=SHAKE_BOTH_AXES):void
485 {
486 if(!Force && ((_fxShakeOffset.x != 0) || (_fxShakeOffset.y != 0)))
487 return;
488 _fxShakeIntensity = Intensity;
489 _fxShakeDuration = Duration;
490 _fxShakeComplete = OnComplete;
491 _fxShakeDirection = Direction;
492 _fxShakeOffset.make();
493 }
494
495 /**
496 * Just turns off all the camera effects instantly.
497 */
498 public function stopFX():void
499 {
500 _fxFlashAlpha = 0.0;
501 _fxFadeAlpha = 0.0;
502 _fxShakeDuration = 0;
503 _flashSprite.x = x + width*0.5;
504 _flashSprite.y = y + height*0.5;
505 }
506
507 /**
508 * Copy the bounds, focus object, and deadzone info from an existing camera.
509 *
510 * @param Camera The camera you want to copy from.
511 *
512 * @return A reference to this <code>FlxCamera</code> object.
513 */
514 public function copyFrom(Camera:FlxCamera):FlxCamera
515 {
516 if(Camera.bounds == null)
517 bounds = null;
518 else
519 {
520 if(bounds == null)
521 bounds = new FlxRect();
522 bounds.copyFrom(Camera.bounds);
523 }
524 target = Camera.target;
525 if(target != null)
526 {
527 if(Camera.deadzone == null)
528 deadzone = null;
529 else
530 {
531 if(deadzone == null)
532 deadzone = new FlxRect();
533 deadzone.copyFrom(Camera.deadzone);
534 }
535 }
536 return this;
537 }
538
539 /**
540 * The zoom level of this camera. 1 = 1:1, 2 = 2x zoom, etc.
541 */
542 public function get zoom():Number
543 {
544 return _zoom;
545 }
546
547 /**
548 * @private
549 */
550 public function set zoom(Zoom:Number):void
551 {
552 if(Zoom == 0)
553 _zoom = defaultZoom;
554 else
555 _zoom = Zoom;
556 setScale(_zoom,_zoom);
557 }
558
559 /**
560 * The alpha value of this camera display (a Number between 0.0 and 1.0).
561 */
562 public function get alpha():Number
563 {
564 return _flashBitmap.alpha;
565 }
566
567 /**
568 * @private
569 */
570 public function set alpha(Alpha:Number):void
571 {
572 _flashBitmap.alpha = Alpha;
573 }
574
575 /**
576 * The angle of the camera display (in degrees).
577 * Currently yields weird display results,
578 * since cameras aren't nested in an extra display object yet.
579 */
580 public function get angle():Number
581 {
582 return _flashSprite.rotation;
583 }
584
585 /**
586 * @private
587 */
588 public function set angle(Angle:Number):void
589 {
590 _flashSprite.rotation = Angle;
591 }
592
593 /**
594 * The color tint of the camera display.
595 */
596 public function get color():uint
597 {
598 return _color;
599 }
600
601 /**
602 * @private
603 */
604 public function set color(Color:uint):void
605 {
606 _color = Color;
607 var colorTransform:ColorTransform = _flashBitmap.transform.colorTransform;
608 colorTransform.redMultiplier = (_color>>16)*0.00392;
609 colorTransform.greenMultiplier = (_color>>8&0xff)*0.00392;
610 colorTransform.blueMultiplier = (_color&0xff)*0.00392;
611 _flashBitmap.transform.colorTransform = colorTransform;
612 }
613
614 /**
615 * Whether the camera display is smooth and filtered, or chunky and pixelated.
616 * Default behavior is chunky-style.
617 */
618 public function get antialiasing():Boolean
619 {
620 return _flashBitmap.smoothing;
621 }
622
623 /**
624 * @private
625 */
626 public function set antialiasing(Antialiasing:Boolean):void
627 {
628 _flashBitmap.smoothing = Antialiasing;
629 }
630
631 /**
632 * The scale of the camera object, irrespective of zoom.
633 * Currently yields weird display results,
634 * since cameras aren't nested in an extra display object yet.
635 */
636 public function getScale():FlxPoint
637 {
638 return _point.make(_flashSprite.scaleX,_flashSprite.scaleY);
639 }
640
641 /**
642 * @private
643 */
644 public function setScale(X:Number,Y:Number):void
645 {
646 _flashSprite.scaleX = X;
647 _flashSprite.scaleY = Y;
648 }
649
650 /**
651 * Fetches a reference to the Flash <code>Sprite</code> object
652 * that contains the camera display in the Flash display list.
653 * Uses include 3D projection, advanced display list modification, and more.
654 * NOTE: We don't recommend modifying this directly unless you are
655 * fairly experienced. For simple changes to the camera display,
656 * like scaling, rotation, and color tinting, we recommend
657 * using the existing <code>FlxCamera</code> variables.
658 *
659 * @return A Flash <code>Sprite</code> object containing the camera display.
660 */
661 public function getContainerSprite():Sprite
662 {
663 return _flashSprite;
664 }
665
666 /**
667 * Fill the camera with the specified color.
668 *
669 * @param Color The color to fill with in 0xAARRGGBB hex format.
670 * @param BlendAlpha Whether to blend the alpha value or just wipe the previous contents. Default is true.
671 */
672 public function fill(Color:uint,BlendAlpha:Boolean=true):void
673 {
674 _fill.fillRect(_flashRect,Color);
675 buffer.copyPixels(_fill,_flashRect,_flashPoint,null,null,BlendAlpha);
676 }
677
678 /**
679 * Internal helper function, handles the actual drawing of all the special effects.
680 */
681 internal function drawFX():void
682 {
683 var alphaComponent:Number;
684
685 //Draw the "flash" special effect onto the buffer
686 if(_fxFlashAlpha > 0.0)
687 {
688 alphaComponent = _fxFlashColor>>24;
689 fill((uint(((alphaComponent <= 0)?0xff:alphaComponent)*_fxFlashAlpha)<<24)+(_fxFlashColor&0x00ffffff));
690 }
691
692 //Draw the "fade" special effect onto the buffer
693 if(_fxFadeAlpha > 0.0)
694 {
695 alphaComponent = _fxFadeColor>>24;
696 fill((uint(((alphaComponent <= 0)?0xff:alphaComponent)*_fxFadeAlpha)<<24)+(_fxFadeColor&0x00ffffff));
697 }
698
699 if((_fxShakeOffset.x != 0) || (_fxShakeOffset.y != 0))
700 {
701 _flashSprite.x = x + _flashOffsetX + _fxShakeOffset.x;
702 _flashSprite.y = y + _flashOffsetY + _fxShakeOffset.y;
703 }
704 }
705 }
706}