I am developing a game with LibGdx. I am using scene2d actors in my game.
I have 2 arrows with this body
private void creatBody() {
BodyDef bd = new BodyDef();
bd.position.set(getX(), getY());
bd.type = BodyType.DynamicBody;
FixtureDef fd = new FixtureDef();
fd.density = 15f;
fd.friction = 0.6f;
fd.restitution = 0.02f;
if (body != null)
removeBodySafely(body);
body = world.createBody(bd);
body.setTransform(body.getWorldCenter(), MathUtils.degreesToRadians
* getRotation());
GameScreen.shapeLoader.attachFixture(body, type, fd, 1);
}
public void draw(SpriteBatch batch, float parentAlpha) {
setRotation(MathUtils.radiansToDegrees * body.getAngle());
setPosition(body.getPosition().x, body.getPosition().y);
TextureRegion keyFrame = GameScreen.getAtlas("arrows").findRegion(type);
batch.draw(keyFrame, getX(), getY(), 0, 0, getWidth(), getHeight(),
1, 1, getRotation());
}
Picture of the body
but, when I drop one arrow above another they overlap instead of colliding.
From description of body editor I found that there is a red mark which is the reference point.
How do I make the arrows collide?
I found the answer
GameScreen.shapeLoader.attachFixture(body, type, fd, 90);
where 90 is the width of arrow.
Related
I got a problem on Box2d I'm new on Box2d so couldnt handle it movement of my player.
I want to move my player to the left and right when the user touch left and right buttons in my game.
in my game player already going through the up y axis .
I wanted see cool smooth animation while controlling player.
I just couldnt move my player to the left how can I do that ?
thanks in advance
//EDITED
I created a fixture for testing , I can move fixture but not my player.
how can I attach my player sprite to body ?
and I have to find a proper way to controlling body. it wont stop once you started.
this is my codes
public World world;
public Body bplayer;
public Box2DDebugRenderer b2dr;
public Matrix4 cameraBox2D;
PlayScreen
buttonimage.addListener(new ClickListener() {
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button)
{
bplayer.setLinearVelocity(-5*PPM , 0);
return true;
}
});
world = new World(new Vector2(player.getPosition().x , player.getPosition().y) , false);
b2dr = new Box2DDebugRenderer();
bplayer = createPlayer(player.getPosition().x , player.getPosition().y);
show method
buttonimage.setPosition(160,0);
rightbuttonimage.setPosition(320,0);
pauseimage.setPosition(220,-20);
cameraBox2D = camera.combined.cpy();
Render method
Gdx.gl.glClearColor(0, 0, 2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
sb.setProjectionMatrix(camera.combined);
player.position.y += 500 * Gdx.graphics.getDeltaTime();
sb.begin();
sb.draw(bg, 0, camera.position.y - (camera.viewportHeight/2));
sb.draw(player.sprite, player.getPosition().x , player.getPosition().y);
for (Tube tube : tubes) {
sb.draw(tube.getlefttube(), tube.getposlefttube().x, tube.getposlefttube().y);
sb.draw(tube.getrighttube(), tube.getposrighttube().x, tube.getposrighttube().y);
sb.draw(tube.getLight() , tube.getPoslight().x , tube.getPoslight().y);
}
delta*=speed;
sb.end();
update(delta);
b2dr.render(world , cameraBox2D);
stage.draw();
app.batch.begin();
app.font23.draw(app.batch,"Lights collected :" + dropsGathered , 0, 720);
app.batch.end();
cameraUpdate method
Vector3 position = camera.position;
position.x = player.position.x;
position.y = player.position.y;
camera.position.set(position);
createPlayer method
Body pBody;
BodyDef def = new BodyDef();
def.type = BodyDef.BodyType.DynamicBody;
def.position.set(x * PPM, y * PPM );
def.fixedRotation = true;
pBody = world.createBody(def);
return pBody;
update method
world.step(1 / 60f , 6 , 2);
for(int i = 0; i < tubes.size; i++) {
Tube tube = tubes.get(i);
if (camera.position.y - (camera.viewportWidth/2) > tube.getposlefttube().y + tube.getlefttube().getWidth()) {
tube.reposition(tube.getposlefttube().y + ( TUBE_COUNT) );
}
if (tube.collides(player.getBounds())){
app.setScreen(new GameOver(app));
}
if (tube.gathered(player.getBounds())){
dropsGathered++;
}
if (dropsGathered >= 50){
//app.setScreen(new Stage2(app));
}
}
camera.update();
handleInput();
camera.position.y = player.getPosition().y + 300;
player.update(delta);
camera.update();
cameraUpdate(delta);
stage.act(delta);
In your createPlayer method you can do body.setUserData(sprite). Then in your render method, you can do this (similar to how you are rendering tubes):
for (Body body : bodies) {
Sprite playerSprite = (Sprite) body.getUserData();
playerSprite.setPosition(body.getPosition().x, body.getPosition().y);
playerSprite.draw(spriteBatch);
}
This will update the sprite's position with the body's position.
I could really use some help I'm stuck on this. I'm trying to make a block in a simple game be the ground and another block fall onto it and then possibly bounce a little but be able to move the box left or right to fall off of that first block. Can anyone help me with this please?
For anyone interested in the answer thanks to PoprostuRonin and dermetfan's YouTube videos I was able to get the results I was looking for, you can try the below code in your project and just change the sprite textures.
private Box2DDebugRenderer debugRenderer;
private OrthographicCamera camera;
private float spriteSpeed = 500000;
private World world;
private Sprite playersprite;
private Sprite groundsprite;
private Body playerBody;
private Body groundbody;
private Vector2 movement = new Vector2();
private Array<Body> tmpBodies = new Array<Body>();
public TestState(GameStateManager gsm) {
super(gsm);
Gdx.input.setInputProcessor(this);
batch = new SpriteBatch();
debugRenderer = new Box2DDebugRenderer();
camera = new OrthographicCamera();
// Sprites
playersprite = new Sprite(new Texture("badlogic.jpg"));
groundsprite = new Sprite(new Texture("ground-tiles-01.gif"));
// World
world = new World(new Vector2(0, -9.8f), true);
// Player Sprite
// Body definition
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.DynamicBody;
bodyDef.position.set(0, 200); //1m
PolygonShape shape = new PolygonShape();
shape.setAsBox(10, 10);
// Fixture definition
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
fixtureDef.density = 2.5f; //2.5kg
fixtureDef.friction = 0; //0-1
fixtureDef.restitution = .75f; //0-1
playerBody = world.createBody(bodyDef);
playerBody.createFixture(fixtureDef);
playerBody.setUserData(playersprite);
playersprite.setSize(20, 20);
playersprite.setOrigin(playersprite.getWidth() / 2, playersprite.getHeight() / 2);
shape.dispose();
// Ground Sprite
// Body definition
BodyDef groundbodyDef = new BodyDef();
groundbodyDef.type = BodyType.StaticBody;
groundbodyDef.position.set(0, 0); //1m
PolygonShape groundshape = new PolygonShape();
groundshape.setAsBox(groundsprite.getHeight() / 2, groundsprite.getWidth() / 2);
// Fixture definition
FixtureDef groundfixtureDef = new FixtureDef();
groundfixtureDef.shape = groundshape;
groundfixtureDef.density = 100; //2.5kg
groundfixtureDef.friction = .25f; //0-1
groundfixtureDef.restitution = 0; //0-1
groundbody = world.createBody(groundbodyDef);
groundbody.createFixture(groundfixtureDef);
groundbody.setUserData(groundsprite);
groundsprite.setSize(groundsprite.getHeight(), groundsprite.getWidth());
groundsprite.setOrigin(groundsprite.getWidth() / 2, groundsprite.getHeight() / 2);
groundshape.dispose();
}
#Override
public void update(float delta) {
camera.viewportWidth = Gdx.graphics.getWidth();
camera.viewportHeight = Gdx.graphics.getHeight();
}
#Override
public void render() {
Gdx.gl.glClearColor(50 / 255f, 213 / 255f, 237 / 255f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
debugRenderer.render(world, camera.combined);
batch.setProjectionMatrix(camera.combined);
world.step(Gdx.graphics.getDeltaTime(), 6, 2);
playerBody.applyForceToCenter(movement, true);
batch.begin();
world.getBodies(tmpBodies);
for (Body body : tmpBodies){
if (body.getUserData() != null && body.getUserData() instanceof Sprite) {
Sprite sprite = (Sprite) body.getUserData();
sprite.setPosition(body.getPosition().x - sprite.getWidth() / 2, body.getPosition().y - sprite.getHeight() / 2);
sprite.setRotation(body.getAngle() * MathUtils.radiansToDegrees);
sprite.draw(batch);
}
}
batch.end();
camera.position.set(playerBody.getPosition().x, playerBody.getPosition().y, 0);
camera.update();
}
#Override
public void dispose() {
world.dispose();
playersprite.getTexture().dispose();
groundsprite.getTexture().dispose();
}
#Override
public boolean keyDown(int keycode) {
switch (keycode) {
case Keys.DPAD_LEFT:
movement.x = -spriteSpeed;
break;
case Keys.DPAD_RIGHT:
movement.x = spriteSpeed;
break;
case Keys.DPAD_UP:
movement.y = spriteSpeed;
break;
case Keys.DPAD_DOWN:
movement.y = -spriteSpeed;
}
return true;
}
#Override
public boolean keyUp(int keycode) {
switch (keycode) {
case Keys.DPAD_LEFT:
case Keys.DPAD_RIGHT:
movement.x = 0;
break;
case Keys.DPAD_UP:
case Keys.DPAD_DOWN:
movement.y = 0;
}
return true;
}
You code is valid, the problem are numbers.
Mass of your body is big, 20480kg. Gravity is inverted because you ask for it:
world = new World(new Vector2(0, -1000f), true);
-1000, it is under 0 so it is inverted and big number (1000 on your "planet" and 9.8 on Earth) cause object to by less controllable.
Changes values:
float spriteSpeed = 60000;
...
world = new World(new Vector2(0, 9.8F), true);
...
fixtureDef.density = 1;
fixtureDef2.density = 1;
You will notice that now the body is controllable (it can even fly). Play with these numbers and learn some basic physics rules to have idea what are you doing.
The sprite and body position are synced, but are not aligned properly. Use this code to make the first step to make aligned texture with body.
sprite.setCenter(body.getPosition().x, body.getPosition().y);
groundsprite.setCenter(ground.getPosition().x, ground.getPosition().y);
But what about rotation
groundsprite.setOriginCenter();
sprite.setOriginCenter();
groundsprite.setRotation(ground.getAngle() * MathUtils.radDeg);
sprite.setRotation(body.getAngle() * MathUtils.radDeg);
This is simple as your object is single texture and we can simply align it at center, but as objects become more complicated code need to be changed.
This happen when my Body object touching with wall and I hold right key. So it is stop as long I hold right key.
When I release my right key it dropped. How to fix this problem?
Here my script:
World Definition:
this.world = new World(new Vector2(0, -9.8f), true);
Create ground body from tilemap:
//create body and fixture variables
BodyDef bdef = new BodyDef();
PolygonShape shape = new PolygonShape();
FixtureDef fdef = new FixtureDef();
Body body;
//create ground bodies/fixtures
for(MapObject object : tileMap.getLayers().get(2).getObjects().getByType(RectangleMapObject.class)){
rect = ((RectangleMapObject) object).getRectangle();
x = Static.toMeter(rect.getX() + rect.getWidth() / 2);
y = Static.toMeter(rect.getY() + rect.getHeight() / 2);
bdef.type = BodyDef.BodyType.StaticBody;
bdef.position.set(x, y);
body = world.createBody(bdef);
x = Static.toMeter(rect.getWidth() / 2);
y = Static.toMeter(rect.getHeight() / 2);
shape.setAsBox(x, y);
fdef.shape = shape;
fdef.filter.categoryBits = HookaHookaGame.GROUND_BIT;
body.createFixture(fdef);
}
Player body definition:
BodyDef bodyDef = new BodyDef();
bodyDef.position.set(Static.toMeter(128), Static.toMeter(HookaHookaGame.HEIGHT));
bodyDef.type = BodyDef.BodyType.DynamicBody;
body = world.createBody(bodyDef);
// Define mario shape
PolygonShape shape = new PolygonShape();
shape.setAsBox(Static.toMeter(32) / 2, Static.toMeter(32) / 2);
FixtureDef fixture = new FixtureDef();
fixture.shape = shape;
body.createFixture(fixture);
// Define foot shape
shape = new PolygonShape();
shape.setAsBox(Static.toMeter(32 / 4) / 2, Static.toMeter(32 / 4) / 2, new Vector2(0, Static.toMeter((-32 + 8) / 2)), 0);
fixture = new FixtureDef();
fixture.shape = shape;
// Create filter
fixture.filter.categoryBits = HookaHookaGame.MARIO_BIT;
fixture.filter.maskBits = HookaHookaGame.GROUND_BIT;
body.createFixture(fixture).setUserData(this);
My ContactListener here my beginContact() method:
int cDef;
Fixture a, b;
MySprite spriteA, spriteB;
a = contact.getFixtureA();
b = contact.getFixtureB();
cDef = a.getFilterData().categoryBits | b.getFilterData().categoryBits;
switch (cDef) {
case HookaHookaGame.GROUND_BIT | HookaHookaGame.MARIO_BIT:
System.out.println("Foot with ground");
if(a.getUserData() != null) {
spriteA = (MySprite) a.getUserData();
spriteA.onHit();
}
if(b.getUserData() != null) {
spriteB = (MySprite) b.getUserData();
spriteB.onHit();
}
break;
}
Handling user input:
private void handleInput() {
//control our player using immediate impulses
if (Gdx.input.isKeyPressed(Input.Keys.D))
player.moveRight();
else if (Gdx.input.isKeyPressed(Input.Keys.A))
player.moveLeft();
else
player.stopMove();
if (Gdx.input.isKeyPressed(Input.Keys.SPACE))
player.jump();
}
I read his tutorial and it seem he didn't do anything about it and it works fine. His source code GitHub
The problem is that your linear impulse is to strong.
You can test this with the Mario project, if you increase the impulse you will get stuck at walls, too.
To solve this either lower the impulse, lower the friction (but then you might need code that stops Mario) or instead of applying an linear impulse you could apply a torch and use a circle shape to let the player "roll".
None of these solutions are perfect, but you can try and see which one suits you most.
I managed to use box2d in libgdx. However the example code given here is for dynamic body only. I tried to use it and it works great but when i change the Dynamic to KinematicBody the code does not work. here is my code
#Override
public void create() {
// Create our body definition
BodyDef groundBodyDef =new BodyDef();
groundBodyDef.type = BodyDef.BodyType.StaticBody;
// Set its world position
groundBodyDef.position.set(new Vector2(0, 10));
// Create a body from the defintion and add it to the world
groundBody = world.createBody(groundBodyDef);
// Create a polygon shape
PolygonShape groundBox = new PolygonShape();
// Set the polygon shape as a box which is twice the size of our view port and 20 high
// (setAsBox takes half-width and half-height as arguments)
groundBox.setAsBox(camera.viewportWidth, 10.0f);
// Create a fixture from our polygon shape and add it to our ground body
groundBody.createFixture(groundBox, 0.0f);
// Clean up after ourselves
groundBox.dispose();
//endregion
BodyDef bodyDef = new BodyDef();
// We set our body to dynamic, for something like ground which doesnt move we would set it to StaticBody
bodyDef.type = BodyDef.BodyType.KinematicBody;
// Set our body's starting position in the world
bodyDef.position.set(bolaX, bolaY);
// Create our body in the world using our body definition
body = world.createBody(bodyDef);
// Create a circle shape and set its radius to 6
CircleShape circle = new CircleShape();
circle.setRadius(20f);
// Create a fixture definition to apply our shape to
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = circle;
fixtureDef.density = 20;
fixtureDef.friction = 0;
fixtureDef.restitution = 0.6f; // Make it bounce a little bit
// Create our fixture and attach it to the body
Fixture 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.
circle.dispose();
Gdx.input.setInputProcessor(this);
}
#Override
public void render() {
world.step(1/60f, 6, 2);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
camera.update();
//batch.getProjectionMatrix().set(camera.combined);
batch.begin();
textureBodies = body.getPosition();
float angle = MathUtils.radiansToDegrees * body.getAngle();
//batch.draw(ball,textureBodies.x ,textureBodies.y );
batch.draw(ball, textureBodies.x - (bola.getWidth() / 2) , textureBodies.y - (bola.getHeight()/2) , // the bottom left corner of the box, unrotated
1f, 1f, // the rotation center relative to the bottom left corner of the box
bola.getWidth(), bola.getHeight(), // the width and height of the box
1, 1, // the scale on the x- and y-axis
angle);
batch.end();
debugRenderer.render(world, camera.combined);
if(Gdx.input.isKeyPressed(Input.Keys.DPAD_UP)){
Gdx.app.log("Input Test", "key down: " + "aw ---- x" + body.getPosition().x + " y " + body.getPosition().y);
vel += 1;
//Gdx.app.log("vel","" + vel);
body.setLinearVelocity(0f,vel);
} else{
vel -= 1;
// Gdx.app.log("vel","" + body.getPosition().y);
body.setLinearVelocity(0f,vel);
}
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");
}
}
And here is the image.. You can see in the left side is the DynamicBody and the right side is the KinematicBody.
Getting the contacts works on Dynamic Body but not on Kinematic Body. Can you tell how to detect the collision in kinematic body?
Kinematic body dont collide with static body.
May be this will help you.
Kinematic Bodies are only influenced by velocities and not by forces
Static bodies have O displacement but are effected by forces i.e. they generate impulse upon collision whereas Dynamic Bodies have both
So I understand the concept. The idea is that box2d more or less works in meters, so you need to do a conversion from pixels to it. Makes sense. I was following the tutorial/intro to box2d here. It mentions to do the conversion and gives you some example amounts to use. Now, that's all well and good, but I find when I'm using such techniques, the debugger box doesn't seem to render where they should. The collision does work as expected however.
In my GameScreen class, here's how I initialize the ground:
ground = new BodyDef();
// set the position half way up the ground
ground.position.set(0,16 * GameScreen.WORLD_TO_BOX);
groundBody = world.createBody(ground);
groundShape = new PolygonShape();
// make the height 16px so it doubles to 32
groundShape.setAsBox(Gdx.graphics.getWidth() * GameScreen.WORLD_TO_BOX, 16.0f * GameScreen.WORLD_TO_BOX);
groundBody.createFixture(groundShape, 0.0f);
My render method in that screen is like so:
public void render(float delta) {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
camera.update();
stateTime += Gdx.graphics.getDeltaTime();
batch.begin();
batch.draw(background, 0, Gdx.graphics.getHeight() - 512, 512, 512);
batch.draw(trailingBackground, 512, Gdx.graphics.getHeight() - 512);
int heightToCover = Gdx.graphics.getHeight() - 512;
int widthToCover = Gdx.graphics.getWidth();
for(int w = 0; w < widthToCover; w += 32) {
for(int h = 0; h < heightToCover; h += 32) {
batch.draw(lightBackgroundTile, w, h, 32, 32);
}
}
player.update();
player.render(stateTime, batch);
batch.end();
levels.get(currentLevel).render(camera);
// physics updates
world.step(1/60f, 6, 2);
debugRenderer.render(world, camera.combined);
}
Here's the constructor of the player class, so you can see how im setting up its collision box2d objects. I also pasted the update method which is called in the above render loop to adjust the sprites position.
public Player(int x, int y, World world) {
super();
playerTexture = new Texture(Gdx.files.internal("assets/hero.png"));
init(x, y, 128, 128, playerTexture, false, world);
bodyDef = new BodyDef();
bodyDef.type = BodyType.DynamicBody;
bodyDef.position.set(x * GameScreen.WORLD_TO_BOX, y * GameScreen.WORLD_TO_BOX);
body = getWorld().createBody(bodyDef);
collisionBox = new PolygonShape();
collisionBox.setAsBox(32 * GameScreen.WORLD_TO_BOX, 64 * GameScreen.WORLD_TO_BOX, new Vector2(64 * GameScreen.WORLD_TO_BOX, 64 * GameScreen.WORLD_TO_BOX), 0.0f);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = collisionBox;
fixtureDef.density = 10f;
fixtureDef.friction = 0.4f;
fixtureDef.restitution = 0f;
body.createFixture(fixtureDef);
collisionBox.dispose();
addFrame(0, 0, 128, 128);
}
public void update() {
this.setX((int) ((body.getPosition().x) * GameScreen.BOX_TO_WORLD));
this.setY((int) ((body.getPosition().y) * GameScreen.BOX_TO_WORLD));
}
Now, when I remove the multiplication of those static floats in the various calculations, sizes, etc, the collision remains correct and the debugger box shows up. However passing the raw pixels to box2d feels wrong. Is there something I'm missing here as to why the debugging boxes don't show up as is?
I do like you are doing but might be a little different.
Might be a little overkill but here's my jucl port
jucl/Android - pastebin
Then I just have utility classes ie:
public class Pixel {
public static float toMeter(float pixels) {
return (float)LengthConversions.Pixel2SIf(pixels);
}
public static Vector2 toMeter(Vector2 vecPixel) {
return new Vector2(Pixel.toMeter(vecPixel.x), Pixel.toMeter(vecPixel.y));
}
}
public class Meter {
public static final float METERS_PER_PIXEL = (float) LengthConversions.SI_PIXEL;
public static float toPixel(float meter) {
return (float)LengthConversions.SI2Pixelf(meter);
}
}
In my initialize:
int graphicsWidth = Gdx.graphics.getWidth();
int graphicsHeight = Gdx.graphics.getHeight();
CAMERA_WIDTH_METERS = Pixel.toMeter(graphicsWidth);
CAMERA_HEIGHT_METERS = Pixel.toMeter(graphicsHeight);
Then in my game classes (like your Player class). In my case it was a pinball game so i have Flipper, Ball, Bumper, etc. I have a #Override render() method where I sync up the sprite with the physics body.
Here's an example file..sry it's messy but might be helpful.
Pinball Engine Class file