diff --git a/Performance/PixelPerfectCollision/Project.xml b/Performance/PixelPerfectCollision/Project.xml index ddb1fcd3e..faa24f06b 100644 --- a/Performance/PixelPerfectCollision/Project.xml +++ b/Performance/PixelPerfectCollision/Project.xml @@ -16,7 +16,7 @@ - + diff --git a/Performance/PixelPerfectCollision/assets/alien.png b/Performance/PixelPerfectCollision/assets/alien.png index 514d1d081..b0f2b9b38 100644 Binary files a/Performance/PixelPerfectCollision/assets/alien.png and b/Performance/PixelPerfectCollision/assets/alien.png differ diff --git a/Performance/PixelPerfectCollision/source/Alien.hx b/Performance/PixelPerfectCollision/source/Alien.hx new file mode 100644 index 000000000..0cab584dc --- /dev/null +++ b/Performance/PixelPerfectCollision/source/Alien.hx @@ -0,0 +1,59 @@ +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; + +class Alien extends DemoSprite +{ + var collides:Bool = false; + + public function new() + { + super(); + + // set dance dance interstellar animations + loadGraphic("assets/alien.png", true); + /* + * WebGL has trouble displaying large number of sprites with dynamic coloring, + * so we switch bewteen two similar animations with different colors. + */ + var fps = FlxG.random.int(6, 10); + animation.add("dance_off", [0, 1, 0, 2], fps); + animation.add("dance_on", [3, 4, 3, 5], fps); + animation.play("dance_off"); // dance! + } + + /** + * Randomize position, scrollFactor, velocity and alpha. + */ + public function randomize() + { + var ran = FlxG.random; + x = ran.int(0, Std.int(FlxG.width - width)) + FlxG.camera.scroll.x; + y = ran.int(0, Std.int(FlxG.height - height)) + FlxG.camera.scroll.y; + alpha = ran.float(0.1, 1.0); + scrollFactor.x = alpha; + // negate the x-offset caused from scrollFactor + x += FlxG.camera.scroll.x * (scrollFactor.x - 1); + velocity.set(-ran.float(5, 25), 0); + + // Neat tweening effect for new aliens appearing + var toY = y; + y = (y < FlxG.height / 2) ? -20 : FlxG.height + 20; + FlxTween.tween(this, {y: toY}, 1.0, { ease: FlxEase.expoOut, startDelay: ran.float() * 0.5 }); + } + + /** + * Switches the between the red/white color to show whether an overlap is occurring. + * @param value + */ + override function setCollides(value:Bool) + { + if (value != collides) + { + collides = value; + var frame = animation.curAnim.curFrame; + animation.play("dance_" + (collides ? "on" : "off"), false, false, frame); + } + } +} \ No newline at end of file diff --git a/Performance/PixelPerfectCollision/source/DemoSprite.hx b/Performance/PixelPerfectCollision/source/DemoSprite.hx new file mode 100644 index 000000000..b68d63325 --- /dev/null +++ b/Performance/PixelPerfectCollision/source/DemoSprite.hx @@ -0,0 +1,66 @@ +import flixel.FlxG; +import flixel.FlxObject; +import flixel.FlxSprite; +import flixel.math.FlxRect; + +class DemoSprite extends FlxSprite +{ + public function new (x = 0.0, y = 0.0) + { + super(x, y); + } + + /** + * Switches the between the red/white color to show whether an overlap is occurring. + */ + public function setCollides(value:Bool) + { + color = value ? 0xFFac3232 : 0xFF6abe30; + } + + public function resetAngle() + { + angle = 0; + angularVelocity = 0; + } + + public function resetScale() + { + scale.set(1, 1); + } + + public function randomAngle() + { + angle = FlxG.random.float() * 360; + angularVelocity = FlxG.random.float(50, 100); + } + + public function randomScale() + { + scale.x = FlxG.random.float(0.5, 2.0); + scale.y = scale.x; + } + + #if debug + override function draw() + { + // For debugging + // drawDebugScreenBounds(); + + super.draw(); + } + + function drawDebugScreenBounds() + { + for (camera in cameras) + { + var rect = getScreenBounds(camera); + var gfx = beginDrawDebug(camera); + // fill static graphics object with square shape + gfx.lineStyle(1, 0x0000ff, 0.5); + gfx.drawRect(rect.x, rect.y, rect.width, rect.height); + endDrawDebug(camera); + } + } + #end +} \ No newline at end of file diff --git a/Performance/PixelPerfectCollision/source/PlayState.hx b/Performance/PixelPerfectCollision/source/PlayState.hx index b48c5e4ca..2b2ab1d46 100644 --- a/Performance/PixelPerfectCollision/source/PlayState.hx +++ b/Performance/PixelPerfectCollision/source/PlayState.hx @@ -1,6 +1,5 @@ package; -import flash.system.System; import flixel.FlxG; import flixel.FlxSprite; import flixel.FlxState; @@ -8,10 +7,12 @@ import flixel.text.FlxText; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; import flixel.math.FlxPoint; +import flixel.math.FlxVector; import flixel.math.FlxAngle; import flixel.util.FlxColor; import flixel.util.FlxCollision; import flixel.group.FlxGroup; +import flash.system.System; import openfl.display.FPS; using flixel.util.FlxSpriteUtil; @@ -25,14 +26,26 @@ class PlayState extends FlxState // how many aliens are created initially inline static var NUM_ALIENS:Int = 50; - // How fast the player moves - inline static var PLAYER_SPEED:Int = 75; - - inline static var INFO:String = "Collisions: |hits|\n" + "FPS: |fps| \n\n" + "[W/S] Objects: |objects|\n" - + "[A/D] Alpha tolerance: |alpha|\n" + "[ARROWS] Move\n" + "[R] Randomize\n" + "[SPACE] Toggle rotations"; + inline static var INFO_FULL:String + = "Collisions: |hits|\n" + #if debug + "Checks: |checks|\n" #end + + "FPS: |fps|\n\n" + + "[W/S] Objects: |objects|\n" + + "[A/D] Alpha tolerance: |alpha|\n" + + "[ARROWS] Move\n" + + "[R] Randomize\n" + + "[SPACE] |rotate| rotation\n" + + "[T] |scale| scale\n" + + "[H] Toggle instructions"; + + inline static var INFO_MIN:String + = "Objects: |objects|\n" + #if debug + "Checks: |checks|\n" #end + + "Collisions: |hits|\n" + + "FPS: |fps|"; // group holding the player and the aliens - var aliens:FlxTypedGroup; + var aliens:FlxTypedGroup; // the player ship var player:Player; @@ -40,6 +53,9 @@ class PlayState extends FlxState // number of collisions at any given time var numCollisions:Int = 0; + // number of collisions at any given time + var numChecks:Int = 0; + // setting this to 255 means two object will collide only if totally opaque var alphaTolerance:Int = 1; @@ -49,27 +65,40 @@ class PlayState extends FlxState // to track fps var fps:FPS; - // wether the objects should rotate - var rotate(default, set):Bool = true; + // whether the objects should rotate + var rotate:Bool = false; + // whether the objects should be scaled + var scale:Bool = false; + // whether the key instructions will show + var showFullInfo = true; override public function create():Void { super.create(); + FlxG.camera.bgColor = FlxG.stage.color; // the group containing all the objects - add(aliens = new FlxTypedGroup()); + add(aliens = new FlxTypedGroup()); // create the player add(player = new Player()); + FlxG.camera.follow(player); + FlxG.camera.setScrollBounds(0, FlxG.width * 10, 0, FlxG.height); + FlxG.worldBounds.set(-10, -10, FlxG.camera.maxScrollX + 20, FlxG.camera.maxScrollY + 20); + + updateSpriteAngles(); + updateSpriteScales(); // add objects for more interstellar fun! for (i in 1...NUM_ALIENS) addAlien(); + // add in some text so we know what's happening - infoText = new FlxText(2, 0, 400, INFO); + infoText = new FlxText(2, 0, 400, INFO_FULL); infoText.y = FlxG.height - infoText.height; - infoText.setBorderStyle(OUTLINE); + infoText.setBorderStyle(OUTLINE, FlxColor.BLACK); + infoText.scrollFactor.x = 0; add(infoText); // just need this to get the fps, so we display it outside view range @@ -77,9 +106,6 @@ class PlayState extends FlxState // makes low fps less noticable FlxG.fixedTimestep = false; - - // don't need the cursor - FlxG.mouse.visible = false; } /** @@ -87,35 +113,20 @@ class PlayState extends FlxState */ function addAlien():FlxSprite { - var alien = aliens.recycle(FlxSprite); - alien.loadGraphic("assets/alien.png", true); // load graphics from asset - alien.animation.add("dance", [0, 1, 0, 2], FlxG.random.int(6, 10)); // set dance dance interstellar animation - alien.animation.play("dance"); // dance! - randomize(alien); // set position, angle and alpha to random values - return alien; - } - - /** - * Randomize position, angle and alpha of `obj`. - */ - function randomize(obj:FlxSprite):FlxSprite - { - // The start position of the alien is offscreen on a circle - var point = getRandomCirclePos(); - obj.setPosition(point.x, point.y); - point.put(); // recycle point - - var destX = FlxG.random.int(0, Std.int(FlxG.width - obj.width)); - var destY = FlxG.random.int(0, Std.int(FlxG.height - obj.height)); - obj.alpha = FlxG.random.float(0.3, 1.0); - - // Neat tweening effect for new aliens appearing - FlxTween.tween(obj, {x: destX, y: destY}, 2, {ease: FlxEase.expoOut}); - + var alien = aliens.recycle(Alien); + alien.randomize(); + if (rotate) - randomizeRotation(obj); - - return obj; + alien.randomAngle(); + else + alien.resetAngle(); + + if (scale) + alien.randomScale(); + else + alien.resetScale(); + + return alien; } /** @@ -124,47 +135,33 @@ class PlayState extends FlxState override public function update(elapsed:Float):Void { super.update(elapsed); - + handleInput(); checkCollisions(); updateInfo(); - - player.screenWrap(); // make sure the player can't go offscreen } function handleInput():Void { - // Reset velocity to (0,0) - player.velocity.set(); - - // player movement - if (FlxG.keys.pressed.LEFT) - player.velocity.x = -PLAYER_SPEED; - if (FlxG.keys.pressed.RIGHT) - player.velocity.x = PLAYER_SPEED; - if (FlxG.keys.pressed.UP) - player.velocity.y = -PLAYER_SPEED; - if (FlxG.keys.pressed.DOWN) - player.velocity.y = PLAYER_SPEED; - // toggle rotation if (FlxG.keys.justReleased.SPACE) { rotate = !rotate; - rotate ? randomizeRotation(player) : resetRotation(player); + updateSpriteAngles(); + } + + // toggle scale + if (FlxG.keys.justReleased.T) + { + scale = !scale; + updateSpriteScales(); } // randomize if (FlxG.keys.justReleased.R) { - for (obj in aliens) - { - // Don't randomize the player's position - if (obj != player) - { - randomize(obj); - } - } + for (a in aliens) + a.randomize(); } // increment/decrement number of objects @@ -173,6 +170,7 @@ class PlayState extends FlxState for (i in 0...3) addAlien(); } + if (FlxG.keys.justReleased.S) { for (i in 0...3) @@ -182,6 +180,13 @@ class PlayState extends FlxState alien.kill(); } } + + if (FlxG.keys.justReleased.H) + { + showFullInfo = !showFullInfo; + updateInfo(); + infoText.y = FlxG.height - infoText.height; + } // increment/decrement alpha tolerance if (FlxG.keys.pressed.D) @@ -205,105 +210,94 @@ class PlayState extends FlxState */ function checkCollisions():Void { + numChecks = 0; numCollisions = 0; - player.color = FlxColor.GREEN; + player.color = 0xFF6abe30; - for (i in 0...aliens.length) + for (alien in aliens) + alien.cameraWrap(WALL); + + for (alien1 in aliens) { - var obj1 = aliens.members[i]; var collides = false; // Only collide alive members - if (!obj1.alive) + if (!alien1.alive) continue; - - for (j in 0...aliens.length) - { - var obj2 = aliens.members[j]; - - // Only collide alive members and don't collide an object with itself - if (!obj2.alive || (i == j)) - continue; - - // this is how we check if obj1 and obj2 are colliding - if (FlxCollision.pixelPerfectCheck(obj1, obj2, alphaTolerance)) - { - collides = true; - numCollisions++; - break; - } - } - + + numChecks++; // We check collisions with the player seperately, since he's not in the group - if (FlxCollision.pixelPerfectCheck(obj1, player, alphaTolerance)) + if (FlxCollision.pixelPerfectCheck(alien1, player, alphaTolerance)) { collides = true; numCollisions++; - player.color = FlxColor.RED; + player.color = 0xFFac3232; } - - obj1.color = collides ? FlxColor.RED : FlxColor.WHITE; + else + { + for (alien2 in aliens) + { + // Only collide alive members and don't collide an object with itself + if (!alien2.alive || alien1 == alien2) + continue; + + numChecks++; + // this is how we check if obj1 and obj2 are colliding + if (FlxCollision.pixelPerfectCheck(alien1, alien2, alphaTolerance)) + { + collides = true; + numCollisions++; + break; + } + } + } + + alien1.setCollides(collides); } } - - function updateInfo():Void - { - infoText.text = INFO // + 1 for the player that is not in the group - .replace("|objects|", Std.string(aliens.countLiving() + 1)) - .replace("|alpha|", Std.string(alphaTolerance)) - .replace("|hits|", Std.string(numCollisions)) - .replace("|fps|", Std.string(fps.currentFPS)); - } - - function set_rotate(Value:Bool):Bool + + function updateSpriteAngles() { - if (Value) - aliens.forEach(randomizeRotation); + if (rotate) + { + for (alien in aliens) + alien.randomAngle(); + + player.randomAngle(); + } else - aliens.forEach(resetRotation); - - return rotate = Value; - } - - function randomizeRotation(obj:FlxSprite):Void - { - obj.angle = FlxG.random.float() * 360; - obj.angularVelocity = 100; - } - - function resetRotation(obj:FlxSprite):Void - { - obj.angle = 0; - obj.angularVelocity = 0; + { + for (alien in aliens) + alien.resetAngle(); + + player.resetAngle(); + } } - - /** - * Returns a random position on an offscreen circle to tween the aliens from / to - */ - function getRandomCirclePos():FlxPoint + + function updateSpriteScales() { - // choose a random position on our circle, from 1° to 360° - var startAngle = FlxG.random.int(1, 360); - - // make sure the radius of our circle is 200 px bigger than the game's width / height (whichever is bigger) - var startRadius = (FlxG.width > FlxG.height) ? (FlxG.height + 200) : (FlxG.width + 200); - - var coords:FlxPoint = FlxAngle.getCartesianCoords(startRadius, startAngle); - // Currently, the coords represent a circle with its center at (0,0) - let's move them! - coords.x += FlxG.width / 2; - coords.y += FlxG.height / 2; - - return coords; + if (scale) + { + for (alien in aliens) + alien.randomScale(); + } + else + { + for (alien in aliens) + alien.resetScale(); + } } -} -class Player extends FlxSprite -{ - public function new() + function updateInfo():Void { - super(); - loadGraphic("assets/ship.png"); - screenCenter(); - angularVelocity = 50; + infoText.text = (showFullInfo ? INFO_FULL : INFO_MIN) + .replace("|checks|", Std.string(numChecks)) + .replace("|hits|", Std.string(numCollisions)) + .replace("|fps|", Std.string(fps.currentFPS)) + .replace("|objects|", Std.string(aliens.countLiving() + 1))// + 1 for the player + .replace("|alpha|", Std.string(alphaTolerance)) + .replace("|rotate|", rotate ? "Disable" : "Enable") + .replace("|scale|", scale ? "Disable" : "Enable"); + } } diff --git a/Performance/PixelPerfectCollision/source/Player.hx b/Performance/PixelPerfectCollision/source/Player.hx new file mode 100644 index 000000000..c3323358d --- /dev/null +++ b/Performance/PixelPerfectCollision/source/Player.hx @@ -0,0 +1,33 @@ +import flixel.FlxG; +import flixel.FlxSprite; + +class Player extends DemoSprite +{ + // How fast the player moves + inline static var PLAYER_SPEED:Int = 75; + + public function new() + { + super(); + loadGraphic("assets/ship.png"); + screenCenter(); + x += FlxG.width; + } + + override function update(elapsed:Float) + { + super.update(elapsed); + + // player movement + velocity.set(0, 0); + + if (FlxG.keys.pressed.LEFT) + velocity.x = -PLAYER_SPEED; + if (FlxG.keys.pressed.RIGHT) + velocity.x = PLAYER_SPEED; + if (FlxG.keys.pressed.UP) + velocity.y = -PLAYER_SPEED; + if (FlxG.keys.pressed.DOWN) + velocity.y = PLAYER_SPEED; + } +} \ No newline at end of file