PageRenderTime 175ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/org/flixel/FlxObject.as

https://github.com/initials/flixel
ActionScript | 1173 lines | 682 code | 84 blank | 407 comment | 187 complexity | 1aca37ce89b9e7a5b655b3c1ece7db3d MD5 | raw file
  1. package org.flixel
  2. {
  3. import flash.display.Graphics;
  4. import flash.display.Sprite;
  5. import flash.geom.Point;
  6. import org.flixel.FlxBasic;
  7. /**
  8. * This is the base class for most of the display objects (<code>FlxSprite</code>, <code>FlxText</code>, etc).
  9. * It includes some basic attributes about game objects, including retro-style flickering,
  10. * basic state information, sizes, scrolling, and basic physics and motion.
  11. *
  12. * @author Adam Atomic
  13. */
  14. public class FlxObject extends FlxBasic
  15. {
  16. /**
  17. * Generic value for "left" Used by <code>facing</code>, <code>allowCollisions</code>, and <code>touching</code>.
  18. */
  19. static public const LEFT:uint = 0x0001;
  20. /**
  21. * Generic value for "right" Used by <code>facing</code>, <code>allowCollisions</code>, and <code>touching</code>.
  22. */
  23. static public const RIGHT:uint = 0x0010;
  24. /**
  25. * Generic value for "up" Used by <code>facing</code>, <code>allowCollisions</code>, and <code>touching</code>.
  26. */
  27. static public const UP:uint = 0x0100;
  28. /**
  29. * Generic value for "down" Used by <code>facing</code>, <code>allowCollisions</code>, and <code>touching</code>.
  30. */
  31. static public const DOWN:uint = 0x1000;
  32. /**
  33. * Special-case constant meaning no collisions, used mainly by <code>allowCollisions</code> and <code>touching</code>.
  34. */
  35. static public const NONE:uint = 0;
  36. /**
  37. * Special-case constant meaning up, used mainly by <code>allowCollisions</code> and <code>touching</code>.
  38. */
  39. static public const CEILING:uint= UP;
  40. /**
  41. * Special-case constant meaning down, used mainly by <code>allowCollisions</code> and <code>touching</code>.
  42. */
  43. static public const FLOOR:uint = DOWN;
  44. /**
  45. * Special-case constant meaning only the left and right sides, used mainly by <code>allowCollisions</code> and <code>touching</code>.
  46. */
  47. static public const WALL:uint = LEFT | RIGHT;
  48. /**
  49. * Special-case constant meaning any direction, used mainly by <code>allowCollisions</code> and <code>touching</code>.
  50. */
  51. static public const ANY:uint = LEFT | RIGHT | UP | DOWN;
  52. /**
  53. * Handy constant used during collision resolution (see <code>separateX()</code> and <code>separateY()</code>).
  54. */
  55. static public const OVERLAP_BIAS:Number = 4;
  56. /**
  57. * Path behavior controls: move from the start of the path to the end then stop.
  58. */
  59. static public const PATH_FORWARD:uint = 0x000000;
  60. /**
  61. * Path behavior controls: move from the end of the path to the start then stop.
  62. */
  63. static public const PATH_BACKWARD:uint = 0x000001;
  64. /**
  65. * Path behavior controls: move from the start of the path to the end then directly back to the start, and start over.
  66. */
  67. static public const PATH_LOOP_FORWARD:uint = 0x000010;
  68. /**
  69. * Path behavior controls: move from the end of the path to the start then directly back to the end, and start over.
  70. */
  71. static public const PATH_LOOP_BACKWARD:uint = 0x000100;
  72. /**
  73. * Path behavior controls: move from the start of the path to the end then turn around and go back to the start, over and over.
  74. */
  75. static public const PATH_YOYO:uint = 0x001000;
  76. /**
  77. * Path behavior controls: ignores any vertical component to the path data, only follows side to side.
  78. */
  79. static public const PATH_HORIZONTAL_ONLY:uint = 0x010000;
  80. /**
  81. * Path behavior controls: ignores any horizontal component to the path data, only follows up and down.
  82. */
  83. static public const PATH_VERTICAL_ONLY:uint = 0x100000;
  84. /**
  85. * X position of the upper left corner of this object in world space.
  86. */
  87. public var x:Number;
  88. /**
  89. * Y position of the upper left corner of this object in world space.
  90. */
  91. public var y:Number;
  92. /**
  93. * The width of this object.
  94. */
  95. public var width:Number;
  96. /**
  97. * The height of this object.
  98. */
  99. public var height:Number;
  100. /**
  101. * Whether an object will move/alter position after a collision.
  102. */
  103. public var immovable:Boolean;
  104. /**
  105. * The basic speed of this object.
  106. */
  107. public var velocity:FlxPoint;
  108. /**
  109. * The virtual mass of the object. Default value is 1.
  110. * Currently only used with <code>elasticity</code> during collision resolution.
  111. * Change at your own risk; effects seem crazy unpredictable so far!
  112. */
  113. public var mass:Number;
  114. /**
  115. * The bounciness of this object. Only affects collisions. Default value is 0, or "not bouncy at all."
  116. */
  117. public var elasticity:Number;
  118. /**
  119. * How fast the speed of this object is changing.
  120. * Useful for smooth movement and gravity.
  121. */
  122. public var acceleration:FlxPoint;
  123. /**
  124. * This isn't drag exactly, more like deceleration that is only applied
  125. * when acceleration is not affecting the sprite.
  126. */
  127. public var drag:FlxPoint;
  128. /**
  129. * If you are using <code>acceleration</code>, you can use <code>maxVelocity</code> with it
  130. * to cap the speed automatically (very useful!).
  131. */
  132. public var maxVelocity:FlxPoint;
  133. /**
  134. * Set the angle of a sprite to rotate it.
  135. * WARNING: rotating sprites decreases rendering
  136. * performance for this sprite by a factor of 10x!
  137. */
  138. public var angle:Number;
  139. /**
  140. * This is how fast you want this sprite to spin.
  141. */
  142. public var angularVelocity:Number;
  143. /**
  144. * How fast the spin speed should change.
  145. */
  146. public var angularAcceleration:Number;
  147. /**
  148. * Like <code>drag</code> but for spinning.
  149. */
  150. public var angularDrag:Number;
  151. /**
  152. * Use in conjunction with <code>angularAcceleration</code> for fluid spin speed control.
  153. */
  154. public var maxAngular:Number;
  155. /**
  156. * Should always represent (0,0) - useful for different things, for avoiding unnecessary <code>new</code> calls.
  157. */
  158. static protected const _pZero:FlxPoint = new FlxPoint();
  159. /**
  160. * A point that can store numbers from 0 to 1 (for X and Y independently)
  161. * that governs how much this object is affected by the camera subsystem.
  162. * 0 means it never moves, like a HUD element or far background graphic.
  163. * 1 means it scrolls along a the same speed as the foreground layer.
  164. * scrollFactor is initialized as (1,1) by default.
  165. */
  166. public var scrollFactor:FlxPoint;
  167. /**
  168. * Internal helper used for retro-style flickering.
  169. */
  170. protected var _flicker:Boolean;
  171. /**
  172. * Internal helper used for retro-style flickering.
  173. */
  174. protected var _flickerTimer:Number;
  175. /**
  176. * Handy for storing health percentage or armor points or whatever.
  177. */
  178. public var health:Number;
  179. /**
  180. * This is just a pre-allocated x-y point container to be used however you like
  181. */
  182. protected var _point:FlxPoint;
  183. /**
  184. * This is just a pre-allocated rectangle container to be used however you like
  185. */
  186. protected var _rect:FlxRect;
  187. /**
  188. * Set this to false if you want to skip the automatic motion/movement stuff (see <code>updateMotion()</code>).
  189. * FlxObject and FlxSprite default to true.
  190. * FlxText, FlxTileblock, FlxTilemap and FlxSound default to false.
  191. */
  192. public var moves:Boolean;
  193. /**
  194. * Bit field of flags (use with UP, DOWN, LEFT, RIGHT, etc) indicating surface contacts.
  195. * Use bitwise operators to check the values stored here, or use touching(), justStartedTouching(), etc.
  196. * You can even use them broadly as boolean values if you're feeling saucy!
  197. */
  198. public var touching:uint;
  199. /**
  200. * Bit field of flags (use with UP, DOWN, LEFT, RIGHT, etc) indicating surface contacts from the previous game loop step.
  201. * Use bitwise operators to check the values stored here, or use touching(), justStartedTouching(), etc.
  202. * You can even use them broadly as boolean values if you're feeling saucy!
  203. */
  204. public var wasTouching:uint;
  205. /**
  206. * Bit field of flags (use with UP, DOWN, LEFT, RIGHT, etc) indicating collision directions.
  207. * Use bitwise operators to check the values stored here.
  208. * Useful for things like one-way platforms (e.g. allowCollisions = UP;)
  209. * The accessor "solid" just flips this variable between NONE and ANY.
  210. */
  211. public var allowCollisions:uint;
  212. /**
  213. * Important variable for collision processing.
  214. * By default this value is set automatically during <code>preUpdate()</code>.
  215. */
  216. public var last:FlxPoint;
  217. /**
  218. * A reference to a path object. Null by default, assigned by <code>followPath()</code>.
  219. */
  220. public var path:FlxPath;
  221. /**
  222. * The speed at which the object is moving on the path.
  223. * When an object completes a non-looping path circuit,
  224. * the pathSpeed will be zeroed out, but the <code>path</code> reference
  225. * will NOT be nulled out. So <code>pathSpeed</code> is a good way
  226. * to check if this object is currently following a path or not.
  227. */
  228. public var pathSpeed:Number;
  229. /**
  230. * The angle in degrees between this object and the next node, where 0 is directly upward, and 90 is to the right.
  231. */
  232. public var pathAngle:Number;
  233. /**
  234. * Internal helper, tracks which node of the path this object is moving toward.
  235. */
  236. protected var _pathNodeIndex:int;
  237. /**
  238. * Internal tracker for path behavior flags (like looping, horizontal only, etc).
  239. */
  240. protected var _pathMode:uint;
  241. /**
  242. * Internal helper for node navigation, specifically yo-yo and backwards movement.
  243. */
  244. protected var _pathInc:int;
  245. /**
  246. * Internal flag for whether hte object's angle should be adjusted to the path angle during path follow behavior.
  247. */
  248. protected var _pathRotate:Boolean;
  249. /**
  250. * Instantiates a <code>FlxObject</code>.
  251. *
  252. * @param X The X-coordinate of the point in space.
  253. * @param Y The Y-coordinate of the point in space.
  254. * @param Width Desired width of the rectangle.
  255. * @param Height Desired height of the rectangle.
  256. */
  257. public function FlxObject(X:Number=0,Y:Number=0,Width:Number=0,Height:Number=0)
  258. {
  259. x = X;
  260. y = Y;
  261. last = new FlxPoint(x,y);
  262. width = Width;
  263. height = Height;
  264. mass = 1.0;
  265. elasticity = 0.0;
  266. immovable = false;
  267. moves = true;
  268. touching = NONE;
  269. wasTouching = NONE;
  270. allowCollisions = ANY;
  271. velocity = new FlxPoint();
  272. acceleration = new FlxPoint();
  273. drag = new FlxPoint();
  274. maxVelocity = new FlxPoint(10000,10000);
  275. angle = 0;
  276. angularVelocity = 0;
  277. angularAcceleration = 0;
  278. angularDrag = 0;
  279. maxAngular = 10000;
  280. scrollFactor = new FlxPoint(1.0,1.0);
  281. _flicker = false;
  282. _flickerTimer = 0;
  283. _point = new FlxPoint();
  284. _rect = new FlxRect();
  285. path = null;
  286. pathSpeed = 0;
  287. pathAngle = 0;
  288. }
  289. /**
  290. * Override this function to null out variables or
  291. * manually call destroy() on class members if necessary.
  292. * Don't forget to call super.destroy()!
  293. */
  294. override public function destroy():void
  295. {
  296. velocity = null;
  297. acceleration = null;
  298. drag = null;
  299. maxVelocity = null;
  300. scrollFactor = null;
  301. _point = null;
  302. _rect = null;
  303. last = null;
  304. cameras = null;
  305. if(path != null)
  306. path.destroy();
  307. path = null;
  308. }
  309. /**
  310. * Pre-update is called right before <code>update()</code> on each object in the game loop.
  311. * In <code>FlxObject</code> it controls the flicker timer,
  312. * tracking the last coordinates for collision purposes,
  313. * and checking if the object is moving along a path or not.
  314. */
  315. override public function preUpdate():void
  316. {
  317. _ACTIVECOUNT++;
  318. if(_flickerTimer != 0)
  319. {
  320. if(_flickerTimer > 0)
  321. {
  322. _flickerTimer = _flickerTimer - FlxG.elapsed;
  323. if(_flickerTimer <= 0)
  324. {
  325. _flickerTimer = 0;
  326. _flicker = false;
  327. }
  328. }
  329. }
  330. last.x = x;
  331. last.y = y;
  332. if((path != null) && (pathSpeed != 0) && (path.nodes[_pathNodeIndex] != null))
  333. updatePathMotion();
  334. }
  335. /**
  336. * Post-update is called right after <code>update()</code> on each object in the game loop.
  337. * In <code>FlxObject</code> this function handles integrating the objects motion
  338. * based on the velocity and acceleration settings, and tracking/clearing the <code>touching</code> flags.
  339. */
  340. override public function postUpdate():void
  341. {
  342. if(moves)
  343. updateMotion();
  344. wasTouching = touching;
  345. touching = NONE;
  346. }
  347. /**
  348. * Internal function for updating the position and speed of this object.
  349. * Useful for cases when you need to update this but are buried down in too many supers.
  350. * Does a slightly fancier-than-normal integration to help with higher fidelity framerate-independenct motion.
  351. */
  352. protected function updateMotion():void
  353. {
  354. var delta:Number;
  355. var velocityDelta:Number;
  356. velocityDelta = (FlxU.computeVelocity(angularVelocity,angularAcceleration,angularDrag,maxAngular) - angularVelocity)/2;
  357. angularVelocity += velocityDelta;
  358. angle += angularVelocity*FlxG.elapsed;
  359. angularVelocity += velocityDelta;
  360. velocityDelta = (FlxU.computeVelocity(velocity.x,acceleration.x,drag.x,maxVelocity.x) - velocity.x)/2;
  361. velocity.x += velocityDelta;
  362. delta = velocity.x*FlxG.elapsed;
  363. velocity.x += velocityDelta;
  364. x += delta;
  365. velocityDelta = (FlxU.computeVelocity(velocity.y,acceleration.y,drag.y,maxVelocity.y) - velocity.y)/2;
  366. velocity.y += velocityDelta;
  367. delta = velocity.y*FlxG.elapsed;
  368. velocity.y += velocityDelta;
  369. y += delta;
  370. }
  371. /**
  372. * Rarely called, and in this case just increments the visible objects count and calls <code>drawDebug()</code> if necessary.
  373. */
  374. override public function draw():void
  375. {
  376. if(cameras == null)
  377. cameras = FlxG.cameras;
  378. var camera:FlxCamera;
  379. var i:uint = 0;
  380. var l:uint = cameras.length;
  381. while(i < l)
  382. {
  383. camera = cameras[i++];
  384. if(!onScreen(camera))
  385. continue;
  386. _VISIBLECOUNT++;
  387. if(FlxG.visualDebug && !ignoreDrawDebug)
  388. drawDebug(camera);
  389. }
  390. }
  391. /**
  392. * Override this function to draw custom "debug mode" graphics to the
  393. * specified camera while the debugger's visual mode is toggled on.
  394. *
  395. * @param Camera Which camera to draw the debug visuals to.
  396. */
  397. override public function drawDebug(Camera:FlxCamera=null):void
  398. {
  399. if(Camera == null)
  400. Camera = FlxG.camera;
  401. //get bounding box coordinates
  402. var boundingBoxX:Number = x - int(Camera.scroll.x*scrollFactor.x); //copied from getScreenXY()
  403. var boundingBoxY:Number = y - int(Camera.scroll.y*scrollFactor.y);
  404. boundingBoxX = int(boundingBoxX + ((boundingBoxX > 0)?0.0000001:-0.0000001));
  405. boundingBoxY = int(boundingBoxY + ((boundingBoxY > 0)?0.0000001:-0.0000001));
  406. var boundingBoxWidth:int = (width != int(width))?width:width-1;
  407. var boundingBoxHeight:int = (height != int(height))?height:height-1;
  408. //fill static graphics object with square shape
  409. var gfx:Graphics = FlxG.flashGfx;
  410. gfx.clear();
  411. gfx.moveTo(boundingBoxX,boundingBoxY);
  412. var boundingBoxColor:uint;
  413. if(allowCollisions)
  414. {
  415. if(allowCollisions != ANY)
  416. boundingBoxColor = FlxG.PINK;
  417. if(immovable)
  418. boundingBoxColor = FlxG.GREEN;
  419. else
  420. boundingBoxColor = FlxG.RED;
  421. }
  422. else
  423. boundingBoxColor = FlxG.BLUE;
  424. gfx.lineStyle(1,boundingBoxColor,0.5);
  425. gfx.lineTo(boundingBoxX+boundingBoxWidth,boundingBoxY);
  426. gfx.lineTo(boundingBoxX+boundingBoxWidth,boundingBoxY+boundingBoxHeight);
  427. gfx.lineTo(boundingBoxX,boundingBoxY+boundingBoxHeight);
  428. gfx.lineTo(boundingBoxX,boundingBoxY);
  429. //draw graphics shape to camera buffer
  430. Camera.buffer.draw(FlxG.flashGfxSprite);
  431. }
  432. /**
  433. * Call this function to give this object a path to follow.
  434. * If the path does not have at least one node in it, this function
  435. * will log a warning message and return.
  436. *
  437. * @param Path The <code>FlxPath</code> you want this object to follow.
  438. * @param Speed How fast to travel along the path in pixels per second.
  439. * @param Mode Optional, controls the behavior of the object following the path using the path behavior constants. Can use multiple flags at once, for example PATH_YOYO|PATH_HORIZONTAL_ONLY will make an object move back and forth along the X axis of the path only.
  440. * @param AutoRotate Automatically point the object toward the next node. Assumes the graphic is pointing upward. Default behavior is false, or no automatic rotation.
  441. */
  442. public function followPath(Path:FlxPath,Speed:Number=100,Mode:uint=PATH_FORWARD,AutoRotate:Boolean=false):void
  443. {
  444. if(Path.nodes.length <= 0)
  445. {
  446. FlxG.log("WARNING: Paths need at least one node in them to be followed.");
  447. return;
  448. }
  449. path = Path;
  450. pathSpeed = FlxU.abs(Speed);
  451. _pathMode = Mode;
  452. _pathRotate = AutoRotate;
  453. //get starting node
  454. if((_pathMode == PATH_BACKWARD) || (_pathMode == PATH_LOOP_BACKWARD))
  455. {
  456. _pathNodeIndex = path.nodes.length-1;
  457. _pathInc = -1;
  458. }
  459. else
  460. {
  461. _pathNodeIndex = 0;
  462. _pathInc = 1;
  463. }
  464. }
  465. /**
  466. * Tells this object to stop following the path its on.
  467. *
  468. * @param DestroyPath Tells this function whether to call destroy on the path object. Default value is false.
  469. */
  470. public function stopFollowingPath(DestroyPath:Boolean=false):void
  471. {
  472. pathSpeed = 0;
  473. if(DestroyPath && (path != null))
  474. {
  475. path.destroy();
  476. path = null;
  477. }
  478. }
  479. /**
  480. * Internal function that decides what node in the path to aim for next based on the behavior flags.
  481. *
  482. * @return The node (a <code>FlxPoint</code> object) we are aiming for next.
  483. */
  484. protected function advancePath(Snap:Boolean=true):FlxPoint
  485. {
  486. if(Snap)
  487. {
  488. var oldNode:FlxPoint = path.nodes[_pathNodeIndex];
  489. if(oldNode != null)
  490. {
  491. if((_pathMode & PATH_VERTICAL_ONLY) == 0)
  492. x = oldNode.x - width*0.5;
  493. if((_pathMode & PATH_HORIZONTAL_ONLY) == 0)
  494. y = oldNode.y - height*0.5;
  495. }
  496. }
  497. _pathNodeIndex += _pathInc;
  498. if((_pathMode & PATH_BACKWARD) > 0)
  499. {
  500. if(_pathNodeIndex < 0)
  501. {
  502. _pathNodeIndex = 0;
  503. pathSpeed = 0;
  504. }
  505. }
  506. else if((_pathMode & PATH_LOOP_FORWARD) > 0)
  507. {
  508. if(_pathNodeIndex >= path.nodes.length)
  509. _pathNodeIndex = 0;
  510. }
  511. else if((_pathMode & PATH_LOOP_BACKWARD) > 0)
  512. {
  513. if(_pathNodeIndex < 0)
  514. {
  515. _pathNodeIndex = path.nodes.length-1;
  516. if(_pathNodeIndex < 0)
  517. _pathNodeIndex = 0;
  518. }
  519. }
  520. else if((_pathMode & PATH_YOYO) > 0)
  521. {
  522. if(_pathInc > 0)
  523. {
  524. if(_pathNodeIndex >= path.nodes.length)
  525. {
  526. _pathNodeIndex = path.nodes.length-2;
  527. if(_pathNodeIndex < 0)
  528. _pathNodeIndex = 0;
  529. _pathInc = -_pathInc;
  530. }
  531. }
  532. else if(_pathNodeIndex < 0)
  533. {
  534. _pathNodeIndex = 1;
  535. if(_pathNodeIndex >= path.nodes.length)
  536. _pathNodeIndex = path.nodes.length-1;
  537. if(_pathNodeIndex < 0)
  538. _pathNodeIndex = 0;
  539. _pathInc = -_pathInc;
  540. }
  541. }
  542. else
  543. {
  544. if(_pathNodeIndex >= path.nodes.length)
  545. {
  546. _pathNodeIndex = path.nodes.length-1;
  547. pathSpeed = 0;
  548. }
  549. }
  550. return path.nodes[_pathNodeIndex];
  551. }
  552. /**
  553. * Internal function for moving the object along the path.
  554. * Generally this function is called automatically by <code>preUpdate()</code>.
  555. * The first half of the function decides if the object can advance to the next node in the path,
  556. * while the second half handles actually picking a velocity toward the next node.
  557. */
  558. protected function updatePathMotion():void
  559. {
  560. //first check if we need to be pointing at the next node yet
  561. _point.x = x + width*0.5;
  562. _point.y = y + height*0.5;
  563. var node:FlxPoint = path.nodes[_pathNodeIndex];
  564. var deltaX:Number = node.x - _point.x;
  565. var deltaY:Number = node.y - _point.y;
  566. var horizontalOnly:Boolean = (_pathMode & PATH_HORIZONTAL_ONLY) > 0;
  567. var verticalOnly:Boolean = (_pathMode & PATH_VERTICAL_ONLY) > 0;
  568. if(horizontalOnly)
  569. {
  570. if(((deltaX>0)?deltaX:-deltaX) < pathSpeed*FlxG.elapsed)
  571. node = advancePath();
  572. }
  573. else if(verticalOnly)
  574. {
  575. if(((deltaY>0)?deltaY:-deltaY) < pathSpeed*FlxG.elapsed)
  576. node = advancePath();
  577. }
  578. else
  579. {
  580. if(Math.sqrt(deltaX*deltaX + deltaY*deltaY) < pathSpeed*FlxG.elapsed)
  581. node = advancePath();
  582. }
  583. //then just move toward the current node at the requested speed
  584. if(pathSpeed != 0)
  585. {
  586. //set velocity based on path mode
  587. _point.x = x + width*0.5;
  588. _point.y = y + height*0.5;
  589. if(horizontalOnly || (_point.y == node.y))
  590. {
  591. velocity.x = (_point.x < node.x)?pathSpeed:-pathSpeed;
  592. if(velocity.x < 0)
  593. pathAngle = -90;
  594. else
  595. pathAngle = 90;
  596. if(!horizontalOnly)
  597. velocity.y = 0;
  598. }
  599. else if(verticalOnly || (_point.x == node.x))
  600. {
  601. velocity.y = (_point.y < node.y)?pathSpeed:-pathSpeed;
  602. if(velocity.y < 0)
  603. pathAngle = 0;
  604. else
  605. pathAngle = 180;
  606. if(!verticalOnly)
  607. velocity.x = 0;
  608. }
  609. else
  610. {
  611. pathAngle = FlxU.getAngle(_point,node);
  612. FlxU.rotatePoint(0,pathSpeed,0,0,pathAngle,velocity);
  613. }
  614. //then set object rotation if necessary
  615. if(_pathRotate)
  616. {
  617. angularVelocity = 0;
  618. angularAcceleration = 0;
  619. angle = pathAngle;
  620. }
  621. }
  622. }
  623. /**
  624. * Checks to see if some <code>FlxObject</code> overlaps this <code>FlxObject</code> or <code>FlxGroup</code>.
  625. * If the group has a LOT of things in it, it might be faster to use <code>FlxG.overlaps()</code>.
  626. * WARNING: Currently tilemaps do NOT support screen space overlap checks!
  627. *
  628. * @param ObjectOrGroup The object or group being tested.
  629. * @param InScreenSpace Whether to take scroll factors into account when checking for overlap. Default is false, or "only compare in world space."
  630. * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera.
  631. *
  632. * @return Whether or not the two objects overlap.
  633. */
  634. public function overlaps(ObjectOrGroup:FlxBasic,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean
  635. {
  636. if(ObjectOrGroup is FlxGroup)
  637. {
  638. var results:Boolean = false;
  639. var i:uint = 0;
  640. var members:Array = (ObjectOrGroup as FlxGroup).members;
  641. while(i < length)
  642. {
  643. if(overlaps(members[i++],InScreenSpace,Camera))
  644. results = true;
  645. }
  646. return results;
  647. }
  648. if(ObjectOrGroup is FlxTilemap)
  649. {
  650. //Since tilemap's have to be the caller, not the target, to do proper tile-based collisions,
  651. // we redirect the call to the tilemap overlap here.
  652. return (ObjectOrGroup as FlxTilemap).overlaps(this,InScreenSpace,Camera);
  653. }
  654. var object:FlxObject = ObjectOrGroup as FlxObject;
  655. if(!InScreenSpace)
  656. {
  657. return (object.x + object.width > x) && (object.x < x + width) &&
  658. (object.y + object.height > y) && (object.y < y + height);
  659. }
  660. if(Camera == null)
  661. Camera = FlxG.camera;
  662. var objectScreenPos:FlxPoint = object.getScreenXY(null,Camera);
  663. getScreenXY(_point,Camera);
  664. return (objectScreenPos.x + object.width > _point.x) && (objectScreenPos.x < _point.x + width) &&
  665. (objectScreenPos.y + object.height > _point.y) && (objectScreenPos.y < _point.y + height);
  666. }
  667. /**
  668. * Checks to see if this <code>FlxObject</code> were located at the given position, would it overlap the <code>FlxObject</code> or <code>FlxGroup</code>?
  669. * This is distinct from overlapsPoint(), which just checks that point, rather than taking the object's size into account.
  670. * WARNING: Currently tilemaps do NOT support screen space overlap checks!
  671. *
  672. * @param X The X position you want to check. Pretends this object (the caller, not the parameter) is located here.
  673. * @param Y The Y position you want to check. Pretends this object (the caller, not the parameter) is located here.
  674. * @param ObjectOrGroup The object or group being tested.
  675. * @param InScreenSpace Whether to take scroll factors into account when checking for overlap. Default is false, or "only compare in world space."
  676. * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera.
  677. *
  678. * @return Whether or not the two objects overlap.
  679. */
  680. public function overlapsAt(X:Number,Y:Number,ObjectOrGroup:FlxBasic,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean
  681. {
  682. if(ObjectOrGroup is FlxGroup)
  683. {
  684. var results:Boolean = false;
  685. var basic:FlxBasic;
  686. var i:uint = 0;
  687. var members:Array = (ObjectOrGroup as FlxGroup).members;
  688. while(i < length)
  689. {
  690. if(overlapsAt(X,Y,members[i++],InScreenSpace,Camera))
  691. results = true;
  692. }
  693. return results;
  694. }
  695. if(ObjectOrGroup is FlxTilemap)
  696. {
  697. //Since tilemap's have to be the caller, not the target, to do proper tile-based collisions,
  698. // we redirect the call to the tilemap overlap here.
  699. //However, since this is overlapsAt(), we also have to invent the appropriate position for the tilemap.
  700. //So we calculate the offset between the player and the requested position, and subtract that from the tilemap.
  701. var tilemap:FlxTilemap = ObjectOrGroup as FlxTilemap;
  702. return tilemap.overlapsAt(tilemap.x - (X - x),tilemap.y - (Y - y),this,InScreenSpace,Camera);
  703. }
  704. var object:FlxObject = ObjectOrGroup as FlxObject;
  705. if(!InScreenSpace)
  706. {
  707. return (object.x + object.width > X) && (object.x < X + width) &&
  708. (object.y + object.height > Y) && (object.y < Y + height);
  709. }
  710. if(Camera == null)
  711. Camera = FlxG.camera;
  712. var objectScreenPos:FlxPoint = object.getScreenXY(null,Camera);
  713. _point.x = X - int(Camera.scroll.x*scrollFactor.x); //copied from getScreenXY()
  714. _point.y = Y - int(Camera.scroll.y*scrollFactor.y);
  715. _point.x += (_point.x > 0)?0.0000001:-0.0000001;
  716. _point.y += (_point.y > 0)?0.0000001:-0.0000001;
  717. return (objectScreenPos.x + object.width > _point.x) && (objectScreenPos.x < _point.x + width) &&
  718. (objectScreenPos.y + object.height > _point.y) && (objectScreenPos.y < _point.y + height);
  719. }
  720. /**
  721. * Checks to see if a point in 2D world space overlaps this <code>FlxObject</code> object.
  722. *
  723. * @param Point The point in world space you want to check.
  724. * @param InScreenSpace Whether to take scroll factors into account when checking for overlap.
  725. * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera.
  726. *
  727. * @return Whether or not the point overlaps this object.
  728. */
  729. public function overlapsPoint(Point:FlxPoint,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean
  730. {
  731. if(!InScreenSpace)
  732. return (Point.x > x) && (Point.x < x + width) && (Point.y > y) && (Point.y < y + height);
  733. if(Camera == null)
  734. Camera = FlxG.camera;
  735. var X:Number = Point.x - Camera.scroll.x;
  736. var Y:Number = Point.y - Camera.scroll.y;
  737. getScreenXY(_point,Camera);
  738. return (X > _point.x) && (X < _point.x+width) && (Y > _point.y) && (Y < _point.y+height);
  739. }
  740. /**
  741. * Check and see if this object is currently on screen.
  742. *
  743. * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera.
  744. *
  745. * @return Whether the object is on screen or not.
  746. */
  747. public function onScreen(Camera:FlxCamera=null):Boolean
  748. {
  749. if(Camera == null)
  750. Camera = FlxG.camera;
  751. getScreenXY(_point,Camera);
  752. return (_point.x + width > 0) && (_point.x < Camera.width) && (_point.y + height > 0) && (_point.y < Camera.height);
  753. }
  754. /**
  755. * Call this function to figure out the on-screen position of the object.
  756. *
  757. * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera.
  758. * @param Point Takes a <code>FlxPoint</code> object and assigns the post-scrolled X and Y values of this object to it.
  759. *
  760. * @return The <code>Point</code> you passed in, or a new <code>Point</code> if you didn't pass one, containing the screen X and Y position of this object.
  761. */
  762. public function getScreenXY(Point:FlxPoint=null,Camera:FlxCamera=null):FlxPoint
  763. {
  764. if(Point == null)
  765. Point = new FlxPoint();
  766. if(Camera == null)
  767. Camera = FlxG.camera;
  768. Point.x = x - int(Camera.scroll.x*scrollFactor.x);
  769. Point.y = y - int(Camera.scroll.y*scrollFactor.y);
  770. Point.x += (Point.x > 0)?0.0000001:-0.0000001;
  771. Point.y += (Point.y > 0)?0.0000001:-0.0000001;
  772. return Point;
  773. }
  774. /**
  775. * Tells this object to flicker, retro-style.
  776. * Pass a negative value to flicker forever.
  777. *
  778. * @param Duration How many seconds to flicker for.
  779. */
  780. public function flicker(Duration:Number=1):void
  781. {
  782. _flickerTimer = Duration;
  783. if(_flickerTimer == 0)
  784. _flicker = false;
  785. }
  786. /**
  787. * Check to see if the object is still flickering.
  788. *
  789. * @return Whether the object is flickering or not.
  790. */
  791. public function get flickering():Boolean
  792. {
  793. return _flickerTimer != 0;
  794. }
  795. /**
  796. * Whether the object collides or not. For more control over what directions
  797. * the object will collide from, use collision constants (like LEFT, FLOOR, etc)
  798. * to set the value of allowCollisions directly.
  799. */
  800. public function get solid():Boolean
  801. {
  802. return (allowCollisions & ANY) > NONE;
  803. }
  804. /**
  805. * @private
  806. */
  807. public function set solid(Solid:Boolean):void
  808. {
  809. if(Solid)
  810. allowCollisions = ANY;
  811. else
  812. allowCollisions = NONE;
  813. }
  814. /**
  815. * Retrieve the midpoint of this object in world coordinates.
  816. *
  817. * @Point Allows you to pass in an existing <code>FlxPoint</code> object if you're so inclined. Otherwise a new one is created.
  818. *
  819. * @return A <code>FlxPoint</code> object containing the midpoint of this object in world coordinates.
  820. */
  821. public function getMidpoint(Point:FlxPoint=null):FlxPoint
  822. {
  823. if(Point == null)
  824. Point = new FlxPoint();
  825. Point.x = x + width*0.5;
  826. Point.y = y + height*0.5;
  827. return Point;
  828. }
  829. /**
  830. * Handy function for reviving game objects.
  831. * Resets their existence flags and position.
  832. *
  833. * @param X The new X position of this object.
  834. * @param Y The new Y position of this object.
  835. */
  836. public function reset(X:Number,Y:Number):void
  837. {
  838. revive();
  839. touching = NONE;
  840. wasTouching = NONE;
  841. x = X;
  842. y = Y;
  843. last.x = x;
  844. last.y = y;
  845. velocity.x = 0;
  846. velocity.y = 0;
  847. }
  848. /**
  849. * Handy function for checking if this object is touching a particular surface.
  850. * For slightly better performance you can just &amp; the value directly into <code>touching</code>.
  851. * However, this method is good for readability and accessibility.
  852. *
  853. * @param Direction Any of the collision flags (e.g. LEFT, FLOOR, etc).
  854. *
  855. * @return Whether the object is touching an object in (any of) the specified direction(s) this frame.
  856. */
  857. public function isTouching(Direction:uint):Boolean
  858. {
  859. return (touching & Direction) > NONE;
  860. }
  861. /**
  862. * Handy function for checking if this object is just landed on a particular surface.
  863. *
  864. * @param Direction Any of the collision flags (e.g. LEFT, FLOOR, etc).
  865. *
  866. * @return Whether the object just landed on (any of) the specified surface(s) this frame.
  867. */
  868. public function justTouched(Direction:uint):Boolean
  869. {
  870. return ((touching & Direction) > NONE) && ((wasTouching & Direction) <= NONE);
  871. }
  872. /**
  873. * Reduces the "health" variable of this sprite by the amount specified in Damage.
  874. * Calls kill() if health drops to or below zero.
  875. *
  876. * @param Damage How much health to take away (use a negative number to give a health bonus).
  877. */
  878. public function hurt(Damage:Number):void
  879. {
  880. health = health - Damage;
  881. if(health <= 0)
  882. kill();
  883. }
  884. /**
  885. * The main collision resolution function in flixel.
  886. *
  887. * @param Object1 Any <code>FlxObject</code>.
  888. * @param Object2 Any other <code>FlxObject</code>.
  889. *
  890. * @return Whether the objects in fact touched and were separated.
  891. */
  892. static public function separate(Object1:FlxObject, Object2:FlxObject):Boolean
  893. {
  894. var separatedX:Boolean = separateX(Object1,Object2);
  895. var separatedY:Boolean = separateY(Object1,Object2);
  896. return separatedX || separatedY;
  897. }
  898. /**
  899. * The X-axis component of the object separation process.
  900. *
  901. * @param Object1 Any <code>FlxObject</code>.
  902. * @param Object2 Any other <code>FlxObject</code>.
  903. *
  904. * @return Whether the objects in fact touched and were separated along the X axis.
  905. */
  906. static public function separateX(Object1:FlxObject, Object2:FlxObject):Boolean
  907. {
  908. //can't separate two immovable objects
  909. var obj1immovable:Boolean = Object1.immovable;
  910. var obj2immovable:Boolean = Object2.immovable;
  911. if(obj1immovable && obj2immovable)
  912. return false;
  913. //If one of the objects is a tilemap, just pass it off.
  914. if(Object1 is FlxTilemap)
  915. return (Object1 as FlxTilemap).overlapsWithCallback(Object2,separateX);
  916. if(Object2 is FlxTilemap)
  917. return (Object2 as FlxTilemap).overlapsWithCallback(Object1,separateX,true);
  918. //First, get the two object deltas
  919. var overlap:Number = 0;
  920. var obj1delta:Number = Object1.x - Object1.last.x;
  921. var obj2delta:Number = Object2.x - Object2.last.x;
  922. if(obj1delta != obj2delta)
  923. {
  924. //Check if the X hulls actually overlap
  925. var obj1deltaAbs:Number = (obj1delta > 0)?obj1delta:-obj1delta;
  926. var obj2deltaAbs:Number = (obj2delta > 0)?obj2delta:-obj2delta;
  927. var obj1rect:FlxRect = new FlxRect(Object1.x-((obj1delta > 0)?obj1delta:0),Object1.last.y,Object1.width+((obj1delta > 0)?obj1delta:-obj1delta),Object1.height);
  928. var obj2rect:FlxRect = new FlxRect(Object2.x-((obj2delta > 0)?obj2delta:0),Object2.last.y,Object2.width+((obj2delta > 0)?obj2delta:-obj2delta),Object2.height);
  929. if((obj1rect.x + obj1rect.width > obj2rect.x) && (obj1rect.x < obj2rect.x + obj2rect.width) && (obj1rect.y + obj1rect.height > obj2rect.y) && (obj1rect.y < obj2rect.y + obj2rect.height))
  930. {
  931. var maxOverlap:Number = obj1deltaAbs + obj2deltaAbs + OVERLAP_BIAS;
  932. //If they did overlap (and can), figure out by how much and flip the corresponding flags
  933. if(obj1delta > obj2delta)
  934. {
  935. overlap = Object1.x + Object1.width - Object2.x;
  936. if((overlap > maxOverlap) || !(Object1.allowCollisions & RIGHT) || !(Object2.allowCollisions & LEFT))
  937. overlap = 0;
  938. else
  939. {
  940. Object1.touching |= RIGHT;
  941. Object2.touching |= LEFT;
  942. }
  943. }
  944. else if(obj1delta < obj2delta)
  945. {
  946. overlap = Object1.x - Object2.width - Object2.x;
  947. if((-overlap > maxOverlap) || !(Object1.allowCollisions & LEFT) || !(Object2.allowCollisions & RIGHT))
  948. overlap = 0;
  949. else
  950. {
  951. Object1.touching |= LEFT;
  952. Object2.touching |= RIGHT;
  953. }
  954. }
  955. }
  956. }
  957. //Then adjust their positions and velocities accordingly (if there was any overlap)
  958. if(overlap != 0)
  959. {
  960. var obj1v:Number = Object1.velocity.x;
  961. var obj2v:Number = Object2.velocity.x;
  962. if(!obj1immovable && !obj2immovable)
  963. {
  964. overlap *= 0.5;
  965. Object1.x = Object1.x - overlap;
  966. Object2.x += overlap;
  967. var obj1velocity:Number = Math.sqrt((obj2v * obj2v * Object2.mass)/Object1.mass) * ((obj2v > 0)?1:-1);
  968. var obj2velocity:Number = Math.sqrt((obj1v * obj1v * Object1.mass)/Object2.mass) * ((obj1v > 0)?1:-1);
  969. var average:Number = (obj1velocity + obj2velocity)*0.5;
  970. obj1velocity -= average;
  971. obj2velocity -= average;
  972. Object1.velocity.x = average + obj1velocity * Object1.elasticity;
  973. Object2.velocity.x = average + obj2velocity * Object2.elasticity;
  974. }
  975. else if(!obj1immovable)
  976. {
  977. Object1.x = Object1.x - overlap;
  978. Object1.velocity.x = obj2v - obj1v*Object1.elasticity;
  979. }
  980. else if(!obj2immovable)
  981. {
  982. Object2.x += overlap;
  983. Object2.velocity.x = obj1v - obj2v*Object2.elasticity;
  984. }
  985. return true;
  986. }
  987. else
  988. return false;
  989. }
  990. /**
  991. * The Y-axis component of the object separation process.
  992. *
  993. * @param Object1 Any <code>FlxObject</code>.
  994. * @param Object2 Any other <code>FlxObject</code>.
  995. *
  996. * @return Whether the objects in fact touched and were separated along the Y axis.
  997. */
  998. static public function separateY(Object1:FlxObject, Object2:FlxObject):Boolean
  999. {
  1000. //can't separate two immovable objects
  1001. var obj1immovable:Boolean = Object1.immovable;
  1002. var obj2immovable:Boolean = Object2.immovable;
  1003. if(obj1immovable && obj2immovable)
  1004. return false;
  1005. //If one of the objects is a tilemap, just pass it off.
  1006. if(Object1 is FlxTilemap)
  1007. return (Object1 as FlxTilemap).overlapsWithCallback(Object2,separateY);
  1008. if(Object2 is FlxTilemap)
  1009. return (Object2 as FlxTilemap).overlapsWithCallback(Object1,separateY,true);
  1010. //First, get the two object deltas
  1011. var overlap:Number = 0;
  1012. var obj1delta:Number = Object1.y - Object1.last.y;
  1013. var obj2delta:Number = Object2.y - Object2.last.y;
  1014. if(obj1delta != obj2delta)
  1015. {
  1016. //Check if the Y hulls actually overlap
  1017. var obj1deltaAbs:Number = (obj1delta > 0)?obj1delta:-obj1delta;
  1018. var obj2deltaAbs:Number = (obj2delta > 0)?obj2delta:-obj2delta;
  1019. var obj1rect:FlxRect = new FlxRect(Object1.x,Object1.y-((obj1delta > 0)?obj1delta:0),Object1.width,Object1.height+obj1deltaAbs);
  1020. var obj2rect:FlxRect = new FlxRect(Object2.x,Object2.y-((obj2delta > 0)?obj2delta:0),Object2.width,Object2.height+obj2deltaAbs);
  1021. if((obj1rect.x + obj1rect.width > obj2rect.x) && (obj1rect.x < obj2rect.x + obj2rect.width) && (obj1rect.y + obj1rect.height > obj2rect.y) && (obj1rect.y < obj2rect.y + obj2rect.height))
  1022. {
  1023. var maxOverlap:Number = obj1deltaAbs + obj2deltaAbs + OVERLAP_BIAS;
  1024. //If they did overlap (and can), figure out by how much and flip the corresponding flags
  1025. if(obj1delta > obj2delta)
  1026. {
  1027. overlap = Object1.y + Object1.height - Object2.y;
  1028. if((overlap > maxOverlap) || !(Object1.allowCollisions & DOWN) || !(Object2.allowCollisions & UP))
  1029. overlap = 0;
  1030. else
  1031. {
  1032. Object1.touching |= DOWN;
  1033. Object2.touching |= UP;
  1034. }
  1035. }
  1036. else if(obj1delta < obj2delta)
  1037. {
  1038. overlap = Object1.y - Object2.height - Object2.y;
  1039. if((-overlap > maxOverlap) || !(Object1.allowCollisions & UP) || !(Object2.allowCollisions & DOWN))
  1040. overlap = 0;
  1041. else
  1042. {
  1043. Object1.touching |= UP;
  1044. Object2.touching |= DOWN;
  1045. }
  1046. }
  1047. }
  1048. }
  1049. //Then adjust their positions and velocities accordingly (if there was any overlap)
  1050. if(overlap != 0)
  1051. {
  1052. var obj1v:Number = Object1.velocity.y;
  1053. var obj2v:Number = Object2.velocity.y;
  1054. if(!obj1immovable && !obj2immovable)
  1055. {
  1056. overlap *= 0.5;
  1057. Object1.y = Object1.y - overlap;
  1058. Object2.y += overlap;
  1059. var obj1velocity:Number = Math.sqrt((obj2v * obj2v * Object2.mass)/Object1.mass) * ((obj2v > 0)?1:-1);
  1060. var obj2velocity:Number = Math.sqrt((obj1v * obj1v * Object1.mass)/Object2.mass) * ((obj1v > 0)?1:-1);
  1061. var average:Number = (obj1velocity + obj2velocity)*0.5;
  1062. obj1velocity -= average;
  1063. obj2velocity -= average;
  1064. Object1.velocity.y = average + obj1velocity * Object1.elasticity;
  1065. Object2.velocity.y = average + obj2velocity * Object2.elasticity;
  1066. }
  1067. else if(!obj1immovable)
  1068. {
  1069. Object1.y = Object1.y - overlap;
  1070. Object1.velocity.y = obj2v - obj1v*Object1.elasticity;
  1071. //This is special case code that handles cases like horizontal moving platforms you can ride
  1072. if(Object2.active && Object2.moves && (obj1delta > obj2delta))
  1073. Object1.x += Object2.x - Object2.last.x;
  1074. }
  1075. else if(!obj2immovable)
  1076. {
  1077. Object2.y += overlap;
  1078. Object2.velocity.y = obj1v - obj2v*Object2.elasticity;
  1079. //This is special case code that handles cases like horizontal moving platforms you can ride
  1080. if(Object1.active && Object1.moves && (obj1delta < obj2delta))
  1081. Object2.x += Object1.x - Object1.last.x;
  1082. }
  1083. return true;
  1084. }
  1085. else
  1086. return false;
  1087. }
  1088. }
  1089. }