Centering an object in the screen

Hey guys! First post here :slight_smile:

So I have a little problem. I have a 2D platformer, and I want to keep the player in the center of the screen. Well, that should be easy right? Just set the camera position to half the size of the screen plus half the size of the player. So, I did that. But the camera actually scrolls slower than the player, so that the player can actually escape the screen. I’ll show you some code now:

The camera class:

public Camera(){
	}
	
	public void update(Player p){
		glTranslatef(Main.WIDTH / 64 / 2 + -p.getPos().x / 2, Main.HEIGHT / 64 / 2 - -p.getPos().y / 2, 0);
	}

Here’s how I update my player’s position:

public void update(){
		if(Keyboard.isKeyDown(Keyboard.KEY_D)){
			this.move(MOVESPEED, 0);
		}
		
		if(Keyboard.isKeyDown(Keyboard.KEY_A)){
			this.move(-MOVESPEED, 0);
		}
	}

Which then in turn calls the move method:

public void move(float x, float y){
		this.getPos().set(this.getPos().x + x, this.getPos().y + y);
	}

I create the player and initialize it’s position to half the screen, which works fine. But when I start scrolling, the player isn’t centered in the screen! How can I fix this?

If you are scaling your screen remember to increase the xoffset and yoffset correspondingly.

I already do, that shouldn’t matter.

try adding a call to glLoadIdentity() before your glTranslatef() call.

I have a feeling that your glTranslate( … ) calls are stacking up causing the viewport to go out of bounds.

Nope, that didn’t work :confused: Here, I’ll show you how I update the camera, maybe that’ll help:

public void render() {
		glLoadIdentity();
		glPushMatrix();
		cam.update(mobManager.getP());
		tileManager.render();
		mobManager.render();
		glPopMatrix();
	}

That’s my method for rendering all my stuff. cam.update is there because it calls glTranslate, and it needs to translate the geometry in the tileManager and mobManager. I added the glLoadIdentity call like you said.

Edit: The weird thing is, is that I print out the camera and player position vectors, and they’re the same. Yet, the “camera” scrolls slower.

Note how you have

glTranslatef(Main.WIDTH / 64 / 2 + -p.getPos().x / 2, Main.HEIGHT / 64 / 2 - -p.getPos().y / 2, 0);

?

See how it says p.getPos().x / 2? That should not be divided by 2.

That’s why your viewport is moving at half the speed of the player.

Quick example of how I do it in my platformer:


Core.view.tx = x - Core.GameWidth/2;
        Core.view.ty = y - _area.height / 2 - 350 * Core.GameHeight / 800;

So what I’d change your code to is:

glTranslatef(-(p.getPos().x - Main.WIDTH/2 + 64/2), -(p.getPos().y - Main.HEIGHT/2 + 64/2), 0);

Assuming your player is 64x64 and origin is top left.

Hope this helps :slight_smile:

PS: your glPushMatrix and glPopMatrix functions are pointless, you’re saving a copy of the identity matrix to the stack, doing nothing with it and then popping it after.
Those can be removed.

I had a problem like this just a couple of weeks ago. I fixed it by subtracting the camera position when moving the player.
So something like this:


public void move(float x, float y){
      this.getPos().set((this.getPos().x + x) - camera.getPos.x, (this.getPos().y + y) - camera.getPos.y);
}

@Dxu1994 That still didn’t work :frowning: Quite honestly, I think the camera moved slower! And the player wasn’t centered in the window anymore, he was up and to the left! I should note that my origin is in the top left, no the bottom because I am using slick-util to render my text.

@Troubleshoots, I would do that, but my camera doesn’t have a vector. I literally just use my player’s vector for the camera.

I only just realised that haha.

Well if your origin is top left try this code:

glTranslatef(-(p.getPos().x - Main.WIDTH/2 + 64/2), -(p.getPos().y - Main.HEIGHT/2 + 64/2), 0);

Well, I think I should give up :frowning: Still not working… I have absolutely no idea what the problem could be! I mean, here’s how I create the player:

public Player() {
		super(new Vector2f(Main.WIDTH / 2 / 64, Main.HEIGHT / 2 / 64), Type.PLAYER);
	}

And then the update method:

public void update(){
		MOVESPEED += .0001f;
		if(Keyboard.isKeyDown(Keyboard.KEY_D)){
			this.move(MOVESPEED, 0);
		}
		
		if(Keyboard.isKeyDown(Keyboard.KEY_A)){
			this.move(-MOVESPEED, 0);
		}
	}

