Issues with Multiple Joined Bodies

Posts that don't fit into other categories.
SCRevival
Posts: 6
Joined: Thu Jun 29, 2017 1:45 pm

Issues with Multiple Joined Bodies

Postby SCRevival » Sat Jul 22, 2017 12:11 pm

I'm currently making an Android game that uses dyn4j as the physics engine. Recently, I've been trying to get a 'turret' object to work. The turret object is supposed to be made of a center/anchor body and multiple barrel bodies. This makes for a nice system where you can slap on as many barrels as you want, and it also allows for destructible bodies (:D dyn4j). However, I have been running into problems with joints.

First, I tried to use a revolute joint between the center/anchor body and a barrel (rectangular body). This led to some very unexpected behavior, and even when I made sure the collisionisAllowed property was set to false, the joint appeared to break (but it was still listed under body.getJoints()).

https://youtu.be/kaYk1Iubl2g

I was unsure of how to fix this so I moved onto a weld joint. The weld joint worked perfectly well for 1 barrel, but things began to get crazy with 2 barrels (clarification: each individual barrel has one weld joint with the center/anchor body). I'm not a physicist, but things didn't quite look right to me:

https://youtu.be/zLk8oozfM1Q

So I came up with a few theories about the break. I assumed it was either because the barrels were too close or 'inside' of the center/anchor body or because the barrels were colliding with each other. I added a collision listener which returned false if a barrel collided with another barrel belonging to the same 'turret' entity. This fix did not work, so I am left wondering if the joint somehow intercepts collision events and controls the collision pipeline on its own. As a side note, I am using set angular velocity on the turret center/anchor body to help the turret aim (I'm not sure if this is causing the issue).

I decided to do one final thing and weld both the barrels together. It worked and doesn't appear to break! I'm hoping for a different solution, however, because this would require me to generate n*(n-1)/2 joints for n bodies joined. Also, on the death of the center/anchor body, I would need to iterate through and dissolve many extra joints. Here's the expected/working behavior:

https://youtu.be/TDwMcGuwsYw

I know it's very hard to troubleshoot without any code (and I would happy to provide if needed), but I think I'm missing something in the bigger picture. I would be grateful for any help in figuring out what this is!

Thank you,
SCr

SCRevival
Posts: 6
Joined: Thu Jun 29, 2017 1:45 pm

Re: Issues with Multiple Joined Bodies

Postby SCRevival » Sun Jul 23, 2017 6:27 am

Quick Update: I found out that my fix isn't really a fix. With a sufficiently hard collision, the joints still break.

William
Site Admin
Posts: 351
Joined: Sat Feb 06, 2010 10:23 pm

Re: Issues with Multiple Joined Bodies

Postby William » Mon Jul 24, 2017 2:03 pm

Yeah, it will be difficult to determine the problem without some code. From your videos it looked like the 'barrels' where fixed to the body. In that case, why not just add them as extra fixtures?

Is this what you are trying to do? You could probably do this much better, it's just a quick example.
Code: [Select all] [Expand/Collapse] [Download] (Untitled.java)
  1. package org.dyn4j.test.samples;
  2.  
  3. import java.awt.Graphics2D;
  4. import java.util.Iterator;
  5.  
  6. import org.dyn4j.collision.narrowphase.Penetration;
  7. import org.dyn4j.dynamics.Body;
  8. import org.dyn4j.dynamics.BodyFixture;
  9. import org.dyn4j.dynamics.CollisionAdapter;
  10. import org.dyn4j.dynamics.Step;
  11. import org.dyn4j.dynamics.StepAdapter;
  12. import org.dyn4j.dynamics.World;
  13. import org.dyn4j.geometry.Convex;
  14. import org.dyn4j.geometry.Geometry;
  15. import org.dyn4j.geometry.MassType;
  16. import org.dyn4j.test.framework.SimulationBody;
  17. import org.dyn4j.test.framework.SimulationFrame;
  18.  
  19. /**
  20.  * @author William Bittle
  21.  * @version 3.2.1
  22.  * @since 3.2.0
  23.  */
  24. public final class RevoluteJointExample extends SimulationFrame {
  25.     /** The serial version id */
  26.     private static final long serialVersionUID = -8518496343422955267L;
  27.  
  28.     /**
  29.      * Default constructor.
  30.      */
  31.     public RevoluteJointExample() {
  32.         super("Revolute Joint Example", 50.0);
  33.     }
  34.    
  35.     public class MyData {
  36.         public int hits = 0;
  37.     }
  38.    
  39.     private static final Object BALL = new Object();
  40.    
  41.     /* (non-Javadoc)
  42.      * @see org.dyn4j.samples.SimulationFrame#initializeWorld()
  43.      */
  44.     @Override
  45.     protected void initializeWorld() {
  46.         // no gravity on a top-down view
  47.         this.world.setGravity(World.ZERO_GRAVITY);
  48.        
  49.         SimulationBody tank = new SimulationBody();
  50.         // main body
  51.         tank.addFixture(Geometry.createSquare(1));
  52.        
  53.         // left side (w/ local rotation/translation)
  54.         Convex left = Geometry.createRectangle(0.25, 1.25);
  55.         left.rotate(Math.toRadians(-45));
  56.         left.translate(0.75, -0.2);
  57.        
  58.         // right side (w/ local rotation/translation)
  59.         Convex right = Geometry.createRectangle(0.25, 1.25);
  60.         right.rotate(Math.toRadians(45));
  61.         right.translate(-0.75, -0.2);
  62.        
  63.         BodyFixture bfl = tank.addFixture(left);
  64.         BodyFixture bfr = tank.addFixture(right);
  65.        
  66.         bfl.setSensor(true);
  67.         bfl.setUserData(new MyData());
  68.         bfr.setSensor(true);
  69.         bfr.setUserData(new MyData());
  70.        
  71.         tank.setMass(MassType.NORMAL);
  72.        
  73.         world.addBody(tank);
  74.        
  75.         SimulationBody ball1 = new SimulationBody();
  76.         ball1.addFixture(Geometry.createCircle(0.5));
  77.         ball1.translate(10, 0);
  78.         ball1.setLinearVelocity(-5, 0);
  79.         ball1.setMass(MassType.NORMAL);
  80.         ball1.setUserData(BALL);
  81.         world.addBody(ball1);
  82.        
  83.         SimulationBody ball2 = new SimulationBody();
  84.         ball2.addFixture(Geometry.createCircle(0.5));
  85.         ball2.translate(5, 0);
  86.         ball2.setLinearVelocity(-5, 0);
  87.         ball2.setMass(MassType.NORMAL);
  88.         ball2.setUserData(BALL);
  89.         world.addBody(ball2);
  90.        
  91.         world.addListener(new CollisionAdapter() {
  92.             @Override
  93.             public boolean collision(Body body1, BodyFixture fixture1, Body body2, BodyFixture fixture2, Penetration penetration) {
  94.                 if (fixture1.isSensor() || fixture2.isSensor()) {
  95.                     Object o1 = fixture1.getUserData();
  96.                     Object o2 = fixture2.getUserData();
  97.                     if (o1 != null && o1 instanceof MyData) {
  98.                         MyData d = (MyData)o1;
  99.                         d.hits++;
  100.                         System.out.println(fixture1.getId() + ": " + d.hits);
  101.                     } else if (o2 != null && o2 instanceof MyData) {
  102.                         MyData d = (MyData)o2;
  103.                         d.hits++;
  104.                         System.out.println(fixture1.getId() + ": " + d.hits);
  105.                     }
  106.                 }
  107.                 return super.collision(body1, fixture1, body2, fixture2, penetration);
  108.             }
  109.         });
  110.        
  111.         world.addListener(new StepAdapter() {
  112.             @Override
  113.             public void end(Step step, World world) {
  114.                 Iterator<Body> bit = world.getBodyIterator();
  115.                 while(bit.hasNext()) {
  116.                     Body body = bit.next();
  117.                     if (body.getUserData() == BALL) {
  118.                         if (body.getContacts(true).size() > 0) {
  119.                             // then remove the body
  120.                             bit.remove();
  121.                         }
  122.                     } else {
  123.                         boolean removed = false;
  124.                         Iterator<BodyFixture> it = body.getFixtureIterator();
  125.                         while (it.hasNext()) {
  126.                             BodyFixture bf = it.next();
  127.                             if (bf.isSensor()) {
  128.                                 Object ud = bf.getUserData();
  129.                                 if (ud != null && ud instanceof MyData && ((MyData)ud).hits >= 2) {
  130.                                     // then remove this fixture
  131.                                     it.remove();
  132.                                     removed = true;
  133.                                 }
  134.                             }
  135.                         }
  136.                         if (removed) {
  137.                             body.updateMass();
  138.                         }
  139.                     }
  140.                 }
  141.             }
  142.         });
  143.     }
  144.    
  145.     /* (non-Javadoc)
  146.      * @see org.dyn4j.samples.SimulationFrame#render(java.awt.Graphics2D, double)
  147.      */
  148.     @Override
  149.     protected void render(Graphics2D g, double elapsedTime) {
  150.         super.render(g, elapsedTime);
  151.     }
  152.    
  153.     /**
  154.      * Entry point for the example application.
  155.      * @param args command line arguments
  156.      */
  157.     public static void main(String[] args) {
  158.         RevoluteJointExample simulation = new RevoluteJointExample();
  159.         simulation.run();
  160.     }
  161. }

Here's an example of the RevoluteJoint working as a "turret":

Code: [Select all] [Expand/Collapse] [Download] (Untitled.java)
  1. package org.dyn4j.test.samples;
  2.  
  3. import java.awt.Graphics2D;
  4.  
  5. import org.dyn4j.dynamics.World;
  6. import org.dyn4j.dynamics.joint.RevoluteJoint;
  7. import org.dyn4j.geometry.Convex;
  8. import org.dyn4j.geometry.Geometry;
  9. import org.dyn4j.geometry.MassType;
  10. import org.dyn4j.geometry.Vector2;
  11. import org.dyn4j.test.framework.SimulationBody;
  12. import org.dyn4j.test.framework.SimulationFrame;
  13.  
  14. /**
  15.  * @author William Bittle
  16.  * @version 3.2.1
  17.  * @since 3.2.0
  18.  */
  19. public final class RevoluteJointExample extends SimulationFrame {
  20.     /** The serial version id */
  21.     private static final long serialVersionUID = -8518496343422955267L;
  22.  
  23.     /**
  24.      * Default constructor.
  25.      */
  26.     public RevoluteJointExample() {
  27.         super("Revolute Joint Example", 150.0);
  28.     }
  29.    
  30.     /* (non-Javadoc)
  31.      * @see org.dyn4j.samples.SimulationFrame#initializeWorld()
  32.      */
  33.     @Override
  34.     protected void initializeWorld() {
  35.         // no gravity on a top-down view
  36.         this.world.setGravity(World.ZERO_GRAVITY);
  37.        
  38.         // create all your bodies/joints
  39.         SimulationBody tank = new SimulationBody();
  40.         tank.addFixture(Geometry.createSquare(1));
  41.         tank.setMass(MassType.NORMAL);
  42.         tank.setLinearDamping(0.3);
  43.         world.addBody(tank);
  44.        
  45.         SimulationBody turret = new SimulationBody();
  46.         turret.addFixture(Geometry.createCircle(0.25));
  47.         Convex convex = Geometry.createRectangle(1.0, 0.25);
  48.         convex.translate(0.4, 0);
  49.         turret.addFixture(convex);
  50.         turret.setMass(MassType.NORMAL);
  51.         world.addBody(turret);
  52.        
  53.         RevoluteJoint rj = new RevoluteJoint(tank, turret, new Vector2(0, 0));
  54.         rj.setCollisionAllowed(false);
  55.         world.addJoint(rj);
  56.        
  57.         SimulationBody bullet = new SimulationBody();
  58.         bullet.addFixture(Geometry.createCircle(0.25));
  59.         bullet.translate(0.75, 3.0);
  60.         bullet.setLinearVelocity(0, -5);
  61.         bullet.setMass(MassType.NORMAL);
  62.         world.addBody(bullet);
  63.     }
  64.    
  65.     /* (non-Javadoc)
  66.      * @see org.dyn4j.samples.SimulationFrame#render(java.awt.Graphics2D, double)
  67.      */
  68.     @Override
  69.     protected void render(Graphics2D g, double elapsedTime) {
  70.         super.render(g, elapsedTime);
  71.     }
  72.    
  73.     /**
  74.      * Entry point for the example application.
  75.      * @param args command line arguments
  76.      */
  77.     public static void main(String[] args) {
  78.         RevoluteJointExample simulation = new RevoluteJointExample();
  79.         simulation.run();
  80.     }
  81. }


William

SCRevival
Posts: 6
Joined: Thu Jun 29, 2017 1:45 pm

Re: Issues with Multiple Joined Bodies

Postby SCRevival » Tue Jul 25, 2017 2:41 pm

Hi William,

It turned out the problem was relatively simple (the joint was not being placed in the correct location because of scaling issues). But thank you very much for the help and for the code!

Happy coding,
SCr


Return to “General Discussion”

Who is online

Users browsing this forum: No registered users and 1 guest