Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package
- {
- import flash.display.BitmapData;
- import flash.geom.Matrix;
- import org.flixel.FlxRect;
- import org.flixel.FlxState;
- import org.flixel.FlxG;
- import org.flixel.FlxObject;
- import org.flixel.FlxCamera;
- import org.flixel.FlxSprite;
- import org.flixel.plugin.photonstorm.FlxCollision;
- import flash.display.BitmapData;
- import flash.display.Sprite;
- import flash.geom.ColorTransform;
- import flash.geom.Matrix;
- import flash.geom.Point;
- import flash.geom.Rectangle;
- import flash.display.BlendMode;
- /* PerfectCollisionSprite
- * This class is an extension of FlxSprite which fills a crucial gap in Flixel's feature-set: you can't do pixel-perfect
- * collision with a rotated sprite.
- */
- public class PerfectCollisionSprite extends FlxSprite
- {
- public var rotatedFrame:BitmapData;
- // The offset between the top-left-hand corner of the bounding box of the rotated frame and the origin.
- // Necessary for collision detection. Stored rather than calculated each time for performance reasons (calls
- // to trig functions and stuff).
- public var dx:Number = 0;
- public var dy:Number = 0;
- public function PerfectCollisionSprite(X:int, Y:int)
- {
- super(X, Y);
- rotatedFrame = framePixels;
- dx = origin.x;
- dy = origin.y;
- }
- override public function draw():void
- {
- // Execute the regular rendering code.
- super.draw();
- // Destroy the previous bitap to avoid memory leaks.
- rotatedFrame.dispose();
- // This if condition is identical to the main one from the default draw() function in FlxSprite. The if code
- // is executed if there are no rotations, the else code if there are. You don't want to run the rotation logic
- // if you don't have to for performance issues.
- if (((angle == 0) || (_bakedRotation > 0)) && (scale.x == 1) && (scale.y == 1) && (blend == null))
- {
- rotatedFrame = new BitmapData(width, height, true, 0x00F556FF);
- rotatedFrame.draw(framePixels, null, null, null, null, false);
- dx = origin.x;
- dy = origin.y;
- }
- else
- {
- // There's a rotation! We need to create and store the rotated bitmap.
- var sin:Number = Math.abs( Math.sin( -angle * 0.017453293) );
- var cos:Number = Math.abs( Math.cos( -angle * 0.017453293) );
- //
- var rotatedWidth:Number =
- Math.ceil(
- cos * width + sin * height
- )
- ;
- var rotatedHeight:Number =
- Math.ceil(
- cos * height + sin * width
- )
- ;
- rotatedFrame = new BitmapData(rotatedWidth, rotatedHeight, true, 0x00F556FF);
- // Distances between the origin and the top left corner of the new bounding box.
- // I dislike the fact that this uses an if statement. Ideally I would have a single mathematical formula for doing this,
- // but I just can't figure out the math.
- if (angle > 0)
- {
- dy = origin.x * sin + origin.y * cos;
- dx = (height - origin.y) * sin + origin.x * cos;
- }
- else
- {
- dy = rotatedHeight - ((height - origin.y) * cos + origin.x * sin);
- dx = rotatedWidth - (cos * (width - origin.x) + sin * (height - origin.y) * sin);
- }
- // We subtract _point so as to cancel out the fact that super.draw() is acting within the absolute coordinate
- // system. We subtract 'origin' so that the origin is positioned at the top-left corner of the bounding box;
- // that way we just have to add (dx, dy) to get the bitmap positioned correctly.
- _matrix.translate(dx - _point.x - origin.x, dy - _point.y - origin.y);
- rotatedFrame.draw(framePixels, _matrix, null, null, null, false);
- }
- }
- /*
- * This function is a slightly modified verion of the pixelPerfectCheck in FlxCollision.
- */
- public static function pixelPerfectCheck(contact:PerfectCollisionSprite, target:PerfectCollisionSprite, alphaTolerance:int = 255, camera:FlxCamera = null):Boolean
- {
- var debug:BitmapData = new BitmapData(1, 1, false);
- var pointA:Point = new Point;
- var pointB:Point = new Point;
- if (camera)
- {
- pointA.x = contact.X - int(camera.scroll.x * contact.scrollFactor.x) - contact.offset.x - contact.dx;
- pointA.y = contact.Y - int(camera.scroll.y * contact.scrollFactor.y) - contact.offset.y - contact.dy;
- pointB.x = target.X - int(camera.scroll.x * target.scrollFactor.x) - target.offset.x - target.dx;
- pointB.y = target.Y - int(camera.scroll.y * target.scrollFactor.y) - target.offset.y - target.dy;
- }
- else
- {
- pointA.x = contact.X - int(FlxG.camera.scroll.x * contact.scrollFactor.x) - contact.offset.x - contact.dx;
- pointA.y = contact.Y - int(FlxG.camera.scroll.y * contact.scrollFactor.y) - contact.offset.y - contact.dy;
- pointB.x = target.X - int(FlxG.camera.scroll.x * target.scrollFactor.x) - target.offset.x - target.dx;
- pointB.y = target.Y - int(FlxG.camera.scroll.y * target.scrollFactor.y) - target.offset.y - target.dy;
- }
- var boundsA:Rectangle = new Rectangle(pointA.x, pointA.y, contact.rotatedFrame.width, contact.rotatedFrame.height);
- var boundsB:Rectangle = new Rectangle(pointB.x, pointB.y, target.rotatedFrame.width, target.rotatedFrame.height);
- var intersect:Rectangle = boundsA.intersection(boundsB);
- if (intersect.isEmpty() || intersect.width == 0 || intersect.height == 0)
- {
- return false;
- }
- // Normalise the values or it'll break the BitmapData creation below
- intersect.x = Math.floor(intersect.x);
- intersect.y = Math.floor(intersect.y);
- intersect.width = Math.ceil(intersect.width);
- intersect.height = Math.ceil(intersect.height);
- if (intersect.isEmpty())
- {
- return false;
- }
- // Thanks to Chris Underwood for helping with the translate logic :)
- var matrixA:Matrix = new Matrix;
- matrixA.translate(-(intersect.x - boundsA.x), -(intersect.y - boundsA.y));
- var matrixB:Matrix = new Matrix;
- matrixB.translate(-(intersect.x - boundsB.x), -(intersect.y - boundsB.y));
- var testA:BitmapData = contact.rotatedFrame;
- var testB:BitmapData = target.rotatedFrame;
- var overlapArea:BitmapData = new BitmapData(intersect.width, intersect.height, false);
- overlapArea.draw(testA, matrixA, new ColorTransform(1, 1, 1, 1, 255, -255, -255, alphaTolerance), BlendMode.NORMAL);
- overlapArea.draw(testB, matrixB, new ColorTransform(1, 1, 1, 1, 255, 255, 255, alphaTolerance), BlendMode.DIFFERENCE);
- // Developers: If you'd like to see how this works, display it in your game somewhere. Or you can comment it out to save a tiny bit of performance
- debug = overlapArea;
- //(FlxG.state as PlayState).drawBMP(debug, intersect.x, intersect.y, 0);
- //drawPoint(pointA, 0xFFFF00FF, -1);
- //drawPoint(pointB, 0xFF00FF00, 2);
- //drawRect(boundsA, 0xFFFF00FF, 3);
- //drawRect(boundsB, 0x8800FF00, 4);
- //(FlxG.state as PlayState).drawRect(intersect, 0xFFFFFFFF, 5);
- var overlap:Rectangle = overlapArea.getColorBoundsRect(0xffffffff, 0xff00ffff);
- overlap.offset(intersect.x, intersect.y);
- if (overlap.isEmpty())
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- // Due to all the rotation logic, we frequently have to work with the coordinates of the pivot point, which
- // is 'origin'. So we implement the variables X and Y to stand in for the absolute coordinates of that point.
- public function get X():Number
- {
- return x + origin.x;
- }
- public function set X(arg:Number):void
- {
- x = arg - origin.x;
- }
- public function get Y():Number
- {
- return y + origin.y;
- }
- public function set Y(arg:Number):void
- {
- y = arg - origin.y;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement