Removing bodies from the engine ingame

Jul 8, 2007 at 10:47 AM
Edited Jul 8, 2007 at 10:50 AM
Hi everyone,

I've been playing around with adding a bullet manager into my game.

At first I tried creating a farseer body everytime the right trigger was pressed, this caused my game to pause while the object was made.

The next technique I tried was pre-creating 100 RectangleRigidBodies without adding them to Farseer.Physics, then when the right trigger was pressed I added one of the bodies.

This works really well, however now I'm having trouble removing the rectangleRigidBody once it has collided with another object.

Here is the code I use to create the RectangleRigidBodies...

physicsBody = new RectangleRigidBody[maxamountof_bullets];
for (int i = 0; i < maxamountof_bullets; i++)
{
physicsBody[i] = new RectangleRigidBody(5f, 5f, 1f);
physicsBody[i].LinearDragCoefficient = 0f;
physicsBody[i].Position = new Vector2(positionm[i].X, positionm[i].Z);
physicsBody[i].Collision += new EventHandler<CollisionEventArgs>(remove_bullet);
}

And here is my collision event....

public void remove_bullet(object sender, CollisionEventArgs e)
{
Farseer.Physics.Remove(e.Body1);
}

Is this the correct way to remove an object from Farseer? Maybe there is a better way to acheive what I'm doing? Any help is very appreciated.

Regards,
SuperArmySoldier.
Coordinator
Jul 8, 2007 at 11:27 AM
does this not remove it? You may need to also set enabled=false if there is an enabled propery can't remember at the moment. The other option is to Dispose it, but then you can't reuse it. I would have thought "Remove" alone would work.

What happens when you call remove? Does it error out or just not get removed.
Jul 8, 2007 at 11:56 AM
Thanks for the quick response. It doesn't seem to always work. The body will dissapear from the screen but then other objects on the screen start moving as if they were struck by the removed object.

I thought it might be the way in which I'm creating the objects in the array. Have you done this before?

e.Body1 is the object that has the collision event associated to it, right?

I'll try the 'enabled=false' suggestion and get back to you.

Of course, the problem could have nothing to do with Farseer, and actually be residing somewhere between my chair and my monitor - I'm still new to computer programming. :-).

Thanks again,
SAS.
Coordinator
Jul 9, 2007 at 4:36 PM
This could be because I'm not queueing up the removal and removing it at a set time during the simulation update. I'll be doing this for the next release and this will hopefully reduce any anamolies like you are seeing.
Aug 4, 2007 at 5:35 AM
Hi, I am having a great time mucking around with this and learning to use C# at the same time. It is brilliant.

I too am having a little trouble with the colide event. I get it to work but having trouble working out how the Entity object is associated with the view object. That is -> I can dispose of the entity object but cant work out how to specify which view object to remove with it. see below.

protected void BulletHit(object sender, CollisionEventArgs e)
{
e.Body1.Dispose(); // << yeap removes the entity , the part used to do all the physics on
SceneManager.Remove(vBullet); //<< ?? hard coded this what if I have many bullets how do I know which Bullet grafic (vBullet) belongs with e.Body1

e.Body2.LinearVelocity = new Vector2(0f,5f); // <<== just to show it works
}

The olny way I can think of is to loop through all the bullet objects to see which is this e.Body1 and then use the index from the loop to identify the view object.
But this seems to be a pretty inefficient method.

Any suggestions.
Coordinator
Aug 4, 2007 at 11:35 AM
Edited Aug 4, 2007 at 11:40 AM
I think you might need to add an "OnDisposed" event to the body then subscribe to that in your "view". This is what I've done with the new engine.

Here is the code from the new Body class:

Up near the top of the class:

public event EventHandler<EventArgs> OnDisposed;

The new Dispose method:

protected virtual void Dispose(bool disposing) {
//subclasses can override incase they need to dispose of resources
//otherwise do nothing.
if (!isDisposed) {
if (disposing) {
//dispose managed resources
};
//dispose unmanaged resources
}
isDisposed = true;
if (OnDisposed != null) {
OnDisposed(this, new EventArgs());
}
//base.Dispose(disposing)
}
}

