PageRenderTime 58ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/org/flixel/FlxObject.hx

http://flixel-haxelib.googlecode.com/
Haxe | 1182 lines | 684 code | 91 blank | 407 comment | 187 complexity | 26889593c41db2c9df0295a0c808900e MD5 | raw file
  1. package org.flixel;
  2. import flash.display.Graphics;
  3. import flash.display.Sprite;
  4. import flash.geom.Point;
  5. import org.flixel.FlxBasic;
  6. /**
  7. * This is the base class for most of the display objects (<code>FlxSprite</code>, <code>FlxText</code>, etc).
  8. * It includes some basic attributes about game objects, including retro-style flickering,
  9. * basic state information, sizes, scrolling, and basic physics and motion.
  10. *
  11. * @author Adam Atomic
  12. */
  13. class FlxObject extends FlxBasic
  14. {
  15. /**
  16. * Generic value for "left" Used by <code>facing</code>, <code>allowCollisions</code>, and <code>touching</code>.
  17. */
  18. static public var LEFT:Int = 0x0001;
  19. /**
  20. * Generic value for "right" Used by <code>facing</code>, <code>allowCollisions</code>, and <code>touching</code>.
  21. */
  22. static public var RIGHT:Int = 0x0010;
  23. /**
  24. * Generic value for "up" Used by <code>facing</code>, <code>allowCollisions</code>, and <code>touching</code>.
  25. */
  26. static public var UP:Int = 0x0100;
  27. /**
  28. * Generic value for "down" Used by <code>facing</code>, <code>allowCollisions</code>, and <code>touching</code>.
  29. */
  30. static public var DOWN:Int = 0x1000;
  31. /**
  32. * Special-case constant meaning no collisions, used mainly by <code>allowCollisions</code> and <code>touching</code>.
  33. */
  34. static public var NONE:Int = 0;
  35. /**
  36. * Special-case constant meaning up, used mainly by <code>allowCollisions</code> and <code>touching</code>.
  37. */
  38. static public var CEILING:Int= UP;
  39. /**
  40. * Special-case constant meaning down, used mainly by <code>allowCollisions</code> and <code>touching</code>.
  41. */
  42. static public var FLOOR:Int = DOWN;
  43. /**
  44. * Special-case constant meaning only the left and right sides, used mainly by <code>allowCollisions</code> and <code>touching</code>.
  45. */
  46. static public var WALL:Int = LEFT | RIGHT;
  47. /**
  48. * Special-case constant meaning any direction, used mainly by <code>allowCollisions</code> and <code>touching</code>.
  49. */
  50. static public var ANY:Int = LEFT | RIGHT | UP | DOWN;
  51. /**
  52. * Handy constant used during collision resolution (see <code>separateX()</code> and <code>separateY()</code>).
  53. */
  54. static public var OVERLAP_BIAS:Float = 4;
  55. /**
  56. * Path behavior controls: move from the start of the path to the end then stop.
  57. */
  58. static public var PATH_FORWARD:Int = 0x000000;
  59. /**
  60. * Path behavior controls: move from the end of the path to the start then stop.
  61. */
  62. static public var PATH_BACKWARD:Int = 0x000001;
  63. /**
  64. * Path behavior controls: move from the start of the path to the end then directly back to the start, and start over.
  65. */
  66. static public var PATH_LOOP_FORWARD:Int = 0x000010;
  67. /**
  68. * Path behavior controls: move from the end of the path to the start then directly back to the end, and start over.
  69. */
  70. static public var PATH_LOOP_BACKWARD:Int = 0x000100;
  71. /**
  72. * 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.
  73. */
  74. static public var PATH_YOYO:Int = 0x001000;
  75. /**
  76. * Path behavior controls: ignores any vertical component to the path data, only follows side to side.
  77. */
  78. static public var PATH_HORIZONTAL_ONLY:Int = 0x010000;
  79. /**
  80. * Path behavior controls: ignores any horizontal component to the path data, only follows up and down.
  81. */
  82. static public var PATH_VERTICAL_ONLY:Int = 0x100000;
  83. /**
  84. * X position of the upper left corner of this object in world space.
  85. */
  86. public var x:Float;
  87. /**
  88. * Y position of the upper left corner of this object in world space.
  89. */
  90. public var y:Float;
  91. /**
  92. * The width of this object.
  93. */
  94. public var width:Float;
  95. /**
  96. * The height of this object.
  97. */
  98. public var height:Float;
  99. /**
  100. * Whether an object will move/alter position after a collision.
  101. */
  102. public var immovable:Bool;
  103. /**
  104. * The basic speed of this object.
  105. */
  106. public var velocity:FlxPoint;
  107. /**
  108. * The virtual mass of the object. Default value is 1.
  109. * Currently only used with <code>elasticity</code> during collision resolution.
  110. * Change at your own risk; effects seem crazy unpredictable so far!
  111. */
  112. public var mass:Float;
  113. /**
  114. * The bounciness of this object. Only affects collisions. Default value is 0, or "not bouncy at all."
  115. */
  116. public var elasticity:Float;
  117. /**
  118. * How fast the speed of this object is changing.
  119. * Useful for smooth movement and gravity.
  120. */
  121. public var acceleration:FlxPoint;
  122. /**
  123. * This isn't drag exactly, more like deceleration that is only applied
  124. * when acceleration is not affecting the sprite.
  125. */
  126. public var drag:FlxPoint;
  127. /**
  128. * If you are using <code>acceleration</code>, you can use <code>maxVelocity</code> with it
  129. * to cap the speed automatically (very useful!).
  130. */
  131. public var maxVelocity:FlxPoint;
  132. /**
  133. * Set the angle of a sprite to rotate it.
  134. * WARNING: rotating sprites decreases rendering
  135. * performance for this sprite by a factor of 10x!
  136. */
  137. public var angle:Float;
  138. /**
  139. * This is how fast you want this sprite to spin.
  140. */
  141. public var angularVelocity:Float;
  142. /**
  143. * How fast the spin speed should change.
  144. */
  145. public var angularAcceleration:Float;
  146. /**
  147. * Like <code>drag</code> but for spinning.
  148. */
  149. public var angularDrag:Float;
  150. /**
  151. * Use in conjunction with <code>angularAcceleration</code> for fluid spin speed control.
  152. */
  153. public var maxAngular:Float;
  154. /**
  155. * Should always represent (0,0) - useful for different things, for avoiding unnecessary <code>new</code> calls.
  156. */
  157. static private var _pZero:FlxPoint = new FlxPoint();
  158. /**
  159. * A point that can store numbers from 0 to 1 (for X and Y independently)
  160. * that governs how much this object is affected by the camera subsystem.
  161. * 0 means it never moves, like a HUD element or far background graphic.
  162. * 1 means it scrolls along a the same speed as the foreground layer.
  163. * scrollFactor is initialized as (1,1) by default.
  164. */
  165. public var scrollFactor:FlxPoint;
  166. /**
  167. * Internal helper used for retro-style flickering.
  168. */
  169. private var _flicker:Bool;
  170. /**
  171. * Internal helper used for retro-style flickering.
  172. */
  173. private var _flickerTimer:Float;
  174. /**
  175. * Handy for storing health percentage or armor points or whatever.
  176. */
  177. public var health:Float;
  178. /**
  179. * This is just a pre-allocated x-y point container to be used however you like
  180. */
  181. private var _point:FlxPoint;
  182. /**
  183. * This is just a pre-allocated rectangle container to be used however you like
  184. */
  185. private var _rect:FlxRect;
  186. /**
  187. * Set this to false if you want to skip the automatic motion/movement stuff (see <code>updateMotion()</code>).
  188. * FlxObject and FlxSprite default to true.
  189. * FlxText, FlxTileblock, FlxTilemap and FlxSound default to false.
  190. */
  191. public var moves:Bool;
  192. /**
  193. * Bit field of flags (use with UP, DOWN, LEFT, RIGHT, etc) indicating surface contacts.
  194. * Use bitwise operators to check the values stored here, or use touching(), justStartedTouching(), etc.
  195. * You can even use them broadly as boolean values if you're feeling saucy!
  196. */
  197. public var touching:Int;
  198. /**
  199. * Bit field of flags (use with UP, DOWN, LEFT, RIGHT, etc) indicating surface contacts from the previous game loop step.
  200. * Use bitwise operators to check the values stored here, or use touching(), justStartedTouching(), etc.
  201. * You can even use them broadly as boolean values if you're feeling saucy!
  202. */
  203. public var wasTouching:Int;
  204. /**
  205. * Bit field of flags (use with UP, DOWN, LEFT, RIGHT, etc) indicating collision directions.
  206. * Use bitwise operators to check the values stored here.
  207. * Useful for things like one-way platforms (e.g. allowCollisions = UP;)
  208. * The accessor "solid" just flips this variable between NONE and ANY.
  209. */
  210. public var allowCollisions:Int;
  211. /**
  212. * Important variable for collision processing.
  213. * By default this value is set automatically during <code>preUpdate()</code>.
  214. */
  215. public var last:FlxPoint;
  216. /**
  217. * A reference to a path object. Null by default, assigned by <code>followPath()</code>.
  218. */
  219. public var path:FlxPath;
  220. /**
  221. * The speed at which the object is moving on the path.
  222. * When an object completes a non-looping path circuit,
  223. * the pathSpeed will be zeroed out, but the <code>path</code> reference
  224. * will NOT be nulled out. So <code>pathSpeed</code> is a good way
  225. * to check if this object is currently following a path or not.
  226. */
  227. public var pathSpeed:Float;
  228. /**
  229. * The angle in degrees between this object and the next node, where 0 is directly upward, and 90 is to the right.
  230. */
  231. public var pathAngle:Float;
  232. /**
  233. * Internal helper, tracks which node of the path this object is moving toward.
  234. */
  235. private var _pathNodeIndex:Int;
  236. /**
  237. * Internal tracker for path behavior flags (like looping, horizontal only, etc).
  238. */
  239. private var _pathMode:Int;
  240. /**
  241. * Internal helper for node navigation, specifically yo-yo and backwards movement.
  242. */
  243. private var _pathInc:Int;
  244. /**
  245. * Internal flag for whether hte object's angle should be adjusted to the path angle during path follow behavior.
  246. */
  247. private var _pathRotate:Bool;
  248. /**
  249. * Instantiates a <code>FlxObject</code>.
  250. *
  251. * @param X The X-coordinate of the point in space.
  252. * @param Y The Y-coordinate of the point in space.
  253. * @param Width Desired width of the rectangle.
  254. * @param Height Desired height of the rectangle.
  255. */
  256. public function FlxObject(X:Float=0,Y:Float=0,Width:Float=0,Height:Float=0)
  257. {
  258. x = X;
  259. y = Y;
  260. last = new FlxPoint(x,y);
  261. width = Width;
  262. height = Height;
  263. mass = 1.0;
  264. elasticity = 0.0;
  265. health = 1;
  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. private function updateMotion():Void
  353. {
  354. var delta:Float;
  355. var velocityDelta:Float;
  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:Int = 0;
  380. var l:Int = 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:Float = x - Std.int(Camera.scroll.x*scrollFactor.x); //copied from getScreenXY()
  403. var boundingBoxY:Float = y - Std.int(Camera.scroll.y*scrollFactor.y);
  404. boundingBoxX = Std.int(boundingBoxX + ((boundingBoxX > 0)?0.0000001:-0.0000001));
  405. boundingBoxY = Std.int(boundingBoxY + ((boundingBoxY > 0)?0.0000001:-0.0000001));
  406. var boundingBoxWidth:Int = (width != Std.int(width))?width:width-1;
  407. var boundingBoxHeight:Int = (height != Std.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:Int;
  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:Float=100,Mode:Int=PATH_FORWARD,AutoRotate:Bool=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:Bool=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. private function advancePath(Snap:Bool=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. private 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:Float = node.x - _point.x;
  565. var deltaY:Float = node.y - _point.y;
  566. var horizontalOnly:Bool = (_pathMode & PATH_HORIZONTAL_ONLY) > 0;
  567. var verticalOnly:Bool = (_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:Bool=false,Camera:FlxCamera=null):Bool
  635. {
  636. if(Std.is (ObjectOrGroup, FlxGroup))
  637. {
  638. var results:Bool = false;
  639. var i:Int = 0;
  640. var members:Array <FlxBasic> = cast (ObjectOrGroup, FlxGroup).members;
  641. while(i < length)
  642. {
  643. if(overlaps(members[i++],InScreenSpace,Camera))
  644. results = true;
  645. }
  646. return results;
  647. }
  648. if(Std.is (ObjectOrGroup, 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 cast (ObjectOrGroup, FlxTilemap).overlaps(this,InScreenSpace,Camera);
  653. }
  654. var object:FlxObject = cast (ObjectOrGroup, 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:Float,Y:Float,ObjectOrGroup:FlxBasic,InScreenSpace:Bool=false,Camera:FlxCamera=null):Bool
  681. {
  682. if(Std.is (ObjectOrGroup, FlxGroup))
  683. {
  684. var results:Bool = false;
  685. var basic:FlxBasic;
  686. var i:Int = 0;
  687. var members:Array <FlxBasic> = cast (ObjectOrGroup, 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(Std.is (ObjectOrGroup, 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 = cast (ObjectOrGroup, FlxTilemap);
  702. return tilemap.overlapsAt(tilemap.x - (X - x),tilemap.y - (Y - y),this,InScreenSpace,Camera);
  703. }
  704. var object:FlxObject = cast (ObjectOrGroup, 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 - Std.int(Camera.scroll.x*scrollFactor.x); //copied from getScreenXY()
  714. _point.y = Y - Std.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:Bool=false,Camera:FlxCamera=null):Bool
  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:Float = Point.x - Camera.scroll.x;
  736. var Y:Float = 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):Bool
  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 - Std.int(Camera.scroll.x*scrollFactor.x);
  769. Point.y = y - Std.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:Float=1):Void
  781. {
  782. _flickerTimer = Duration;
  783. if(_flickerTimer == 0)
  784. _flicker = false;
  785. }
  786. public function flickering (getFlickering, null):Bool;
  787. /**
  788. * Check to see if the object is still flickering.
  789. *
  790. * @return Whether the object is flickering or not.
  791. */
  792. private function getFlickering():Bool
  793. {
  794. return _flickerTimer != 0;
  795. }
  796. public function solid (getSolid, setSolid):Bool;
  797. /**
  798. * Whether the object collides or not. For more control over what directions
  799. * the object will collide from, use collision constants (like LEFT, FLOOR, etc)
  800. * to set the value of allowCollisions directly.
  801. */
  802. private function getSolid():Bool
  803. {
  804. return (allowCollisions & ANY) > NONE;
  805. }
  806. /**
  807. * @private
  808. */
  809. private function setSolid(Solid:Bool):Bool
  810. {
  811. if(Solid)
  812. allowCollisions = ANY;
  813. else
  814. allowCollisions = NONE;
  815. return Solid;
  816. }
  817. /**
  818. * Retrieve the midpoint of this object in world coordinates.
  819. *
  820. * @Point Allows you to pass in an existing <code>FlxPoint</code> object if you're so inclined. Otherwise a new one is created.
  821. *
  822. * @return A <code>FlxPoint</code> object containing the midpoint of this object in world coordinates.
  823. */
  824. public function getMidpoint(Point:FlxPoint=null):FlxPoint
  825. {
  826. if(Point == null)
  827. Point = new FlxPoint();
  828. Point.x = x + width*0.5;
  829. Point.y = y + height*0.5;
  830. return Point;
  831. }
  832. /**
  833. * Handy function for reviving game objects.
  834. * Resets their existence flags and position.
  835. *
  836. * @param X The new X position of this object.
  837. * @param Y The new Y position of this object.
  838. */
  839. public function reset(X:Float,Y:Float):Void
  840. {
  841. revive();
  842. touching = NONE;
  843. wasTouching = NONE;
  844. x = X;
  845. y = Y;
  846. last.x = x;
  847. last.y = y;
  848. velocity.x = 0;
  849. velocity.y = 0;
  850. }
  851. /**
  852. * Handy function for checking if this object is touching a particular surface.
  853. * For slightly better performance you can just &amp; the value directly into <code>touching</code>.
  854. * However, this method is good for readability and accessibility.
  855. *
  856. * @param Direction Any of the collision flags (e.g. LEFT, FLOOR, etc).
  857. *
  858. * @return Whether the object is touching an object in (any of) the specified direction(s) this frame.
  859. */
  860. public function isTouching(Direction:Int):Bool
  861. {
  862. return (touching & Direction) > NONE;
  863. }
  864. /**
  865. * Handy function for checking if this object is just landed on a particular surface.
  866. *
  867. * @param Direction Any of the collision flags (e.g. LEFT, FLOOR, etc).
  868. *
  869. * @return Whether the object just landed on (any of) the specified surface(s) this frame.
  870. */
  871. public function justTouched(Direction:Int):Bool
  872. {
  873. return ((touching & Direction) > NONE) && ((wasTouching & Direction) <= NONE);
  874. }
  875. /**
  876. * Reduces the "health" variable of this sprite by the amount specified in Damage.
  877. * Calls kill() if health drops to or below zero.
  878. *
  879. * @param Damage How much health to take away (use a negative number to give a health bonus).
  880. */
  881. public function hurt(Damage:Float):Void
  882. {
  883. health = health - Damage;
  884. if(health <= 0)
  885. kill();
  886. }
  887. /**
  888. * The main collision resolution function in flixel.
  889. *
  890. * @param Object1 Any <code>FlxObject</code>.
  891. * @param Object2 Any other <code>FlxObject</code>.
  892. *
  893. * @return Whether the objects in fact touched and were separated.
  894. */
  895. static public function separate(Object1:FlxObject, Object2:FlxObject):Bool
  896. {
  897. var separatedX:Bool = separateX(Object1,Object2);
  898. var separatedY:Bool = separateY(Object1,Object2);
  899. return separatedX || separatedY;
  900. }
  901. /**
  902. * The X-axis component of the object separation process.
  903. *
  904. * @param Object1 Any <code>FlxObject</code>.
  905. * @param Object2 Any other <code>FlxObject</code>.
  906. *
  907. * @return Whether the objects in fact touched and were separated along the X axis.
  908. */
  909. static public function separateX(Object1:FlxObject, Object2:FlxObject):Bool
  910. {
  911. //can't separate two immovable objects
  912. var obj1immovable:Bool = Object1.immovable;
  913. var obj2immovable:Bool = Object2.immovable;
  914. if(obj1immovable && obj2immovable)
  915. return false;
  916. //If one of the objects is a tilemap, just pass it off.
  917. if(Std.is (Object1, FlxTilemap))
  918. return cast (Object1, FlxTilemap).overlapsWithCallback(Object2,separateX);
  919. if(Std.is (Object2, FlxTilemap))
  920. return cast (Object2, FlxTilemap).overlapsWithCallback(Object1,separateX,true);
  921. //First, get the two object deltas
  922. var overlap:Float = 0;
  923. var obj1delta:Float = Object1.x - Object1.last.x;
  924. var obj2delta:Float = Object2.x - Object2.last.x;
  925. if(obj1delta != obj2delta)
  926. {
  927. //Check if the X hulls actually overlap
  928. var obj1deltaAbs:Float = (obj1delta > 0)?obj1delta:-obj1delta;
  929. var obj2deltaAbs:Float = (obj2delta > 0)?obj2delta:-obj2delta;
  930. var obj1rect:FlxRect = new FlxRect(Object1.x-((obj1delta > 0)?obj1delta:0),Object1.last.y,Object1.width+((obj1delta > 0)?obj1delta:-obj1delta),Object1.height);
  931. var obj2rect:FlxRect = new FlxRect(Object2.x-((obj2delta > 0)?obj2delta:0),Object2.last.y,Object2.width+((obj2delta > 0)?obj2delta:-obj2delta),Object2.height);
  932. 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))
  933. {
  934. var maxOverlap:Float = obj1deltaAbs + obj2deltaAbs + OVERLAP_BIAS;
  935. //If they did overlap (and can), figure out by how much and flip the corresponding flags
  936. if(obj1delta > obj2delta)
  937. {
  938. overlap = Object1.x + Object1.width - Object2.x;
  939. if((overlap > maxOverlap) || !(Object1.allowCollisions & RIGHT) || !(Object2.allowCollisions & LEFT))
  940. overlap = 0;
  941. else
  942. {
  943. Object1.touching |= RIGHT;
  944. Object2.touching |= LEFT;
  945. }
  946. }
  947. else if(obj1delta < obj2delta)
  948. {
  949. overlap = Object1.x - Object2.width - Object2.x;
  950. if((-overlap > maxOverlap) || !(Object1.allowCollisions & LEFT) || !(Object2.allowCollisions & RIGHT))
  951. overlap = 0;
  952. else
  953. {
  954. Object1.touching |= LEFT;
  955. Object2.touching |= RIGHT;
  956. }
  957. }
  958. }
  959. }
  960. //Then adjust their positions and velocities accordingly (if there was any overlap)
  961. if(overlap != 0)
  962. {
  963. var obj1v:Float = Object1.velocity.x;
  964. var obj2v:Float = Object2.velocity.x;
  965. if(!obj1immovable && !obj2immovable)
  966. {
  967. overlap *= 0.5;
  968. Object1.x = Object1.x - overlap;
  969. Object2.x += overlap;
  970. var obj1velocity:Float = Math.sqrt((obj2v * obj2v * Object2.mass)/Object1.mass) * ((obj2v > 0)?1:-1);
  971. var obj2velocity:Float = Math.sqrt((obj1v * obj1v * Object1.mass)/Object2.mass) * ((obj1v > 0)?1:-1);
  972. var average:Float = (obj1velocity + obj2velocity)*0.5;
  973. obj1velocity -= average;
  974. obj2velocity -= average;
  975. Object1.velocity.x = average + obj1velocity * Object1.elasticity;
  976. Object2.velocity.x = average + obj2velocity * Object2.elasticity;
  977. }
  978. else if(!obj1immovable)
  979. {
  980. Object1.x = Object1.x - overlap;
  981. Object1.velocity.x = obj2v - obj1v*Object1.elasticity;
  982. }
  983. else if(!obj2immovable)
  984. {
  985. Object2.x += overlap;
  986. Object2.velocity.x = obj1v - obj2v*Object2.elasticity;
  987. }
  988. return true;
  989. }
  990. else
  991. return false;
  992. }
  993. /**
  994. * The Y-axis component of the object separation process.
  995. *
  996. * @param Object1 Any <code>FlxObject</code>.
  997. * @param Object2 Any other <code>FlxObject</code>.
  998. *
  999. * @return Whether the objects in fact touched and were separated along the Y axis.
  1000. */
  1001. static public function separateY(Object1:FlxObject, Object2:FlxObject):Bool
  1002. {
  1003. //can't separate two immovable objects
  1004. var obj1immovable:Bool = Object1.immovable;
  1005. var obj2immovable:Bool = Object2.immovable;
  1006. if(obj1immovable && obj2immovable)
  1007. return false;
  1008. //If one of the objects is a tilemap, just pass it off.
  1009. if(Std.is (Object1, FlxTilemap))
  1010. return cast (Object1, FlxTilemap).overlapsWithCallback(Object2,separateY);
  1011. if(Std.is (Object2, FlxTilemap))
  1012. return cast (Object2, FlxTilemap).overlapsWithCallback(Object1,separateY,true);
  1013. //First, get the two object deltas
  1014. var overlap:Float = 0;
  1015. var obj1delta:Float = Object1.y - Object1.last.y;
  1016. var obj2delta:Float = Object2.y - Object2.last.y;
  1017. if(obj1delta != obj2delta)
  1018. {
  1019. //Check if the Y hulls actually overlap
  1020. var obj1deltaAbs:Float = (obj1delta > 0)?obj1delta:-obj1delta;
  1021. var obj2deltaAbs:Float = (obj2delta > 0)?obj2delta:-obj2delta;
  1022. var obj1rect:FlxRect = new FlxRect(Object1.x,Object1.y-((obj1delta > 0)?obj1delta:0),Object1.width,Object1.height+obj1deltaAbs);
  1023. var obj2rect:FlxRect = new FlxRect(Object2.x,Object2.y-((obj2delta > 0)?obj2delta:0),Object2.width,Object2.height+obj2deltaAbs);
  1024. 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))
  1025. {
  1026. var maxOverlap:Float = obj1deltaAbs + obj2deltaAbs + OVERLAP_BIAS;
  1027. //If they did overlap (and can), figure out by how much and flip the corresponding flags
  1028. if(obj1delta > obj2delta)
  1029. {
  1030. overlap = Object1.y + Object1.height - Object2.y;
  1031. if((overlap > maxOverlap) || !(Object1.allowCollisions & DOWN) || !(Object2.allowCollisions & UP))
  1032. overlap = 0;
  1033. else
  1034. {
  1035. Object1.touching |= DOWN;
  1036. Object2.touching |= UP;
  1037. }
  1038. }
  1039. else if(obj1delta < obj2delta)
  1040. {
  1041. overlap = Object1.y - Object2.height - Object2.y;
  1042. if((-overlap > maxOverlap) || !(Object1.allowCollisions & UP) || !(Object2.allowCollisions & DOWN))
  1043. overlap = 0;
  1044. else
  1045. {
  1046. Object1.touching |= UP;
  1047. Object2.touching |= DOWN;
  1048. }
  1049. }
  1050. }
  1051. }
  1052. //Then adjust their positions and velocities accordingly (if there was any overlap)
  1053. if(overlap != 0)
  1054. {
  1055. var obj1v:Float = Object1.velocity.y;
  1056. var obj2v:Float = Object2.velocity.y;
  1057. if(!obj1immovable && !obj2immovable)
  1058. {
  1059. overlap *= 0.5;
  1060. Object1.y = Object1.y - overlap;
  1061. Object2.y += overlap;
  1062. var obj1velocity:Float = Math.sqrt((obj2v * obj2v * Object2.mass)/Object1.mass) * ((obj2v > 0)?1:-1);
  1063. var obj2velocity:Float = Math.sqrt((obj1v * obj1v * Object1.mass)/Object2.mass) * ((obj1v > 0)?1:-1);
  1064. var average:Float = (obj1velocity + obj2velocity)*0.5;
  1065. obj1velocity -= average;
  1066. obj2velocity -= average;
  1067. Object1.velocity.y = average + obj1velocity * Object1.elasticity;
  1068. Object2.velocity.y = average + obj2velocity * Object2.elasticity;
  1069. }
  1070. else if(!obj1immovable)
  1071. {
  1072. Object1.y = Object1.y - overlap;
  1073. Object1.velocity.y = obj2v - obj1v*Object1.elasticity;
  1074. //This is special case code that handles cases like horizontal moving platforms you can ride
  1075. if(Object2.active && Object2.moves && (obj1delta > obj2delta))
  1076. Object1.x += Object2.x - Object2.last.x;
  1077. }
  1078. else if(!obj2immovable)
  1079. {
  1080. Object2.y += overlap;
  1081. Object2.velocity.y = obj1v - obj2v*Object2.elasticity;
  1082. //This is special case code that handles cases like horizontal moving platforms you can ride
  1083. if(Object1.active && Object1.moves && (obj1delta < obj2delta))
  1084. Object2.x += Object1.x - Object1.last.x;
  1085. }
  1086. return true;
  1087. }
  1088. else
  1089. return false;
  1090. }
  1091. }