diff --git a/Features/FlxCamera/assets/BorderSlice.png b/Features/FlxCamera/assets/BorderSlice.png new file mode 100644 index 000000000..1ff3f8345 Binary files /dev/null and b/Features/FlxCamera/assets/BorderSlice.png differ diff --git a/Features/FlxCamera/source/HUD.hx b/Features/FlxCamera/source/HUD.hx deleted file mode 100644 index 6f5bdcced..000000000 --- a/Features/FlxCamera/source/HUD.hx +++ /dev/null @@ -1,75 +0,0 @@ -package; - -import flixel.FlxSprite; -import flixel.group.FlxGroup; -import flixel.text.FlxText; -import flixel.util.FlxColor; - -/** - * @author TiagoLr ( ~~~ProG4mr~~~ ) - */ -class HUD extends FlxGroup -{ - public var width:Int = 200; - public var height:Int = 180; - public var background:FlxSprite; - - var txtStyle:FlxText; - var txtLerp:FlxText; - var txtLead:FlxText; - var txtZoom:FlxText; - - public function new() - { - super(); - - var x:Int = 10000; - - background = new FlxSprite(x, 0); - background.makeGraphic(width, height, FlxColor.BLACK); - add(background); - - x += 6; - var startY:Int = 10; - - add(new FlxText(x, startY, width, "[W,A,S,D] or arrows to control the orb.")); - - add(new FlxText(x, startY + 20, 300, "[H] to change follow style.")); - addGreenText(txtStyle = new FlxText(x, startY + 33, width, "LOCKON")); - - add(new FlxText(x, startY + 55, width, "[U] or [J] to change lerp.")); - addGreenText(txtLerp = new FlxText(x, startY + 68, width, "Camera lerp: 1")); - - add(new FlxText(x, startY + 95, width, "[I] or [K] to change lead.")); - addGreenText(txtLead = new FlxText(x, startY + 108, width, "Camera lead: 0")); - - add(new FlxText(x, startY + 135, width, "[O] or [L] to change zoom.")); - addGreenText(txtZoom = new FlxText(x, startY + 148, width, "Camera zoom: 1")); - } - - function addGreenText(text:FlxText) - { - text.setFormat(null, 11, 0x55FF55); - add(text); - } - - public function updateStyle(string:String) - { - txtStyle.text = string; - } - - public function updateCamLerp(lerp:Float) - { - txtLerp.text = "Camera lerp: " + lerp; - } - - public function updateCamLead(lead:Float) - { - txtLead.text = "Camera lead: " + lead; - } - - public function updateZoom(zoom:Float) - { - txtZoom.text = "Camera Zoom: " + Math.floor(zoom * 10) / 10; - } -} diff --git a/Features/FlxCamera/source/Main.hx b/Features/FlxCamera/source/Main.hx index 9ba757312..443fac7e1 100644 --- a/Features/FlxCamera/source/Main.hx +++ b/Features/FlxCamera/source/Main.hx @@ -1,6 +1,7 @@ package; import flixel.FlxGame; +import flixel.FlxSprite; import openfl.display.Sprite; class Main extends Sprite @@ -8,6 +9,8 @@ class Main extends Sprite public function new() { super(); + + FlxSprite.defaultAntialiasing = true; addChild(new FlxGame(640, 480, PlayState)); } } diff --git a/Features/FlxCamera/source/Orb.hx b/Features/FlxCamera/source/Orb.hx deleted file mode 100644 index dd019b068..000000000 --- a/Features/FlxCamera/source/Orb.hx +++ /dev/null @@ -1,36 +0,0 @@ -package; - -import flixel.FlxG; -import flixel.FlxSprite; -import flixel.addons.nape.FlxNapeSprite; -import openfl.Assets; - -/** - * @author TiagoLr ( ~~~ProG4mr~~~ ) - */ -class Orb extends FlxNapeSprite -{ - public var shadow:FlxSprite; - - public function new() - { - super(FlxG.width / 2, FlxG.height / 2, "assets/Orb.png"); - createCircularBody(18); - body.allowRotation = false; - setDrag(0.98, 1); - } - - override public function update(elapsed:Float):Void - { - super.update(elapsed); - - if (FlxG.camera.target != null && FlxG.camera.followLead.x == 0) // target check is used for debug purposes. - { - x = Math.round(x); // Smooths camera and orb shadow following. Does not work well with camera lead. - y = Math.round(y); // Smooths camera and orb shadow following. Does not work well with camera lead. - } - - shadow.x = Math.round(x); - shadow.y = Math.round(y); - } -} diff --git a/Features/FlxCamera/source/PlayState.hx b/Features/FlxCamera/source/PlayState.hx index c8ce551e7..12edeb639 100644 --- a/Features/FlxCamera/source/PlayState.hx +++ b/Features/FlxCamera/source/PlayState.hx @@ -1,17 +1,20 @@ package; -import flash.Lib; -import flash.display.BlendMode; +import haxe.EnumTools; import flixel.FlxCamera; import flixel.FlxG; -import flixel.FlxSprite; import flixel.FlxState; +import flixel.FlxSprite; +import flixel.addons.display.FlxBackdrop; import flixel.addons.nape.FlxNapeSpace; import flixel.math.FlxMath; -import flixel.math.FlxRect; -import flixel.util.FlxColor; -import nape.geom.Vec2; -import openfl.Assets; +import nape.phys.Material; +import props.BorderSlice; +import props.Orb; +import props.OtherOrb; +import props.PlayerOrb; +import ui.DeadzoneOverlay; +import ui.HUD; using flixel.util.FlxSpriteUtil; @@ -20,248 +23,139 @@ using flixel.util.FlxSpriteUtil; */ class PlayState extends FlxState { - // Demo arena boundaries - static var LEVEL_MIN_X:Float; - static var LEVEL_MAX_X:Float; - static var LEVEL_MIN_Y:Float; - static var LEVEL_MAX_Y:Float; - - var orb:Orb; - var orbShadow:FlxSprite; + static var followStyles = EnumTools.createAll(FlxCameraFollowStyle); + + var player:PlayerOrb; var hud:HUD; - var hudCam:FlxCamera; - var overlayCamera:FlxCamera; - var deadzoneOverlay:FlxSprite; + var deadzoneOverlay:DeadzoneOverlay; override public function create():Void { FlxNapeSpace.init(); - LEVEL_MIN_X = -FlxG.stage.stageWidth / 2; - LEVEL_MAX_X = FlxG.stage.stageWidth * 1.5; - LEVEL_MIN_Y = -FlxG.stage.stageHeight / 2; - LEVEL_MAX_Y = FlxG.stage.stageHeight * 1.5; + // final levelMinX = -FlxG.stage.stageWidth; + // final levelMaxX = FlxG.stage.stageWidth; + // final levelMinY = -FlxG.stage.stageHeight; + // final levelMaxY = FlxG.stage.stageHeight; + final levelMinX = 0; + final levelMaxX = FlxG.stage.stageWidth * 2; + final levelMinY = 0; + final levelMaxY = FlxG.stage.stageHeight * 2; + final levelWidth = levelMaxX - levelMinX; + final levelHeight = levelMaxY - levelMinY; super.create(); - FlxG.mouse.visible = false; - FlxNapeSpace.velocityIterations = 5; FlxNapeSpace.positionIterations = 5; - createFloorTiles(); - FlxNapeSpace.createWalls(LEVEL_MIN_X, LEVEL_MIN_Y, LEVEL_MAX_X, LEVEL_MAX_Y); - // Walls border. - add(new FlxSprite(-FlxG.width / 2, -FlxG.height / 2, "assets/Border.png")); + // repeating backdrop + final backdrop = new FlxBackdrop("assets/FloorTexture.png"); + #if debug + backdrop.ignoreDrawDebug = true; + #end + add(backdrop); + + // create nape wall colliders + final border = 10; + FlxNapeSpace.createWalls(levelMinX + border, levelMinY + border, levelMaxX - border, levelMaxY - border, border, new Material(1.0, 0.0, 0.0, 1)); + + // Walls border sprite + final borderSprite = new BorderSlice(levelMinX, levelMinY, levelWidth, levelHeight); + add(borderSprite); // Player orb - orbShadow = new FlxSprite(FlxG.width / 2, FlxG.height / 2, "assets/OrbShadow.png"); - orbShadow.centerOffsets(); - orbShadow.blend = BlendMode.MULTIPLY; - - orb = new Orb(); - - add(orbShadow); - add(orb); - - orb.shadow = orbShadow; - + player = new PlayerOrb(levelMinX + levelWidth / 2, levelMinY + levelHeight / 2); + add(player); + // if the player is using a virtual pad, add it to the state + if (player.controls.virtualPad != null) + add(player.controls.virtualPad); + // Other orbs for (i in 0...5) { - var otherOrbShadow = new FlxSprite(100, 100, "assets/OtherOrbShadow.png"); - otherOrbShadow.centerOffsets(); - otherOrbShadow.blend = BlendMode.MULTIPLY; - - var otherOrb = new Orb(); - otherOrb.loadGraphic("assets/OtherOrb.png", true, 140, 140); - otherOrb.createCircularBody(50); - otherOrb.setBodyMaterial(1, 0.2, 0.4, 0.5); - otherOrb.antialiasing = true; - otherOrb.setDrag(1, 1); - - add(otherOrbShadow); - add(otherOrb); - - otherOrb.shadow = otherOrbShadow; - - switch (i) - { - case 0: - otherOrb.body.position.setxy(320 - 400, 240 - 400); - otherOrb.animation.frameIndex = 0; - case 1: - otherOrb.body.position.setxy(320 + 400, 240 - 400); - otherOrb.animation.frameIndex = 4; - case 2: - otherOrb.body.position.setxy(320 + 400, 240 + 400); - otherOrb.animation.frameIndex = 3; - case 3: - otherOrb.body.position.setxy(-300, 240); - otherOrb.animation.frameIndex = 2; - case 4: - otherOrb.body.position.setxy(0, 240 + 400); - otherOrb.animation.frameIndex = 1; - } - otherOrb.body.velocity.setxy(FlxG.random.int(75, 150), FlxG.random.int(75, 150)); + final orb = new OtherOrb(0, 0, i); + add(orb); + orb.randomizeVelocity(); + + // randomize spawn position until it's far enough from the player, up to 20 times + var tries = 20; + do orb.randomizePosition(levelMinX, levelMaxX, levelMinY, levelMaxY) + while (Math.abs(orb.x - player.x) < 200 && Math.abs(orb.y - player.y) < 200 && tries-- > 0); } - + hud = new HUD(); add(hud); - + // Camera Overlay - deadzoneOverlay = new FlxSprite(-10000, -10000); - deadzoneOverlay.makeGraphic(FlxG.width, FlxG.height, FlxColor.TRANSPARENT, true); - deadzoneOverlay.antialiasing = true; - - overlayCamera = new FlxCamera(0, 0, 640, 720); - overlayCamera.bgColor = FlxColor.TRANSPARENT; - overlayCamera.follow(deadzoneOverlay); - FlxG.cameras.add(overlayCamera); + deadzoneOverlay = new DeadzoneOverlay(); add(deadzoneOverlay); - - FlxG.camera.setScrollBoundsRect(LEVEL_MIN_X, LEVEL_MIN_Y, LEVEL_MAX_X + Math.abs(LEVEL_MIN_X), LEVEL_MAX_Y + Math.abs(LEVEL_MIN_Y), true); - FlxG.camera.follow(orb, LOCKON, 1); - drawDeadzone(); // now that deadzone is present - - hudCam = new FlxCamera(440, 0, hud.width, hud.height); - hudCam.zoom = 1; // For 1/2 zoom out. - hudCam.follow(hud.background, FlxCameraFollowStyle.NO_DEAD_ZONE); - hudCam.alpha = .5; - FlxG.cameras.add(hudCam); + + FlxG.camera.pixelPerfectRender = false; + FlxG.camera.setScrollBounds(levelMinX, levelMaxX, levelMinY, levelMaxY); + FlxG.worldBounds.set(levelMinX, levelMinY, levelWidth, levelHeight); + FlxG.camera.follow(player, followStyles[0], 1); + deadzoneOverlay.redraw(FlxG.camera); // now that deadzone is present } - - function drawDeadzone() + + override public function update(elapsed:Float):Void { - deadzoneOverlay.fill(FlxColor.TRANSPARENT); - var dz:FlxRect = FlxG.camera.deadzone; - if (dz == null) - return; - - var lineLength:Int = 20; - var lineStyle:LineStyle = {color: FlxColor.WHITE, thickness: 3}; - - // adjust points slightly so lines will be visible when at screen edges - dz.x += lineStyle.thickness / 2; - dz.width -= lineStyle.thickness; - dz.y += lineStyle.thickness / 2; - dz.height -= lineStyle.thickness; - - // Left Up Corner - deadzoneOverlay.drawLine(dz.left, dz.top, dz.left + lineLength, dz.top, lineStyle); - deadzoneOverlay.drawLine(dz.left, dz.top, dz.left, dz.top + lineLength, lineStyle); - // Right Up Corner - deadzoneOverlay.drawLine(dz.right, dz.top, dz.right - lineLength, dz.top, lineStyle); - deadzoneOverlay.drawLine(dz.right, dz.top, dz.right, dz.top + lineLength, lineStyle); - // Bottom Left Corner - deadzoneOverlay.drawLine(dz.left, dz.bottom, dz.left + lineLength, dz.bottom, lineStyle); - deadzoneOverlay.drawLine(dz.left, dz.bottom, dz.left, dz.bottom - lineLength, lineStyle); - // Bottom Right Corner - deadzoneOverlay.drawLine(dz.right, dz.bottom, dz.right - lineLength, dz.bottom, lineStyle); - deadzoneOverlay.drawLine(dz.right, dz.bottom, dz.right, dz.bottom - lineLength, lineStyle); + super.update(elapsed); + + final justPressed = FlxG.keys.justPressed; + + if (justPressed.Y) setStyle(1); + if (justPressed.H) setStyle(-1); + + if (justPressed.U) setLerp(.1); + if (justPressed.J) setLerp(-.1); + + if (justPressed.I) setLead(.5); + if (justPressed.K) setLead(-.5); + + if (justPressed.O) setZoom(.1); + if (justPressed.L) setZoom(-.1); + + if (justPressed.M) FlxG.camera.shake(); } - - public function setZoom(zoom:Float) + + public function setZoom(delta:Float) { - FlxG.camera.zoom = FlxMath.bound(zoom, 0.5, 4); + final newZoom = FlxG.camera.zoom + delta; + FlxG.camera.zoom = FlxMath.bound(Math.round(newZoom * 10) / 10, 0.5, 4); hud.updateZoom(FlxG.camera.zoom); } - - function createFloorTiles() - { - var floorImg = Assets.getBitmapData("assets/FloorTexture.png"); - var imgWidth = floorImg.width; - var imgHeight = floorImg.height; - var i = LEVEL_MIN_X; - var j = LEVEL_MIN_Y; - - while (i <= LEVEL_MAX_X) - { - while (j <= LEVEL_MAX_Y) - { - add(new FlxSprite(i, j, floorImg)); - j += imgHeight; - } - i += imgWidth; - j = LEVEL_MIN_Y; - } - } - - override public function update(elapsed:Float):Void - { - super.update(elapsed); - - var speed = 20; - if (FlxG.keys.anyPressed([A, LEFT])) - orb.body.applyImpulse(new Vec2(-speed, 0)); - if (FlxG.keys.anyPressed([S, DOWN])) - orb.body.applyImpulse(new Vec2(0, speed)); - if (FlxG.keys.anyPressed([D, RIGHT])) - orb.body.applyImpulse(new Vec2(speed, 0)); - if (FlxG.keys.anyPressed([W, UP])) - orb.body.applyImpulse(new Vec2(0, -speed)); - - if (FlxG.keys.justPressed.Y) - setStyle(1); - if (FlxG.keys.justPressed.H) - setStyle(-1); - - if (FlxG.keys.justPressed.U) - setLerp(.1); - if (FlxG.keys.justPressed.J) - setLerp(-.1); - - if (FlxG.keys.justPressed.I) - setLead(.5); - if (FlxG.keys.justPressed.K) - setLead(-.5); - - if (FlxG.keys.justPressed.O) - setZoom(FlxG.camera.zoom + .1); - if (FlxG.keys.justPressed.L) - setZoom(FlxG.camera.zoom - .1); - - if (FlxG.keys.justPressed.M) - FlxG.camera.shake(); - } - - function setLead(lead:Float) + + function setLead(delta:Float) { var cam = FlxG.camera; - cam.followLead.x += lead; - cam.followLead.y += lead; - + cam.followLead.x += delta; + cam.followLead.y += delta; + if (cam.followLead.x < 0) { cam.followLead.x = 0; cam.followLead.y = 0; } - + hud.updateCamLead(cam.followLead.x); } - - function setLerp(lerp:Float) + + function setLerp(delta:Float) { var cam = FlxG.camera; - cam.followLerp += lerp; + cam.followLerp += delta; cam.followLerp = Math.round(10 * cam.followLerp) / 10; // adding or subtracting .1 causes roundoff errors hud.updateCamLerp(cam.followLerp); } - - function setStyle(i:Int) + + function setStyle(delta:Int) { - var newCamStyleIndex:Int = Type.enumIndex(FlxG.camera.style) + i; - newCamStyleIndex < 0 ? newCamStyleIndex += 6 : newCamStyleIndex %= 6; - - var newCamStyle = Type.createEnumIndex(FlxCameraFollowStyle, newCamStyleIndex); - FlxG.camera.follow(orb, newCamStyle, FlxG.camera.followLerp); - drawDeadzone(); - - hud.updateStyle(Std.string(FlxG.camera.style)); - - if (FlxG.camera.style == SCREEN_BY_SCREEN) - { - setZoom(1); - } + final nextStyleIndex = (followStyles.indexOf(FlxG.camera.style) + delta) % followStyles.length; + FlxG.camera.follow(player, followStyles[nextStyleIndex], FlxG.camera.followLerp); + + deadzoneOverlay.redraw(FlxG.camera); + + hud.updateStyle(FlxG.camera.style); } } diff --git a/Features/FlxCamera/source/input/PlayerControls.hx b/Features/FlxCamera/source/input/PlayerControls.hx new file mode 100644 index 000000000..beb0696f8 --- /dev/null +++ b/Features/FlxCamera/source/input/PlayerControls.hx @@ -0,0 +1,123 @@ +package input; + +import flixel.FlxG; +import flixel.input.gamepad.FlxGamepadInputID; +import flixel.input.keyboard.FlxKey; +import flixel.ui.FlxVirtualPad; + +class PlayerControls +{ + /** + * Maps input types to their corresponding keyboard button + */ + static public var keyMap:Map> = + [ + Input.LEFT => [FlxKey.A, FlxKey.LEFT ], + Input.DOWN => [FlxKey.S, FlxKey.DOWN ], + Input.RIGHT => [FlxKey.D, FlxKey.RIGHT], + Input.UP => [FlxKey.W, FlxKey.UP ] + ]; + + #if FLX_GAMEPAD + /** + * Maps input types to their corresponding gamepad dpad button + */ + static public var buttonMap:Map = + [ + Input.LEFT => { dpad:DPAD_LEFT , analog:LEFT_STICK_DIGITAL_LEFT }, + Input.DOWN => { dpad:DPAD_DOWN , analog:LEFT_STICK_DIGITAL_DOWN }, + Input.RIGHT => { dpad:DPAD_RIGHT, analog:LEFT_STICK_DIGITAL_RIGHT }, + Input.UP => { dpad:DPAD_UP , analog:LEFT_STICK_DIGITAL_UP } + ]; + #end + + /** + * Reference to the gamepad controlling this orb + */ + public var virtualPad:VirtualPad = null; + + public function new() + { + // create a virtual pad to play on mobile devices + final useVirtualPad = #if html5 FlxG.html5.onMobile #elseif mobile true #else false #end; + if (useVirtualPad) + virtualPad = new VirtualPad(); + } + + public function isGamepadConnected() + { + #if FLX_GAMEPAD + return FlxG.gamepads.numActiveGamepads > 0; + #else + return false; + #end + } + + /** + * Helper to detect keyboard or virtual pad presses + */ + inline public function inputPressed(input:Input) + { + return keyPressed(input) || virtualPadPressed(input) || gamePadPressed(input); + } + + /** + * Helper to detect keyboard presses + */ + inline function keyPressed(input:Input) + { + return FlxG.keys.anyPressed(keyMap[input]); + } + + /** + * Helper to detect virtual pad presses + */ + inline function virtualPadPressed(input:Input) + { + return virtualPad != null && virtualPad.pressed(input); + } + + /** + * Helper to detect gamepad presses + */ + inline function gamePadPressed(input:Input) + { + #if FLX_GAMEPAD + final buttons = buttonMap[input]; + return FlxG.gamepads.anyPressed(buttons.dpad) || FlxG.gamepads.anyPressed(buttons.analog); + #else + return false; + #end + } +} + +/** + * Simplified virtual pad that takes an Input and returns whether the corresponding button is pressed + */ +abstract VirtualPad(FlxVirtualPad) from FlxVirtualPad to FlxVirtualPad +{ + inline public function new() + { + this = new FlxVirtualPad(FULL, NONE); + } + + public function pressed(input:Input) + { + return switch(input) + { + case Input.LEFT : this.buttonLeft.pressed; + case Input.RIGHT: this.buttonRight.pressed; + case Input.UP : this.buttonUp.pressed; + case Input.DOWN : this.buttonDown.pressed; + default: false; + } + } +} + +enum Input +{ + LEFT; + RIGHT; + UP; + DOWN; +} \ No newline at end of file diff --git a/Features/FlxCamera/source/props/BorderSlice.hx b/Features/FlxCamera/source/props/BorderSlice.hx new file mode 100644 index 000000000..5c59e4503 --- /dev/null +++ b/Features/FlxCamera/source/props/BorderSlice.hx @@ -0,0 +1,25 @@ +package props; + +import flixel.math.FlxRect; +import flixel.addons.display.FlxSliceSprite; + +@:forward +abstract BorderSlice(FlxSliceSprite) from FlxSliceSprite to FlxSliceSprite +{ + inline public function new (x = 0.0, y = 0.0, width:Float, height:Float) + { + this = new FlxSliceSprite("assets/BorderSlice.png", new FlxRect(15, 15, 20, 20), width, height); + this.x = x; + this.y = y; + // reduce vertice counts by stretching rather than tiling + this.fillCenter = false; + this.stretchBottom = true; + this.stretchTop = true; + this.stretchLeft = true; + this.stretchRight = true; + this.stretchCenter = true; + #if debug + this.ignoreDrawDebug = true; + #end + } +} \ No newline at end of file diff --git a/Features/FlxCamera/source/props/Orb.hx b/Features/FlxCamera/source/props/Orb.hx new file mode 100644 index 000000000..3f501c844 --- /dev/null +++ b/Features/FlxCamera/source/props/Orb.hx @@ -0,0 +1,77 @@ +package props; + +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.addons.nape.FlxNapeSprite; +import flixel.util.FlxDestroyUtil; + +/** + * @author TiagoLr ( ~~~ProG4mr~~~ ) + */ +class Orb extends FlxNapeSprite +{ + var shadow:FlxSprite; + + public function new(x = 0.0, y = 0.0, radius:Int, ?graphic, ?shadowGraphic) + { + super(x, y, null, false); + createCircularBody(radius); + body.allowRotation = false; + pixelPerfectPosition = false; + pixelPerfectRender = false; + + // create a shadow that follows the sprite around + shadow = new FlxSprite(x, y, shadowGraphic); + shadow.blend = MULTIPLY; + shadow.pixelPerfectPosition = false; + shadow.pixelPerfectRender = false; + #if debug + shadow.ignoreDrawDebug = true; + #end + + // call loadGraphic after the body is created so it adjusts the hitbox + if (graphic != null) + loadGraphic(graphic); + } + + override function loadGraphic(graphic, animated = false, frameWidth = 0, frameHeight = 0, unique = false, ?key:String) + { + super.loadGraphic(graphic, animated, frameWidth, frameHeight, unique, key); + + // adjust flixel hitbox to match the radius for tighter camera following + if (body != null && body.shapes != null) + { + width = body.bounds.width; + height = body.bounds.height; + centerOffsets(false); + origin.set(width / 2, height / 2); + + // same offset for shadowP + shadow.offset.copyFrom(offset); + } + + return this; + } + + override public function update(elapsed:Float):Void + { + super.update(elapsed); + + shadow.update(elapsed); + shadow.x = x; + shadow.y = y; + } + + override function draw() + { + // draw shadow first, so it's underneath + shadow.draw(); + super.draw(); + } + + override function destroy() + { + super.destroy(); + shadow = FlxDestroyUtil.destroy(shadow); + } +} diff --git a/Features/FlxCamera/source/props/OtherOrb.hx b/Features/FlxCamera/source/props/OtherOrb.hx new file mode 100644 index 000000000..22459afa3 --- /dev/null +++ b/Features/FlxCamera/source/props/OtherOrb.hx @@ -0,0 +1,39 @@ +package props; + +import flixel.FlxG; +import flixel.FlxSprite; +// import flixel.math.FlxMath; +import flixel.util.FlxDestroyUtil; +// import nape.geom.Vec2; + +class OtherOrb extends Orb +{ + public function new(x = 0.0, y = 0.0, colorIndex:Int) + { + super(x, y, 50, null, "assets/OtherOrbShadow.png"); + + loadGraphic("assets/OtherOrb.png", true, 140, 140); + animation.frameIndex = colorIndex; + setBodyMaterial(1, 0.0, 0.0, 0.5); + } + + /** + * Randomizes the position of this orb within the given bounds + */ + public function randomizePosition(minX = 0, maxX = 0, minY = 0, maxY = 0) + { + x = body.position.x = FlxG.random.int(minX, maxX - Math.ceil(width)); + y = body.position.y = FlxG.random.int(minY, maxY - Math.ceil(height)); + return this; + } + + /** + * Randomizes the velocity of this orb with the given absolute speed range and a random angle + */ + public function randomizeVelocity(min = 100, max = 200) + { + body.velocity.setxy(FlxG.random.float(min, max), 0); + body.velocity.angle = FlxG.random.float(0, 2 * Math.PI); + return this; + } +} \ No newline at end of file diff --git a/Features/FlxCamera/source/props/PlayerOrb.hx b/Features/FlxCamera/source/props/PlayerOrb.hx new file mode 100644 index 000000000..a2ae435a9 --- /dev/null +++ b/Features/FlxCamera/source/props/PlayerOrb.hx @@ -0,0 +1,59 @@ +package props; + +import flixel.FlxG; +import input.PlayerControls; +import nape.geom.Vec2; + +/** + * User controlled orb + */ +class PlayerOrb extends Orb +{ + /** + * The impulse applied each frame when pressing the corresponding key + */ + static inline var IMPULSE = 20; + + /** + * Used Internally to avoid creating new instances each frame + */ + static final impulseHelper = new Vec2(); + + public var controls:PlayerControls; + + public function new(x = 0.0, y = 0.0) + { + super(x, y, 18, "assets/Orb.png", "assets/OrbShadow.png"); + // small amount of drag + setDrag(0.98); + + controls = new PlayerControls(); + } + + override function update(elapsed:Float) + { + super.update(elapsed); + + // apply impusles to the body based on key presses + + if (controls.inputPressed(LEFT)) + applyImpulseXY(-IMPULSE, 0); + + if (controls.inputPressed(DOWN)) + applyImpulseXY(0, IMPULSE); + + if (controls.inputPressed(RIGHT)) + applyImpulseXY(IMPULSE, 0); + + if (controls.inputPressed(UP)) + applyImpulseXY(0, -IMPULSE); + } + + /** + * Helper to apply impulse via x and y floats to avoid creating new Vec2 instances each frame + */ + inline function applyImpulseXY(x:Float, y:Float) + { + body.applyImpulse(impulseHelper.setxy(x, y)); + } +} \ No newline at end of file diff --git a/Features/FlxCamera/source/ui/DeadzoneOverlay.hx b/Features/FlxCamera/source/ui/DeadzoneOverlay.hx new file mode 100644 index 000000000..909738dd9 --- /dev/null +++ b/Features/FlxCamera/source/ui/DeadzoneOverlay.hx @@ -0,0 +1,81 @@ +package ui; + +import flixel.FlxCamera; +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.math.FlxRect; +import flixel.util.FlxColor; + +using flixel.util.FlxSpriteUtil; + +/** + * Sprite used to draw a representation of the current follow styles. + * For most styles, it draws the deadzone. + */ +class DeadzoneOverlay extends FlxSprite +{ + public function new () + { + super(); + + scrollFactor.set(0, 0);// move with the camera + + #if debug + ignoreDrawDebug = true; + #end + } + + public function redraw(targetCamera:FlxCamera) + { + if (targetCamera.style == SCREEN_BY_SCREEN) + { + // just hide it, otherwise we'd need to redraw it with zoom changes + visible = false; + return; + } + visible = true; + + final lineLength = 12; + final padding = 2; + final thickness = 3 + padding; + final halfThickness = thickness / 2; + final lineStyle:LineStyle = {color: FlxColor.WHITE, thickness: thickness - padding}; + + if (targetCamera.style == NO_DEAD_ZONE) + { + // No deadzone, draw a simple crosshair in the center of the camera's view + final reticalSize = 20; + // pad the graphic a little, for thick lines + makeGraphic(reticalSize + thickness, reticalSize + thickness, FlxColor.TRANSPARENT, true); + x = (camera.width - frameWidth) / 2; + y = (camera.height - frameHeight) / 2; + + final centerX = frameWidth / 2; + final centerY = frameHeight / 2; + final reticalHalfSize = reticalSize / 2; + this.drawLine(centerX, centerY - reticalHalfSize, centerX, centerY + reticalHalfSize, lineStyle); + this.drawLine(centerX - reticalHalfSize, centerY, centerX + reticalHalfSize, centerY, lineStyle); + return; + } + + // draw the deadzone's corners + final dz:FlxRect = targetCamera.deadzone; + x = dz.x - halfThickness; + y = dz.y - halfThickness; + // pad the graphic a little, for thick lines + makeGraphic(Std.int(dz.width + thickness), Std.int(dz.height + thickness), FlxColor.TRANSPARENT, true); + + // Top-Left + this.drawLine(dz.left - x, dz.top - y, dz.left - x + lineLength, dz.top - y, lineStyle); + this.drawLine(dz.left - x, dz.top - y, dz.left - x, dz.top + lineLength - y, lineStyle); + // Top-Right + this.drawLine(dz.right - x, dz.top - y, dz.right - x - lineLength, dz.top - y, lineStyle); + this.drawLine(dz.right - x, dz.top - y, dz.right - x, dz.top + lineLength - y, lineStyle); + // Bottom-Left + this.drawLine(dz.left - x, dz.bottom - y, dz.left - x + lineLength, dz.bottom - y, lineStyle); + this.drawLine(dz.left - x, dz.bottom - y, dz.left - x, dz.bottom - lineLength - y, lineStyle); + // Bottom-Right + this.drawLine(dz.right - x, dz.bottom - y, dz.right - x - lineLength, dz.bottom - y, lineStyle); + this.drawLine(dz.right - x, dz.bottom - y, dz.right - x, dz.bottom - lineLength - y, lineStyle); + } +} \ No newline at end of file diff --git a/Features/FlxCamera/source/ui/HUD.hx b/Features/FlxCamera/source/ui/HUD.hx new file mode 100644 index 000000000..6e9862c39 --- /dev/null +++ b/Features/FlxCamera/source/ui/HUD.hx @@ -0,0 +1,97 @@ +package ui; + +import flixel.FlxCamera; +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.group.FlxGroup; +import flixel.text.FlxText; + +inline var WIDTH = 200; +inline var HEIGHT = 180; + +/** + * @author TiagoLr ( ~~~ProG4mr~~~ ) + */ +class HUD extends FlxGroup +{ + var txtStyle:Text; + var txtLerp:Text; + var txtLead:Text; + var txtZoom:Text; + + public function new() + { + super(); + + var left = 6; + var startY = 10; + + add(new Text(left, startY, "[W,A,S,D] or arrows to control the orb.")); + + add(new Text(left, startY + 20, "[H] or [Y] to change follow style.")); + add(txtStyle = new GreenText(left, startY + 33, "LOCKON")); + + add(new Text(left, startY + 55, "[U] or [J] to change lerp.")); + add(txtLerp = new GreenText(left, startY + 68, "Camera lerp: 1")); + + add(new Text(left, startY + 95, "[I] or [K] to change lead.")); + add(txtLead = new GreenText(left, startY + 108, "Camera lead: 0")); + + add(new Text(left, startY + 135, "[O] or [L] to change zoom.")); + add(txtZoom = new GreenText(left, startY + 148, "Camera zoom: 1")); + + // create new camera in the top-right corner that only draws this + camera = new FlxCamera(440, 0, WIDTH, HEIGHT, 1.0); + camera.alpha = .5; + camera.bgColor = 0x80000000; + FlxG.cameras.add(camera, false); + } + + public function updateStyle(style:FlxCameraFollowStyle) + { + txtStyle.text = Std.string(style); + } + + public function updateCamLerp(lerp:Float) + { + txtLerp.text = "Camera lerp: " + lerp; + } + + public function updateCamLead(lead:Float) + { + txtLead.text = "Camera lead: " + lead; + } + + public function updateZoom(zoom:Float) + { + txtZoom.text = "Camera Zoom: " + Math.floor(zoom * 10) / 10; + } +} + +/** + * A simplified, specialized FlxText instance, mainly used to omit the fieldWidth arg + */ +@:forward +abstract Text(FlxText) from FlxText to FlxText +{ + inline public function new (x = 0.0, y = 0.0, ?text:String, size = 8) + { + this = new FlxText(x, y, WIDTH, text, size); + #if debug + this.ignoreDrawDebug = true; + #end + } +} + +/** + * An even more specialized version of Text used to highlight the changing camera values + */ +@:forward +abstract GreenText(Text) from Text to Text +{ + inline public function new (x = 0.0, y = 0.0, ?text:String) + { + this = new Text(x, y, text); + this.setFormat(null, 11, 0x55FF55); + } +}