stitchs_login

2D Camera Jitters

Recommended Posts

Hey,

I'm running through some game tutorials and have just implemented my camera. It scrolls, following the player sprite, and culls tiles from the map outside of bounds.

One thing I'm noticing is some minor artifact tearing between some of the tiles that make up the on-screen map. Originally, I assumed it was because I coded my camera X and Y as Integers, and my player coordinates as floats, so there was some classic data loss from downcasting float -> int. I modified that, and removed integer casts that related to passing player coordinates to the camera. This has reduced the tearing, but everything still jitters a small amount.

The player only moves at a speed of 1.0f. If I run it at 3.0f, it is more noticable. Where could I be going wrong?

Thanks,

Stitchs (code below).

package dev.mygame.display;

public class Camera
{
	private float x;
	private float y;
	private int w;
	private int h;
	private int focusX;
	private int focusY;
	
	public Camera(float x, float y, int w, int h)
	{
		this.x = x;
		this.y = y;
		this.w = w;
		this.h = h;
	}
	
	public float getX()
	{
		return x;
	}
	
	public void setX(float x)
	{
		this.x = x;
	}
	
	public float getY()
	{
		return y;
	}
	
	public void setY(float y)
	{
		this.y = y;
	}
	
	public int getFocusX()
	{
		return focusX;
	}
	
	public void setFocus(float focusX, float focusY)
	{
		float lerp = 1.0f;
		x += lerp * (focusX - w/2 - x);
		y += lerp * (focusY - h/2 - y);
	}
	
	public int getFocusY()
	{
		return focusY;
	}
	
	public int getWidth()
	{
		return w;
	}
	
	public int getHeight()
	{
		return h;
	}
	
	public void tick()
	{
		// centre the camera on the focus point
		//focusX -= w/2;
		//focusY -= h/2;
		// set the origin so we can offset everything else
		x = focusX;
		y = focusY;
	}
}
package dev.mygame.entities.creatures;

import java.awt.Graphics;

import dev.mygame.gfx.Assets;
import dev.mygame.Game;

public class Player extends Creature
{
	private Game game;
	
	public Player(Game game, float x, float y)
	{
		super(game, x, y, Creature.DEFAULT_WIDTH, Creature.DEFAULT_HEIGHT);
		this.game = game;
	}
	
	@Override
	public void tick()
	{
		getInput();
		move();
	}
	
	private void getInput()
	{
		xMove = 0;
		yMove = 0;
		
		if(game.getKeyManager().up)
		{
			yMove = -speed;
		}
		if(game.getKeyManager().down)
		{
			yMove = +speed;
		}
		if(game.getKeyManager().left)
		{
			xMove = -speed;
		}
		if(game.getKeyManager().right)
		{
			xMove = +speed;
		}
	}
	
	@Override
	public void render(Graphics g)
	{
		g.drawImage(Assets.playerOne, (int)(x - game.getCamera().getX()), (int)(y - game.getCamera().getY()), width, height, null);
	}
}
package dev.mygame.states;

import java.awt.Graphics;

import dev.mygame.entities.creatures.Player;
import dev.mygame.gfx.Assets;
import dev.mygame.Game;
import dev.mygame.worlds.World;
import dev.mygame.display.Camera;

public class GameState extends State
{
	private Player player;
	private World world;
	
	public GameState(Game game)
	{
		super(game);
		world = new World(game, "/maps/World1.json");
		player = new Player(game, world.getSpawnX(), world.getSpawnY());
	}
	
	@Override
	public void tick()
	{
		player.tick();
		game.getCamera().setFocus(player.getX() + player.getWidth()/2, player.getY() + player.getHeight()/2);
	}
	
	@Override
	public void render(Graphics g)
	{
		world.render(g);
		player.render(g);
	}
}

 

Edited by stitchs_login

Share this post


Link to post
Share on other sites

I would focus on your camera::setFocus function.

The (focusX - w/2) bit gives the offset by itself but (focusX - w/2 - x) seems like it throws it an additional unnecessary amount. I don't see you call camera::tick which is good because then we'd probably be talking half screen jitter. The other mess would be your variable naming between function arguments and the class members. Refactor some naming to make sure you are setting what you think you're setting and double check your camera position logic for one or two render frames by walking the logic by hand.

It's difficult because I don't see your base Creature::move is doing with your user input.

Share this post


Link to post
Share on other sites

Hi @GoliathForge,

