/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
/*
* Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package com.badlogic.gdx.tests.box2d;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
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.Fixture;
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.utils.TimeUtils;
/** Base class for all Box2D Testbed tests, all subclasses must implement the createWorld() method.
*
* @author badlogicgames@gmail.com */
public abstract class Box2DTest implements ApplicationListener, InputProcessor {
/** the camera **/
protected OrthographicCamera camera;
/** the renderer **/
protected Box2DDebugRenderer renderer;
SpriteBatch batch;
BitmapFont font;
/** our box2D world **/
protected World world;
/** ground body to connect the mouse joint to **/
protected Body groundBody;
/** our mouse joint **/
protected MouseJoint mouseJoint = null;
/** a hit body **/
protected Body hitBody = null;
protected abstract void createWorld (World world);
/** temp vector **/
protected Vector2 tmp = new Vector2();
@Override
public void render () {
// update the world with a fixed time step
long startTime = TimeUtils.nanoTime();
world.step(Gdx.app.getGraphics().getDeltaTime(), 3, 3);
float updateTime = (TimeUtils.nanoTime() - startTime) / 1000000000.0f;
startTime = TimeUtils.nanoTime();
// clear the screen and setup the projection matrix
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
// render the world using the debug renderer
renderer.render(world, camera.combined);
float renderTime = (TimeUtils.nanoTime() - startTime) / 1000000000.0f;
batch.begin();
font.draw(batch, "fps:" + Gdx.graphics.getFramesPerSecond() + ", update: " + updateTime + ", render: " + renderTime, 0, 20);
batch.end();
}
@Override
public void create () {
// setup the camera. In Box2D we operate on a
// meter scale, pixels won't do it. So we use
// an orthographic camera with a viewport of
// 48 meters in width and 32 meters in height.
// We also position the camera so that it
// looks at (0,16) (that's where the middle of the
// screen will be located).
camera = new OrthographicCamera(48, 32);
camera.position.set(0, 15, 0);
// create the debug renderer
renderer = new Box2DDebugRenderer();
// create the world
world = new World(new Vector2(0, -10), true);
// we also need an invisible zero size ground body
// to which we can connect the mouse joint
BodyDef bodyDef = new BodyDef();
groundBody = world.createBody(bodyDef);
// call abstract method to populate the world
createWorld(world);
batch = new SpriteBatch();
font = new BitmapFont(Gdx.files.internal("data/arial-15.fnt"), false);
}
@Override
public void dispose () {
renderer.dispose();
world.dispose();
renderer = null;
world = null;
mouseJoint = null;
hitBody = null;
}
@Override
public boolean keyDown (int keycode) {
return false;
}
@Override
public boolean keyTyped (char character) {
return false;
}
@Override
public boolean keyUp (int keycode) {
return false;
}
/** we instantiate this vector and the callback here so we don't irritate the GC **/
Vector3 testPoint = new Vector3();
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(testPoint.x, testPoint.y)) {
hitBody = fixture.getBody();
return false;
} else
return true;
}
};
@Override
public boolean touchDown (int x, int y, int pointer, int button) {
// translate the mouse coordinates to world coordinates
camera.unproject(testPoint.set(x, y, 0));
// ask the world which bodies are within the given
// bounding box around the mouse pointer
hitBody = null;
world.QueryAABB(callback, testPoint.x - 0.0001f, testPoint.y - 0.0001f, testPoint.x + 0.0001f, testPoint.y + 0.0001f);
if (hitBody == groundBody) hitBody = null;
// ignore kinematic bodies, they don't work with the mouse joint
if (hitBody != null && hitBody.getType() == BodyType.KinematicBody) return false;
// if we hit something 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(testPoint.x, testPoint.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) {
// if a mouse joint exists we simply update
// the target of the joint based on the new
// mouse coordinates
if (mouseJoint != null) {
camera.unproject(testPoint.set(x, y, 0));
mouseJoint.setTarget(target.set(testPoint.x, testPoint.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 (mouseJoint != null) {
world.destroyJoint(mouseJoint);
mouseJoint = null;
}
return false;
}
@Override
public boolean mouseMoved (int x, int y) {
return false;
}
@Override
public boolean scrolled (int amount) {
return false;
}
public void pause () {
}
public void resume () {
}
public void resize (int width, int height) {
}
}