Skip to content

Commit

Permalink
recreate the old effect using shaders (#317)
Browse files Browse the repository at this point in the history
* recreate the old effect using shaders

* blue tint on shadows
  • Loading branch information
Geokureli authored Aug 8, 2023
1 parent 56529d3 commit 39bc43e
Show file tree
Hide file tree
Showing 5 changed files with 357 additions and 121 deletions.
3 changes: 2 additions & 1 deletion Effects/DynamicShadows/source/Main.hx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Main extends Sprite
public function new()
{
super();
addChild(new FlxGame(640, 320, PlayState));

addChild(new FlxGame(640, 320, #if flash PlayStateFlash #else PlayStateShader #end));
}
}
146 changes: 26 additions & 120 deletions Effects/DynamicShadows/source/PlayState.hx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package;

import flixel.FlxCamera;
import flixel.addons.nape.FlxNapeSpace;
import flixel.addons.nape.FlxNapeTilemap;
import flixel.FlxG;
Expand All @@ -19,7 +20,7 @@ using flixel.util.FlxSpriteUtil;

/**
* This was based on a guide from this forum post: http://forums.tigsource.com/index.php?topic=8803.0
* Ported to HaxeFlixel by Xerosugar
* Ported to HaxeFlixel by Xerosugar, then converted to use Shaders by GeoKureli
*
* If you're feeling up the challenge, here's how YOU help can improve this demo:
* - Make it possible to extends the shadows to the edge of the screen
Expand All @@ -35,9 +36,6 @@ class PlayState extends FlxState
{
public static inline var TILE_SIZE:Int = 16;

static inline var SHADOW_COLOR = 0xff2a2963;
static inline var OVERLAY_COLOR = 0xff887fff;

/**
* Only contains non-collidabe tiles
*/
Expand All @@ -47,76 +45,62 @@ class PlayState extends FlxState
* The layer into which the actual "level" will be drawn, and also the one objects will collide with
*/
var foreground:FlxNapeTilemap;

/**
* The sprite that shadows will be drawn to
* Anything you want to show above the shadows
*/
var shadowCanvas:FlxSprite;

/**
* The sprite that the actual darkness and the gem's flare-like effect will be drawn to
*/
var shadowOverlay:FlxSprite;

var uiCam:FlxCamera;

/**
* The light source!
*/
var gem:Gem;

/**
* If there's a small gap between something (could be two tiles,
* even if they're right next to each other), this should cover it up for us
*/
var lineStyle:LineStyle = {color: SHADOW_COLOR, thickness: 1};

var infoText:FlxText;
var fps:FPS;

override public function create():Void
{
super.create();

FlxG.camera.bgColor = 0x5a81ad;

FlxNapeSpace.init();
FlxNapeSpace.space.gravity.setxy(0, 1200);
FlxNapeSpace.drawDebug = false; // You can toggle this on/off one by pressing 'D'

var background:FlxTilemap = new FlxTilemap();
background = new FlxTilemap();
background.loadMapFromCSV("assets/data/background.txt", "assets/images/tiles.png", TILE_SIZE, TILE_SIZE, null, 1, 1);
add(background);

// Note: The tilemap used in this demo was drawn with 'Tiled' (http://www.mapeditor.org/),
// but the level data was picked from the .tmx file and put into two separate
// .txt files for simplicity. If you wish to learn how to use Tiled with your project,
// have a look at this demo: http://haxeflixel.com/demos/TiledEditor/

// If we add the shadows *before* all of the foreground elements (stage included)
// they will only cover the background, which is usually what you'd want I'd guess :)
shadowCanvas = new FlxSprite();
shadowCanvas.blend = BlendMode.MULTIPLY;
shadowCanvas.makeGraphic(FlxG.width, FlxG.height, FlxColor.TRANSPARENT, true);
add(shadowCanvas);

foreground = new FlxNapeTilemap();
foreground.loadMapFromCSV("assets/data/foreground.txt", "assets/images/tiles.png", TILE_SIZE, TILE_SIZE, null, 1, 1);
add(foreground);

foreground.setupTileIndices([4]);
createProps();

shadowOverlay = new FlxSprite();
shadowOverlay.makeGraphic(FlxG.width, FlxG.height, FlxColor.TRANSPARENT, true);
shadowOverlay.blend = BlendMode.MULTIPLY;
add(shadowOverlay);

infoText = new FlxText(10, 10, 100, "");
add(infoText);

// This here is only used to get the current FPS in a simple way, without having to run the application in Debug mode
fps = new FPS(10, 10, 0xffffff);
FlxG.stage.addChild(fps);
fps.visible = false;

createCams();
}

function createCams()
{
// FlxG.camera draws the actual world. In this case, that means everything except infoText
FlxG.camera.bgColor = 0x5a81ad;
gem.camera = FlxG.camera;
background.camera = FlxG.camera;

// draws anything above the sahdows, in this case infoText
uiCam = new FlxCamera(0, 0, FlxG.width, FlxG.height);
FlxG.cameras.add(uiCam, false);
uiCam.bgColor = 0x0;
infoText.camera = uiCam;
}

function createProps():Void
Expand Down Expand Up @@ -154,97 +138,19 @@ class PlayState extends FlxState

override public function update(elapsed:Float):Void
{
super.update(elapsed);

infoText.text = "FPS: " + fps.currentFPS + "\n\nObjects can be dragged/thrown around.\n\nPress 'R' to restart.";

if (FlxG.keys.justPressed.R)
FlxG.resetState();

if (FlxG.keys.justPressed.D)
FlxNapeSpace.drawDebug = !FlxNapeSpace.drawDebug;

processShadows();
super.update(elapsed);
}

public function processShadows():Void
{
shadowCanvas.fill(FlxColor.TRANSPARENT);
shadowOverlay.fill(OVERLAY_COLOR);

shadowOverlay.drawCircle( // outer red circle
gem.body.position.x + FlxG.random.float(-.6, .6), gem.body.position.y + FlxG.random.float(-.6, .6),
(FlxG.random.bool(5) ? 16 : 16.5), 0xffff5f5f);

shadowOverlay.drawCircle( // inner red circle
gem.body.position.x + FlxG.random.float(-.25, .25), gem.body.position.y + FlxG.random.float(-.25, .25),
(FlxG.random.bool(5) ? 13 : 13.5), 0xffff7070);

for (body in FlxNapeSpace.space.bodies)
{
// We don't want to draw any shadows around the gem, since it's the light source
if (body.userData.type != "Gem")
processBodyShapes(body);
}
}

function processBodyShapes(body:Body)
{
for (shape in body.shapes)
{
var verts:Vec2List = shape.castPolygon.worldVerts;

for (i in 0...verts.length)
{
var startVertex:Vec2 = (i == 0) ? verts.at(verts.length - 1) : verts.at(i - 1);
processShapeVertex(startVertex, verts.at(i));
}
}
}

function processShapeVertex(startVertex:Vec2, endVertex:Vec2):Void
{
var tempLightOrigin:Vec2 = Vec2.get(gem.body.position.x + FlxG.random.float(-.3, 3), gem.body.position.y + FlxG.random.float(-.3, .3));

if (doesEdgeCastShadow(startVertex, endVertex, tempLightOrigin))
{
var projectedPoint:Vec2 = projectPoint(startVertex, tempLightOrigin);
var prevProjectedPt:Vec2 = projectPoint(endVertex, tempLightOrigin);
var vts:Array<FlxPoint> = [
FlxPoint.weak(startVertex.x, startVertex.y),
FlxPoint.weak(projectedPoint.x, projectedPoint.y),
FlxPoint.weak(prevProjectedPt.x, prevProjectedPt.y),
FlxPoint.weak(endVertex.x, endVertex.y)
];

shadowCanvas.drawPolygon(vts, SHADOW_COLOR, lineStyle);
}
}

function projectPoint(point:Vec2, light:Vec2):Vec2
{
var lightToPoint:Vec2 = point.copy();
lightToPoint.subeq(light);

var projectedPoint:Vec2 = point.copy();
return projectedPoint.addeq(lightToPoint.muleq(.45));
}

function doesEdgeCastShadow(start:Vec2, end:Vec2, light:Vec2):Bool
{
var startToEnd:Vec2 = end.copy();
startToEnd.subeq(start);

var normal:Vec2 = new Vec2(startToEnd.y, -1 * startToEnd.x);

var lightToStart:Vec2 = start.copy();
lightToStart.subeq(light);

return normal.dot(lightToStart) > 0;
}
}

@:enum
abstract Prop(Int) to Int
enum abstract Prop(Int) to Int
{
var BARREL = 5;
var GEM = 6;
Expand Down
Loading

0 comments on commit 39bc43e

Please sign in to comment.