The tick() method in Camera is a hangover that I'm removing in this phase. When you say that I should refactor some naming, do you mean that having the parameter names the same as the instance-level members could be confusing? Or am I missing some consistency in the naming pattern?

As for Creature::move()

package dev.mygame.entities.creatures;

import dev.mygame.entities.Entity;
import dev.mygame.Handler;

public abstract class Creature extends Entity
{
	public static final int DEFAULT_HEALTH = 10;
	public static final float DEFAULT_SPEED = 1.0f;
	public static final int DEFAULT_WIDTH = 64;
	public static final int DEFAULT_HEIGHT = 64;
	
	protected int health;
	protected float speed;
	protected float xMove;
	protected float yMove;
	
	public Creature(Handler handler, float x, float y, int width, int height)
	{
		super(handler, x, y, width, height);
		health = DEFAULT_HEALTH;
		speed = DEFAULT_SPEED;
		xMove = 0f;
		yMove = 0f;
	}
	
	public void move()
	{
		x += xMove;
		y += yMove;
	}
	....
}

I admit, I haven't yet printed the values to the setFocus method, so I will do that in my next round of implementation. Thinking about my original wording, if anything, it's not camera jitter I'm experiencing. It's more akin to a tear in some graphic rendering between tiles when I move my player vertically.

@the incredible smoker

On 10/6/2017 at 4:34 PM, the incredible smoker said:

Floats are not accurate.

Could you elaborate in relation to the screen-tearing that I'm experiencing? Right now, I don't *need* the precision provided by doubles. It would be something I'd consider in the future when I begin to implement physics calculations that may require it. Currently, I'm following a tutorial series that doesn't suggest using them. If this is a possible solution to my issues, I would love to hear some more context to your answer.

Thanks,

Stitchs.

Share this post


Link to post
Share on other sites
3 hours ago, stitchs_login said:

When you say that I should refactor some naming, do you mean that having the parameter names the same as the instance-level members could be confusing?

right...it's just a little thing, but every bit helps.

3 hours ago, stitchs_login said:

Thinking about my original wording, if anything, it's not camera jitter I'm experiencing. It's more akin to a tear in some graphic rendering between tiles when I move my player vertically.

