7
\$\begingroup\$

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.

\$\endgroup\$
4
  • \$\begingroup\$ I'm not sure about this particular system, but can't you just turn off kinematics for the "ghost" and move/rotate it manually? \$\endgroup\$
    – Tetrad
    Dec 20, 2011 at 23:46
  • \$\begingroup\$ Wouldn't that also disable collisions on the ghost? Maybe ghost wasn't the best analogy for it - perhaps a clone. \$\endgroup\$ Dec 21, 2011 at 0:11
  • \$\begingroup\$ I.e. collisions with the clone should affect the main body as well (in the exact same way). Would your suggestion work in that case? \$\endgroup\$ Dec 21, 2011 at 0:40
  • \$\begingroup\$ Beats me, probably not. \$\endgroup\$
    – Tetrad
    Dec 21, 2011 at 1:11

2 Answers 2

6
\$\begingroup\$

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.

\$\endgroup\$
4
  • \$\begingroup\$ It seems there is still one missing piece of the puzzle missing. How to instantly relocate the entire system to another position in the world. I need to have this to "wrap" the objects around the screen borders. Just setting the position on the main holder is not working. \$\endgroup\$ Jan 5, 2012 at 16:54
  • \$\begingroup\$ Nevermind the previous question, it seems to work if relocate all holders and main bodies at the same time. \$\endgroup\$ Jan 5, 2012 at 17:03
  • \$\begingroup\$ Glad to help and, yes, you'd need to relocate all bodies. You could build it into a class and store the relative positions to the main body (Vector2 difference between the body position and the main body). Then reposition the main one and just update their positions relative to the main one. \$\endgroup\$ Jan 5, 2012 at 18:07
  • \$\begingroup\$ That's pretty much what I did. :) \$\endgroup\$ Jan 5, 2012 at 18:08
0
\$\begingroup\$

Unfortunately I cannot check this answer since i'm on a phone right now, so apologies if it doesn't work as I expect.

I think you could use several existing joints to achieve what you want. Create a body with no collision geometry at the corner of your triangle, you can then use three distance joints to achieve the translation you want. Then if you connect the two collidable bodies together with a fixed angle joint they will rotate together.

\$\endgroup\$
12
  • \$\begingroup\$ I'm also a bit short on time at the moment, and will give it a try tonight. But although my example only had two bodies, I will actually need something more complex than this, with 9 connected bodies instead. Does your idea still apply? \$\endgroup\$ Dec 21, 2011 at 9:23
  • \$\begingroup\$ @David: That looks... interesting. Can you say more about what you're trying to achieve with this setup? I wonder if there might not be some alternative way to achieve what you want. \$\endgroup\$ Dec 21, 2011 at 12:05
  • \$\begingroup\$ @IlmariKaronen There's a link at the end of my question to another question describing exactly what I'm trying to achieve. Basically I'm trying to handle physics correctly in a 2D environment where bodies can "wrap" around the borders, and appear on several sides of the screen simultaneously. \$\endgroup\$ Dec 21, 2011 at 19:12
  • \$\begingroup\$ @Martin I still won't have time to test this today, so I'll report back again tomorrow. \$\endgroup\$ Dec 21, 2011 at 19:12
  • \$\begingroup\$ Okay, I've tried this now but couldn't get it the work. The problem is that I want to prevent the whole system from rotating. The 3 distance joints (which can be replaced by 2 weld joints too) keeps the system rigid but does not prevent it from rotating. \$\endgroup\$ Dec 22, 2011 at 14:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .