flixel
flixel copied to clipboard
Multiple overlap between two objects
flixel ver 3.3.5.
I met with strange (for me) behavior of FlxG.overlap function. If this is normal, please don't beat me :)
I call FlxG.overlap function with such type of objects: FlxObject (hero sprite), FlxGroup (contains triggers) - FlxG.overlap(hero, _Triggers, onTriggerOverlap);
So, when hero overlaps trigger (no other triggers overlapping with hero at the same time), the callback function for that trigger executes a few times - one or two or, sometimes, three-four times at one FlxG.overlap call.
After debugging I find out that trigger is adding to few ListTrees (north, south, ...) and thus when next tree call execute(), it again finds overlapping between hero and same trigger again. And it again calls callback function for trigger.
Is it normal behavior?
Demo here: https://dl.dropboxusercontent.com/u/18869478/flxOverlap/Tower.swf Source here: https://dl.dropboxusercontent.com/u/18869478/flxOverlap/flxOverlap.zip
I've reduced this to the bare minimum, not really sure what's going on yet...
package;
import flixel.FlxState;
import flixel.group.FlxGroup;
import flixel.FlxObject;
import flixel.FlxG;
class PlayState extends FlxState
{
var player = new Player();
var group = new FlxGroup();
var overlaps = 0;
override public function create()
{
var x = [300, 320];
var y = [100, 180];
for (i in 0...2)
{
group.add(new FlxObject(0, 0, x[i], y[i]));
}
add(group);
add(player);
FlxG.signals.preUpdate.add(function()
{
overlaps = 0;
});
FlxG.watch.add(this, "overlaps");
FlxG.debugger.visible = true;
FlxG.debugger.drawDebug = true;
}
override public function update()
{
super.update();
FlxG.overlap(player, group, function(_, _)
{
overlaps++;
});
}
}
class Player extends FlxObject
{
public function new()
{
super(50, 50, 50, 50);
}
override public function update()
{
super.update();
velocity.set();
if (FlxG.keys.pressed.A) velocity.x -= 300;
if (FlxG.keys.pressed.D) velocity.x += 300;
if (FlxG.keys.pressed.W) velocity.y -= 300;
if (FlxG.keys.pressed.S) velocity.y += 300;
}
}
So, do you confirm that it's not a normal behavior?
There definitely shouldn't be 8 overlap callback calls when 3 objects are overlapping... And I can't really see any obvious issues with this code. So, yeah, something strange is going on (or I'm missing something).
I did some digging about this, and I think I found something that could help with solving this. I'm not sure I understand Quad-Trees exactly, so this can be all wrong, but I think the problem lies in the fact that if two of the same objects are in more than one quadrant, then when you call the overlapNode() on the parent-node, both branch-node will execute the callback function. To demonstrate this, I've made a little change in the example above:
- I've made the FlxG.worldDivisions variable changeable with the keyboard.
- I've also added lines to show where each quadrant should lie on-screen.
package;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.group.FlxGroup;
import flixel.FlxObject;
import flixel.FlxG;
class PlayState extends FlxState
{
var player = new Player();
var group = new FlxGroup();
var lines = new FlxGroup();
var overlaps = 0;
override public function create()
{
FlxG.debugger.visible = true;
var x = [300, 320];
var y = [100, 180];
for (i in 0...2)
{
group.add(new FlxObject(0, 0, x[i], y[i]));
}
add(group);
add(player);
add(lines);
FlxG.signals.preUpdate.add(function()
{
overlaps = 0;
});
FlxG.watch.add(this, "overlaps");
FlxG.watch.add(FlxG, "worldDivisions");
FlxG.debugger.drawDebug = true;
}
override public function update()
{
super.update();
var changed = true;
if (FlxG.keys.justPressed.O)
FlxG.worldDivisions++;
else if (FlxG.keys.justPressed.L)
FlxG.worldDivisions--;
else
changed = false;
if (changed)
{
lines.callAll("destroy");
lines.clear();
for (i in 0...Std.int(Math.pow(FlxG.worldDivisions,2)))
{
var sprite = new FlxSprite(0, (i + 1) * FlxG.height / (Math.pow(FlxG.worldDivisions,2)));
sprite.makeGraphic(FlxG.width, 2, 0x0);
lines.add(sprite);
sprite = new FlxSprite((i + 1) * FlxG.width / (Math.pow(FlxG.worldDivisions,2)), 0);
sprite.makeGraphic(2, FlxG.height,0x0);
lines.add(sprite);
}
}
FlxG.overlap(player, group, function(_, _)
{
overlaps++;
});
}
}
class Player extends FlxObject
{
public function new()
{
super(50, 50, 50, 50);
}
override public function update()
{
super.update();
velocity.set();
if (FlxG.keys.pressed.A) velocity.x -= 300;
if (FlxG.keys.pressed.D) velocity.x += 300;
if (FlxG.keys.pressed.W) velocity.y -= 300;
if (FlxG.keys.pressed.S) velocity.y += 300;
}
}
If you start testing, you can see, that at worldDivisions = 1, the overlaps works as expected. The problem can be seen the best when you change the worldDivisions to 2, and start moving the player. The number of overlaps changes almost exactly where the lines are, suggesting that both quadrants call the callback function. At higher levels, this doesn't always work (although when the numbers change, it's always at a line), so maybe I'm completely in the wrong here.
As for fixes, I couldn't find one that wouldn't break everything else. In this simple case changing overlapNode() to only call recursively the next quadrant if it didn't find the overlap on the previous was enough, but that, as expected, broke everything else, like the collision and grouping demo, where the boxes would sometimes collide, but go through the floor most of the time:
if ((_northWestTree != null) && _northWestTree.execute())
{
overlapProcessed = true;
}
else if ((_northEastTree != null) && _northEastTree.execute())
{
overlapProcessed = true;
}
else if ((_southEastTree != null) && _southEastTree.execute())
{
overlapProcessed = true;
}
else if ((_southWestTree != null) && _southWestTree.execute())
{
overlapProcessed = true;
}
So what do you guys think? Hope this helped somewhat.
I think one needs to check "overlap" function in original Flixel (AS3). May be it works the same "wrong" way. Or may be not
I think one needs to check "overlap" function in original Flixel (AS3). May be it works the same "wrong" way. Or may be not
It seems like the FlxQuadTree source is literally copy pasted from the as3 with haxe syntax changes.
I think the only way to get around this is to keep a record of which objects have already been overlapped.
There were some minor changes though. Might be worth checking if this example produces the same result in AS3.
I can confirm it's the same. I won't include the code, since it's basically the same, but if you need it, I can post that as well.
http://www.fastswf.com/wT_rW4I
After that, I did a search on the flixel forums, and found that many people have had this problem, but no good solution was found.
http://forums.flixel.org/index.php?topic=7090.0
Okay, after fiddling around a little, I've modified FlxQuadTree so that it only calls the functions once for every overlapping object pair. I've added two arrays to it to make everything work, so that could increase the memory usage a little. While testing, I haven't seen any difference, except the issue gone. Should I upload it, is there any chance that this could be a useful edit? (Since no one have really noticed this issue until now.) If it is, I will make it a little more memory efficient, then post it here.
Just post it or make a pull request and everyone can check it out and make suggestions. It'll either be a clever solution or a brute-force Map or Array check.
Okay, here's the first version of it: http://pastebin.com/mKagqzB6
The main differences: -Overlaps are now checked during the load function instead of the execute function, if the loaded object goes to the A list. -For this reason, I first add the objects of B group to the list -If an overlap has been checked in one quadrant, it won't check ever again For this, I'm using the _checked array, which gets reset every time a new object gets added to the A list. This contains every object that has been checked against the specific _object. -If an overlap has been found, the two object gets added to the _overlappedArray. -In execute, I iterate through this array, and call the functions.
Although this is more of a poc than a real solution, since it creates a big array at every collide(), and there's no comment in my part of the code. I will post a pull request later with a better version.
(Edited for pastebin)
Please make a pull request directly through Github.
Also, if you're going to paste a lot of code like this, it is much better to use a service like http://pastebin.com/ or http://hastebin.com/about.md
Regards,
Tiago Ling Alexandre Tel: +55 41 8819-3191
Yeah, please use a pull request to address this issue: https://help.github.com/articles/using-pull-requests
I've made the pull request : https://github.com/HaxeFlixel/flixel/pull/1267
I think I ran into this, here is a very simplified use case with two sprites:
package;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxState;
class PlayState extends FlxState
{
var obj1:FlxSprite;
var obj2:FlxSprite;
var frameCount:Int = 0;
var overlapCount:Int = 0;
override public function create():Void
{
super.create();
obj1 = new FlxSprite(1, 302);
obj1.makeGraphic(1, 1);
obj2 = new FlxSprite(1, 302);
obj2.makeGraphic(1, 1);
add(obj2);
add(obj1);
}
override public function update(elapsed:Float):Void
{
frameCount++;
FlxG.overlap(obj2, obj1, overlapCheck);
super.update(elapsed);
}
public function overlapCheck(pobj2:FlxSprite, pobj1:FlxSprite)
{
overlapCount++;
if (frameCount < 10)
{
trace("frameCount: " + frameCount + " overlapCount: " + overlapCount);
}
}
}
Output:
source/PlayState.hx:40: frameCount: 1 overlapCount: 1
source/PlayState.hx:40: frameCount: 1 overlapCount: 2
source/PlayState.hx:40: frameCount: 2 overlapCount: 3
source/PlayState.hx:40: frameCount: 2 overlapCount: 4
source/PlayState.hx:40: frameCount: 3 overlapCount: 5
source/PlayState.hx:40: frameCount: 3 overlapCount: 6
source/PlayState.hx:40: frameCount: 4 overlapCount: 7
source/PlayState.hx:40: frameCount: 4 overlapCount: 8
source/PlayState.hx:40: frameCount: 5 overlapCount: 9
source/PlayState.hx:40: frameCount: 5 overlapCount: 10
source/PlayState.hx:40: frameCount: 6 overlapCount: 11
source/PlayState.hx:40: frameCount: 6 overlapCount: 12
source/PlayState.hx:40: frameCount: 7 overlapCount: 13
source/PlayState.hx:40: frameCount: 7 overlapCount: 14
source/PlayState.hx:40: frameCount: 8 overlapCount: 15
source/PlayState.hx:40: frameCount: 8 overlapCount: 16
source/PlayState.hx:40: frameCount: 9 overlapCount: 17
source/PlayState.hx:40: frameCount: 9 overlapCount: 18
Expected Output: (you get this if y != 302)
source/PlayState.hx:40: frameCount: 1 overlapCount: 1
source/PlayState.hx:40: frameCount: 2 overlapCount: 2
source/PlayState.hx:40: frameCount: 3 overlapCount: 3
source/PlayState.hx:40: frameCount: 4 overlapCount: 4
source/PlayState.hx:40: frameCount: 5 overlapCount: 5
source/PlayState.hx:40: frameCount: 6 overlapCount: 6
source/PlayState.hx:40: frameCount: 7 overlapCount: 7
source/PlayState.hx:40: frameCount: 8 overlapCount: 8
source/PlayState.hx:40: frameCount: 9 overlapCount: 9
In the case I ran into this, I had a trigger checking if a key had been just been pressed, and having a callback call twice introduced some bugs. I was able to work around it by keep tracking of the frame number though and checking against it. Maybe that could be a quick fix to prevent a callback from being called more than once in a frame (though this assumes a function is being uniquely called back and not reused in other overlap callback functions)?