Monday, May 20, 2019

xna - How to create a very specific kind of joint in Farseer?



Edit


Problem solved (see Drackir's answer). Here's a demo of what I was trying to achieve with this joint. More info about the scenario on this other question.




Problem


I'm trying to create a very specific type of joint in Farseer that behaves like this:



  • Objects should always keep the same distance between them in both the X and Y axes.

  • Translation - A collision with one of the objects should move the entire group together, but the group must not rotate - only the individual objects may rotate around their centers as a result of torque.

  • Rotation - Objects should always have the same rotation. A collision with one of the objects should make all others rotate the exact same amount.



Here's a picture:


enter image description here


If there's no way to achieve this with Farseer out of the box, how would I go about extending it to create a new type of join that works like this? Basically I want every body to behave like a "clone" of the others, but with a fixed offset between them.



Answer



Ok, after about two hours of tinkering I managed to do this but it requires adding some extra bodies. You'll probably want to extract this stuff into a method/class but the basic idea is this:




  1. Create what I call "holder" bodies for your objects. These bodies share the size and position of your "objects" but don't participate in collisions. Essentially, they are clones.


    //Create "Main" (center) body
    _bodyMain = BodyFactory.CreateRectangle(World, 5f, 5f, 1f);

    _bodyMain.BodyType = BodyType.Dynamic;
    _bodyMain.Position = new Vector2(2, 2);
    //Create "MainHolder"
    _bodyMainHolder = BodyFactory.CreateRectangle(World, 5f, 5f, 1f);
    _bodyMainHolder.BodyType = BodyType.Dynamic;
    _bodyMainHolder.CollisionCategories = Category.None; //Prevents collisions
    _bodyMainHolder.Position = new Vector2(2, 2);
    _bodyMainHolder.IsSensor = true;
    //See http://farseerphysics.codeplex.com/discussions/222524 for why they're sensors
    _bodyMainHolder.FixedRotation = true;

    //Note: Only add FixedRotation for the main one, leave it off of the other
    // holders. I'll explain later.


  2. Attach each holder to the "mainHolder" using a WeldJoint. This will prevent them from moving away from one another. The FixedRotation on the mainHolder prevents them from rotating. I don't know if there's a bug in farseer but if you add a WeldJoint and both bodies have FixedRotation == true, the joint doesn't work properly. This is why only the main holder has .FixedRotation = true.


    //Weld the holders together so they don't move. Assumes another holder is defined
    // for the object on the right side (bodyRightHolder).
    JointFactory.CreateWeldJoint(World, _bodyMainHolder, _bodyRightHolder,
    _bodyRightHolder.Position - _bodyMainHolder.Position, Vector2.Zero);



  3. Attach your "object" bodies to their respective holders using RevoluteJoints. This locks your objects to the holders but allows them to rotate freely.


    //Lock the actual bodies to the holders
    JointFactory.CreateRevoluteJoint(World, _bodyMainHolder, _bodyMain, Vector2.Zero);
    JointFactory.CreateRevoluteJoint(World, _bodyRightHolder, _bodyRight, Vector2.Zero);


  4. Make your real objects rotations match each other by attaching an angle joint. Note: you don't have to attach an angle joint to each and every pair of objects. Just add an angle joint to the main body and the other body and the rotations will translate across all objects.


    //Make them rotate the same as each other
    JointFactory.CreateAngleJoint(World, _bodyMain, _bodyRight);





That's it! Just add the extra code for each other object/object holder you want and it will handle the rest. Here's an image to illustrate my test:Example image of test bodies.


Here you will see the green bodies are the holders. They do not rotate or collide and are welded together. The yellow and red bodies are your "objects" (yellow is the main). You can see that they are rotated by the same amount and are rotating around their respective holders. Also, only the red and yellow bodies participate in collisions. I believe this meets all three of your conditions above.


Working Example


If you load up the Farseer "Samples XNA" solution and find SimpleDemo1.cs ("Samples XNA" project > "Samples" folder), I rewrote it (code here) to test.


Hope this helps. Let me know if you have any questions.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...