package com.cookbook.samples;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.Joint;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.QueryCallback;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.physics.box2d.joints.MouseJoint;
import com.badlogic.gdx.physics.box2d.joints.MouseJointDef;
import com.badlogic.gdx.physics.box2d.joints.PrismaticJointDef;
import com.badlogic.gdx.physics.box2d.joints.RevoluteJointDef;
import com.badlogic.gdx.utils.viewport.FitViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
public class Box2DBikeSimulatorSample extends GdxSample {
private static final String TAG = "Box2DBikeSimulatorSample";
private static final float SCENE_WIDTH = 12.80f; // 12.8 metres wide
private static final float SCENE_HEIGHT = 7.20f; // 7.2 metres high
private Viewport viewport;
private Vector3 point;
private SpriteBatch batch;
// General Box2D
Box2DDebugRenderer debugRenderer;
BodyDef defaultDynamicBodyDef;
World world;
Body groundBody;
// Bike components
Body frameBody, frontDamperBody, backDamperBody, backWheelBody, frontWheelBody;
Texture wheelTex, backDamperTex, frontDamperTex, frameTex;
final float WHEEL_WIDTH = 1.25f;
final float BACKDAMPER_WIDTH = 1.13f, BACKDAMPER_HEIGHT = .39f;
final float FRONTDAMPER_WIDTH = .71f, FRONTDAMPER_HEIGHT = 1.069f;
final float FRAME_WIDTH = 1.8f, FRAME_HEIGHT = 1.53f;
// It will allow us to interact with the physics bodies
MouseJoint mouseJoint;
Body hitBody = null;
@Override
public void create () {
super.create();
viewport = new FitViewport(SCENE_WIDTH, SCENE_HEIGHT);
// Center camera to get (0,0) as the origin of the Box2D world
viewport.getCamera().position.set(viewport.getCamera().position.x + SCENE_WIDTH*0.5f,
viewport.getCamera().position.y + SCENE_HEIGHT*0.5f,
0);
viewport.getCamera().update();
batch = new SpriteBatch();
Gdx.input.setInputProcessor(this);
// Create Physics World
world = new World(new Vector2(0,-9.8f), true);
debugRenderer = new Box2DDebugRenderer();
// Creates a ground to avoid objects falling forever
createGround();
point = new Vector3();
createBike();
debugRenderer = new Box2DDebugRenderer();
}
private void createBike() {
// Define images
frameTex = new Texture(Gdx.files.internal("data/box2D/frame.png"));
backDamperTex = new Texture(Gdx.files.internal("data/box2D/backDamper.png"));
frontDamperTex = new Texture(Gdx.files.internal("data/box2D/frontDamper.png"));
wheelTex = new Texture(Gdx.files.internal("data/box2D/wheel.png"));
frontWheelBody = createSphere(BodyType.DynamicBody, 0f, 1f, 0.8f, 0f, 1.0f, WHEEL_WIDTH * .5f);
backWheelBody = createSphere(BodyType.DynamicBody, -1.4f, 1f, 0.8f, 0f, 1.0f, WHEEL_WIDTH * .5f);
frameBody = createPolygon(BodyType.DynamicBody, 0f, 3f, 1f, 0f, 0f, FRAME_WIDTH * .5f, FRAME_HEIGHT * .5f);
frontDamperBody = createPolygon(BodyType.DynamicBody, 0f, 2f, 1f, 0f, 0f, FRONTDAMPER_WIDTH * .5f, FRONTDAMPER_HEIGHT *.5f);
backDamperBody = createPolygon(BodyType.DynamicBody, -1f, 2f, 1f, 0f, 0f, BACKDAMPER_WIDTH *.5f, BACKDAMPER_HEIGHT *.5f);
// Crete joints for the shapes
// Connects front damper with frame
PrismaticJointDef prismaticJointDef = new PrismaticJointDef();
prismaticJointDef.initialize(frontDamperBody, frameBody, new Vector2(0f,0f), new Vector2(-0.5f,1f));
prismaticJointDef.lowerTranslation =-.2f;
prismaticJointDef.upperTranslation = 0f;
prismaticJointDef.enableLimit = true;
prismaticJointDef.localAnchorA.set(-FRONTDAMPER_WIDTH * .5f + 0.065f,FRONTDAMPER_HEIGHT *.5f);
prismaticJointDef.localAnchorB.set(FRAME_WIDTH * .5f - 0.165f,0.1f);
prismaticJointDef.collideConnected=false;
Joint fDamperFrameJoint = world.createJoint(prismaticJointDef);
// Connects front damper with front wheel
RevoluteJointDef revoluteJointDef = new RevoluteJointDef();
revoluteJointDef.bodyA = frontDamperBody;
revoluteJointDef.bodyB = frontWheelBody;
revoluteJointDef.localAnchorA.set(FRONTDAMPER_WIDTH * .5f - 0.065f,-FRONTDAMPER_HEIGHT *.5f);
revoluteJointDef.localAnchorB.set(0,0);
revoluteJointDef.collideConnected=false;
Joint fDamperFwheelJoint = world.createJoint(revoluteJointDef);
//Connects backdamper with frame
RevoluteJointDef revoluteJointDef2 = new RevoluteJointDef();
revoluteJointDef2.bodyA=backDamperBody;
revoluteJointDef2.bodyB=frameBody;
revoluteJointDef2.collideConnected=false;
revoluteJointDef2.localAnchorA.set(BACKDAMPER_WIDTH *.5f,0f);
revoluteJointDef2.localAnchorB.set(-FRAME_WIDTH * .5f + 0.5f,-FRAME_HEIGHT * .5f + 0.25f);
revoluteJointDef2.lowerAngle = -4 * MathUtils.degreesToRadians;
revoluteJointDef2.upperAngle = 26 * MathUtils.degreesToRadians;
revoluteJointDef2.enableLimit = true;
Joint bDamperFrameJoint = world.createJoint(revoluteJointDef2);
//Connects backdamper with backWheel
RevoluteJointDef revoluteJointDef3 = new RevoluteJointDef();
revoluteJointDef3.bodyA=backDamperBody;
revoluteJointDef3.bodyB=backWheelBody;
revoluteJointDef3.collideConnected=false;
revoluteJointDef3.localAnchorA.set(-BACKDAMPER_WIDTH * .5f,-0.1f);
revoluteJointDef3.localAnchorB.set(0,0);
revoluteJointDef3.enableMotor = true;
revoluteJointDef3.maxMotorTorque = 100f;
revoluteJointDef3.motorSpeed = -135f * MathUtils.degreesToRadians;
Joint bDamperBwheelJoint = world.createJoint(revoluteJointDef3);
// Disable contact between certain bodies
MouseJointDef mjd1 = new MouseJointDef();
mjd1.bodyA = frameBody;
mjd1.bodyB = backWheelBody;
Joint frameBwheelJoint = world.createJoint(mjd1);
MouseJointDef mjd2 = new MouseJointDef();
mjd2.bodyA = frameBody;
mjd2.bodyB = frontWheelBody;
Joint frameFwheelJoint = world.createJoint(mjd2);
}
private void createGround() {
float halfGroundWidth = SCENE_WIDTH;
float halfGroundHeight = 0.5f; // 1 meter high
// Create a static body definition
BodyDef groundBodyDef = new BodyDef();
groundBodyDef.type = BodyType.StaticBody;
// Set the ground position to (0,0) -> libgdx coordinates system
//groundBodyDef.position.set(new Vector2(0, -(VIRTUAL_HEIGHT*0.5f) + halfGroundHeight));
groundBodyDef.position.set(halfGroundWidth*0.5f, halfGroundHeight);
// Create a body from the defintion and add it to the world
groundBody = world.createBody(groundBodyDef);
// Create a rectangle shape which will fit the virtual_width and 1 meter high
// (setAsBox takes half-width and half-height as arguments)
PolygonShape groundBox = new PolygonShape();
groundBox.setAsBox(halfGroundWidth * 0.5f, halfGroundHeight);
// Create a fixture from our rectangle shape and add it to our ground body
groundBody.createFixture(groundBox, 0.0f);
// Free resources
groundBox.dispose();
}
@Override
public void resize(int width, int height) {
viewport.update(width, height);
}
@Override
public void dispose() {
wheelTex.dispose();
backDamperTex.dispose();
frontDamperTex.dispose();
frameTex.dispose();
debugRenderer.dispose();
batch.dispose();
world.dispose();
}
private Body createPolygon(BodyType type, float x, float y, float d, float r, float f, float halfwidth, float halfheight) {
BodyDef bodyDef = new BodyDef();
bodyDef.type = type;
bodyDef.position.set(SCENE_WIDTH*0.5f+x,y);
bodyDef.angle=0;
Body square = world.createBody(bodyDef);
FixtureDef fixtureDef=new FixtureDef();
fixtureDef.density=d;
fixtureDef.restitution=r;
fixtureDef.friction=f;
fixtureDef.shape=new PolygonShape();
((PolygonShape) fixtureDef.shape).setAsBox(halfwidth, halfheight);
square.createFixture(fixtureDef);
fixtureDef.shape.dispose();
return square;
}
private Body createSphere(BodyType type, float x, float y, float d, float r, float f, float radius) {
BodyDef bodyDef = new BodyDef();
bodyDef.type = type;
bodyDef.position.set(SCENE_WIDTH*0.5f+x,y);
bodyDef.angle=0;
Body ball = world.createBody(bodyDef);
FixtureDef fixtureDef=new FixtureDef();
fixtureDef.density=d;
fixtureDef.restitution=r;
fixtureDef.friction=f;
fixtureDef.shape=new CircleShape();
fixtureDef.shape.setRadius(radius);
ball.createFixture(fixtureDef);
fixtureDef.shape.dispose();
return ball;
}
@Override
public void render () {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
world.step(1/60f, 6, 2);
batch.begin();
batch.setProjectionMatrix(viewport.getCamera().combined);
batch.draw(
wheelTex,
frontWheelBody.getPosition().x - (WHEEL_WIDTH*.5f), frontWheelBody.getPosition().y - (WHEEL_WIDTH*.5f),
WHEEL_WIDTH*.5f, WHEEL_WIDTH*.5f,
WHEEL_WIDTH, WHEEL_WIDTH,
1f, 1f,
frontWheelBody.getAngle() * MathUtils.radDeg,
0, 0,
wheelTex.getWidth(), wheelTex.getHeight(),
false, false);
batch.draw(
wheelTex,
backWheelBody.getPosition().x - (WHEEL_WIDTH*.5f), backWheelBody.getPosition().y - (WHEEL_WIDTH*.5f),
WHEEL_WIDTH*.5f, WHEEL_WIDTH*.5f,
WHEEL_WIDTH, WHEEL_WIDTH,
1f, 1f,
backWheelBody.getAngle() * MathUtils.radDeg,
0, 0,
wheelTex.getWidth(), wheelTex.getHeight(),
false, false);
batch.draw(
backDamperTex,
backDamperBody.getPosition().x - (BACKDAMPER_WIDTH*.5f), backDamperBody.getPosition().y - (BACKDAMPER_HEIGHT*.5f),
BACKDAMPER_WIDTH*.5f, BACKDAMPER_HEIGHT*.5f,
BACKDAMPER_WIDTH, BACKDAMPER_HEIGHT,
1f, 1f,
backDamperBody.getAngle() * MathUtils.radDeg,
0, 0,
backDamperTex.getWidth(), backDamperTex.getHeight(),
false, false);
batch.draw(
frontDamperTex,
frontDamperBody.getPosition().x - (FRONTDAMPER_WIDTH*.5f), frontDamperBody.getPosition().y - (FRONTDAMPER_HEIGHT*.5f),
FRONTDAMPER_WIDTH*.5f, FRONTDAMPER_HEIGHT*.5f,
FRONTDAMPER_WIDTH, FRONTDAMPER_HEIGHT,
1f, 1f,
frontDamperBody.getAngle() * MathUtils.radDeg,
0, 0,
frontDamperTex.getWidth(), frontDamperTex.getHeight(),
false, false);
batch.draw(
frameTex,
frameBody.getPosition().x - (FRAME_WIDTH*.5f), frameBody.getPosition().y - (FRAME_HEIGHT*.5f),
FRAME_WIDTH*.5f, FRAME_HEIGHT*.5f,
FRAME_WIDTH, FRAME_HEIGHT,
1f, 1f,
frameBody.getAngle() * MathUtils.radDeg,
0, 0,
frameTex.getWidth(), frameTex.getHeight(),
false, false);
batch.end();
debugRenderer.render(world, viewport.getCamera().combined);
}
@Override
public boolean touchDown (int x, int y, int pointer, int button) {
// Mouse coordinates to world coordinates
viewport.getCamera().unproject(point.set(x, y, 0));
if(button == Input.Buttons.RIGHT) {
hitBody = null;
// Check if a interactive body has been right-clicked
world.QueryAABB(callback, point.x - 0.0001f, point.y - 0.0001f, point.x + 0.0001f, point.y + 0.0001f);
if (hitBody == groundBody) hitBody = null;
// if we hit an interactive body we create a new mouse joint
// and attach it to the hit body.
if (hitBody != null) {
MouseJointDef def = new MouseJointDef();
def.bodyA = groundBody;
def.bodyB = hitBody;
def.collideConnected = true;
def.target.set(point.x, point.y);
def.maxForce = 1000.0f * hitBody.getMass();
mouseJoint = (MouseJoint)world.createJoint(def);
hitBody.setAwake(true);
}
}
return false;
}
/** another temporary vector **/
Vector2 target = new Vector2();
@Override
public boolean touchDragged (int x, int y, int pointer) {
// To keep the object responsive after clicking the first time
// It will just update the target coordinates
if (mouseJoint != null) {
viewport.getCamera().unproject(point.set(x, y, 0));
mouseJoint.setTarget(target.set(point.x, point.y));
}
return false;
}
@Override
public boolean touchUp (int x, int y, int pointer, int button) {
// if a mouse joint exists we simply destroy it
if(button == Input.Buttons.RIGHT) {
if (mouseJoint != null) {
world.destroyJoint(mouseJoint);
mouseJoint = null;
}
}
return false;
}
QueryCallback callback = new QueryCallback() {
@Override
public boolean reportFixture (Fixture fixture) {
// if the hit point is inside the fixture of the body
// we report it
if (fixture.testPoint(point.x, point.y)) {
hitBody = fixture.getBody();
return false;
} else
return true;
}
};
}