I may end up changing this code a bit so that it's not "new-ing" up an EventArgs object but this should be fine as long as your not Disposing a LOT of bodies per game loop. One thought would be to just change the one line to:

OnDisposed(this, null); since the EventArgs really isn't being used anyway.
Aug 4, 2007 at 7:40 PM
Edited Aug 4, 2007 at 7:58 PM
Hi BigTree,

Not sure if this will help you, but I had exactly the same problem as yourself with trying to work out which bullet graphic (I'm using point vectors) to get rid of after it has collided. I ended up adding my own property to the RectangleRigidBody class (called index_number) and then simply accessed the number that I stored in it like so...

physicsBody[e.Body1.index_number].active = false;

Not sure what implications this might have on performance for you, but it hasn't effected the speed of my game and it hasn't seemed to interferred with farseer.

Regards,
SuperArmySoldier.
Coordinator
Aug 5, 2007 at 11:12 AM
Edited Aug 5, 2007 at 11:12 AM
Along these lines, i'm planning on adding a "UserData" object to each Body so that users can attach whatever type of object they want. ODE does this and it seems pretty useful.
Oct 9, 2007 at 2:38 AM
Just as a heads up, I'm having the same issue even now. I can go all out trying to remove a body and its geometry from the physics engine, but it will not always remove.

Here is my remove code:
if(bulletsi.Expired((float)curTime))
{
bulletsi.Enabled = false;
unusedBullets.Push(i);
bulletsi.Body.Enabled = false;
bulletsi.Geometry.CollisionEnabled = false;
bulletsi.Geometry.CollisionResponseEnabled = false;
bulletsi.Body.Dispose();
bulletsi.Geometry.Dispose();
physicsSimulator.Remove(bulletsi.Body);
physicsSimulator.Remove(bulletsi.Geometry);
}

And in my Draw code I draw all the geometries in the scene, and sure enough, they remain there, physics enabled and bouncing around:
foreach (Geometry g in physicsSimulator.GeometryList)
{
spriteBatch.Draw(bulletTex, g.Position, null, Color.Brown, 0.0f, new Vector2(8.0f, 8.0f), 1.0f, SpriteEffects.None,
0.0f);
}

-Jeremy
Coordinator
Oct 9, 2007 at 11:40 AM
I will have to look into this. One thing I noticed, though is you don't need to Dispose and Remove objects. Disposing an object will automatically remove it from the list. I'm pretty sure this isn't the issue but thought you should know.

-Jeff
Oct 9, 2007 at 3:28 PM
Edited Oct 9, 2007 at 3:29 PM
It doesn't seem to happen all the time, only when a lot of objects get spawned on top of each other (ie rapidly firing "bullets"), and if I set my local references to the body and geometry to null, the bodies that stay in the system when they shouldn't do go away. So, this code DOES work:

if(bulletsi.Enabled && bulletsi.Expired((float)curTime))
{
bulletsi.Enabled = false;
unusedBullets.Push(i);
bulletsi.Body.Enabled = false;
bulletsi.Geometry.CollisionEnabled = false;
bulletsi.Geometry.CollisionResponseEnabled = false;
bulletsi.Body.Dispose();
bulletsi.Geometry.Dispose();
physicsSimulator.Remove(bulletsi.Body);
physicsSimulator.Remove(bulletsi.Geometry);
bulletsi.Body = null;
bulletsi.Geometry = null;
}
Coordinator
Oct 10, 2007 at 11:37 AM
I'm still looking into this.

If it worked the way I intended it to work you should only need this:

bulletsi.Body.Enabled = false;
physicsSimulator.Remove(bulletsi.Body);
physicsSimulator.Remove(bulletsi.Geometry);

I've got a test project setup. I'll play with it tomorrow morning.
Oct 11, 2007 at 2:40 AM
I can send over a copy of my code too if you want to take a look at it.

-Jeremy
Coordinator
Oct 11, 2007 at 10:21 AM
sure, send it to 'jeffreyweber AT hotmail dot com'

I did find a small bug where any existing contacts would hang around after a geometry was removed, but not the entire geometry.

Other than that though, I was able to add a bunch of objects then loop thru and remove them without issue.
Coordinator
Oct 14, 2007 at 4:29 PM
The bug you just mentioned CrashLander, is a bug that I also discovered when i had debug mode on (PhysicsSimulatorView, the one from your samples).

When do you expect the bugfixed release ?
Oct 14, 2007 at 6:17 PM
Is there a way to do a simple ray-body intersect test with farseer??? I'm also currently trying to implement a bullet system, but I've found that using actual physics object to represent a bullet can be cumbersome / buggy (at least from my experience). One reason is that I'm trying to set a normal bullet velocity and it seems that it's too fast for the engine. Sometimes the bullet passes through objects.

I think a simple ray/geometry collision test would be more appropriate for bullets.
Coordinator
Oct 15, 2007 at 11:26 AM
@genbox. I haven't fully been able to recreate this bug. I've created a bunch of geometries, then removed them all in response to a button press and they went away as expected. If someone could create a simplified sample of this happening and send it to me that would be helpful.

@ alexmontreal, I have no immediate plans for a ray test. The only work around I can think of would be to use slower bullets or decrease the dt for the simulation to gain more accuracy.
Coordinator
Oct 15, 2007 at 1:52 PM
@crashlander - I tried to reproduce it, and I found the cause. It happens when 2 objects are stuck inside each other. In my game, I spawn a lot of balls, and position them random on the screen, this causes some of the balls to end up inside each other.
When removing the bodies, their contacts seem to remain.
Oct 15, 2007 at 2:19 PM
Edited Oct 15, 2007 at 2:56 PM
@genbox: yeah, same happens on mine. If I fire the bullets at a corner so that they spawn inside each other, that's when it happens. In mine, however, not just the contacts remain, but also the bodies and the geometries (they continue to bounce around and collide with other objects I haven't removed).

