Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

Earlier, I posted a question asking how to swap fixtures on the fly in a 2D side-scroller using Farseer Physics Engine. The ultimate goal being that the player's physical body changes when the player is in different states (I.e. standing, walking, jumping, etc). After reading this answer, I changed my approach to the following:

  1. Create a physical body for each state when the player is loaded
  2. Save those bodies and their corresponding states in parallel lists
  3. Swap those physical bodies out when the player state changes (which causes an exception, see below)

The following is my function to change states and swap physical bodies:

new protected void SetState(object nState)
{
    //If mBody == null, the player is being loaded for the first time
    if (mBody == null)
    {
        mBody = mBodies[mStates.IndexOf(nState)];
        mBody.Enabled = true;
    }
    else
    {
        //Get the body for the given state
        Body nBody = mBodies[mStates.IndexOf(nState)];

        //Enable the new body
        nBody.Enabled = true;

        //Disable the current body
        mBody.Enabled = false;

        //Copy the current body's attributes to the new one
        nBody.SetTransform(mBody.Position, mBody.Rotation);
        nBody.LinearVelocity = mBody.LinearVelocity;
        nBody.AngularVelocity = mBody.AngularVelocity;

        mBody = nBody;
    }

    base.SetState(nState);
}

Using the above method causes an IndexOutOfRangeException when calling World.Step:

mWorld.Step(Math.Min((float)nGameTime.ElapsedGameTime.TotalSeconds, (1f / 30f)));

I found that the problem is related to changing the .Enabled setting on a body. I tried the above function without setting .Enabled, and there was no error thrown. Turning on the debug views, I saw that the bodies were updating positions/rotations/etc properly when the state was changes, but since they were all enabled, they were just colliding wildly with each other.

Does Enabling/Disabling a body remove it from the world's body list, which then causes the error because the list is shorter than expected?

Update:

For such a straightforward issue, I feel this question has not received enough attention. Has anyone else experienced this? Would anyone try a quick test case? I know this issue can be sidestepped - I.e. by not disabling a body during the simulation - but it seems strange that this issue would exist in the first place, especially when I see no mention of it in the documentation for farseer or box2d. I can't find any cases of the issue online where things are more or less kosher, like in my case. Any leads on this would be helpful.

share|improve this question
    
have you tried calling world.processChanges() after? –  Jon Jan 24 '12 at 21:07
    
@Jon Yes, it gets called within the call to base.SetState, and also when the bodies are initially created and set to .Enabled = false;. –  WilHall Jan 25 '12 at 0:09
add comment

3 Answers

up vote 2 down vote accepted
+50

Does the erroneous line in your World.step() method have square brackets in it anywhere? Or a get() method of some sort with an index in it.

This error suggests that you're asking for an object in an array or list by providing an index outside of the array bounds. Array bounds start at 0, and end at array.Length - 1.

So a lot of the time this error is caused by looping through objects in an array up to and including array.Length or list.Count

EDIT:

The issue appears to be here (like you suggested):

//Enable the new body
nBody.Enabled = true;

//Disable the current body
mBody.Enabled = false;

//Copy the current body's attributes to the new one
nBody.SetTransform(mBody.Position, mBody.Rotation);
nBody.LinearVelocity = mBody.LinearVelocity;
nBody.AngularVelocity = mBody.AngularVelocity;

I was looking at the code for the .Enabled setter in body.cs in Farseer -- I haven't used Farseer personally so I don't know the exact details of the implementation, but it does a lot of disposing and destroying things when you set enabled to false. I would recommend just rearranging your code like this and giving it a go:

//Enable the new body
nBody.Enabled = true;

//Copy the current body's attributes to the new one
nBody.SetTransform(mBody.Position, mBody.Rotation);
nBody.LinearVelocity = mBody.LinearVelocity;
nBody.AngularVelocity = mBody.AngularVelocity;

//Disable the current body
mBody.Enabled = false;

mBody = nBody;
share|improve this answer
    
I think World.Step() is a method from the Farseer Physics Engine, probably from this class. –  David Gouveia Jan 30 '12 at 3:58
    
Ahh yes, so it is. Thanks. I'll take a look. –  Suds Jan 30 '12 at 4:16
    
Added a potential solution. –  Suds Jan 30 '12 at 4:36
    
I tried your suggestion with a newer, updated version of my above code - only disabling the body after accessing any attributes needed - and it worked. The updated code places the bodies in different collision groups. If I try the same thing without them, the error occurs. The IndexOutOfRangeException probably refers to the world's ContactList changing length because the bodies were colliding when disabled - where setting a different collision group on the body first appears to fix the problem. –  WilHall Jan 30 '12 at 16:43
add comment

I had this same issue and it seems like the broad phase collision detection is still run including the disabled body if this body was disabled before starting the game (something must not be catching up quickly enough). I would assume this is a bug.

I've found two solutions:

  • Create the body wou will disable somewhere it will not generate a collision in the broadphase collision detection. (Very ugly, I know)
  • Add the following to the ContactManager.cs-file in the method Collide() before calling BroadPhase.TestOverlap-function. (Actually I added it at line 249)

            if (bodyA.Enabled == false || bodyB.Enabled == false)
            {
                continue;
            }
    

If someone else has a better explanation or solution please comment or edit, I'd love to know!

share|improve this answer
    
I would pass this on to the devs of Farseer so they can create a bug report and/or look into it –  WilHall Sep 7 '12 at 15:14
    
Yeah, I'll do that! –  Mikael Högström Sep 7 '12 at 15:23
    
Added in issue tracker: farseerphysics.codeplex.com/workitem/33101 If this bothers you then you can vote for it to get more attention from the dev team. –  Mikael Högström Sep 7 '12 at 17:18
add comment

It looks like the error is most likely coming from one of the two instances of this

mBodies[mStates.IndexOf(nState)];

There are two primary reasons this would happen:

  • mStates is larger than mBodies, and nState occurs late enough in the sequence that mStates.IndexOf(nState) is greater than or equal to the size of mBodies.
  • nState is null, or otherwise does not occur in mStates, causing mStates.IndexOf(nState) to return -1, which is most certainly out of bounds.

I suggest wrapping these occurences with a try-catch block, and throwing a new exception that gives you more detail, such as this:

try
{
     //Get the body for the given state
     Body nBody = mBodies[mStates.IndexOf(nState)];
}
catch
{
    throw new Exception("mBodies.Length = " + mBodies.Length + " and mStates.IndexOf(nState) = " + mStates.IndexOf(nState));
}

Then instead of the IndexOutOfRange exception, you will get an explanation of why it is out of range. If the second value is -1, nState is not in mStates. If the first value is smaller than you expected, then mBodies does not contain all the items it should (Disabled => Removed).

share|improve this answer
    
I stated that the error happened on the line World.Step was called, not in my code. I have stepped through my code in the debugger, and everything is fine. I also noted that I tried the same code without disabling the bodies, and the error wasn't present. –  WilHall Feb 3 '12 at 18:14
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.