0
\$\begingroup\$

First of all, I am aware that there's no camera in OpenGL (ES 2), but from my understanding proper manipulation of the projection matrix can simulate the concept of a camera.

What I'm trying to do is make my camera follow my character. My game is 2D, btw. I think the principle is the following (take Super Mario Bros or Doodle Jump as reference - actually I'm trying to replicate the mechanics of the latter): when the caracter goes beyond the center of the screen (in the positive axis/direction), update the camera to be centred on the character. Else keep the camera still. I did accomplish that, however the camera movement is noticeably jittery and I ran out of ideas how to make it smoother.

First of all, my game loop (following this article):

private int TICKS_PER_SECOND = 30;
private int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
private int MAX_FRAMESKIP = 5;

@Override
public void run() {
    loops = 0;

    if(firstLoop) {
        nextGameTick = SystemClock.elapsedRealtime();
        firstLoop = false;
    }

    while(SystemClock.elapsedRealtime() > nextGameTick && loops < MAX_FRAMESKIP) {
        step();
        nextGameTick += SKIP_TICKS;
        loops++;
    }

    interpolation = ( SystemClock.elapsedRealtime() + SKIP_TICKS - nextGameTick ) / (float)SKIP_TICKS;

    draw();
}

And the following code deals with moving the camera. I was unsure whether to place it in step() or draw(), but it doesn't make a difference to my problem at the moment, as I tried both and neither seemed to fix it. center just represents the y coordinate of the centre of the screen at any time. Initially it is 0. The camera object is my own custom "camera" which basically is a class that just manipulates the view and projection matrices.

if(character.getVerticalSpeed() >= 0) { //only update camera if going up
        float[] projectionMatrix = camera.getProjectionMatrix();

        if( character.getY() > center) {

            center += character.getVerticalSpeed();

            cameraBottom = center + camera.getBottom();
            cameraTop = center + camera.getTop();

            Matrix.orthoM(projectionMatrix, 0, camera.getLeft(), camera.getRight(), center + camera.getBottom(), center + camera.getTop(), camera.getNear(), camera.getFar());
        }
    }

Any thought about what I should try or what I am doing wrong?

Update 1: I think I updated every value you can see on screen to check whether the jittery movement is affected by that, but nothing changed, so something must be fundamentally flawed with my approach/calculations.

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

First I agree with the other answer about camera location code. On your display loop I would draw everything first, then update everything after that, then repeat.

Second, I have a game I'm working on right now that has a character (space ship) that can travel anywhere in a 2d world (3d game with a flat map). I use something like this to change the camera's position:

        // set camera targets to the ship's x,y - T is TargetX and TargetY
        cameraTX = myShip.currentPos.x;
        cameraTY = myShip.currentPos.y;

        // V is velocity
        GLfloat cameraVXT = (cameraTX - cameraX) * 4.0; // magic numbers that work for my game
        GLfloat cameraVYT = (cameraTY - cameraY) * 4.0;
        GLfloat cameraVZT = (cameraTZ - cameraZ) * 8.0;

        //but velocity can't change that fast due to inertia.  So move towards that velocity over time
        cameraVX +=  (cameraVXT - cameraVX) * 3.0 * deltaTimeThisRound;
        cameraVY +=  (cameraVYT - cameraVY) * 6.0 * deltaTimeThisRound;

        cameraX += cameraVX * 4.0 * deltaTimeThisRound;
        cameraY += cameraVY * 4.0 * deltaTimeThisRound;
        cameraZ += (cameraTZ - cameraZ) * 6.0 * deltaTimeThisRound;

The target x and y of the camera is set according to the ships last known position. The z doesn't usually change but is set in another function. Then I take the distance between the current location of the camera and the target location of the camera and create a target velocity. Then move the velocity towards the target velocity over time. Then apply that velocity to the camera over time.

All the different numbers like * 4.0 and * 8.0 were found over trial and error to dampen or speed up the camera's following of the ship. Too big and looks jerky and follows too tight. Too small and it lags too much and makes you motion sick. So you have to mess with these to get it to feel right for your game. Maybe you don't even need one of the steps and you just add a simple rubber banding to the characters position without using a target velocity.

After all of this I call a bindCamera method that sets my view matrix.

\$\endgroup\$
0
\$\begingroup\$

About the place of the camera location code: If you are not using the camera in the step() function (which your probably don't), it might get updated multiple times per frame, but only get used once in the draw() function. So to prevent a waste of calculations, I'd place it in the latter.

Related to the jittery camera: there is no good reason for using a projection matrix for camera movement. The projection matrix is generally only generated once in the initialization, and a view matrix is used to determine the rotations and translations of the camera. You can use the Matrix.translateM() function to create a view matrix that does nothing more than translate the camera. Then, before you render, use the combined view * projection matrix. (You can combine them using the Matrix.multiplyMM() function)

For more information about the topic of view/projection matrices, have a look at the following resources:

Update 1: In your code, you are using two different variables (character.getY() for the check, character.getVerticalSpeed() for the update).

Although the code

center += character.getVerticalSpeed();

and

center = character.getY(); 

should virtually do the same, could you give that second one a try? (The actual values might differ due to things such as floating point roundoffs).

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for your advice, Miklas! Indeed, it makes more sense to have the code in the draw() method and use the view matrix rather than the projection matrix, but unfortunately I'm still nowhere near fixing my problem. \$\endgroup\$
    – async
    Commented Apr 12, 2014 at 20:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .