Units conversion Box2D with weird results - libgdx

i'm using LibGDX and trying to learn box2D, but this units conversions are confusing me, what i think: i got a 256x256 pixels image and want to create a body representing this image, using the 1:32 scale, so everytime i want to pass values to the box2D scale i must divide it by 32 or mutiply by 1/32(it's the same equation), and everytime i want to get values from this world to pixel scale i must mutiply it by 32, but the problems still the same: can't get good simulations, i'll put my code here for you see how i'm doing things(i'll put the variables too so you can just ctrl+c and ctrl+v):
private SpriteBatch batch;
private ShapeRenderer srender;
private Texture img;
private World world;
private Body bad, bridge;
private OrthographicCamera cam, cam2;
private Box2DDebugRenderer render;
#Override
public void create () {
srender = new ShapeRenderer();
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
cam = new OrthographicCamera(500f, 500f);
cam.translate(250f, 250f);
cam.update();
//Used to draw the box2D world scaled to the pixels size
cam2 = new OrthographicCamera(500 * 0.03125f, 500 * 0.03125f);
cam2.translate(250f * 0.03125f, 250f * 0.03125f);
cam2.update();
render = new Box2DDebugRenderer();
world = new World(new Vector2(0f, -10f), true);
BodyDef bdef = new BodyDef();
bdef.type = BodyType.DynamicBody;
bad = world.createBody(bdef);
PolygonShape pshape = new PolygonShape();
pshape.setAsBox(img.getWidth() * 0.03125f, img.getHeight() * 0.03125f);
FixtureDef fdef = new FixtureDef();
fdef.shape = pshape;
bad.createFixture(fdef);
/*Criando a ponte*/
bdef.type = BodyType.StaticBody;
bridge = world.createBody(bdef);
pshape.setAsBox(500f * 0.03125f, 5f * 0.03125f);
bridge.createFixture(fdef);
bad.setTransform(new Vector2(250f * 0.03125f, 500f * 0.03125f) , 0);
bridge.setTransform(new Vector2(250f * 0.03125f, 0f * 0.03125f), 0);
batch.setProjectionMatrix(cam.combined);
srender.setProjectionMatrix(cam.combined);
}
#Override
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(img, (bad.getPosition().x - img.getWidth()/2 * 0.03125f) * 32,
(bad.getPosition().y - img.getHeight()/2 * 0.03125f) * 32);
batch.end();
render.render(world, cam2.combined);
srender.begin(ShapeRenderer.ShapeType.Filled);
srender.circle(250f, 500f, 5f);
srender.circle(250f, 0f, 5f);
srender.end();
world.step(1/60f, 6, 2);
}
if you run this code you will see that the box created still not fitting the image size, the image stop walking on the middle of the screen while it should stop only at the bottom, can anyone help me here? thanks for attention.
Ps:i have already heard about changing the viewport, but i'm still trying to learn this conversions, after that i'll go look for the viewports.

Related

Sprite is not attached with a body in libgdx

I want the sprite to get attached with a polygon shape body. When I run the code, both the sprite and body is getting displayed but they are not linked with each other. I looked at all the stack overflow questions but nothing helped.
I am creating this in a class and I am instantiating this from main class.
This is my code,
public SpriteButtons(PlayScreen screen) {
this.world = screen.getWorld();
batch = new SpriteBatch();
region = new TextureRegion(screen.getAtlas().findRegion("fireball"), 0, 0, 16, 16);
sprite = new Sprite(region);
this.gamecame = new OrthographicCamera();
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.StaticBody;
bodyDef.position.set(0, 0);
body = world.createBody(bodyDef);
PolygonShape shape = new PolygonShape();
shape.setAsBox(0.2f, 0.2f);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
fixtureDef.density = 1f;
sprite.setSize(25, 25);
Fixture fixture = body.createFixture(fixtureDef);
}
public OrthographicCamera getGamecame() {
return gamecame;
}
public void render(float dt) {
sprite.setPosition(body.getPosition().x - sprite.getWidth()/2f,
body.getPosition().y - sprite.getHeight()/2f );
Gdx.app.log("" + body.getPosition().x, "");
batch.begin();
sprite.draw(batch);
setRegion(region);
batch.end();
}
My output is as follows,
The badlogic sprite is not getting attached with a polygon body and I am instantiating this class from main class and I will be calling this class render method from main class render method. Sorry if the question seems stupid. I don't know where I am missing this. Please help..!! Thanks in advance..!!
You must update sprite's position according to the body position in the render method - if you are not doing this how the sprite can know that it is attached to body?
Please notice that box2d body's origin is in the center of the body when libgdx'es sprite's origin is at it's left botto corner. Then you need to subtract half of width and height of body's position.
So this is what you've got to do:
//render()
sprite.setPosition(body.getPosition().x - sprite.getWidth()/2f,
body.getPosition().y - sprite.getHeight()/2f );
...
sprite.draw(batch);
...
try doing this
batch.draw(sprite, sprite.getX(), sprite.getY()); and change body's inital postion somewher above,so that it fall and you can check under gravity by initializing world like world = new World(new Vector2(0, -98f), true);

