I'm using Android Studio with Java and LibGDX.
I need to make some balls revoluting on another moving body.
It works if the body doesn't move:
Otherwise the balls are "pulled" by the moving body instead of revoluting on it.
These are some screenshots while the body is moving to the bottom:
I noticed that if I heavy increase the revoluting speed, the balls revolute almost correctly but I need to make it works with many different speeds.
Either I move the main ball with setTransform or with linearVelocity the result is the same.
How to maintain a smooth revoluting movement while the main ball is moving?
Thanks
public Ball(World world, float x, float y, float sizeInWorld) {
this.sizeInWorld = sizeInWorld;
this.ballInitialSize = sizeInWorld;
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.KinematicBody;
bodyDef.position.set(x, y);
bodyDef.fixedRotation = false;
bodyDef.bullet = true;
body = world.createBody(bodyDef);
body.setLinearDamping(0);
CircleShape circle = new CircleShape();
circle.setRadius(sizeInWorld / 2);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = circle;
fixtureDef.friction = 0f;
fixtureDef.restitution = 1f;
fixtureDef.density = 1f;
circle.dispose();
body.setLinearVelocity(0, -10);
}
public BallRevoluting(World world, float x, float y, float sizeInWorld) {
this.sizeInWorld = sizeInWorld;
this.ballInitialSize = sizeInWorld;
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(x, y);
bodyDef.fixedRotation = false;
bodyDef.awake = true;
bodyDef.bullet = true;
body = world.createBody(bodyDef);
body.setLinearDamping(0);
CircleShape circle = new CircleShape();
circle.setRadius(sizeInWorld / 2);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = circle;
fixtureDef.friction = 0.1f;
fixtureDef.isSensor = true;
fixtureDef.restitution = 0;
fixtureDef.density = 1f;
circle.dispose();
}
public static void createRevoluteJoint(World world, Body body, Body revolutingBody, float speed, boolean clockWise) {
RevoluteJointDef revoluteJointDef = new RevoluteJointDef();
revoluteJointDef.initialize(body, revolutingBody, body.getPosition());
revoluteJointDef.enableMotor = true;
float targetVelocity = speed * 5;
float radius = revolutingBody.getPosition().dst(body.getPosition());
revoluteJointDef.maxMotorTorque = calculateMotorTorque(revolutingBody, targetVelocity, radius);
revoluteJointDef.motorSpeed = targetVelocity / radius;
if(clockWise) {
Gdx.app.log(TAG, "using revoluteToClockwise direction");
revoluteJointDef.motorSpeed = -revoluteJointDef.motorSpeed;
}
world.createJoint(revoluteJointDef);
}
public calculateMotorTorque(Body body, float targetVelocity, float radius) {
float mass = body.getMass();
float angularVelocity = targetVelocity / radius;
float inertia = mass * radius * radius;
return inertia * angularVelocity;
}
public Ball mainBall = new Ball(world, 0, 0, 1);
int numOrbitingBalls = 4;
float orbitRadius = 2.0f;
for (int i = 0; i < numOrbitingBalls; i++) {
float angle = (float) (i * 2 * Math.PI / numOrbitingBalls);
float x = mainBall.getPosition().x + orbitRadius * (float)Math.cos(angle);
float y = mainBall.getPosition().y + orbitRadius * (float)Math.sin(angle);
BallRevoluting newBall = new BallRevoluting(world, x, y, mainBall.sizeInWorld/2);
createRevoluteJoint(world, mainBall.body, newBall.body, 1, true);
}
Consider using a single Body
with the satellite-balls as Fixture
s on that Body
.
If that doesn't work for your use-case, one approach is to use WeldJoint
s instead:
In the example above I enable movement first and then rotation of the mainBall
, but it could have been done in one step.
If this approach doesn't work either (let's say because your 'mainBall' is not allowed to have a rotation), then I would create a body with 4 fixtures for the satellites, and then attach that body to the mainBall
with a MotorJoint
with offset zero.
Full source for the gif:
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.*;
import com.badlogic.gdx.physics.box2d.joints.DistanceJointDef;
import com.badlogic.gdx.physics.box2d.joints.RevoluteJointDef;
import com.badlogic.gdx.physics.box2d.joints.WeldJointDef;
import com.badlogic.gdx.utils.ScreenUtils;
public class SandboxGame extends ApplicationAdapter {
public OrthographicCamera camera;
public World world;
public Box2DDebugRenderer box2DDebugRenderer;
public Ball mainBall;
@Override
public void create() {
float w = 32.0f;
float h = w * Gdx.graphics.getHeight() / (float)Gdx.graphics.getWidth();
camera = new OrthographicCamera(w, h);
camera.position.set(0, 0, 1);
camera.update();
world = new World(Vector2.Zero, false);
box2DDebugRenderer =new Box2DDebugRenderer();
mainBall = new Ball(world, -10, 0, 1);
int numOrbitingBalls = 4;
float orbitRadius = 2.0f;
for (int i = 0; i < numOrbitingBalls; i++) {
float angle = (float) (i * 2 * Math.PI / numOrbitingBalls);
float x = mainBall.body.getWorldCenter().x + orbitRadius * (float)Math.cos(angle);
float y = mainBall.body.getWorldCenter().y + orbitRadius * (float)Math.sin(angle);
BallRevoluting newBall = new BallRevoluting(world, x, y, mainBall.sizeInWorld/2);
createWeldJoint(world, mainBall.body, newBall.body);
}
}
@Override
public void render() {
if (Gdx.input.isKeyJustPressed(Input.Keys.ENTER))
mainBall.body.setAngularVelocity(8);
if (Gdx.input.isKeyJustPressed(Input.Keys.SPACE))
mainBall.body.setLinearVelocity(4, 0);
camera.update();
world.step(Gdx.graphics.getDeltaTime(), 8, 8);
ScreenUtils.clear(0.15f, 0.15f, 0.2f, 1f);
box2DDebugRenderer.render(world, camera.combined);
}
public static void createWeldJoint(World world, Body body, Body revolutingBody) {
WeldJointDef weldJointDef = new WeldJointDef();
weldJointDef.initialize(body, revolutingBody, body.getWorldCenter());
world.createJoint(weldJointDef);
}
public static class Ball
{
private final float sizeInWorld;
private final Body body;
public Ball(World world, float x, float y, float sizeInWorld) {
this.sizeInWorld = sizeInWorld;
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.KinematicBody;
bodyDef.position.set(x, y);
bodyDef.fixedRotation = false;
bodyDef.bullet = true;
body = world.createBody(bodyDef);
body.setLinearDamping(0);
CircleShape circle = new CircleShape();
circle.setRadius(sizeInWorld / 2.0f);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = circle;
fixtureDef.friction = 0f;
fixtureDef.restitution = 1f;
fixtureDef.density = 1f;
circle.dispose();
body.createFixture(fixtureDef);
//body.setLinearVelocity(0, -10);
}
}
public static class BallRevoluting
{
private final Body body;
public BallRevoluting(World world, float x, float y, float sizeInWorld) {
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(x, y);
bodyDef.fixedRotation = false;
bodyDef.awake = true;
bodyDef.bullet = true;
body = world.createBody(bodyDef);
body.setLinearDamping(0);
CircleShape circle = new CircleShape();
circle.setRadius(sizeInWorld / 2);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = circle;
fixtureDef.friction = 0.1f;
fixtureDef.isSensor = true;
fixtureDef.restitution = 0;
fixtureDef.density = 1f;
body.createFixture(fixtureDef);
circle.dispose();
}
}
}