My platformer code has become a bit too bulky to send, so I'll try to workup a simpler example to reproduce it.

Edit: Here's one example. Not sure if it is the same issue. Can only reproduce it with .Remove methods of PhysicsSimulator

{"
#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;

using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Dynamics;

#endregion

namespace BugReproducer
{
class Bullet
{
private Body body;
private Geometry geom;
private PhysicsSimulator phys;
private Vector2 position;

public Bullet(Vector2 pos, Vector2 vel, float radius, PhysicsSimulator phys)
{
this.phys = phys;
body = BodyFactory.Instance.CreateCircleBody(phys, radius, 1.0f);
body.Position = pos;
position = pos;
body.LinearVelocity = vel;

geom = GeometryFactory.Instance.CreateCircleGeometry(phys, body, radius, 20);
geom.CollisionHandler += HandleCollision;
}

public bool HandleCollision(Geometry g1, Geometry g2, ContactList contactList)
{
System.Console.WriteLine("Collision");
return true;
}

public Vector2 Position
{
get { return position; }
}

public void Disable()
{
//body.Enabled = false;
//geom.CollisionEnabled = false;
//body.Dispose();
//geom.Dispose();
phys.Remove(body);
phys.Remove(geom);
}
}

/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
ContentManager content;
Texture2D tex;
SpriteBatch spriteBatch;

// no gravity for now
PhysicsSimulator phys = new PhysicsSimulator(new Vector2(0.0f, 0.0f));

List<Bullet> bullets = new List<Bullet>();

public Game1()
{
graphics = new GraphicsDeviceManager(this);
content = new ContentManager(Services);
}


/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
spriteBatch = new SpriteBatch(graphics.GraphicsDevice);

// TODO: Add your initialization logic here

base.Initialize();
}


/// <summary>
/// Load your graphics content. If loadAllContent is true, you should
/// load content from both ResourceManagementMode pools. Otherwise, just
/// load ResourceManagementMode.Manual content.
/// </summary>
/// <param name="loadAllContent">Which type of content to load.</param>
protected override void LoadGraphicsContent(bool loadAllContent)
{
if (loadAllContent)
{
// TODO: Load any ResourceManagementMode.Automatic content
tex = content.Load<Texture2D>("neon_marble");
float radius = tex.Width / 2;

for (int i = 0; i < 10; i++)
{
Vector2 pos = new Vector2(0, 0);
Vector2 vel = new Vector2(0, 0);
bullets.Add(new Bullet(pos, vel, radius, phys));
}

foreach(Bullet b in bullets)
b.Disable();


}

// TODO: Load any ResourceManagementMode.Manual content
}


/// <summary>
/// Unload your graphics content. If unloadAllContent is true, you should
/// unload content from both ResourceManagementMode pools. Otherwise, just
/// unload ResourceManagementMode.Manual content. Manual content will get
/// Disposed by the GraphicsDevice during a Reset.
/// </summary>
/// <param name="unloadAllContent">Which type of content to unload.</param>
protected override void UnloadGraphicsContent(bool unloadAllContent)
{
if (unloadAllContent)
{
// TODO: Unload any ResourceManagementMode.Automatic content
content.Unload();
}

// TODO: Unload any ResourceManagementMode.Manual content
}


/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();

// TODO: Add your update logic here
phys.Update((float)gameTime.ElapsedGameTime.TotalSeconds);
base.Update(gameTime);
}