body falls slowly in any gravity

I've created a World with earth gravity and I place an entity in the scene (contains a sprite and a Body) and it falls down slowly like a balloon.
Here's how I set the World:
world = new World(new Vector2(0, -GRAVITY_EARTH), true);
and here's the relevant Box2D code for the Body etc:
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(positionX, positionY);
// Create our body in the world
body = world.createBody(bodyDef);
// Grab the first idle sprite to use as initial
Sprite sprite = idleSprites.get(0);
// Create a box shape to represent our hit box
PolygonShape box = new PolygonShape();
box.setAsBox(sprite.getWidth() / 2f, sprite.getHeight() / 2f);
// Create a fixture definition to apply our shape
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = box;
fixtureDef.density = 1f; // Give it full density
fixtureDef.friction = 0f; // Give it no friction
fixtureDef.restitution = 0f; // Make it not bouncy
// Create our fixture and attach it to the body
fixture = body.createFixture(fixtureDef);
// Remember to dispose of any shapes after you're done with them!
// BodyDef and FixtureDef don't need disposing, but shapes do.
box.dispose();
and how I draw the sprite:
TextureRegion keyFrame = idleAnimation.getKeyFrame(stateTimeSeconds, true);
Vector2 position = body.getPosition();
batch.draw(keyFrame, position.x - keyFrame.getRegionWidth() / 2f, position.y - keyFrame.getRegionHeight() / 2f);
and the relevant code in the render() method:
#Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
final float deltaTime = Gdx.graphics.getDeltaTime();
camera.update();
spriteBatch.setProjectionMatrix(camera.combined);
spriteBatch.begin();
spriteBatch.draw(sky, 0, 0);
tim.animate(spriteBatch, deltaTime);
spriteBatch.draw(floor, 0, 0);
spriteBatch.end();
// Render physics for debug
debugRenderer.render(world, camera.combined);
// Run physics
doPhysicsStep(deltaTime);
}
private void doPhysicsStep(float deltaTime) {
// fixed time step
// max frame time to avoid spiral of death (on slow devices)
float frameTime = Math.min(deltaTime, 0.25f);
accumulator += frameTime;
while (accumulator >= TIME_STEP) {
world.step(TIME_STEP, VELOCITY_ITERATIONS, POSITION_ITERATIONS);
accumulator -= TIME_STEP;
}
}
I've tried changing the density of the fixture, and I've tried changing the gravity value, and I've tried changing the TIME_STEP and nothing is having an effect. The body just falls down slowly like a balloon.
It looks to me like you're using pixels as your units, box2d treats every unit as a meter and so you're hitting the internal limit of 2.0 units per time step, see http://www.iforce2d.net/b2dtut/gotchas. You can get around this by setting up your camera in world units instead of pixels, you have to scale all your sprites and positions to fit into world units instead of pixels though.
Something like this may do:
float w = (float) Gdx.graphics.getWidth();
float h = (float) Gdx.graphics.getHeight();
camera = new OrthographicCamera(30, 30 * (h / w));
the way the camera is set up here allows the height of the viewport to be variable based on the screens' aspect ratio.
Then to setup the sprite change it by a set factor
sprite.setSize(sprite.getWidth / PIX2M, sprite.getHeight / PIX2M);
where PIX2M is a static field defining how many pixels are a meter in box2d
Alternatively you can set the dimensions of the sprite explicitly to a value which makes physical sense and with the aspect ratio of the original image(my personal preference) . So an image of a person which is 100 x 500 for example could be set like this.
sprite.setSize(.4f, 2f);
meaning the person is 2 meters high and .4 meters wide. Also with this method you don't need a PIX2M conversion factor and you will always know the exact size of your body. Since you set the camera to a specific number of world units, 30 in this case, the sprite will take up the same amount of room on the screen no matter the resolution of the display.

