This is old! I think it probably works, but there's a new, better way to integrate Echo with Flixel!
Check it out: echo-flixel
This is old! I think it probably works, but there's a new, better way to integrate Echo with Flixel!
Check it out: echo-flixel
package util; | |
import echo.data.Options.BodyOptions; | |
import flixel.FlxBasic; | |
import flixel.FlxObject; | |
import flixel.FlxObject.*; | |
import flixel.group.FlxGroup; | |
import echo.Echo; | |
import echo.World; | |
import echo.Body; | |
import echo.data.Options.ListenerOptions; | |
import echo.data.Options.WorldOptions; | |
using hxmath.math.Vector2; | |
class FlxEcho { | |
static var groups:Map<FlxGroup, Array<Body>>; | |
static var bodies:Map<FlxObject, Body>; | |
static var world:World; | |
/** | |
* Init the world | |
*/ | |
public static function init(options:WorldOptions) { | |
groups = []; | |
bodies = []; | |
world = Echo.start(options); | |
} | |
/** | |
* add physics body to FlxObject | |
*/ | |
public static function add_body(object:FlxObject, ?options:BodyOptions) { | |
if (options == null) options = {}; | |
if (options.x == null) options.x = object.x; | |
if (options.y == null) options.y = object.y; | |
if (options.shape == null) options.shape = { | |
type: RECT, | |
width: object.width, | |
height: object.height, | |
offset_x: object.width/2, | |
offset_y: object.height/2 | |
} | |
var body = new Body(options); | |
bodies.set(object, body); | |
world.add(body); | |
} | |
/** | |
* Adds FlxObject to FlxGroup, and the FlxObject's associated physics body to the FlxGroup's associated physics group | |
*/ | |
public static function add_to_group(object:FlxObject, group:FlxGroup) { | |
group.add(object); | |
if (!groups.exists(group)) groups.set(group, []); | |
if (bodies.exists(object)) groups[group].push(bodies[object]); | |
} | |
/** | |
* Creates a physics listener | |
*/ | |
public static function listen(a:FlxBasic, b:FlxBasic, ?options:ListenerOptions) { | |
if (options == null) options = {}; | |
var temp_stay = options.stay; | |
options.stay = (a, b, c) -> { | |
if (temp_stay != null) temp_stay(a, b, c); | |
for (col in c) set_touching(get_object(a), [CEILING, WALL, FLOOR][col.normal.dot(Vector2.yAxis).round() + 1]); | |
} | |
#if ARCADE_PHYSICS | |
var temp_condition = options.condition; | |
options.condition = (a, b, c) -> { | |
for (col in c) square_normal(col.normal); | |
if (temp_condition != null) return temp_condition(a, b, c); | |
return true; | |
} | |
#end | |
world.listen(!a.is(FlxObject) ? groups[cast a] : bodies[cast a], !b.is(FlxObject) ? groups[cast b] : bodies[cast b], options); | |
} | |
/** | |
* Update physics and objects with physics bodies | |
*/ | |
public static function update(elapsed:Float) { | |
world.step(elapsed); | |
for (object => body in bodies) update_body_object(object, body); | |
} | |
/** | |
* Get the physics body associated with a FlxObject | |
*/ | |
public static function get_body(object:FlxObject):Body return bodies[object]; | |
/** | |
* Get the FlxObject associated with a physics body | |
*/ | |
public static function get_object(body:Body):FlxObject { | |
for (o => b in bodies) if (b == body) return o; | |
return null; | |
} | |
static function update_body_object(object:FlxObject, body:Body) { | |
object.setPosition(body.x, body.y); | |
object.angle = body.rotation; | |
} | |
static function set_touching(object:FlxObject, touching:Int) if (object.touching & touching == 0) object.touching += touching; | |
static function square_normal(normal:Vector2) { | |
var len = normal.length; | |
var dot_x = normal.dot(Vector2.xAxis); | |
var dot_y = normal.dot(Vector2.yAxis); | |
if (dot_x.abs() > dot_y.abs()) dot_x > 0 ? normal.set(1, 0) : normal.set(-1, 0); | |
else dot_y > 0 ? normal.set(0, 1) : normal.set(0, -1); | |
normal.normalizeTo(len); | |
} | |
} |
package states; | |
import flixel.FlxState; | |
import flixel.FlxSprite; | |
import flixel.FlxObject.*; | |
import flixel.group.FlxGroup; | |
import flixel.tweens.FlxEase; | |
import flixel.tweens.FlxTween; | |
using util.FlxEcho; | |
using hxmath.math.Vector2; | |
using flixel.util.FlxSpriteUtil; | |
class PlayState extends FlxState | |
{ | |
var player:Box; | |
var level_data = [ | |
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], | |
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], | |
[1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], | |
[1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], | |
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], | |
[1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1], | |
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], | |
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1], | |
[1, 0, 0, 1, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1], | |
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], | |
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], | |
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], | |
[1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1], | |
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], | |
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], | |
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], | |
]; | |
override function create() { | |
// First thing we want to do before creating any physics objects is init() our Echo world. | |
FlxEcho.init({ | |
width: level_data[0].length * 16, // Make the size of your Echo world equal the size of your play field | |
height: level_data.length * 16, | |
gravity_y: 800 | |
}); | |
// Normal, every day FlxGroups! | |
var terrain = new FlxGroup(); | |
add(terrain); | |
var bouncers = new FlxGroup(); | |
add(bouncers); | |
// We'll step through our level data and add objects that way | |
for (j in 0...level_data.length) for (i in 0...level_data[j].length) { | |
switch (level_data[j][i]) { | |
case 1: | |
// Just a regular old terrain block | |
var bluebox = new Box(i * 16, j * 16, 16, 16, 0xFF0080FF); | |
bluebox.add_body({ mass: 0 }); // We'll pass in body options with mass set to 0 so that it's static | |
bluebox.add_to_group(terrain); // Instead of `group.add(object)` we use `object.add_to_group(group)` | |
case 2: | |
// Orange boxes will act like springs! | |
var orangebox = new Box(i * 16, j * 16, 16, 16, 0xFFFF8000); | |
orangebox.origin.set(8, 16); // We'll set the origin here so that we can animate our orange block later | |
orangebox.add_body({ mass: 0 }); | |
orangebox.add_to_group(bouncers); | |
case 3: | |
player = new Box(i * 16, j * 16, 8, 12, 0xFFFF004D, true); | |
player.add_body(); | |
add(player); | |
default: continue; | |
} | |
} | |
// lets add some ramps too! They'll belong to the same collision group as the blue boxes we made earlier. | |
for (i in 0...8) { | |
var ramp = new Ramp(16, 112 + i * 16, 16 + i * 16, 128 - i * 16, NW); | |
ramp.add_to_group(terrain); | |
} | |
// Our first physics listener collides our player with the terrain group. | |
player.listen(terrain); | |
// Our second physics listener collides our player with the bouncers group. | |
player.listen(bouncers, { | |
// We'll add this listener option - every frame our player object is colliding with a bouncer in the bouncers group we'll run this function | |
stay: (a, b, c) -> { // where a is our first physics body (`player`), b is the physics body it's colliding with (`orangebox`), and c is an array of collision data. | |
// for every instance of collision data | |
for (col in c) { | |
// This checks to see if the normal of our collision is pointing downward - you could use it for hop and bop games to see if a player has stomped on an enemy! | |
if (col.normal.dot(Vector2.yAxis).round() == 1) { | |
// set the player's velocity to go up! | |
a.velocity.y = -400; | |
// animate the orange box! | |
var b_object:FlxSprite = cast b.get_object(); | |
b_object.scale.y = 1.5; | |
FlxTween.tween(b_object.scale, { y: 1 }, 0.5, { ease: FlxEase.elasticOut }); | |
} | |
} | |
} | |
}); | |
} | |
override function update(e:Float) { | |
// Make sure to call `FlxEcho.update()` before `super.update()`! | |
FlxEcho.update(e); | |
super.update(e); | |
} | |
} | |
class Box extends FlxSprite { | |
var control:Bool; | |
public function new(x:Float, y:Float, w:Int, h:Int, c:Int, control:Bool = false) { | |
super(x, y); | |
makeGraphic(w, h, c); | |
this.control = control; | |
} | |
override function update(elapsed:Float) { | |
if (control) controls(); | |
super.update(elapsed); | |
} | |
function controls() { | |
var body = this.get_body(); | |
body.velocity.x = 0; | |
if (FlxG.keys.pressed.LEFT) body.velocity.x -= 128; | |
if (FlxG.keys.pressed.RIGHT) body.velocity.x += 128; | |
if (FlxG.keys.justPressed.UP && isTouching(FLOOR)) body.velocity.y -= 256; | |
} | |
} | |
class Ramp extends FlxSprite { | |
public function new(x:Float, y:Float, w:Int, h:Int, d:RampDirection) { | |
trace('$x / $y / $w / $h'); | |
super(x, y); | |
makeGraphic(w, h, 0x00FFFFFF); | |
var verts = [ [0, 0], [w, 0], [w, h], [0, h] ]; | |
switch d { | |
case NE: verts.splice(0, 1); | |
case NW: verts.splice(1, 1); | |
case SE: verts.splice(3, 1); | |
case SW: verts.splice(2, 1); | |
} | |
this.drawPolygon([ for (v in verts) FlxPoint.get(v[0], v[1]) ], 0xFFFF0080); | |
this.add_body({ | |
mass: 0, | |
shape: { | |
type: POLYGON, | |
vertices: [ for (v in verts) new Vector2(v[0], v[1]) ], | |
} | |
}); | |
} | |
} | |
enum RampDirection { | |
NE; | |
NW; | |
SE; | |
SW; | |
} |
I'd like to use Echo with HaxeFlixel in a project so I tried to get this working.
I created a new HaxeFlixel project, added a folder "states" and a folder "util" in my project directory.
I put PlayState.hx in states, FlxEcho.hx in util. Both files are identical to these 2 files in this gist.
I added in my Project.xml.In FlxEcho at line 67 I'm getting the error
Float has no field round
coming fromdot(Vector2.yAxis).round()
In FlxEcho.hx at line 77 I'm getting the errorflixel.FlxBasic has no field is
coming froma.is
andb.is
In FlxEcho at line 112 I'm getting the errorFloat has no field abs
coming fromdot_x.abs()
In PlayState at line 89 I'm getting the errorFloat has no field round
coming fromdot(Vector2.yAxis).round()
I apologize if the solution is straighforward but I cannot see it, thanks in advance for any help!
No problem! I think I probably had an import.hx
with some things in it. In FlxEcho
add:
using Math;
using Std;
and in PlayState
add:
using Math;
and it should get rid of those errors!
I'd like to use Echo with HaxeFlixel in a project so I tried to get this working.
I created a new HaxeFlixel project, added a folder "states" and a folder "util" in my project directory.
I put PlayState.hx in states, FlxEcho.hx in util. Both files are identical to these 2 files in this gist.
I added in my Project.xml.
In FlxEcho at line 67 I'm getting the errorFloat has no field round
coming fromdot(Vector2.yAxis).round()
In FlxEcho.hx at line 77 I'm getting the errorflixel.FlxBasic has no field is
coming froma.is
andb.is
In FlxEcho at line 112 I'm getting the errorFloat has no field abs
coming fromdot_x.abs()
In PlayState at line 89 I'm getting the errorFloat has no field round
coming fromdot(Vector2.yAxis).round()
I apologize if the solution is straighforward but I cannot see it, thanks in advance for any help!No problem! I think I probably had an
import.hx
with some things in it. InFlxEcho
add:using Math; using Std;and in
PlayState
add:using Math;and it should get rid of those errors!
Worked like a charm, thanks a lot for the quick reply!
First, thanks a lot for this piece of code, good work! I'd like to add, though that it has a rather nasty physics glitch still, namely if you jump in a corner and keep pressing towards the wall the "player" will stick to the wall, unaffected by gravity as in picture below. Still, goof work 👍
Yeah! So Echo can help with that too! Instead of just making the blue boxes individual bodies, you'd create a TileMap which iirc simplifies individual "boxes" into bigger rectangles!
I'm actually experimenting with this rn. Thanks for the heads up!
Edit: yup, tilemap util walks flawlessly with collisions.
I'd like to use Echo with HaxeFlixel in a project so I tried to get this working.
I created a new HaxeFlixel project, added a folder "states" and a folder "util" in my project directory.
I put PlayState.hx in states, FlxEcho.hx in util. Both files are identical to these 2 files in this gist.
I added in my Project.xml.
In FlxEcho at line 67 I'm getting the error
Float has no field round
coming fromdot(Vector2.yAxis).round()
In FlxEcho.hx at line 77 I'm getting the error
flixel.FlxBasic has no field is
coming froma.is
andb.is
In FlxEcho at line 112 I'm getting the error
Float has no field abs
coming fromdot_x.abs()
In PlayState at line 89 I'm getting the error
Float has no field round
coming fromdot(Vector2.yAxis).round()
I apologize if the solution is straighforward but I cannot see it, thanks in advance for any help!