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.