Body Removal Help

Dec 3, 2007 at 9:45 PM
Hi,

I'm brand new to XNA and Farseer, and so far they've both been amazing. So thank you all for your contributions to this community :)

I've having a really strange issue removing bodies & geometries from the physics simulation and I just don't understand what's going on. Allow me to paint a basic picture of the situation. I'm working with 3 main classes: Player (a game component), EnemyManager (also a game component), and Enemy. I create a PhysicalSimulation object in my Game1.cs class, and that object gets passed to the player, the enemy manager, and every enemy that's created by the enemy manager.

The problem is this: EnemyManager has a reset/generate method that removes all current enemies from the game and replaces them with a new set (used when transitioning to a new level). But when I remove a set of enemies, not only do I get a nasty memory leak, but it messes with the reference to the physicalSimulation somehow because all future enemies no longer collide with the player. This thing is blowing my mind!

Let me show you the code I'm working with. Here is the removal/loading method in Enemy Manager:

private void LoadSystem(Vector2 playerSpawnPoint)
{
player.Position = playerSpawnPoint;

foreach (Enemy enemy in enemies)
{
//Remove enemy from physical simulation -- memory leak and collision issues are a result of this line
enemy.Remove();
}

enemies.Clear();

//I tried this after browsing the forums, but still didn't fix it
physicsSimulator.Update(0);

Texture2D enemy1 = game.Content.Load<Texture2D>("enemy1");
Texture2D enemy2 = game.Content.Load<Texture2D>("enemy2");
Texture2D enemy3 = game.Content.Load<Texture2D>("enemy3");

//Add enemies
enemies.Add(new Graviton(physicsSimulator, enemy1, new Vector2(700, 300), 4000));
enemies.Add(new Graviton(physicsSimulator, enemy2, new Vector2(200, 500), 3000));
enemies.Add(new Graviton(physicsSimulator, enemy3, new Vector2(1000, 250), 5000));
}

And here is the Remove() method inside of Enemy:

public void Remove()
{
physicsSimulator.Remove(enemyBody);
physicsSimulator.Remove(enemyGeometry);
}

I hope my issue makes sense. Does anyone have any idea what my problem might be? Thank you so much for your help!

>>Bryant
Dec 4, 2007 at 12:33 AM
Not sure if this will help, but maybe try adding a physics debug view from the demos into your game and checking if the bodies are actually removed. The debug view will draw all geometries left in the sim, so if your enemies are still there, floating in space, you'll know they aren't really being removed, and it will help visualize your collision issues (it draws red dots at collision points).
Dec 4, 2007 at 6:37 AM
Ooh, good idea JeroMiya! I did exactly what you said and it seems like the objects are getting removed. They disappear from the screen completely, which is good. But the weird thing is this: any objects I add in the LoadSystem() method appear in the physics debug view with collision points and all of that, but they still don't collide with the player. The player passes right through them. And I'm still getting a terrible memory leak whenever I call that method.

I don't know what else to try :(
Dec 4, 2007 at 6:16 PM
Hi, I have the same problem.
But to call physicsSimulator.Update(0); doesn't make sense
because the first line of the Update method is:
public void Update(float dt) {
if (dt == 0) { return; }
...
So you have to call with > 0.
But that also doesn't work.

If I remove a body and geom and rebuild it with smaller size,
then add it again, the smaller object collide with the geom I removed before.
But the lists in the simulator are empty.
The data is still stored somewhere.
I can't find out where...
Dec 5, 2007 at 5:45 AM
At least now I know I'm not the only one :) I've stepped through the debugger with this thing I don't know how many times, and I still can't see it. I even thought there might be an issue when my Enemy objects were getting garbage collected, so I tried getting rid of the class and did everything inside EnemyManager -- no such luck. Does anyone have any ideas? :-\
Coordinator
Dec 5, 2007 at 8:36 PM
Try using a different broad phase collider. You can set this with the SetBroadPhaseCollider. Try setting it to the BruteForceCollider. Possibly the new broadphase has some issues with removal. Let me know if that fixes it.

-Jeff
Dec 6, 2007 at 7:19 AM
Holy crap, crashlander you just made my day :) It works perfectly now! Thank you for the help! I hope this helps MrJack, too.
Coordinator
Dec 6, 2007 at 12:38 PM
Does it work with the regular SweepAndPrune broad phase? Sounds like SelectiveSweep needs to be looked at. It must not be removing objects properly.

Brute force will the the slowest if you have many objects but it is also probably the most stable as it's been tested more. SweepAndPrune and SelectiveSweep are both still new and need more testing.

-Jeff
Dec 6, 2007 at 4:44 PM
In my tests, it looks like it works with SweepAndPrune and BruteForce.
I remove the simulator.Clear() command and it still works.

So thanks and keep on with this great Project ;)
Dec 6, 2007 at 7:15 PM
Edited Dec 6, 2007 at 10:36 PM
I'm amazed it would work at all!
Here is the code that is needed:
        static bool WrapperIsDisposed(Wrapper wrapper)
        {
            return wrapper.geom.IsDisposed;
        }
        static bool StubIsDisposed(Stub stub)
        {
            return stub.wrapper.geom.IsDisposed;
        }
        static bool WrapperIsRemoved(Wrapper wrapper)
        {
            return wrapper.geom.isRemoved;
        }
        static bool StubIsRemoved(Stub stub)
        {
            return stub.wrapper.geom.isRemoved;
        }
        public void ProcessRemovedGeoms()
        {
            if (wrappers.RemoveAll(WrapperIsRemoved) > 0)
            {
                xStubs.RemoveAll(StubIsRemoved);
                yStubs.RemoveAll(StubIsRemoved);
            }
        }
 
        public void ProcessDisposedGeoms()
        {
            if (wrappers.RemoveAll(WrapperIsDisposed) > 0)
            {
                xStubs.RemoveAll(StubIsDisposed);
                yStubs.RemoveAll(StubIsDisposed);
            }
        }

I was wondering why do you remove removed objects and disposed object separately?


EDIT: removed '!''s. whoops. Instead of isRemoved I have isAdded so the negations are obviously not needed.
Coordinator
Dec 6, 2007 at 8:23 PM
Thanks BioSlayer.

Sorry I didn't get around to looking into this myself.. Hope you didn't think I was blaming you for it not working. I know you just ported this from your engine in a hurry I should have tested it more prior to releasing it but I was short on time as always.

I don't remember the exact reason I process removed and disposed objectsseperately. I think originally I just had the dispose logic in there thinking people would just dispose objects when they were done with them. Then I realized some people would want to be able to remove objects without disposing them so I added the remove logic...

Anyway, thanks again for the fix. I'll get this in and update the download files.
Dec 7, 2007 at 4:15 AM
I got the same results as MrJack -- SweepAndPrune and BruteForce both work, but not SelectiveSweep.

Thanks everyone for the help.
Coordinator
Dec 7, 2007 at 12:31 PM
I'll get BioSlayers fix in this weekend. Then SelectiveSweep should work as well.