Stumped on adding a sprite to my player

Hi, I’m making a simple platformer for learning purposes and I’m currently trying to add a sprite to my player which is currently a square. I’ve managed to get the sprite part working and animated (it’s an idle animation that’s 11 frames), however, I noticed that when the player collides with a wall or something above them they don’t go flush with the surface they stop a few pixels away instead. I’m assuming it’s because the sprite is a bit smaller than the square which is what’s used for collision so that’s what’s causing the problem. Here’s a video to demonstrate what I mean https://imgur.com/a/UekynVT

Lastly here’s here’s some code related to the player sprite animation

Player::Player(SDL_Renderer* renderer) {
    printf("Player Ctor\n");
    position = Vector2(100, 100);
    velocity = Vector2(0, 0);
    size = Size(32 * scale, 32 * scale);

    SDL_Surface* temp = IMG_Load("assets/Spritesheet/Ninja Frog/Idle (32x32).png");  // Load the sprite sheet.
    if (temp == nullptr) {
        std::cerr << "Failed to load sprite sheet: " << IMG_GetError() << std::endl;
        exit(1);
    }
    spriteSheet = SDL_CreateTextureFromSurface(renderer, temp);
    SDL_FreeSurface(temp);
}

void Player::draw(SDL_Renderer* renderer) {
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    SDL_Rect rect = { position.getX(), position.getY(), size.getWidth(), size.getHeight() };
    SDL_RenderFillRect(renderer, &rect);

    SDL_Rect srcRect = { currentFrame * frameWidth, 0, frameWidth, frameHeight };
    SDL_Rect dstRect = { static_cast<int>(position.getX()), static_cast<int>(position.getY()), frameWidth * scale, frameHeight * scale };

    SDL_RenderCopy(renderer, spriteSheet, &srcRect, &dstRect);

    if (SDL_GetTicks() - lastFrameTime >= frameDelay) {
        currentFrame++;
        if (currentFrame >= totalFrames) {
            currentFrame = 0;
        }
        lastFrameTime = SDL_GetTicks();
    }
}

You might want to use a different rect (or other shape) to do collision detection that is not necessarily the same as the size of the sprite. That way you could make the collision rect somewhat smaller and you could even change the sprite size if you want without affecting the logical size of the player.

The other obvious solution would be to change the size of the sprite (i.e. reduce frameWidth and frameHeight to perfectly match the graphics) but this will only look good if your graphics fill up most of the rectangle. Especially collisions at the corner can be a problem.

smaller-collision-rect

2 Likes

Hi, thanks for the response I don’t quite get what you’re saying though haha, but looking at the gif you provided it gave me an idea to rewrite some of the code in a way that I can adjust the offset and size of the collision square. I can’t imagine this is a good solution, but it’s the closest I’ve gotten to getting the collision square to match the sprite.

Here’s a revised version of my code and a quick gif.
https://imgur.com/a/oCPnEOA

// Some member variables in Player.h
SDL_Texture* spriteSheet;
int frameWidth = 32;
int frameHeight = 32;
int totalFrames = 11;
int currentFrame = 0;

int hitboxOffsetX = 13;
int hitboxOffsetY = 14;
int hitboxWidth = frameWidth * 1.8 - 25;
int hitboxHeight = frameHeight * 1.8 - 15;

SDL_Texture* texture;

// Draw definition in Player.cpp
void Player::draw(SDL_Renderer* renderer) {
    SDL_Rect srcRect = { currentFrame * frameWidth, 0, frameWidth, frameHeight };
    SDL_Rect dstRect = { position.x, position.y, frameWidth * 1.8, frameHeight * 1.8 };
    SDL_RenderCopy(renderer, spriteSheet, &srcRect, &dstRect);

    SDL_Rect dstRect1 = { position.x + hitboxOffsetX, position.y + hitboxOffsetY, hitboxWidth, hitboxHeight };
    SDL_RenderCopy(renderer, texture, NULL, &dstRect1);

    if (SDL_GetTicks() - lastFrameTime >= frameDelay) {
        currentFrame++;
        if (currentFrame >= totalFrames) {
            currentFrame = 0;
        }
        lastFrameTime = SDL_GetTicks();
    }
}