7.Libgdx擴充套件學習之Box2D_距離關節 旋轉關節
阿新 • • 發佈:2019-02-17
文章中的概念來自《Box2D》中文手冊
上一節中我們介紹了關節和滑鼠關節的使用方法,本章中我們繼續介紹距離關節和旋轉關節
距離關節(DistanceJoint)
距離關節是兩個物體上各有一點,兩點之間的距離必須固定不變。當指定一個距離關節時,兩個物體必須已在應有的位置上。之後指定世界座標中的兩個錨點。第一個錨點連線到物體1,第二個錨點連線到物體2。這兩點隱含距離約束的長度。
// Define the distance joint
DistanceJointDef distanceJointDef = new DistanceJointDef();
// 距離關節連線的2個Body
distanceJointDef.bodyA=smallBall;
distanceJointDef.bodyB=bigBall;
// 是否允許兩個Body碰撞
distanceJointDef.collideConnected=false;
// 兩個Body之間的距離
distanceJointDef.length = 2.0f;
關節可以具有彈性,通過定義2個常數:頻率(frequency)和阻尼率(damping ratio)。頻率影響震動的快慢,典型情況下頻率要小於時間步的一半。比如每秒執行60次時間步,距離關節的頻率就要小於30。
阻尼率無單位,取值在「0,1」之間。當阻尼率設定為1時,沒有振動。
// 下面2個引數使關節具有彈性
distanceJointDef.dampingRatio = 0.4f;
distanceJointDef.frequencyHz = 4.0f;
下面是測程式碼
/**
* 距離關節
*/
public class DistanceJointTest extends ApplicationAdapter {
World world;
Box2DDebugRenderer box2DDebugRenderer;
Body hitBody, groundBody;
OrthographicCamera camera;
Vector3 point = new Vector3();
float scene_width = 12.8f;
float scene_height = 7.2f;
QueryCallback callback = new QueryCallback() {
@Override
public boolean reportFixture(Fixture fixture) {
if (fixture.testPoint(point.x, point.y)) {
hitBody = fixture.getBody();
return false;
} else
return true;
}
};
@Override
public void create() {
world = new World(new Vector2(0.0f, -9.8f), true);
box2DDebugRenderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(scene_width, scene_height);
camera.position.set(scene_width / 2, scene_height / 2, 0);
camera.update();
groundBody = createGroundWall();
Gdx.input.setInputProcessor(new HandA());
createDistanceJoint();
}
@Override
public void render() {
world.step( 1/ 60f, 6, 2);
Gdx.gl.glClearColor(0.39f, 0.58f, 0.92f, 1.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
box2DDebugRenderer.render(world, camera.combined);
}
@Override
public void dispose() {
world.dispose();
box2DDebugRenderer.dispose();
}
public void createDistanceJoint() {
Body smallBall = createSphere(BodyDef.BodyType.DynamicBody, 0f, 3.75f, .8f, .8f, .4f, .25f);
Body bigBall = createSphere(BodyDef.BodyType.DynamicBody, 3.0f, 4.5f, .8f, 1f, .4f, .5f);
// Define the distance joint
DistanceJointDef distanceJointDef = new DistanceJointDef();
distanceJointDef.bodyA=smallBall;
distanceJointDef.bodyB=bigBall;
distanceJointDef.collideConnected=false;
distanceJointDef.length = 5.0f;
// 下面2個引數使關節具有彈性
distanceJointDef.dampingRatio = 0.4f;
distanceJointDef.frequencyHz = 4.0f;
distanceJointDef.localAnchorA.set(0,0);
distanceJointDef.localAnchorB.set(0,0);
world.createJoint(distanceJointDef);
}
private Body createSphere(BodyDef.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;
}
public Body createGroundWall() {
BodyDef bodyDef = new BodyDef();
bodyDef.position.set(scene_width * 0.5f, 0.2f);
Body body1 = world.createBody(bodyDef);
PolygonShape polygonShape = new PolygonShape();
polygonShape.setAsBox(scene_width * 0.5f, 0.2f);
body1.createFixture(polygonShape, 0.0f);
bodyDef.position.set(0.4f, scene_height * 0.5f);
Body body2 = world.createBody(bodyDef);
polygonShape.setAsBox(0.2f, scene_height * 0.5f);
body2.createFixture(polygonShape, 0);
bodyDef.position.set(12.4f, scene_height * 0.5f);
Body body3 = world.createBody(bodyDef);
polygonShape.setAsBox(0.2f, scene_height * 0.5f);
body3.createFixture(polygonShape, 0);
bodyDef.position.set(scene_width * 0.5f, 7.0f);
Body body4 = world.createBody(bodyDef);
polygonShape.setAsBox(scene_width * 0.5f, 0.2f);
body4.createFixture(polygonShape, 0);
polygonShape.dispose();
return body1;
}
class HandA extends InputAdapter {
MouseJoint mouseJoint;
Vector2 target = new Vector2();
@Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
if (mouseJoint != null) {
camera.unproject(point.set(screenX, screenY, 0));
mouseJoint.setTarget(target.set(point.x, point.y));
}
return false;
}
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
camera.unproject(point.set(screenX, screenY, 0));
hitBody = null;
world.QueryAABB(callback, point.x - 0.0001f, point.y - 0.0001f, point.x + 0.0001f, point.y + 0.0001f);
if (hitBody == null || hitBody.equals(groundBody)) return false;
MouseJointDef mouseJointDef = new MouseJointDef();
mouseJointDef.bodyA = groundBody;
mouseJointDef.bodyB = hitBody;
mouseJointDef.collideConnected = true;
mouseJointDef.target.set(point.x, point.y);
mouseJointDef.maxForce = 1000.0f * hitBody.getMass();
mouseJoint = (MouseJoint) world.createJoint(mouseJointDef);
hitBody.setAwake(true);
return false;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
// 滑鼠關節,不再使用時要銷燬
if (mouseJoint != null) {
world.destroyJoint(mouseJoint);
mouseJoint = null;
}
return false;
}
}
}
旋轉關節(RevoluteJoint)
旋轉關節會強制兩個物體公用一個錨點。旋轉關節只有一個自由度:兩個物體相對旋轉。這稱之為關節角。
要指定一個旋轉關節,需要提供兩個物體以及世界座標的一個錨點,可以參考下面定義:
// Define the revolute joint
RevoluteJointDef revoluteJointDef = new RevoluteJointDef();
revoluteJointDef.bodyA=smallBall;
revoluteJointDef.bodyB=bigBall;
revoluteJointDef.collideConnected=false;
// 指定錨點
revoluteJointDef.localAnchorA.set(0,0);
revoluteJointDef.localAnchorB.set(-2.0f,0);
在Box2D中預設是逆時針旋轉的,此時關節角為正,而且旋轉角也是弧度制的。在建立兩個物體時物理當前的角度是怎樣的,旋轉關節角都為0。
每次執行step後,可以更新馬達的引數。這樣可以實現有些有趣的功能。可以在每個時間步中更新關節速度,使得它像正炫波或者任意一個想要的函式那樣前後擺動
/**
* 旋轉關節
*/
public class RevoluteJointTest extends ApplicationAdapter {
World world;
Box2DDebugRenderer box2DDebugRenderer;
Body hitBody, groundBody;
OrthographicCamera camera;
Vector3 point = new Vector3();
float scene_width = 12.8f;
float scene_height = 7.2f;
QueryCallback callback = new QueryCallback() {
@Override
public boolean reportFixture(Fixture fixture) {
if (fixture.testPoint(point.x, point.y)) {
hitBody = fixture.getBody();
return false;
} else
return true;
}
};
@Override
public void create() {
world = new World(new Vector2(0.0f, -9.8f), true);
box2DDebugRenderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(scene_width, scene_height);
camera.position.set(scene_width / 2, scene_height / 2, 0);
camera.update();
groundBody = createGroundWall();
Gdx.input.setInputProcessor(new HandA());
createRevoluteJoin();
}
@Override
public void render() {
world.step( 1/ 60f, 6, 2);
Gdx.gl.glClearColor(0.39f, 0.58f, 0.92f, 1.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
box2DDebugRenderer.render(world, camera.combined);
}
@Override
public void dispose() {
world.dispose();
box2DDebugRenderer.dispose();
}
public void createRevoluteJoin() {
// 第一個Body要設定為Static才能保證第二個Body圍繞第一個旋轉
Body smallBall = createSphere(BodyDef.BodyType.StaticBody, 0f, 3.75f, 1f, 1f, 0f, .25f);
Body bigBall = createSphere(BodyDef.BodyType.DynamicBody, 0f, 3.75f, 1f, 1f, 0f, .5f);
// Define the revolute joint
RevoluteJointDef revoluteJointDef = new RevoluteJointDef();
revoluteJointDef.bodyA=smallBall;
revoluteJointDef.bodyB=bigBall;
revoluteJointDef.collideConnected=false;
revoluteJointDef.localAnchorA.set(0,0);
revoluteJointDef.localAnchorB.set(-2.0f,0);
revoluteJointDef.enableMotor=true;
revoluteJointDef.maxMotorTorque=360;
revoluteJointDef.motorSpeed=100f* MathUtils.degreesToRadians;
world.createJoint(revoluteJointDef);
}
private Body createSphere(BodyDef.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;
}
public Body createGroundWall() {
BodyDef bodyDef = new BodyDef();
bodyDef.position.set(scene_width * 0.5f, 0.2f);
Body body1 = world.createBody(bodyDef);
PolygonShape polygonShape = new PolygonShape();
polygonShape.setAsBox(scene_width * 0.5f, 0.2f);
body1.createFixture(polygonShape, 0.0f);
bodyDef.position.set(0.4f, scene_height * 0.5f);
Body body2 = world.createBody(bodyDef);
polygonShape.setAsBox(0.2f, scene_height * 0.5f);
body2.createFixture(polygonShape, 0);
bodyDef.position.set(12.4f, scene_height * 0.5f);
Body body3 = world.createBody(bodyDef);
polygonShape.setAsBox(0.2f, scene_height * 0.5f);
body3.createFixture(polygonShape, 0);
bodyDef.position.set(scene_width * 0.5f, 7.0f);
Body body4 = world.createBody(bodyDef);
polygonShape.setAsBox(scene_width * 0.5f, 0.2f);
body4.createFixture(polygonShape, 0);
polygonShape.dispose();
return body1;
}
class HandA extends InputAdapter {
MouseJoint mouseJoint;
Vector2 target = new Vector2();
@Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
if (mouseJoint != null) {
camera.unproject(point.set(screenX, screenY, 0));
mouseJoint.setTarget(target.set(point.x, point.y));
}
return false;
}
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
camera.unproject(point.set(screenX, screenY, 0));
hitBody = null;
world.QueryAABB(callback, point.x - 0.0001f, point.y - 0.0001f, point.x + 0.0001f, point.y + 0.0001f);
if (hitBody == null || hitBody.equals(groundBody)) return false;
MouseJointDef mouseJointDef = new MouseJointDef();
mouseJointDef.bodyA = groundBody;
mouseJointDef.bodyB = hitBody;
mouseJointDef.collideConnected = true;
mouseJointDef.target.set(point.x, point.y);
mouseJointDef.maxForce = 1000.0f * hitBody.getMass();
mouseJoint = (MouseJoint) world.createJoint(mouseJointDef);
hitBody.setAwake(true);
return false;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
// 滑鼠關節,不再使用時要銷燬
if (mouseJoint != null) {
world.destroyJoint(mouseJoint);
mouseJoint = null;
}
return false;
}
}
}