I'm making an endless runner on LibGDX with Box2d. I want to make the camera and player move at the same speed so while everything's fine, the player is in the center of the screen. I don't want it to just always make the player the center though, since I want the player to be left behind by the camera if the player gets stuck (ex. behind a crate). With that, I was thinking that I could translate the camera with the same default velocity that the player has. Doesn't work though. Help?
render() (with the camera update):
dx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//stage.getCamera().translate(Constants.DEFAULT_VELOCITY / Constants.PIXELS_TO_BOX, 0, 0);
//the commented function call above was what I was thinking; doesnt work
stage.getCamera().position.x = player.getX();
batcher.begin();
batcher.draw(AssetLoader.background, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
batcher.end();
stage.draw();
batcher.begin();
debugRenderer.render(gameWorld.getWorld(), debugMatrix);
batcher.end();
part of Player:
public Player(Texture texture, float x, float y, World world) {
this.texture = texture;
setWidth(texture.getWidth()); //pixels
setHeight(texture.getHeight()); //pixels
setPosition((x + getWidth()/2), y + (getHeight()/2)); //pixels
setName("player");
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.DynamicBody;
bodyDef.position.set(x / Constants.PIXELS_TO_BOX, y / Constants.PIXELS_TO_BOX); //meters
body = world.createBody(bodyDef);
PolygonShape shape = new PolygonShape();
shape.setAsBox(texture.getWidth()/2 / Constants.PIXELS_TO_BOX , texture.getHeight()/2 / Constants.PIXELS_TO_BOX); //meters
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.0f;
fixtureDef.restitution = 0.0f;
body.createFixture(fixtureDef);
body.setFixedRotation(true);
shape.dispose();
body.setUserData(this);
}
#Override
public void act(float delta) {
body.setLinearVelocity(new Vector2(Constants.DEFAULT_VELOCITY, body.getLinearVelocity().y));
//what I use to set velocity
if (hasJumped) {
jump();
hasJumped = false;
Gdx.app.log("player", "jumped");
}
}
Related
I am creating a top-down shooter game, and whenever I move the camera, or zoom, black likes appear like a grid
I am using Tiled to create the map, and I have the camera following my centered box2d body. I have found that making the camera position equal the position of the box2d body with an int cast results in the black lines disappearing like this:
The problem though, is that because I have the game scaled down, the player will move for a second or two and then when the player reaches the next whole number on either axis, the camera snaps to the player, which is not what I want for the game as it's jarring. The player's movement is granular, but, while rounded, the camera's is not. I do not know if this is a problem with my tile sheet or if it's something I can fix by altering some code. I have tried all different kinds of combinations of padding, and values of spacing and margins. So ultimately, how can I have the camera match the player's position smoothly and not cause the black lines? I'd greatly appreciate any help or recommendations. Thank you in advance!
Where I am type casting the player's float position to an int in game class:
public void cameraUpdate(float delta) {
//timeStep = 60 times a second, velocity iterations = 6, position iterations = 2
world.step(1/60f, 6, 2); //tells game how many times per second for Box2d to make its calculations
cam.position.x = (int)playerOne.b2body.getPosition().x;
cam.position.y = (int)playerOne.b2body.getPosition().y;
cam.update();
}
Majority of player class:
public class PlayerOne extends Sprite implements Disposable{
public World world; // world player will live in
public Body b2body; //creates body for player
private BodyDef bdef = new BodyDef();
private float speed = 1f;
private boolean running;
TextureAtlas textureAtlas;
Sprite sprite;
TextureRegion textureRegion;
private Sound runningSound;
public PlayerOne(World world) {
this.world = world;
definePlayer();
textureAtlas = new TextureAtlas(Gdx.files.internal("sprites/TDPlayer.atlas"));
textureRegion = textureAtlas.findRegion("TDPlayer");
sprite =new Sprite(new Texture("sprites/TDPlayer.png"));
sprite.setOrigin((sprite.getWidth() / 2) / DunGun.PPM, (float) ((sprite.getHeight() / 2) / DunGun.PPM - .08));
runningSound = Gdx.audio.newSound(Gdx.files.internal("sound effects/running.mp3"));
}
public void definePlayer() {
//define player body
bdef.position.set(750 / DunGun.PPM, 400 / DunGun.PPM);
bdef.type = BodyDef.BodyType.DynamicBody;
//create body in the world
b2body = world.createBody(bdef);
FixtureDef fdef = new FixtureDef();
CircleShape shape = new CircleShape();
shape.setRadius(12 / DunGun.PPM);
fdef.shape = shape;
b2body.createFixture(fdef);
}
public void renderSprite(SpriteBatch batch) {
float posX = b2body.getPosition().x;
float posY = b2body.getPosition().y;
float posX2 = (float) (posX - .14);
float posY2 = (float) (posY - .1);
sprite.setSize(32 / DunGun.PPM, 32 / DunGun.PPM);
sprite.setPosition(posX2, posY2);
float mouseX = Level1.mouse_position.x; //grabs cam.unproject x vector value
float mouseY = Level1.mouse_position.y; //grabs cam.unproject y vector value
float angle = MathUtils.atan2(mouseY - getY(), mouseX - getX()) * MathUtils.radDeg; //find the distance between mouse and player
angle = angle - 90; //makes it a full 360 degrees
if (angle < 0) {
angle += 360 ;
}
float angle2 = MathUtils.atan2(mouseY - getY(), mouseX - getX()); //get distance between mouse and player in radians
b2body.setTransform(b2body.getPosition().x, b2body.getPosition().y, angle2); //sets the position of the body to the position of the body and implements rotation
sprite.setRotation(angle); //rotates sprite
sprite.draw(batch); //draws sprite
}
public void handleInput(float delta) {
setPosition(b2body.getPosition().x - getWidth() / 2, b2body.getPosition().y - getHeight() / 2 + (5 / DunGun.PPM));
this.b2body.setLinearVelocity(0, 0);
if(Gdx.input.isKeyPressed(Input.Keys.W)){
this.b2body.setLinearVelocity(0f, speed);
}if(Gdx.input.isKeyPressed(Input.Keys.S)){
this.b2body.setLinearVelocity(0f, -speed);
}if(Gdx.input.isKeyPressed(Input.Keys.A)){
this.b2body.setLinearVelocity(-speed, 0f);
}if(Gdx.input.isKeyPressed(Input.Keys.D)){
this.b2body.setLinearVelocity(speed, 0f);
}if(Gdx.input.isKeyPressed(Input.Keys.W) && Gdx.input.isKeyPressed(Input.Keys.A)){
this.b2body.setLinearVelocity(-speed, speed);
}if(Gdx.input.isKeyPressed(Input.Keys.W) && Gdx.input.isKeyPressed(Input.Keys.D)){
this.b2body.setLinearVelocity(speed, speed);
}
if(Gdx.input.isKeyPressed(Input.Keys.S) && Gdx.input.isKeyPressed(Input.Keys.A)){
this.b2body.setLinearVelocity(-speed, -speed );
}if(Gdx.input.isKeyPressed(Input.Keys.S) && Gdx.input.isKeyPressed(Input.Keys.D)){
this.b2body.setLinearVelocity(speed, -speed);
}
Where I declare the pixels per meter scale:
public class DunGun extends Game{
public SpriteBatch batch;
//Virtual Screen size and Box2D Scale(Pixels Per Meter)
public static final int V_WIDTH = 1500;
public static final int V_HEIGHT = 800;
public static final float PPM = 100; //Pixels Per Meter
Game render and resize methods:
#Override
public void render(float delta) {
cameraUpdate(delta);
playerOne.handleInput(delta);
//clears screen
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)) {
cam.zoom -= .01;
}
if (Gdx.input.isButtonPressed(Input.Buttons.RIGHT)) {
cam.zoom += .01;
}
mapRenderer.render();
b2dr.render(world, cam.combined); //renders the Box2d world
mapRenderer.setView(cam);
//render our game map
//mapRenderer.render(); // renders map
//mapRenderer.render(layerBackround); //renders layer in Tiled that p1 covers
game.batch.setProjectionMatrix(cam.combined); //keeps player sprite from doing weird out of sync movement
mouse_position.set(Gdx.input.getX(), Gdx.input.getY(), 0);
cam.unproject(mouse_position); //gets mouse coordinates within viewport
game.batch.begin(); //starts sprite spriteBatch
playerOne.renderSprite(game.batch);
game.batch.end(); //starts sprite spriteBatch
//mapRenderer.render(layerAfterBackground); //renders layer of Tiled that hides p1
}
#Override
public void resize(int width, int height) {
viewport.update(width, height, true); //updates the viewport camera
}
I solved it by fiddling around with the padding of the tilesets in GDX Texture Packer. I added 5 pixels of padding around the 32x32 tiles. I set the margins to 2, and spacing to 4 in Tiled. I had tried a lot of different combinations of padding/spacing/margins that didn't work which made me think it was a coding problem, but those settings worked, and I didn't have to round the floats.
I am making a game as a project for a college class. when my main character (the tree) hits the ground, it stops short of the ground
screenshot 1
and when the player lands on the little platform that gap is even bigger
screenshot 2
my code for creating the body of the character and and ground is this
public static Body createBody(int x, int y, float width, float height, boolean isStatic){
Body body;
BodyDef def = new BodyDef();
if(isStatic){
def.type = BodyDef.BodyType.StaticBody;
}
else {
def.type = BodyDef.BodyType.DynamicBody;
}
def.position.set(x / PPM , y / PPM);
// returns a World object
body = GlobalWorld.getInstance().createBody(def);
PolygonShape shape = new PolygonShape();
shape.setAsBox(width / 2 / PPM, height / 2 / PPM);
body.createFixture(shape, 1.0f);
shape.dispose();
return body;
}
The code for the ground is in it's own class
public class Material {
private Texture image;
private Body body;
private Sprite sprite;
public Material(Texture t, int x, int y, int w, int h){
image = t;
sprite = new Sprite(t);
body = BoxBuilder.createBody(x, y, w, h, true);
body.setUserData("Ground");
}
public void draw(){
// returns a SpriteBatch
SpriteBatch batch = GlobalBatch.getInstance();
sprite.setPosition(body.getPosition().x * PPM, body.getPosition().y * PPM);
batch.begin();
sprite.draw(batch);
batch.end();
}
public void dispose(){
image.dispose();
}
}
the code for drawing the ground
public void draw(){
for(Material m : worldObjects){
m.draw();
}
}
my code for drawing the character
public void create(){
// do stuff
// global vars
body = createBody(0, 480, 313, 260, false);
texture = new Texture("tree.PNG");
sprite = new Sprite(texture);
// do stuff
}
public void render(){
// do stuff
sprite.setPosition(body.getPosition().x*PPM, body.getPosition().y*PPM);
batch.begin()
sprite.draw(batch)
batch.end()
// do stuff
}
and PPM(Pixel per Meter) is
public static final float PPM = 32;
I don't know what is the problem here, I want the character to land on the ground, not land above it. So if someone could tell me what's causing this or point me to a good tutorial for learning more about box2d I'd appreciate it.
When we create body with PolygonShape then body is at the center of that Polygon so when draw with body position it starts drawing from center of PolygonShape so we need to draw at left bottom corner.
In Material class
replace
sprite.setPosition(body.getPosition().x * PPM, body.getPosition().y * PPM);
with
sprite.setPosition(body.getPosition().x*PPM-sprite.getWidth()/2,body.getPosition().y-sprite.getHeight()/2);
sprite.setRotation(body.getAngle()*MathUtils.radDeg);
And for Character draw also change
sprite.setPosition(body.getPosition().x*PPM, body.getPosition().y*PPM);
with
sprite.setPosition(body.getPosition().x*PPM-sprite.getWidth()/2, body.getPosition().y*PPM-sprite.getHeight()/2);
sprite.setRotation(body.getAngle()*MathUtils.radDeg);
As I see in your code you're not setting size of Sprite in Material class as well as for Character so set width and height.
In Material Class
sprite = new Sprite(t);
sprite.setSize(w,h);
And for Character
body = createBody(0, 480, 313, 260, false);
texture = new Texture("tree.PNG");
sprite = new Sprite(texture);
sprite.setSize(313,260);
I created a rope in box2d with RevoluteJoint and a RopeJoint but I have several problems with it:
From time to time, the last segment rotates and it looks like it was disconnected from the rope.
How to draw this thing? I create a texture with the size of each link. If the rope isn't moving it looks good, but as soon as the rope moves and the different links start to rotate slightly, you see gaps between the links.
Code:
private void createChain(World world, Body anchorBody) {
Body previousBody = anchorBody;
FixtureDef fixtureDef = new FixtureDef();
PolygonShape robeLinkShape = new PolygonShape();
robeLinkShape.setAsBox(4 / PPM, 8 / PPM);
fixtureDef.shape = robeLinkShape;
fixtureDef.density = 0.1f;
// fixtureDef.friction = 1.0f;
fixtureDef.restitution = 0.1f;
fixtureDef.filter.maskBits = Box2DConst.BIT_PLAYER;
fixtureDef.filter.categoryBits = Box2DConst.BIT_GROUND;
float mapX = anchorBody.getPosition().x * PPM;
float mapY = anchorBody.getPosition().y * PPM;
BodyDef bodyDef = new BodyDef();
bodyDef.angularDamping = 1.0f;
bodyDef.linearDamping = 1.0f;
//create rope
for (int i = 0; i < 10; i++) {
Float robeX = mapX / PPM;
Float robeY = (mapY - (i * 16)) / PPM;
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(robeX, robeY);
final Body link = world.createBody(bodyDef);
link.createFixture(fixtureDef);
RevoluteJointDef jointDef = new RevoluteJointDef();
jointDef.initialize(previousBody, link, new Vector2(robeX, robeY));
//don't need the rope to collide itself
jointDef.collideConnected = false;
jointDef.enableLimit = false;
// because we don't collide with other bodies in the rope, limit rotation to keep the rope bodies from rotating too much.
jointDef.lowerAngle = -5.0f * MathUtils.degreesToRadians;
jointDef.upperAngle = 5.0f * MathUtils.degreesToRadians;
world.createJoint(jointDef);
links.add(link);
previousBody = link;
}
RopeJointDef ropeJointDef = new RopeJointDef();
ropeJointDef.localAnchorB.set(0, 0);
ropeJointDef.maxLength = 90.0f;
ropeJointDef.bodyB = previousBody;
ropeJointDef.bodyA = links.get(0);
ropeJointDef.collideConnected = false;
world.createJoint(ropeJointDef);
}
public void draw(final SpriteBatch batch) {
Texture texture = FipiGame.res.get("rope");
batch.begin();
for (Body link : links) {
float x = (link.getPosition().x * PPM) - 4;
float y = (link.getPosition().y * PPM) - 8;
float angleDeg = MathUtils.radiansToDegrees * link.getAngle();
batch.draw(texture, x, y, 0, 0, texture.getWidth(), texture.getHeight(), 1f, 1f, angleDeg, 0, 0,
texture.getWidth(), texture.getHeight(), false, false);
}
batch.end();
}
Instead of drawing based on body positions and rotations: Create a set of points by looping through the revolute joint positions (midpoint of anchorA and anchorB in world space). Then draw your sprites so they connect those positions. This will be somewhat inaccurate in that it won't perfectly line up with the positions of the rigid bodies, but it should look all right.
I programed a basic Box2d-"Game": No textures and suchlike, Only a player(body), who jumps from one static groundbody to another.
The problem is that the ground respectively the playerbody, which is followed by the camera, stutters every few seconds.
(https://www.dropbox.com/s/llsg2q65lz828t6/stuttering.avi) (download this video, please, otherwise your flashplayer will show much more stuttering)
If you look between the groundelements, you can see this very good.
Binded textures stutters too.
Curiously: There aren't any stucks in fullscreen mode (config.fullscreen = true); Only on Android-Phones and in window mode.
Although it is only a small game, I have tried to use Interpolation and analysed the GC-actions: No result :\
Thanks in advance!
Some peaces of my code (I turned off the Interpolation):
private float step = 1.0f / 60.0f;
public void show()
{
//...
bodyDef.type = BodyType.StaticBody;
bodyDef.position.set(0, 0);
//...
ChainShape groundShape = new ChainShape();
float width = 20;
float height = 0.25f;
fixtureDef.restitution = 0.0f;
fixtureDef.friction = 0.5f;
fixtureDef.shape = groundShape;
fixtureDef.friction = .5f;
fixtureDef.restitution = 0;
groundShape.createChain(new Vector2[] { new Vector2(0, 0), new Vector2(width, 0), new Vector2(width, -height), new Vector2(0, -height), new Vector2(0, 0) });
fixtureDef.shape = groundShape;
world.createBody(bodyDef).createFixture(fixtureDef);
groundShape.dispose();
groundShape = new ChainShape();
bodyDef.position.set(25, 0);
groundShape.createChain(new Vector2[] { new Vector2(0, 0), new Vector2(width, 0), new Vector2(width, -height), new Vector2(0, -height), new Vector2(0, 0) });
fixtureDef.shape = groundShape;
world.createBody(bodyDef).createFixture(fixtureDef);
groundShape.dispose();
//...
}
public void render(float delta)
{
world.step(step, 8, 3);
input.update();
playCam.position.set(player.getBody().getPosition().x + 6.5f, 3.5f, 0);
playCam.update();
player.move();
debugRenderer.render(world, playCam.combined);
}
Player-Class:
move()
{
playerbody.setLinearVelocity(20f, 0);
}
I have a simple Java app setup which uses JOGL and JBox2D.
Below is how the app renders .. 3 Rectangles, 2 are Dynamic and 1 is Static i.e. the ground.
I can move 1 of the Rectangles left, right or by jumping using a KeyListener and adjusting the bodies BodyLinearVelocity accordingly.
But if the Rectangle (which i can move) falls off the other dynamic Rectangle, the graphical representation of the Box2D Body does not rotate correctly.
It looks like the Box2D Body is rotating but the GL2.GL_QUADS remains standing.
See empty space in below pic, I imagine the Rectangle should look like its standing on one of its edges if it rotated correctly?
How can I achieve this mapping between JOGL and JBox2D?
public class Level extends org.jbox2d.dynamics.World {
private Vector<Rectangle> levelObjects = new Vector<Rectangle>();
public Level(Vec2 gravity) {
super(gravity);
this.setGravity(gravity);
levelObjects.add(
new Rectangle(
new Vec2(17.0f, 10.0f),
0.0f,
2.0f,
2.0f,
BodyType.DYNAMIC,
1.0f,
0.8f,
0.3f,
this));
levelObjects.add(
new Rectangle(
new Vec2(22.0f, 10.0f),
0.0f,
2.0f,
2.0f,
BodyType.DYNAMIC,
1.0f,
0.8f,
0.3f,
this));
}
protected void draw(GLAutoDrawable gLDrawable){
gLDrawable.getGL().getGL2().glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
for (Rectangle object : levelObjects){
object.draw(gLDrawable);
}
}
}
This is the Rectangle definition:
public class Rectangle {
private Vec2 centerPoint;
private PolygonShape blockShape;
private BodyDef bodydef;
private Body body;
private World world;
private float width;
private float height;
public Rectangle (Vec2 centerPoint,
float angle, float width, float height,
BodyType bt, float density, float friction, float restitution,
World w) {
this.world = w;
this.angle = angle
this.width = width;
this.height = height;
blockShape = new PolygonShape();
blockShape.setAsBox(this.width, this.height);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = blockShape;
fixtureDef.density = density;
fixtureDef.friction = friction;
fixtureDef.restitution = restitution;
bodydef = new BodyDef();
bodydef.type = BodyType.DYNAMIC;
bodydef.position.set(this.centerPoint.x, this.centerPoint.y);
body = world.createBody(bodydef);
body.createFixture(fixtureDef);
body.setType(bt);
};
void draw(GLAutoDrawable gLDrawable){
gLDrawable.getGL().getGL2().glLoadIdentity();
gLDrawable.getGL().getGL2().glColor3f(0.0f, 60.0f, 120.0f);
gLDrawable.getGL().getGL2().glTranslatef(this.body.getPosition().x, this.body.getPosition().y, -6.0f);
gLDrawable.getGL().getGL2().glRotatef(this.body.getAngle(), 0, 1, 0);
gLDrawable.getGL().getGL2().glBegin(GL2.GL_QUADS);
gLDrawable.getGL().getGL2().glTexCoord2f(0.0f, 0.0f);
gLDrawable.getGL().getGL2().glVertex3f(-this.width, -this.height, 0.0f);
gLDrawable.getGL().getGL2().glTexCoord2f(0.0f, 1.0f);
gLDrawable.getGL().getGL2().glVertex3f(-this.width, this.height, 0.0f);
gLDrawable.getGL().getGL2().glTexCoord2f(1.0f, 1.0f);
gLDrawable.getGL().getGL2().glVertex3f(this.width, this.height, 0.0f);
gLDrawable.getGL().getGL2().glTexCoord2f(1.0f, 0.0f);
gLDrawable.getGL().getGL2().glVertex3f(this.width, -this.height, 0.0f);
gLDrawable.getGL().getGL2().glEnd();
gLDrawable.getGL().getGL2().glFlush();
}
}
glRotatef takes an angle in degrees in input and you should ensure that your rotation axis is correctly oriented and normalized. I advise you to ask the questions about JOGL on our official forum.
this.body.getAngle() returned angle in radians, so i converted to degrees and rotated on Z-axis.
It works now ..
gLDrawable.getGL().getGL2().glTranslatef(this.body.getPosition().x, this.body.getPosition().y, -6.0f);
gLDrawable.getGL().getGL2().glRotated(Math.toDegrees(this.body.getAngle()), 0, 0, 1);
gLDrawable.getGL().getGL2().glBegin(GL2.GL_QUADS);
gLDrawable.getGL().getGL2().glTexCoord2f(0.0f, 0.0f);
gLDrawable.getGL().getGL2().glVertex3f(-this.width, -this.height, 0.0f);
gLDrawable.getGL().getGL2().glTexCoord2f(0.0f, 1.0f);
gLDrawable.getGL().getGL2().glVertex3f(-this.width, this.height, 0.0f);
gLDrawable.getGL().getGL2().glTexCoord2f(1.0f, 1.0f);
gLDrawable.getGL().getGL2().glVertex3f(this.width, this.height, 0.0f);
gLDrawable.getGL().getGL2().glTexCoord2f(1.0f, 0.0f);
gLDrawable.getGL().getGL2().glVertex3f(this.width, -this.height, 0.0f);
gLDrawable.getGL().getGL2().glEnd();
gLDrawable.getGL().getGL2().glFlush();