0
votes

I am dealing with an issue in a small 2d tile-based game that I'm making where the sprite that represents the player is not ending its movement smoothly (or completely) when input from the user stops. The sprite appears to "snap back" a few pixels when it stops moving.

Please see the video here to see what I mean.

I believe the issue has to do with the sync between the sprite's movement and the camera's, but I can't seem to identify what is causing it. The reason I feel the issue is related to the camera's motion is that when the camera is static (meaning, not tied to the sprite's movement) the issue does not appear: the sprite's motion ceases instantaneously in this case. I'm somewhat new to OpenGL so I would certainly appreciate some fresh pairs of eyes.

To provide a bit of context, please see the below code segments.

This is the main loop:

while (!glfwWindowShouldClose(window.getInstance())) {

        glfwPollEvents();
        processInput(window.getInstance());

        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glBindTexture(GL_TEXTURE, spriteSheet);

        glm::mat4 projectionMatrix = glm::ortho(0.0f, (float)window.width, (float)window.height, 0.0f, -1.0f, 1.0f); 
        glm::mat4 viewMatrix = camera.getViewMatrix(); 
        
        spriteShader.setMat4("projectionMatrix", projectionMatrix);
        spriteShader.setMat4("viewMatrix", viewMatrix);
        spriteShader.use()
        spriteShader.setVec2("worldPosition", glm::vec2(player.sprite.x, player.sprite.y));

        glBindVertexArray(player.sprite.getVAO());
        glDrawArrays(GL_TRIANGLES, 0, 6);
        glBindVertexArray(0);
        
        tileShader.setMat4("projectionMatrix", projectionMatrix);
        tileShader.setMat4("viewMatrix", viewMatrix);
        tileShader.use();

        glBindVertexArray(tileMap.VAO);
        glDrawElements(GL_TRIANGLES, 1512, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);
        
        glfwSwapBuffers(window.getInstance());
    }

This is the processInput() function:

void processInput(GLFWwindow* window) {
    float MOVE_SPEED = 5.0f;
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
        camera.processKeyboard(LEFT);
        player.move(-MOVE_SPEED, 0);
    }
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
        camera.processKeyboard(RIGHT);
        player.move(MOVE_SPEED, 0);
    }
    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
        camera.processKeyboard(UP);
        player.move(0, -MOVE_SPEED);
    }
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
        camera.processKeyboard(DOWN);
        player.move(0, MOVE_SPEED);
    }
}

And these are the two functions that update the positions of the player and camera, respectively:

Player:

void Player::move(float x, float y) {
        this->x += x;
        this->y += y;
        sprite.x += x;
        sprite.y += y;
}

Camera:

void Camera::processKeyboard(Camera_Movement direction) {
    if (direction == LEFT) {
        position[0] -= SPEED; 
        front[0] -= SPEED;
    }
    else if (direction == RIGHT) {
        position[0] += SPEED;
        front[0] += SPEED;
    }
    else if (direction == UP) {
        position[1] -= SPEED;
        front[1] -= SPEED;
    }
    else if (direction == DOWN) {
        position[1] += SPEED;
        front[1] += SPEED;
    }
}

Also worth sharing are the camera's getViewMatrix() function and the vertex shader for the player sprite (where the sprite's x,y position is used to offset the vertex positions):

glm::mat4 Camera::getViewMatrix() {
    return glm::lookAt(position, front, up);
}

Vertex shader:

#version 330 core
layout (location = 0) in vec2 vertexPosition;
layout (location = 1) in vec2 textureCoordinates; 

out vec2 vs_textureCoordinates;

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform vec2 worldPosition;

void main()
{
    gl_Position = projectionMatrix * viewMatrix * vec4(vertexPosition[0] + worldPosition[0], vertexPosition[1] + worldPosition[1], 0.0, 1.0);
    vs_textureCoordinates = textureCoordinates;
}

I'm sure it's just a silly mistake but due to my lack of familiarity with OpenGL I'm just not catching it. Thank you in advance to anyone who might be able to offer any insight.

1
Depending on how your shader abstraction works, you must use spriteShader.use(); prior to updating the uniforms. It depends on how setMat4() is implemented.Ext3h
You are completely correct. I must have been following an old habit from some of the first tutorials on learnopengl.com. That fixed the issue. I'd be more than happy to accept it as the answer if you'd like to enter it as one. Thank you so much.Antillies

1 Answers

1
votes

Depending on how your shader abstraction works, you must use spriteShader.use(); prior to updating the uniforms. It depends on how setMat4() is implemented.

When implementing against a modern OpenGL version (4.1 or newer) you should be using glProgramUniform internally as it avoids this specific issue entirely.