How to change size after it has been created

I am using java, libgdx, and box2d
In main class I have created a player. I want to change shape.setAsBox to 100 in player class. So in other words I want to change shape.setAsBox after it has been created. I believe only way to do this is to delete fixture and recreate a new one with 100 size. How can I do this.
public class main{
...
public main(){
//create player
BodyDef bdef = new BodyDef();
Body body;
FixtureDef fdef = new FixtureDef();
PolygonShape shape = new PolygonShape();
/***Body - Player ***/
bdef.type = BodyType.DynamicBody;
bdef.position.set(50 / PPM, 50 / PPM);
bdef.linearVelocity.set(1.5f, 0);
body = world.createBody(bdef);
/*** 1st fixture ***/
shape.setAsBox(50/ PPM, 50 / PPM);
fdef.shape = shape;
fdef.filter.categoryBits = Constants.BIT_PLAYER;
fdef.filter.maskBits = Constants.BIT_GROUND;
body.createFixture(fdef).setUserData("player");
player = new Player(body);
}
....
public void update(float dt) {
playerObj.update(dt);
...
}
}
// playyer class
public class player{
public player(Body body){
super(body);
}
....
public void update(){
//get player x position
currentX = this.getBody().getPosition().x;
// how can I delete old fixture and recreate a new one?
// which will has shape.setAsBox = 100.
}
}
Destroy the Fixture and redefine it. Since your player has one Fixture, either keep track of it to remove it or call:
this.getBody().destroyFixture(this.getBody().getFixtureList().first());
Then recreate a simple shape in the already existing Body:
PolygonShape shape;
FixtureDef fdef;
// Create box shape
shape = new PolygonShape();
shape.setAsBox(100 / PPM, 100 / PPM);
// Create FixtureDef for player collision box
fdef = new FixtureDef();
fdef.shape = shape;
fdef.filter.categoryBits = Constants.BIT_PLAYER;
fdef.filter.maskBits = Constants.BIT_GROUND;
// Create player collision box fixture
this.getBody().createFixture(fdef).setUserData("player");
shape.dispose();

box2d dynamic body is not falling under gravity

