Good evening everyone.
I'm requesting your help ! I'm discovering Box2D but I'm already struggling with a few things :
My gravity is reversed and I don't understand why.
And my second problem is that I can't manage to draw it in a debugged way.
I had to manually draw it to actually see something.
Here is a code sample to play with !
package tests;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import engine.jbox2d.collision.shapes.CircleShape;
import engine.jbox2d.collision.shapes.PolygonShape;
import engine.jbox2d.common.Vec2;
import engine.jbox2d.dynamics.Body;
import engine.jbox2d.dynamics.BodyDef;
import engine.jbox2d.dynamics.BodyType;
import engine.jbox2d.dynamics.FixtureDef;
import engine.jbox2d.dynamics.World;
public class JBox2DTest extends JPanel
{
private static final long serialVersionUID = 7173873195721096625L;
// model
private World world;
private Ball ball;
public JBox2DTest()
{
// Création du monde
world = new World(new Vec2(0.0f, -9.81f));
// Création d'un sol
new Ground(world);
// Création d'une balle
this.ball = new Ball(world);
//Création de la frame
JFrame frame = new JFrame();
frame.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
frame.setVisible(true);
//Loop
Timer simulationTimer = new Timer(10, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
world.step(1.0f / 60.0f, 6, 2);
repaint();
}
});
simulationTimer.start();
}
public void paint(Graphics g)
{
g.setColor(Color.BLACK);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
ball.draw((Graphics2D) g);
}
public static void main(String[] args)
{
new JBox2DTest();
}
}
class Ground
{
Body body;
public Ground(World world)
{
BodyDef groundBodyDef = new BodyDef();
groundBodyDef.position.set(0.0f, -10.0f);
body = world.createBody(groundBodyDef);
PolygonShape groundBox = new PolygonShape();
groundBox.setAsBox(800.0f, 10);
body.createFixture(groundBox, 0.0f);
}
}
class Ball
{
private Body body;
public Ball(World world)
{
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.DYNAMIC; // Sujet aux forces
bodyDef.position.set(800/2, 600/2);
this.body = world.createBody(bodyDef);
CircleShape dynamicCirle = new CircleShape();
dynamicCirle.setRadius(10f);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = dynamicCirle;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
fixtureDef.restitution = 0.3f;
body.createFixture(fixtureDef);
}
public void draw(Graphics2D g2d)
{
g2d.setColor(Color.RED);
Vec2 ballPos = body.getPosition();
float ballRadius = body.getFixtureList().getShape().getRadius();
g2d.drawOval(
(int) (ballPos.x - ballRadius),
(int) (ballPos.y - ballRadius),
(int) (ballRadius * 2),
(int) (ballRadius * 2)
);
}
}
Thanks a lot for your help !
Best regards,
Alann
It apparently depends on the implementation of Box2D you are using. Gravity may been reversed in your version:
world = new World(new Vec2(0.0f, -9.81f));
should be without the minus:
world = new World(new Vec2(0.0f, 9.81f));
Related
I had to adjust my scene coordinates to find useful gravity for my objects.
So I decided to reduce the screen coordinates so that the gravity behavior on my objects was getting ok.
But I have a problem right now, is that when I type sprite.setPosition(0,0), the sprite seems doesn't appear in the right place.
Here is my code:
package com.mygdx.game;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.InputAdapter;
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.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.World;
public class MyGdxGame extends ApplicationAdapter {
public OrthographicCamera camera;
Sound sound;
BitmapFont font,font2;
SpriteBatch batch;
Texture img1,img2,img3,img4,img5,img6;
Sprite sprite1,sprite2,sprite3;
World world;
Body solKose,sagKose,ustKose,altKose,body1,body2,body3;
int width=0;
float axes=0;
private int enemyGoalCounter, playerGoalCounter=0;
Sphere _player;
Sphere _enemyPlayer;
Sphere ball;
Vector2 directionVector;
Vector3 touchCoordinate;
int direction=-1;
#Override
public void create () {
camera = new OrthographicCamera(Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
camera.viewportWidth = Gdx.graphics.getWidth()/100;
camera.viewportHeight = Gdx.graphics.getHeight()/100;
camera.update();
font = new BitmapFont();
font2 = new BitmapFont();
font.setColor(Color.RED);
font2.setColor(Color.BLUE);
touchCoordinate = new Vector3(0,0,0);
sound = Gdx.audio.newSound(Gdx.files.internal("taktak.wav"));
batch = new SpriteBatch();
img1 = new Texture("sphere2.png");
img2 = new Texture("sphere2.png");
img3 = new Texture("ball.png");
sprite1 = new Sprite(img1);
sprite2 = new Sprite(img2);
sprite3 = new Sprite(img3);
sprite1.setPosition(0,0);
sprite2.setPosition(0,0);
world = new World(new Vector2(0, 0),true);
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(sprite1.getX()/2,sprite1.getY()/2);
body1 = world.createBody(bodyDef);
CircleShape shape = new CircleShape();
shape.setRadius(1f);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
fixtureDef.restitution=0.6f;
body1.createFixture(fixtureDef);
shape.dispose();
BodyDef bodyDef2 = new BodyDef();
bodyDef2.type = BodyDef.BodyType.DynamicBody;
bodyDef2.position.set(sprite2.getX()/2,sprite2.getY()/2);
body2 = world.createBody(bodyDef2);
CircleShape shape2 = new CircleShape();
shape2.setRadius(1f);
FixtureDef fixtureDef2 = new FixtureDef();
fixtureDef2.shape = shape2;
fixtureDef.restitution=0.6f;
body2.createFixture(fixtureDef2);
shape2.dispose();
}
#Override
public void render () {
System.out.println();
camera.update();
play();
inputController(); // A function for keyboard input controlling
math(); // For Collision controlling
world.step(1f/60f, 6, 2);
sprite1.setPosition(body1.getPosition().x,body1.getPosition().y);
sprite2.setPosition(body2.getPosition().x,body2.getPosition().y);
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.draw(sprite1,sprite1.getX(),sprite1.getY(),2,2);
batch.draw(sprite2,sprite2.getX(),sprite2.getY(),2,2);
batch.end();
}
#Override
public void dispose () {
batch.dispose();
world.dispose();
}
void play() {
}
void inputController() {
if(Gdx.input.isTouched()){
Vector2 directionVector = new Vector2(0,0);
touchCoordinate.x = Gdx.input.getX();
touchCoordinate.y = Gdx.input.getY();
camera.unproject(touchCoordinate);
double distance = Math.pow(sprite1.getWidth()/2,2)-(Math.pow((sprite1.getOriginX()-touchCoordinate.x),2)+Math.pow((sprite1.getOriginY()-touchCoordinate.y),2));
System.out.println(touchCoordinate);
if(distance>0){
body1.applyForceToCenter(20,20,true);
}else if(distance==0){
body1.applyForceToCenter(20,20,true);
}else{
////
}
}
//_player.setCenterPosition(touchCoordinate.x,touchCoordinate.y);
}
void math() {
goalSystem();
}
void restartPositions(){
}
void goalSystem() {
/*
if(!goal) {
if(ball.getCenterPosition(ball.position.x, ball.position.y).y < 20) {
if((ball.getCenterPosition(ball.position.x, ball.position.y).x >= Gdx.graphics.getWidth()/2-325/2) && (ball.getCenterPosition(ball.position.x, ball.position.y).x <= Gdx.graphics.getWidth()/2+325/2)) {
goal=true;
enemyGoalCounter++;
restartPositions();
}
}
if(ball.getCenterPosition(ball.position.x, ball.position.y).y > Gdx.graphics.getHeight()-20) {
if((ball.getCenterPosition(ball.position.x, ball.position.y).x >= Gdx.graphics.getWidth()/2-325/2) && (ball.getCenterPosition(ball.position.x, ball.position.y).x <= Gdx.graphics.getWidth()/2+325/2)) {
goal=true;
playerGoalCounter++;
restartPositions();
}
}
}
*/
}
public class Sphere{
Vector2 firstPos,firstDirection;
float firstSpeed;
Vector2 position, direction,centerPosition;
float x,y,width,height;
float radius;
float xCenter,yCenter;
float speed;
float velocity;
public Sphere(float x, float y, float radius, float width, float height) {
this.x = x;
this.y = y;
this.radius = radius;
this.width = width;
this.height = height;
this.position = new Vector2(x,y);
this.direction = new Vector2(x,y);
this.speed = 0;
this.firstSpeed = speed;
this.firstPos = new Vector2(x,y);
this.firstDirection = new Vector2(x,y);
}
Vector2 getCenterPosition(float x, float y) {
centerPosition = new Vector2(x+width/2,y);
return centerPosition;
}
void setCenterPosition(float x, float y){
position.x = x-width/2;
position.y = y-height/2;
}
void setSpeed(float speed) {
this.speed = speed;
}
float getSpeed() {
return this.speed;
}
}
}
Image:
I think the issue is the camera and viewport not a rendered position issue. Your code to set the camera and viewport can be changed. If you are using viewports -You- decide the coordinate range for rendering, its arbitary. This rendering is -then- scaled as you like to fit the screen. In your case you get the width and height of the device (which is not constant across devices even aspect ratio changes) before setting your own viewport parameters?
So try instead
camera = new OrthographicCamera(100, 500); //You pick as you like
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0); //centre camera
viewport = new FitViewport(camera.viewportWidth, camera.viewportHeight, camera);//stretch proportionally
Read this as well
How Camera works in Libgdx and together with Viewport
(Also you didn't add the offset to y here.
Vector2 getCenterPosition(float x, float y) {
centerPosition = new Vector2(x+width/2,y);
....
)
Output is a straight line, but should be a curve.
Not sure if the problem is maths or code.
I know the problem is in the bit where it says (Math.sin(sum3.getValue()) however I can't work out what it should be...
I have for some reason got a post it note that say I(V)=SIN(V); which I imagine is what I am trying to implement to get an I(V) curve.
Sum3 is (V) from a different class.
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
public class GraphApp extends JFrame {
int x,y;
int ax,by;
IVChar sum3 = new IVChar();
//create a window in which the graph will be shown
public GraphApp(){
setTitle("Graph App");
setSize(700,700);
setResizable(true);
setVisible(true);
setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
x = 30;
y = 300;
}
// create the axis
#Override
public void paint(Graphics g){
g.drawLine(300, 30, 300, 600); // y axis
g.drawLine(30, 300, 600, 300); // x axis
g.setColor(Color.blue);//colour of drawLine
g.fillOval(x, y, 3, 3);
g.drawString("I", 310, 40);
g.drawString("V'", 600, 314);
run();
repaint(); //makes it run again
}
// implement and draw graphical functions
public void run(){
try{
Thread.sleep(10); //speed line is drawn
float ax,by;
ax = x-300;
by = y-300;
//specify the function
by = (float) Math.sin(sum3.getValue());//makes a sin wave
x = (int) (ax + 300);
y = (int) (300 - by);
x++;
}catch(Exception e){
System.out.println("Error!");
}
}
public static void main(String[]args){
new GraphApp();
}
}
You can use javax.swing.Timer to animate drawing a curve:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class GraphApp extends JFrame {
public GraphApp(){
setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
add(new SineWave());
pack();
setVisible(true);
}
public static void main(String[]args){
SwingUtilities.invokeLater(()->new GraphApp());
}
}
class SineWave extends JPanel{
//use constants for better readability
private static final double W = 600, H = 700, AMPLITUDE = H/3;
private static final int MARGIN =30, GAP = 15,DOT_SIZE = 3, SPEED = 10;
//starting point
private double x = MARGIN;
private final double y = H/2;
private final int dX = 1; //x increment
//you need to use doubles to avoid rounding error and have use non integer coordinates
private final List<Point2D.Double> points;
private final Timer timer;
public SineWave() {
setPreferredSize(new Dimension((int)W, (int)H));
points = new ArrayList<>();
timer = new Timer(SPEED, e->addPoints()); //timer to add sine points
timer.start();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
Shape xAxis = new Line2D.Double(MARGIN, H/2, W-MARGIN, H/2);
g2.draw(xAxis);
Shape yAxis = new Line2D.Double(W/2, MARGIN, W/2, H-MARGIN);
g2.draw(yAxis);
g.drawString("I", (int)(W/2-GAP),MARGIN);
g.drawString("V'", (int)(W-GAP), (int)(H/2+GAP));
g2.setColor(Color.blue);//colour of graph
for(Point2D.Double p : points){
Shape point = new Ellipse2D.Double(p.getX(), p.getY(),DOT_SIZE , DOT_SIZE);
g2.draw(point);
}
}
private void addPoints() {
double angel = - Math.PI + 2* Math.PI * ((x-MARGIN)/(W- 2*MARGIN));//angle in radians
double newY = y + AMPLITUDE * Math.sin(angel);
points.add(new Point2D.Double(x, newY));
x += dX;
if(x >= W-MARGIN) {
timer.stop();
}
repaint();
}
}
This question already has an answer here:
How to make player get destroyed through camera?
(1 answer)
Closed 5 years ago.
I've been having some trouble making the player get destroyed through the camera. In my application, I made the camera follow the player(the ball). But the camera can only follow the ball upwards. So what I want to accomplish is, when the player(the ball) reaches the bottom of the interface(the screen) it gets destroyed. After it gets destroyed it would be good, if a new activity(new screen) pops up, that says "Game over". Thanks a lot for the great support. Please take a look at the interface of the application.
package com.luca.tuninga;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.utils.viewport.Viewport;
public class MyGdxGame extends ApplicationAdapter {
public static float APP_FPS = 60f;
public static int V_WIDTH = 480;
public static int V_HEIGHT = 640;
Box2DDebugRenderer b2dr;
World world;
Body ballBody;
OrthographicCamera camera;
float cameraMaxY;
#Override
public void create() {
world = new World(new Vector2(0, -9.8f), false);
b2dr = new Box2DDebugRenderer();
camera = new OrthographicCamera();
camera.setToOrtho(false, V_WIDTH, V_HEIGHT);
cameraMaxY = camera.position.y;
ballBody = createBall();
createWalls();
}
private void update() {
world.step(1f / APP_FPS, 6, 2);
if (Gdx.input.isTouched()) {
ballBody.setLinearVelocity(0, MathUtils.clamp(ballBody.getLinearVelocity().y, 0, 3));
ballBody.applyForceToCenter(new Vector2(0, 650f), false);
}
if (ballBody.getPosition().y * 32 > cameraMaxY) {
camera.translate(0, (ballBody.getPosition().y * 32) - cameraMaxY);
camera.update();
cameraMaxY = camera.position.y;
}
}
#Override
public void render() {
Gdx.gl.glClearColor(.25f, .25f, .25f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
update();
b2dr.render(world, camera.combined.cpy().scl(32f));
}
#Override
public void dispose() {
super.dispose();
world.dispose();
}
private Body createBall() {
Body body;
BodyDef def = new BodyDef();
def.type = BodyDef.BodyType.DynamicBody;
def.fixedRotation = true;
def.position.set(camera.position.x/ 32 + .5f, camera.position.y/ 32);
def.position.set(camera.position.x, camera.position.y);
def.gravityScale = 3;
CircleShape shape = new CircleShape();
shape.setRadius(0.5f);
body = world.createBody(def);
body.createFixture(shape, 1.0f);
return body;
}
private void createWalls() {
Body body;
BodyDef def = new BodyDef();
def.type = BodyDef.BodyType.StaticBody;
def.fixedRotation = true;
PolygonShape shape = new PolygonShape();
shape.setAsBox(1, 200 / 32);
for(int i = 0; i < 20 ; i++) {
def.position.set(1.01f, i * (200 / 32));
body = world.createBody(def);
body.createFixture(shape, 1.0f);
def.position.set(V_WIDTH / 32 - 1, i * (200 / 32));
body = world.createBody(def);
body.createFixture(shape, 1.0f);
}
}
}
#Override
public void render() {
Gdx.gl.glClearColor(.25f, .25f, .25f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if (ballBody.getPosition().y < camera.position.y){
destroyBall();
} else if (ballBody.getPosition().y > camera.position.y + 300){
camera.position.set(ballBody.getPosition().y - 300);
}
update();
b2dr.render(world, camera.combined.cpy().scl(32f));
}
I hope this works. You can write destroyBall() method yourself.
I've just began to work with the Libgdx's Box2d Engine but i simply do not understand when the methods of the Contactlistener should be called. There is on the one hand "begin contact" and on the other "end contact". Where should i call them, to get the Number of of a certain fixture touching others? And how do I implement the Contactlistener?
A redirec' to a Tutorial would answer my Question. I didn't find anything while searching google.
This one helped me a lot but it is written for C++ and does not refer to the implementation into a main-gamecircle.
Thx for helping me ;)
Here's a short example for libgdx. It shows how to create a ContactListener to show which fixtures are involved when contacts are made and broken. It also shows the use of world.getContactList() which will return a list of contacts that still exist after the physics step. This may miss contacts that were made and broken during the course of the physics step. If you are interested in these then you will want to implement a ContactListener, using beginContact() to detect when contacts are made and endContact() to detect when they are broken.
package hacks;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
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.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
public class Box2DDemoMain extends com.badlogic.gdx.Game {
private static final float SCALING = 0.1f;
private Box2DDebugRenderer debugRenderer;
private OrthographicCamera camera;
private World world;
#Override
public void create() {
debugRenderer = new Box2DDebugRenderer();
camera = new OrthographicCamera();
createWorld();
createCollisionListener();
createGround();
createBox();
}
private void createWorld() {
Vector2 gravity = new Vector2(0, -10);
world = new World(gravity, true);
}
private void createCollisionListener() {
world.setContactListener(new ContactListener() {
#Override
public void beginContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA();
Fixture fixtureB = contact.getFixtureB();
Gdx.app.log("beginContact", "between " + fixtureA.toString() + " and " + fixtureB.toString());
}
#Override
public void endContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA();
Fixture fixtureB = contact.getFixtureB();
Gdx.app.log("endContact", "between " + fixtureA.toString() + " and " + fixtureB.toString());
}
#Override
public void preSolve(Contact contact, Manifold oldManifold) {
}
#Override
public void postSolve(Contact contact, ContactImpulse impulse) {
}
});
}
private void createGround() {
PolygonShape groundShape = new PolygonShape();
groundShape.setAsBox(50, 1);
BodyDef groundBodyDef = new BodyDef();
groundBodyDef.type = BodyType.StaticBody;
groundBodyDef.position.set(0, -20);
Body groundBody = world.createBody(groundBodyDef);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = groundShape;
groundBody.createFixture(fixtureDef);
groundShape.dispose();
}
private void createBox() {
PolygonShape boxShape = new PolygonShape();
boxShape.setAsBox(1, 1);
BodyDef boxBodyDef = new BodyDef();
boxBodyDef.position.set(0, 20);
boxBodyDef.angle = MathUtils.PI / 32;
boxBodyDef.type = BodyType.DynamicBody;
boxBodyDef.fixedRotation = false;
Body boxBody = world.createBody(boxBodyDef);
FixtureDef boxFixtureDef = new FixtureDef();
boxFixtureDef.shape = boxShape;
boxFixtureDef.restitution = 0.75f;
boxFixtureDef.density = 2.0f;
boxBody.createFixture(boxFixtureDef);
boxShape.dispose();
}
#Override
public void resize(int width, int height) {
super.resize(width, height);
float cameraWidth = Gdx.graphics.getWidth() * SCALING;
float cameraHeight = Gdx.graphics.getHeight() * SCALING;
camera.setToOrtho(false, cameraWidth, cameraHeight);
camera.position.set(0, 0, 0);
}
#Override
public void render() {
super.render();
world.step(Gdx.graphics.getDeltaTime(), 8, 3);
int numContacts = world.getContactCount();
if (numContacts > 0) {
Gdx.app.log("contact", "start of contact list");
for (Contact contact : world.getContactList()) {
Fixture fixtureA = contact.getFixtureA();
Fixture fixtureB = contact.getFixtureB();
Gdx.app.log("contact", "between " + fixtureA.toString() + " and " + fixtureB.toString());
}
Gdx.app.log("contact", "end of contact list");
}
Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
camera.update();
debugRenderer.render(world, camera.combined);
}
public static void main(String[] args) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.title = Box2DDemoMain.class.getName();
config.width = 800;
config.height = 480;
config.fullscreen = false;
config.useGL20 = true;
config.useCPUSynch = true;
config.forceExit = true;
config.vSyncEnabled = true;
new LwjglApplication(new Box2DDemoMain(), config);
}
}
you dont have to call those methods anywhere. just create a class and implement ContactListener in it. now in your code just use world.setContactListener(ContactListener listener) .
Whenever collision occur in your world, all 4 methods will be called . You will get fixtureA and fixtureB of 2 colliding bodies
I'm testing the newest Box2d Testbed using Libgdx. It appears they aren't working and need to know if anyone else is having the same issue(s). The first one is called Conveyor Belt, https://github.com/ansman/box2d/blob/master/Testbed/Tests/ConveyorBelt.h
which I converted to:
package com.badlogic.gdx.tests.box2d;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
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.joints.FrictionJointDef;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.EdgeShape;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.Transform;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.tests.utils.GdxTest;
public class ConveyorBelt extends Box2DTest {
Body m_body;
Fixture platform;
#Override
protected void createWorld (World world) {
world.setGravity(new Vector2(0, -10));
/** amount of bounce when colliding */
float k_restitution = 0.4f;
Body ground;
{ /** x,y of entire shapes */
BodyDef bd = new BodyDef();
bd.position.set(0, 20);
ground = world.createBody(bd);
PolygonShape shape = new PolygonShape();
shape.setAsEdge(new Vector2(-20.0f, 0.0f), new Vector2(20.0f, 0.0f));
ground.createFixture(shape, 0.0f);
shape.dispose();
}
// Platform
{
BodyDef bd = new BodyDef();
bd.position.set(-5.0f, 5.0f);
Body body = world.createBody(bd);
PolygonShape shape = new PolygonShape();
shape.setAsBox(10.0f, 0.5f);
FixtureDef fd = new FixtureDef();
fd.shape = shape;
fd.friction = 0.8f;
platform = body.createFixture(fd);
}
// Boxes
for (int i = 0; i < 5; ++i)
{
BodyDef bd = new BodyDef();
bd.type = BodyType.DynamicBody;
bd.position.set(-10.0f + 2.0f * i, 7.0f);
Body body = world.createBody(bd);
PolygonShape shape = new PolygonShape();
shape.setAsBox(0.5f, 0.5f);
body.createFixture(shape, 20.0f);
}
}
/* void PreSolve(Contact contact, Manifold oldManifold)
{
Test extends PreSolve(contact, oldManifold);
Fixture fixtureA = contact.getFixtureA();
Fixture fixtureB = contact.getFixtureB();
if (fixtureA == platform)
{
contact.setTangentSpeed(5.0f);
}
if (fixtureB == platform)
{
contact.setTangentSpeed(-5.0f);
}
}
void Step(Settings settings)
{
Test extends Step(settings);
}
static Test Create()
{
return new ConveyorBelt;
}
Fixture platform;*/
}
This is working by putting it in with the other Box2D tests but I noticed a few things.
Libgdx doesn't have a setTangentSpeed method in its Contact.java class
Settings has to use the org.jbox2d.common import
Test can't be resolved to a type
I also tried using Breakable, https://github.com/ansman/box2d/blob/master/Testbed/Tests/Breakable.h
Which was converted to
package com.badlogic.gdx.tests.box2d;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
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.CircleShape;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.EdgeShape;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.physics.box2d.joints.RevoluteJointDef;
import com.badlogic.gdx.backends.gwt.*;
public class Breakable extends Box2DTest {
Body body1;
Vector2 m_velocity;
float m_angularVelocity;
PolygonShape m_shape1;
PolygonShape m_shape2;
Fixture m_piece1;
Fixture m_piece2;
boolean m_broke;
boolean m_break;
/**
* #see org.jbox2d.testbed.framework.TestbedTest#initTest()
*/
#Override
protected void createWorld (World world) {
world.setGravity(new Vector2(0, -10));
Body ground;
// Ground body
{
BodyDef bd = new BodyDef();
ground = world.createBody(bd);
PolygonShape shape = new PolygonShape();
shape.setAsEdge(new Vector2(-40.0f, 0.0f), new Vector2(40.0f, 0.0f));
ground.createFixture(shape, 0.0f);
shape.dispose();
}
// Breakable dynamic body
{
BodyDef bd = new BodyDef();
bd.type = BodyType.DynamicBody;
bd.position.set(0.0f, 40.0f);
bd.angle = 0.25f * MathUtils.PI;
body1 = world.createBody(bd);
m_shape1 = new PolygonShape();
m_shape1.setAsBox(0.5f, 0.5f, new Vector2(-0.5f, 0.0f), 0.0f);
m_piece1 = body1.createFixture(m_shape1, 1.0f);
m_shape2 = new PolygonShape();
m_shape2.setAsBox(0.5f, 0.5f, new Vector2(0.5f, 0.0f), 0.0f);
m_piece2 = body1.createFixture(m_shape2, 1.0f);
}
m_break = false;
m_broke = false;
}
void PostSolve(Contact contact, ContactImpulse impulse)
{
if (m_broke)
{
// The body already broke.
return;
}
// Should the body break?
int count = contact.getManifold().pointCount;
float maxImpulse = 0.0f;
for (int i = 0; i < count; ++i)
{
maxImpulse = MathUtils.max(maxImpulse, impulse.normalImpulses[i]);
}
if (maxImpulse > 40.0f)
{
// Flag the body for breaking.
m_break = true;
}
}
void Break()
{
// Create two bodies from one.
Body body1 = m_piece1.getBody();
Vector2 center = body1.getWorldCenter();
body1.destroyFixture(m_piece2);
m_piece2 = null;
BodyDef bd = new BodyDef();
bd.position = body1.getPosition();
bd.angle = body1.getAngle();
Body body2 = world.createBody(bd);
m_piece2 = body2.createFixture(m_shape2, 1.0f);
}
}
// Compute consistent velocities for new bodies based on
// cached velocity.
Vector2 center1 = body1.getWorldCenter();
Vector2 center2 = body2.getWorldCenter();
Vector2 velocity1 = m_velocity.add(Vector2.crs(m_angularVelocity, center1.sub(center)));
Vector2 velocity2 = m_velocity.add(Vector2.crs(m_angularVelocity, center2.sub(center)));
body1.setAngularVelocity(m_angularVelocity);
body1.setLinearVelocity(velocity1);
body2.setAngularVelocity(m_angularVelocity);
body2.setLinearVelocity(velocity2);
}
void Step(Settings settings)
{
if (m_break)
{
Break();
m_broke = true;
m_break = false;
}
// Cache velocities to improve movement on breakage.
if (m_broke == false)
{
m_velocity = body1.getLinearVelocity();
m_angularVelocity = body1.getAngularVelocity();
}
Test = Step(settings);
}
static Test Create()
{
return new Breakable;
}
Body m_body1;
}
I Noticed:
Libgdx doesn't have pointCount in Manifold.java. One quick fix was to change to getWorldManifold but didn't do any good
Vector2.java doesn't contain a crs( float x, Vector2 v) which the Vectors for m_velocity won't allow
Settings doesn't exist, unless I use the com.jbox2d.common import
How do I get these to work in Libgdx if these methods aren't included? Is Libgdx not updated anymore? I want to use these but it seems they are behind with porting. I even noticed in Contact.java he stopped just before the code for setTangentSpeed. I have added gwt jars to my tests with no benefit.
If you want the 2.2.1 version of jbox2d, you can grab a build from the trunk. It's almost ready for a release, I just need to do a bit more optimization and inlining.
edit: It looks like libgdx uses it's own wrappers to a the native C++ implementation of Box2D. This is definitely preferable performance-wise, hopefully they will update their library.