/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

spriteBatch.Begin();
// TODO: Add your drawing code here
if (Keyboard.GetState().IsKeyDown(Keys.A))
{
foreach (Geometry g in phys.GeometryList)
{
if(g.CollisionEnabled)
spriteBatch.Draw(tex, g.Position, Color.White);
}
}

if (Keyboard.GetState().IsKeyDown(Keys.S))
{
foreach (Body b in phys.BodyList)
{
if(b.Enabled)
spriteBatch.Draw(tex, b.Position, Color.Green);
}
}

if (Keyboard.GetState().IsKeyDown(Keys.D))
{
foreach (Bullet b in bullets)
{
spriteBatch.Draw(tex, b.Position, Color.Red);
}
}

spriteBatch.End();
base.Draw(gameTime);
}
}
}
"
}
Coordinator
Oct 15, 2007 at 4:33 PM
@genbox,

I did fix the bug with the lingering contacts.

@JeroMiya,

I'll try and load that up tonight and see what I can find out.


I
Oct 15, 2007 at 10:33 PM
@crashlander : thanks =) I guess I will try your advice and reduce the dt for the simulation. However I think this could impact on framerate performance???

My other idea was to use a bigger geometry for the bullet than the actual bullet size. For example, if the bullet has a size of 2.0f width X 0.5f height, I could just use a rectangle geometry of 20.0f, 0.5f . That way the chances are that even if the bullet travels very fast, it won't pass entirely through other objects. Do you think that's a good idea??

Slowing the bullet down is not really an option for me since I want something that feels realistic.. and bullet are pretty fast in reality =)

Thanks a lot for your responses and effort on Farseer, I'm sure everyone appreciates it a lot!
Coordinator
Oct 16, 2007 at 11:41 AM
making the bullet longer could definelty work. As with all physics code, it's a matter of tuning things to find what works.

As for smaller dt's, I read that Forza runs there physics at 300fps so don't be afraid to try smaller dt values. Just make sure you keep your drawing code to somewhere around 60 fps or lower.
Coordinator
Oct 17, 2007 at 10:04 AM
Edited Oct 17, 2007 at 10:30 AM
@JeroMiya,

I found the problem with your code. You were adding your bodies and geometries, then immediatly removing them. The engine queue's up the added geometries and queues up the removed geometries then does the actual removing and adding near the beginning of the update loop.

Since you were not calling the update method in between adding and removing items, the added items never got truly added before the removal code ran.

I can change things so this doesn't happen but for now, just make sure Update gets called at least once inbetween adding and removing items.

For your code I just added phys.Update(0) right above this code:

phys.Update(0)
foreach (Bullet b in bullets)
b.Disable();

Hope this helps.

<edit>
Keep in mind also, that when using the Factory objects to create bodies and geometries, there is an overload for each method that does not take a PhysicsSimulator object as the 1st parameter. This is so you can create bodies and geometries without having them automatically added to the engine.

You can them latter add these objects by calling physicsSimulator.Add yourself. This is usefull if you want to create "template" body and geometries then clone them. You don't normally want the "templates" to be added to the engine.