I am trying to make a dynamic body which should move ahead a bit and then fall according to its gravity(as decided by the box2d engine). The problem is that right after moving upto a certain distance, instead of moving down, it is going up. This is the code i am using:
public class AngryBirdsTrajectoryPrototype implements ApplicationListener
{
private SpriteBatch spriteBatch;
private Texture backgroundTexture;
private Sprite backgroundSprite;
private static Sprite ball_in_hand;
private Texture ball_in_hand_Texture;
private static com.badlogic.gdx.physics.box2d.Body b2Body;
private com.badlogic.gdx.physics.box2d.World box2Dworld;
private boolean isBallShooted;
#Override
public void create() {
spriteBatch = new SpriteBatch();
Texture.setEnforcePotImages(false);
backgroundTexture = new Texture(Gdx.files.internal("angrybirds/background.png"));
backgroundTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
backgroundSprite = new Sprite(backgroundTexture);
backgroundSprite.setPosition(0, 0);
ball_in_hand_Texture = new Texture(Gdx.files.internal("test/ball_in_hand.png"));
ball_in_hand_Texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
ball_in_hand = new Sprite(ball_in_hand_Texture);
this.box2Dworld = new com.badlogic.gdx.physics.box2d.World(new Vector2(0.0F, 10.0F), true);
}
#Override
public void render() {
Gdx.gl.glClearColor(0.5f, 0.5f, 0.5f, 1f);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
if(!isBallShooted)
{
isBallShooted = true;
shoot(this.box2Dworld, 27.0F, 0.F);
}
box2Dworld.step(1.0f/60.0f, 6, 2);
spriteBatch.begin();
backgroundSprite.draw(spriteBatch);
Array<Body> bodies = new Array<Body>();
box2Dworld.getBodies(bodies);
for (Body body : bodies)
{
ball_in_hand.setPosition(30.0F * body.getPosition().x, 30.0F * body.getPosition().y);
Gdx.app.error("", "angleDegree= " + body.getAngle());
}
ball_in_hand.draw(spriteBatch);
spriteBatch.end();
}
#Override
public void dispose() {
spriteBatch.dispose();
backgroundTexture.dispose();
}
public static void shoot(com.badlogic.gdx.physics.box2d.World var1, float var2, float var3)
{
BodyDef var4 = new BodyDef();
var4.position.set(ball_in_hand.getX() / 30.0F, ball_in_hand.getY() / 30.0F);
var4.type = BodyType.DynamicBody;
var4.bullet = true;
var4.angularDamping = 0.5F;
b2Body = var1.createBody(var4);
CircleShape var6 = new CircleShape();
var6.setRadius(0.4F);
FixtureDef var7 = new FixtureDef();
var7.density = 0.8F;
var7.shape = var6;
var7.restitution = 0.7F;
var7.friction = 1.0F;
b2Body.createFixture(var7);
float var9 = var2 * 9.0F;
Vector2 var10 = new Vector2(var9 * (float)Math.cos((double)var3), var9 * (float)Math.sin((double)var3));
b2Body.applyForce(var10, b2Body.getWorldCenter(), true);
}
}
This is the output i am getting.
This is the output i want to achieve:
I have tried all possible options but none of them is working. Please help me. Thanks in advance.
Try this:
You should set your gravity to a negative number because of the Box2d coordinate system, like this:
enter code here`this.world = new World(new Vector2(0, -9.8f), true);
You should create a camera for your physics world and convert all your sizes to meters. For example, you can set 100 pixels = 1 meter in the box2d world, so you will need to set your camera's width and height in meters, too. Here is a full working example.
You can also get delta like this: Gdx.graphics.getDeltaTime()
Please let me know if it helped.
I think the problem in your code is in the create method:
this.box2Dworld = new com.badlogic.gdx.physics.box2d.World(new Vector2(0.0F, 10.0F), true)
You should use a negative value of gravity in y-direction like:
this.box2Dworld = new com.badlogic.gdx.physics.box2d.World(new Vector2(0.0F, -10.0F), true)
Also, please use debug-renderer to spot the error.

Libgdx: screen resize and ClickListener (libgdx)

I develope a 2D game and use OrthographicCamera and Viewport to resize virtaul board to real display size. I add images to stage and use ClickListener to detect clicks. It works fine, but when I change resolution it works incorrent(can't detect correct actor, I think the problem with new and original x and y). Is there any way to fix this?
You will need to translate the screen coordinates to world coordinates.
Your camera can do that. You can do both ways, cam.project(...) and cam.unproject(...)
Or if you are already using Actors, don't initialize a camera yourself, but use a Stage. Create a Stage and add the actors to it. The Stage will then do coordinate translation for you.
Once me too suffered from this problem but at end i got the working solution, for drawing anything using SpriteBatch or Stage in libgdx. Using orthogrphic camera we can do this.
first choose one constant resolution which is best for game. Here i have taken 1280*720(landscape).
class ScreenTest implements Screen{
final float appWidth = 1280, screenWidth = Gdx.graphics.getWidth();
final float appHeight = 720, screenHeight = Gdx.graphics.getHeight();
OrthographicCamera camera;
SpriteBatch batch;
Stage stage;
Texture img1;
Image img2;
public ScreenTest(){
camera = new OrthographicCamera();
camera.setToOrtho(false, appWidth, appHeight);
batch = new SpriteBatch();
batch.setProjectionMatrix(camera.combined);
img1 = new Texture("your_image1.png");
img2 = new Image(new Texture("your_image2.png"));
img2.setPosition(0, 0); // drawing from (0,0)
stage = new Stage(new StretchViewport(appWidth, appHeight, camera));
stage.addActor(img2);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(img, 0, 0);
batch.end();
stage.act();
stage.act(delta);
stage.draw();
// Also You can get touch input according to your Screen.
if (Gdx.input.isTouched()) {
System.out.println(" X " + Gdx.input.getX() * (appWidth / screenWidth));
System.out.println(" Y " + Gdx.input.getY() * (appHeight / screenHeight));
}
}
//
:
:
//
}
run this code in Any type of resolution it will going to adjust in that resolution without any disturbance.
I just think Stage is easy to use.
If there are some wrong,i consider you should check your code:
public Actor hit(float x, float y)