Ah, very good. I'm not a java user but it's my understanding that you can't enable vsync programmatically and has to be done with the graphics card's settings interface. Try that. 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Announcements

  • Forum Statistics

    • Total Topics
      628330
    • Total Posts
      2982112
  • Similar Content

    • By Giannis Marinakis
      Hello everyone!
      I have a GitHub repository on which I'm developing a Open Network Library for Java Developers and I'd like to share it so more people come in and contribute. Till now the repo is being developed only by me so the library is not 100% bug free. I'd really like this repo to be famous and got people working on it!
      [You will find more information in the repo]
      Here's the link for the repo: https://github.com/giannismarinakis/java-open-nl
      Thanks!
    • By PaCkEtPiRaTe
      I'm currently remaking a game I made a few years back using Slick2D (as opposed to Swing/AWT, which was a terrible idea). I've fleshed out a lot of the background architecture, but I'm starting to run into issues with my architecture and I'm not sure how to proceed.
      The game I'm making is an overhead shooter. It's wave-based, with hordes of zombies coming at you. There are 10 weapons to choose from.
      Specifically, my latest issue is with a particular "weapon" I'm designing; the Laser Barrier. In the previous game, I had a "Laser Wire" weapon, which when two terminals were placed on the ground, created a wire made of laser on the ground that would damage enemies that passed through. Problem was that it didn't do enough damage in the short time that the enemies would be colliding with it for it to be of any use, so it was a waste of money.
      In the remake, I'm instead creating the "Laser Barrier", which visually looks the same, but instead of damaging enemies that touch it, it will act as an obstacle that enemies can't walk through. The enemies damage the shield while they are in contact with it until the laser barrier collapses. Projectiles however, can still pass through the barrier, allowing the player, and certain enemies, to shoot through them.
      The issue I'm running into, though, is the method of communicating between the Laser Wire itself and the enemy touching it. I'm currently able to detect a collision between the enemy and wire projected between the two laser terminals, but I'm not sure how I can implement the actual movement blocking part.
      It would take too much text to explain how it works, so here are the relevant files in my project:
      Player class - the checkProjectiles() method on line 344 is where the game loop checks for collisions between the player's weapon projectiles and the an enemy passed as an argument. On line 350, you can see that when there is a collision between the enemy and the LaserNode object (collision method is linked below), the laser node takes damage so that it will eventually be destroyed. I figured this is where the "movement blocking" should be, as I have access to the terminal and the enemy, and this is where a collision is confirmed. LaserNode class - this is the class representing the laser terminals on the ground that project the laser beam between them. The checkCollision() method on line 57 is used to determine if the enemy is touching either of the terminals, or if it is touching the beam itself. Enemy class - this is the base class for all game enemies. You can see what methods are available to all enemies, so perhaps this can provide some insight into what could be done to communicate with the LaserNode. I realize it's a lot to ask considering the scope of my project, but could someone give me an idea of how to communicate between the LaserNode and Enemy so that the enemy knows not to move when touching the LaserNode? The only methods I can think of seem cumbersome and it seems like I'd be adding a lot to the Enemy class just to get this one feature working.
      I'd love to script these weapons with LUA, but I never learned how to integrate a scripting language into my game architecture. I also have limited experience writing game engines, so I'm sure there's a lot of refinement that could be done to make my game architecture less restricting.
      I don't expect anyone to actually comb through my project and make suggestions, but I would super appreciate it.
    • By Oakman771
      Hello everybody     I am interested in learning how to develop an app/game for Android devices. I have experience with UE4 and know the basics of C++.  Would a very simple version of Armory & Machine be a good starter project? It should not contain physics or AI or similar. What would be the way to make something like that? I read a lot about Java related to Android on the internet.   Thanks for your help!
    • By Arnold577
      Hi there, 
      I have worked on multiple games in the past: text based/2D/3D.
      But now I want to make an mmo, specifically a text based one, known as a MUD.
      The thing is I don't know much about making them, I know:
      Loads of: C#, Python
      A bit of: HTML & JS
      Hardly any of: C++, Java
       
      What I would like to know is:
      How should I go about the networking? I know some sockets but don't like using others libraries.
      Is there any information on the topics that I haven't been able to find?
      And mainly, what language should I use. I am willing to learn a new one.
       
      THANK YOU IN ADVANCE
       
      I have done research.
       
    • By Juan Kang
      Hi everyone, 
      I've made a small java game recently, just for education purpose. So this game is a endless scrolling 2D plaformer. all you can do is jumping, with the impulse force to it. So the basic is to keep the player standing(and jumping) and move the background/platform from right to left. And with the forced jump, increase the force everytime the player press the button(e.g spacebar) and jump with the increased force when the button is released.
      Then,  I use Collision/Physics detection, with the platform in the air, or standing like a wall. So I want, when the player is hit against the wall, he will be dragged by it, as it move from right to left. And now I have 2 problems:
      - Sometime when the player is stand against a wall, and being dragged, he can't jump. Actually, just for a small distance and then stuck into a wall against him.

      - Some other times,  when I press/hold the button, he is going through the wall, and not even jump when I released the button.

      - The second problem is, when he falling, if he hit the platform, he got stuck to it, unable to jump anymore.


      I tried checking online for this, I believe I am probably Googling the wrong things to find tutorials for this, most tutorials seem to be for platformers like Rockman, or Flappy Bird,...But nothing seemed to solve my problems. 
      These are some of my code:
      Player.java
      package smithitsmiths; import bases.GameObject; import bases.Vector2D; import bases.inputs.InputManager; import bases.physics.BoxCollider; import bases.physics.Physics; import bases.renderers.ImageRenderer; import jdk.internal.util.xml.impl.Input; public class Player extends GameObject { private Vector2D velocity; private final float GRAVITY = 1f; private final float JUMPSPEED = 10; protected float force = 0; private BoxCollider boxCollider; final static int maxForce = 2; public static float currentForce; public GaugeBar gaugeBar; public Player() { super(); this.renderer = ImageRenderer.create("assets/images/players/player_walk1.png"); velocity = new Vector2D(); boxCollider = new BoxCollider(36, 45); this.children.add(boxCollider); gaugeBar = new GaugeBar(); GameObject.add(gaugeBar); } @Override public float run(Vector2D parentPosition) { super.run(parentPosition); //GRAVITY impact velocity && Inputs this.velocity.y += GRAVITY; if (InputManager.instance.spacePressed) { if (force <= maxForce) { gaugeBar.setValue(force); force += 0.1f; return currentForce = force; } } if (InputManager.instance.spaceReleased) { //when player is at platform(not in the air), enable jump, vice versa BoxCollider boxColliderAtBottom = this.boxCollider.shift(0, 1); if (Physics.collideWith(boxColliderAtBottom, Platform.class) != null) { velocity.y = -JUMPSPEED * force; } force = 0; gaugeBar.reset(); } //Platform physics moveVertical(); moveHorizontal(); //gaugebar update: gaugeBar.setPosition(this.position.x - 40, this.position.y - 40); return 0; } private void moveVertical() { //calculate future position(box collider) & predict collision BoxCollider nextBoxCollider = this.boxCollider.shift(0, velocity.y); Platform platform = Physics.collideWith(nextBoxCollider, Platform.class); if (platform != null) { //move player continously towards platform boolean moveContinue = true; float shiftDistance = Math.signum(velocity.y); while (moveContinue) { if (Physics.collideWith(this.boxCollider.shift(0, shiftDistance), Platform.class) != null) { moveContinue = false; } else { shiftDistance += Math.signum(velocity.y); this.position.addUp(0, Math.signum(velocity.y)); } } //update velocity () velocity.y = 0; } //velocity impact position this.position.addUp(0, velocity.y); this.screenPosition.addUp(0, velocity.y); } private void moveHorizontal() { //calculate future position(box collider) & predict collision BoxCollider nextBoxCollider = this.boxCollider.shift(velocity.x, 0); Platform platform = Physics.collideWith(nextBoxCollider, Platform.class); if (platform != null) { //move player continously towards platform boolean moveContinue = true; float shiftDistance = Math.signum(velocity.x); while (moveContinue) { if (Physics.collideWith(this.boxCollider.shift(shiftDistance, 0), Platform.class) != null) { moveContinue = false; this.position.subtractBy(platform.getSpeed(),0); } else { shiftDistance += Math.signum(velocity.x); this.position.addUp(Math.signum(velocity.x), 0); } } //end move player continously towards platform //update velocity () velocity.x = 0; } //velocity impact position this.position.addUp(velocity.x,0); this.screenPosition.addUp(velocity.x,0); } } Platform.java: package smithitsmiths; import bases.GameObject; import bases.Vector2D; import bases.physics.BoxCollider; import bases.physics.PhysicsBody; import bases.renderers.ImageRenderer; public class Platform extends GameObject implements PhysicsBody{ private BoxCollider boxCollider; private final float SPEED = 2; Vector2D velocity; public Platform() { super(); this.renderer = ImageRenderer.create("assets/images/platform/yellow_square.jpg"); boxCollider = new BoxCollider(30,30); this.children.add(boxCollider); velocity = new Vector2D(); } @Override public float run(Vector2D parentPosition) { super.run(parentPosition); velocity.x = -SPEED; position.addUp(velocity); return 0; } @Override public BoxCollider getBoxCollider() { return boxCollider; } public float getSpeed() { return SPEED; } }  
      BoxCollider.java:
      package bases.physics; import bases.GameObject; public class BoxCollider extends GameObject { private float width; private float height; public BoxCollider(float x, float y, float width, float height) { super(); this.position.set(x, y); this.screenPosition.set(x, y); this.width = width; this.height = height; } public BoxCollider(float width, float height) { this(0, 0, width, height); } public BoxCollider shift(float dx, float dy){ BoxCollider shiftedBoxCollider = new BoxCollider(this.width, this.height); shiftedBoxCollider.position.set(this.position.add(dx,dy)); //new position of newBC = current position + (dx,dy) shiftedBoxCollider.screenPosition.set(this.screenPosition.add(dx,dy)); return shiftedBoxCollider; } public float left() { return this.screenPosition.x - this.width / 2; } public float right() { return this.screenPosition.x + this.width / 2; } public float top() { return this.screenPosition.y - this.height / 2; } public float bottom() { return this.screenPosition.y + this.height / 2; } public float getWidth() { return width; } public float getHeight() { return height; } public boolean intersects(BoxCollider other) { return this.bottom() >= other.top() && this.top() <= other.bottom() && this.right() >= other.left() && this.left() <= other.right(); } @Override public String toString() { return "BoxCollider{" + "width=" + width + ", height=" + height + ", screenPosition=" + screenPosition + '}'; } }  
      So the ground is a total of many square platforms, with the BoxCollider of each. I really confuse where the bug is, and how to fix it. So I post it here hopefully you guys can tell me what to do. 
      I really appreciate it.

      Thanks for reading my post.
  • Popular Now