I am making a game in Libgdx, in which I want to fall a ball from up word to down word and move background towards up word and update camera according to it. My code is given below...
public WorldRenderer(SpriteBatch spriteBatch, World world){
this.world = world;
this.camera = new OrthographicCamera(FRUSTUM_WIDTH, FRUSTUM_HEIGHT);
this.camera.position.set(FRUSTUM_WIDTH/2, FRUSTUM_HEIGHT/2, 0);
this.spriteBatch = spriteBatch;
positionBGY1 = 0;
positionBGY2 = 0;
}
public void render(World world, float deltaTime){
if(world.ball.position.y > - camera.position.y){
camera.position.y = world.ball.position.y;
}
if(camera.position.y<0)
camera.position.y=world.ball.position.y;
camera.update();
spriteBatch.setProjectionMatrix(camera.combined);
renderBackground();
renderObjects(world, deltaTime);
}
private void calculateBGPosition(){
positionBGY2 = positionBGY1 + (int)FRUSTUM_HEIGHT;
if(camera.position.y >= positionBGY2){
positionBGY1 = positionBGY2;
}
}
private void renderBackground() {
spriteBatch.disableBlending();
spriteBatch.begin();
spriteBatch.draw(Assets.gamebackgroundRegion, camera.position.x - FRUSTUM_WIDTH / 2, positionBGY1 - FRUSTUM_HEIGHT / 2, FRUSTUM_WIDTH, 1.5f * FRUSTUM_HEIGHT);
spriteBatch.draw(Assets.gamebackgroundRegion2, camera.position.x - FRUSTUM_WIDTH / 2, positionBGY2 - FRUSTUM_HEIGHT / 2, FRUSTUM_WIDTH, 1.5f * FRUSTUM_HEIGHT);
calculateBGPosition();
spriteBatch.end();
}
Problem with my code is background screen is not moving and camera is not updated with ball movement and ball fall down out of screen.
Well, first thing you need to do is change one of the bg variables, so in render somehwere do something like:
float speed = 1;
positionBGY1+=deltaTime*speed;
positionBGY2+=deltaTime*speed;
As far as the camera moving with the ball I would just do this:
camera.position.y = Math.min(ball.y,0);
And I would in turn omit all of this:
if(world.ball.position.y > - camera.position.y){
camera.position.y = world.ball.position.y;
}
if(camera.position.y<0)
camera.position.y=world.ball.position.y;
Best way to achieve this is using ParrallaxLayer and ParrallaxBackground classes
it optimised the background operation so that u dont have to worry about the performance
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 creating a game in LibGDX. I have taken the tiles used in Pixel Dungeon and created a tile map using Tiled.
The main character's class is a subclass of Actor, and because the character is animated, I am using this code to draw the sprite:
if (direction.isEastwards() || direction.isNorthwards()) {
Vector3 projectedPosition = getLocation().getCamera().project(
new Vector3(16 * getX() + 1, Gdx.graphics.getHeight()
- (16 * getY()) - 15, 0));
batch.draw(current.getKeyFrame(
animTime += Gdx.graphics.getDeltaTime(), true),
projectedPosition.x, projectedPosition.y);
} else {
Vector3 projectedPosition = getLocation().getCamera().project(
new Vector3(16 * getX() + 13, Gdx.graphics.getHeight()
- (16 * getY()) - 15, 0));
batch.draw(current.getKeyFrame(
animTime += Gdx.graphics.getDeltaTime(), true),
projectedPosition.x, projectedPosition.y);
}
When I launch the game in Eclipse, the sprite initially appears in the correct location. However, if I resize the screen, the sprite ceases to be in the correct location and eventually will disappear off the map.
The same thing happened before I started using projection, and I figured the problem was related to projection. As this is an area I have not explored before I decided after an hour of being able to solve this to ask for help.
The animation flips depending on which direction the character faces, which is why the if/else clause is there.
Addendum: The stage is created with new Stage(new ExtendViewport(800,600),batch);, the resize method updates the camera, and the batch is set to the projection matrix.
Here is more relevant code:
Camera and map initialisation:
camera=new OrthographicCamera();
camera.setToOrtho(false,Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
mapRenderer=new OrthogonalTiledMapRenderer(map,batch);
Render method:
camera.update();
batch.setProjectionMatrix(camera.combined);
mapRenderer.setView(camera);
mapRenderer.render();
stage.act();
stage.draw();
Resize method:
camera.viewportWidth=width;
camera.viewportHeight=height;
camera.update();
Actor draw method:
#Override
public void draw(Batch batch, float parentAlpha) {
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
if (direction.isEastwards() || direction.isNorthwards()) {
Vector3 projectedPosition = getLocation().getCamera().project(
new Vector3(16 * getX() + 1, Gdx.graphics.getHeight()
- (16 * getY()) - 15, 0));
batch.draw(current.getKeyFrame(
animTime += Gdx.graphics.getDeltaTime(), true),
projectedPosition.x, projectedPosition.y);
} else {
Vector3 projectedPosition = getLocation().getCamera().project(
new Vector3(16 * getX() + 13, Gdx.graphics.getHeight()
- (16 * getY()) - 15, 0));
batch.draw(current.getKeyFrame(
animTime += Gdx.graphics.getDeltaTime(), true),
projectedPosition.x, projectedPosition.y);
}
// Walk animation displays for 0.2 seconds
if (current == walk && animTime >= 0.2) {
current = idle;
animTime = 0;
}
}
I think the problem it caused because you dont combine the sprite with camera projection. set your spriteBatch like this :
spriteBatch.setProjectionMatrix(camera.combined);
And dont miss update viewport in resize method :
public void resize(int width, int height) {
camera.viewportWidth = width;
camera.viewportHeight = height;
camera.update();
}
With that, you can resize or zoomin zoomout, the spriteBatch it scalled to the camera projection.
I have seen many questions exactly like these with the same answer but it never works for me. I have a player who has a position vector that is constantly updating. I need my camera to constantly follow my players position. This is the player class
public void update(){
//velocity.y += MainScreen.GRAVITY.y;
velocity.x += MainScreen.GRAVITY.x;
oldPosition = getGridPosition();
position.x += velocity.x;
position.y += velocity.y;
if(oldPosition.x == getGridPosition().x){
hasMoved = false;
System.out.println("Hasn't moved");
System.out.println("normX- " + position.x);
System.out.println("oldX- " + oldPosition.x);
}
else{
hasMoved = true;
System.out.println("Has moved");
}
if(Gdx.input.isKeyPressed(Keys.A)){
velocity.x -= 10 * Gdx.graphics.getDeltaTime();
}
else if(Gdx.input.isKeyPressed(Keys.D)){
velocity.x += 10 * Gdx.graphics.getDeltaTime();
}else
velocity.x = 0;
}
And I have tried to set the camera position to my players position but it just does NOT for me. Here is my main render loop.
public void render () {
player.update();
camera.position.set(player.getPosition(), 0);
camera.update();
mapGrid.update();
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
mapGrid.render(tmpRend);
player.render(tmpRend);
}
I am at such a loss I don't know what I am doing wrong, here is how I initialize my camera in my create method
player = new Player();
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.setToOrtho(false);
//camera.position.set(MapGrid.MAP_WIDTH / 2, MapGrid.MAP_HEIGHT, 0);
camera.position.set(player.getPosition(), 0);
camera.zoom = 2f;
camera.update();
tmpRend = new ShapeRenderer();
tmpRend.setAutoShapeType(true);
tmpRend.setProjectionMatrix(camera.combined);
input = new InputListener(camera);
Gdx.input.setInputProcessor(input);
Please I have seen many other questions just like this and I am doing what they say but mine just doesn't work. Here is an image to show what happens.
I just don't know why it won't work when I have followed everything I have been told. Any help you can give me would be extremely appreciated!
I think that you already update wrong camera - you've created an instance of it but where it is connected with other part of program?
Use Scene2d to manage your actors, viewports and cameras. You can read more about it here:
https://github.com/libgdx/libgdx/wiki/Scene2d
You are creating new stage, add player (which should inherit from Actor class) to it and then just instead of creating new Camera just get your current stage camera just like
stage.getCamera()
all you should do in render is
...
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
this.viewport.update(this.screenWidth, this.screenHeight);
this.stage.act();
this.stage.draw();
I have a problem with my game (jumper type game), when a player is falling down or move left/right it isn't smooth, I can see sprite multiple times (like some kind of shadow). After searching the internet I think it's something with deltaTime but I cannot find out how to fix it.
The code for render method in play screen is:
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0,0,0,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//game running
if(state == RUNNING){
//make world step
doPhysicsStep(delta);
//update player and camera
player.update(delta);
if(player.getBody().getPosition().x < bottomLeft.x)
player.getBody().setTransform(bottomRight.x, player.getBody().getPosition().y, player.getBody().getAngle());
else if(player.getBody().getPosition().x > bottomRight.x)
player.getBody().setTransform(bottomLeft.x, player.getBody().getPosition().y, player.getBody().getAngle());
camera.position.y =player.getBody().getPosition().y > camera.position.y ? player.getBody().getPosition().y: camera.position.y;
camera.update();
levelGenerator.generate(camera.position.y + camera.viewportHeight /2,camera.position.y - camera.viewportHeight /2);
}
//game ended
if(player.getBody().getPosition().y< camera.position.y - camera.viewportHeight/2 -player.HEIGHT/2 && state == RUNNING){
gameOverScreen.updateScore(score.getText().toString().substring(7));
state = END;
}
if(state == END){
gameOverScreen.setVisible(true);
}
//drawing game
batch.setProjectionMatrix(camera.combined);
batch.begin();
renderBackground();
renderPlatform();
renderPlayer();
Assets.sparks.draw(batch, delta);
batch.end();
}
public void renderPlatform(){
//world.getBodies(tmpBodies);
//for(Body body : tmpBodies)
// if(body.getUserData() != null && body.getUserData() instanceof Platform)
for(int i=0;i<platforms.size();i++){
Body body = platforms.get(i).getBody();
Sprite sprite = platforms.get(i).getSprite();
sprite.setPosition(body.getPosition().x - sprite.getWidth()/2, body.getPosition().y - sprite.getHeight()/2);
sprite.setRotation(body.getAngle() * MathUtils.radiansToDegrees);
sprite.draw(batch);
}
}
public void renderPlayer(){
world.getBodies(tmpBodies);
for(Body body : tmpBodies)
if(body.getUserData() != null && body.getUserData() instanceof Player){
Player player = (Player) body.getUserData();
Sprite sprite = player.getSprite();
sprite.setPosition(body.getPosition().x - sprite.getWidth()/2, body.getPosition().y - sprite.getHeight()/2);
//sprite.setRotation(body.getAngle() * MathUtils.radiansToDegrees);
sprite.draw(batch);
if(player.isPlayEffect()){
Assets.sparks.setPosition(body.getPosition().x , body.getPosition().y - sprite.getHeight()/2);
Assets.sparks.setDuration(200);
Assets.sparks.start();
}
}
}
public void renderBackground(){
world.getBodies(tmpBodies);
for(Body body : tmpBodies)
if(body.getUserData() != null && body.getUserData() instanceof Background){
Background background = (Background) body.getUserData();
Sprite sprite = background.getSprite();
//sprite.setPosition(body.getPosition().x - sprite.getWidth()/2, body.getPosition().y - sprite.getHeight()/2);
sprite.setBounds(bottomLeft.x, camera.position.y - camera.viewportHeight /2, camera.viewportWidth, camera.viewportHeight);
sprite.draw(batch);
}
}
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.05f);
accumulator += frameTime;
while (accumulator >= TIMESTEP) {
world.step(TIMESTEP, VELOCITYITERATIONS, POSITIONITERATIONS);
accumulator -= TIMESTEP;
}
}
and player update:
public void update(float delta) {
stateTime += delta;
if(state==JUMP){
sprite.setRegion(jump.getKeyFrame(stateTime));
if(jump.isAnimationFinished(stateTime)){
state = IDLE;
stateTime = 0;
}
}else{
sprite.setRegion(idle.getKeyFrame(stateTime));
}
if(Gdx.app.getType() == ApplicationType.Android )
velocity.x = -Gdx.input.getAccelerometerX() * (movementForce / 10f);
if(applyJump){
body.applyLinearImpulse(0, jumpPower, body.getWorldCenter().x, body.getWorldCenter().y, true);
applyJump = false;}
body.setLinearVelocity(velocity.x, body.getLinearVelocity().y);
}
where applyJump variable is setted at contact in postSolve() method.
Thanks in advance
Well, that's two different problems then, right? It isn't smooth, and its drawing multiple sprites when it shouldn't be?
Lets tackle the second one first, that should be pretty obvious. Either you're making multiple calls to render the sprite somewhere, or you're not clearing the frame properly. It looks like you're clearing it, so you've gotta go through all your render code and see how you're drawing that texture twice. Somewhere its happening.
world.getBodies(tmpBodies);
for(Body body : tmpBodies)
Why does this code exist? Does the player character have multiple sprites? I wouldn't handle it this way; does the number change over time? Are there new players being created? You should probably create a separate Player class and allow it to manage its own state and data. Use OOP principles! Too much here is being done by a single GOD class, very procedural style. Bad Java.
If left/right movement isn't smooth you need to work on your physics.
In order to build a tic-tac-toe game for testing, I have following routine. But problem is that I am getting too many events for just one touch. I suspect isTouched() returns all of down, up, and move. Is there any way to just get up event?
UPDATE: Resolved the issue by employing justTouched() instead.
#Override
public void render() {
// we update the game state so things move.
updateGame();
// First we clear the screen
GL10 gl = Gdx.graphics.getGL10();
gl.glViewport(0, 0, width, height);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// Next we update the camera and set the camera matrix
camera.update();
camera.apply(Gdx.gl10);
...
}
private void updateGame() {
// the delta time so we can do frame independant time based movement
float deltaTime = Gdx.graphics.getDeltaTime();
// Has the user touched the screen? then position the paddle
if (Gdx.input.isTouched() && !isProcess) {
// get the touch coordinates and translate them
// to the game coordinate system.
isProcess=true;
int width = Gdx.graphics.getWidth();
int height = Gdx.graphics.getHeight();
int offx=-width/2;
int offy=-height/2;
float x = Gdx.input.getX();
float y = Gdx.input.getY();
float touchX = 480 * (x
/ (float) width - 0.5f);
float touchY = 320 * (0.5f - y
/ (float) height);
for(int i=0;i<3;i++) {
for(int j=0;j<3;j++)
{
if(touchX >= offx+i*width/3 && touchX < offx+(i+1)*width/3 &&
touchY >= offy+j*height/3 && touchY < offy+(j+1)*height/3)
{
if(isCurrentO)
data[i][j]=CellStatus.O;
else
data[i][j]=CellStatus.X;
isCurrentO=!isCurrentO;
break;
}
}
}
isProcess=false;
}
}
An alternative to using justTouched is to implement the InputProcessor interface, as it has a touchUp(x,y,pointer,button) which gives you greater control over the input. There are several classes that implement this or you can have your class implement it.
You can create a board for example (with hash map) and each object in your game wants to be clickable add itself to that board if an object was touched and was in board it will catch the event. If not it will not catch the event. So easy! :)