Its literally the most simple thing ever! Would the moveSpeed variable have anything to do with what’s wrong?

Why do you divide by 2 and then 64?

Because that’s translating it to the center of the screen, I believe. Dividing by two to get to the center of the screen, then dividing by 64 which is the tile size to get to the center of the map. Or something like that.

Currently if you move the player 100px, you’re moving the camera 50px.


+ -p.getPos().x / 2

Thus why you need to update the player based on the camera position.

Edit:
Create a vector for the camera position, and do this:


public void move(float x, float y){
      this.getPos().set(this.getPos().x + x, this.getPos().y + y).sub(camera.getPos);
}

No, translating to the center of the screen would be:

x: ScreenWidth / 2 - TileSize / 2
y: ScreenHeight / 2 - TileSize / 2

Your code is translating somewhere near the top left corner.

Ok, so how should I move the camera now? Should I not have the input change the players position? I did it like you said, and the player doesn’t move anymore because I don’t know what I should be putting in my translate method of my camera anymore.

Set its position based on the players position:

glTranslateF((p.getPos().sub(GameContainer.getWidth/2, GameContainer.getHeight/2)), 0);

I use this code and it works fine for me.


public void follow(GObject obj)
{
    int cam_x = 0;
    int cam_y = 0;

    if (Map.getWidth() > SCREEN_WIDTH)
    {
        cam_x = Math.round(SCREEN_WIDTH / 2 - obj.getX() - Map.TILE_WIDTH);
        cam_x = Math.min(cam_x, 0);
        cam_x = Math.max(cam_x, SCREEN_WIDTH - Map.getWidth());
    }
    if (Map.getHeight() > SCREEN_HEIGHT)
    {
        cam_y = Math.round(SCREEN_HEIGHT / 2 - obj.getY() - Map.TILE_HEIGHT);
        cam_y = Math.min(cam_y, 0);
        cam_y = Math.max(cam_y, SCREEN_HEIGHT - Map.getHeight());
    }

    glTranslatef(-cam_x, -cam_y);
}

Hope this helps.

So I rewrote all my stuff, and I figured out most of the problem. Now, however, the player slowly slides out of the center of the screen. Here’s my new camera class:

private Player p;
	private Vector2f pos;
	
	public Camera(Player p){
		this.p = p;
		this.pos = new Vector2f(0, 0);
	}
	
	public void update(){
		glTranslatef(-p.getPos().x + pos.x, -p.getPos().y + pos.y, 0);
		if(Keyboard.isKeyDown(Keyboard.KEY_D)){
			p.getPos().setX(p.getPos().x + 1);
			pos.setX(pos.x - 1 * 64);
		}
		if(Keyboard.isKeyDown(Keyboard.KEY_A)){
			p.getPos().setX(p.getPos().x - 1);
			pos.setX(pos.x + 1 * 64);
		}
	}

That’s really the only relevant code, nothing else has changed. I’m so close, I just need to fix this last part! Any help?

When you are stuck with this kind of problems I recommend to take a sheet of paper and pen and draw. It can really help when you visualize the problem instead just playing around with variables and finding a solution by guessing.

Also, do not be afraid to name your variables in their lenght (p -> player, pos -> position). It’s easier for other people to study your code.

Try this.


public class Camera
{
    private Player player;

    public Camera(Player player)
    {
        this.player = player;
    }

    public void update()
    {
        int cam_x = 0;
        int cam_y = 0;

        if (Map.getWidth() > SCREEN_WIDTH)
        {
            cam_x = Math.round(SCREEN_WIDTH / 2 - player.getX() - player.getWidth());
            cam_x = Math.min(cam_x, 0);
            cam_x = Math.max(cam_x, SCREEN_WIDTH - Map.getWidth());
        }
        if (Map.getHeight() > SCREEN_HEIGHT)
        {
            cam_y = Math.round(SCREEN_HEIGHT / 2 - player.getY() - player.getHeight());
            cam_y = Math.min(cam_y, 0);
            cam_y = Math.max(cam_y, SCREEN_HEIGHT - Map.getHeight());
        }

        glTranslatef(-cam_x, -cam_y);
    }
}

This way, the camera always centers the player, there are no slowness etc.,

Hope this helps.