0

I'm really stucked with the following code, it renders the wrong image of the obstacles on the screen. instead of a blue circle ("obstacle.png") on the screen appears the image of the char spreadsheet (the whole "char_anim.png")

Screenshot

Here's my code:

// C-Jumper.cpp : Diese Datei enthält die Funktion "main". Hier beginnt und endet die Ausführung des Programms.
//


#include <stdio.h>
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include <iostream>
#include <vector>

using namespace std;

const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
const int GROUND = 50;
const int BOTTOM = SCREEN_HEIGHT - GROUND;

const char* FONT_FILE = "assets/fonts/arial.ttf";
const int FONT_SIZE = 24;

const int FPS = 60;
const int sollzeit = 1000 / FPS;

class Character {
public:
    Character(SDL_Renderer* renderer) {
        // Load sprite sheet image
        SDL_Surface* spriteSheetSurface = IMG_Load("assets/images/char_anim.png");
        spriteSheetTexture = SDL_CreateTextureFromSurface(renderer, spriteSheetSurface);
        SDL_FreeSurface(spriteSheetSurface);

        // Define frames for sprite sheet
        const int NUM_FRAMES = 8;
        SDL_Rect frames[NUM_FRAMES] = {
            {0, 0, 40, 58},
            {40, 0, 40, 58},
            {80, 0, 40, 58},
            {120, 0, 40, 58},
            {160, 0, 40, 58},
            {200, 0, 40, 58},
            {240, 0, 40, 58},
            {280, 0, 40, 58}
        };
        for (int i = 0; i < NUM_FRAMES; i++) {
            spriteClips[i] = frames[i];
        }

        // Set initial animation state
        currentFrame = 0;
        currentSpriteClip = &spriteClips[currentFrame];
        lastFrameTime = 0;
    }

    ~Character() {
        SDL_DestroyTexture(spriteSheetTexture);
    }

    void updateAnimation() {
        // Animate character
        Uint32 currentTime = SDL_GetTicks();
        if (currentTime - lastFrameTime >= frameTime) {
            // Update the current frame
            currentFrame = (currentFrame + 1) % NUM_FRAMES;
            currentSpriteClip = &spriteClips[currentFrame];
            lastFrameTime = currentTime;
        }
    }

    void render(SDL_Renderer* renderer, int x, int y) {
        SDL_Rect destRect = { x, y, currentSpriteClip->w, currentSpriteClip->h };
        SDL_RenderCopy(renderer, spriteSheetTexture, currentSpriteClip, &destRect);
    }

private:
    // SDL_Surface* spriteSheetSurface;
    SDL_Texture* spriteSheetTexture;
    SDL_Rect spriteClips[8];
    SDL_Rect* currentSpriteClip;
    const int NUM_FRAMES = 8;
    int currentFrame;
    Uint32 lastFrameTime;
    int frameTime = 100; // time in milliseconds between frames
};


class Obstacle {
public:
    Obstacle(int x, int y, int width, int height, SDL_Renderer* renderer)
        : x_(x), y_(y), width_(width), height_(height)
    {
        SDL_Surface* ObjSurface = IMG_Load("assets/images/obstacle.png");
        ObjTexture = SDL_CreateTextureFromSurface(renderer, ObjSurface);
        if (ObjTexture == NULL) {
            std::cout << "Failed to create texture from surface: " << SDL_GetError() << std::endl;
        }
        SDL_FreeSurface(ObjSurface);
    }

    ~Obstacle() {
        SDL_DestroyTexture(ObjTexture);
    }
    void move() {
        x_ -= 2;
        if (x_ < 10) { x_ = 600; }
    }
    bool isColliding(int x, int y, int width, int height) {
        return (x_ < x + width && x_ + width_ > x && y_ < y + height && y_ + height_ > y);
    }
    void draw(SDL_Renderer* renderer) {
        SDL_Rect drect = { x_, y_, width_, height_ };
        SDL_RenderCopy(renderer, ObjTexture, NULL, &drect);
    }

private:
    int x_;
    int y_;
    int width_;
    int height_;
    SDL_Texture* ObjTexture;
    // SDL_Surface* ObjSurface;
};



int main(int argc, char* argv[])
{
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("Platformer Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); // | SDL_RENDERER_PRESENTVSYNC);
    int BORDER = SCREEN_WIDTH - 40;

    // Load background texture
    SDL_Surface* backgroundSurface = IMG_Load("assets/images/background.png");
    SDL_Texture* backgroundTexture = SDL_CreateTextureFromSurface(renderer, backgroundSurface);
    SDL_FreeSurface(backgroundSurface);
    
    // Definiere Font
    TTF_Init();
    TTF_Font* font = TTF_OpenFont(FONT_FILE, FONT_SIZE);
    if (!font) {
        printf("Failed to load font: %s\n", TTF_GetError());
        return 1;
    }
    SDL_Color color = { 255, 255, 255 }; // white text color
    SDL_Surface* textSurface = TTF_RenderText_Solid(font, "Hello, SDL!", color);
    SDL_Texture* textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
    SDL_FreeSurface(textSurface);

    
    // Obstacles
    vector<Obstacle> obstacles;
    // Set up variables for timing
    int lastTime = SDL_GetTicks();
    int currentTime = 0;
    int deltaTime = 0;
    int id = 0;

    obstacles.push_back(Obstacle(300, BOTTOM - 50, 50, 50, renderer));
    obstacles.push_back(Obstacle(550, BOTTOM - 50, 50, 50, renderer));
    obstacles.push_back(Obstacle(800, BOTTOM - 50, 50, 50, renderer));
        
    // Create character object
    Character character(renderer);
    bool move = true;

    // Regualte Frames per Second
    Uint32 frameStart = SDL_GetTicks();
    Uint32 FPS_Start = SDL_GetTicks();
    int frameCount = 0;
    int Sekunden = 0;
    char puffer[50];
    sprintf_s(puffer, "Frames: --");

    SDL_Event event;
    // Define constants for movement speed and jump height
    const int MOVEMENT_SPEED = 5;
    const int JUMP_SPEED = 20;

    // Set initial velocity and position
    int xVel = 0;
    int yVel = 0;
    int xPos = 100;
    int xPos_last = 0;
    int yPos = 100;

    bool quit = false;
    // Game loop here
    while (!quit) {
        // Calculate delta time Frames & Objects
        frameStart = SDL_GetTicks();
        currentTime = SDL_GetTicks();
        deltaTime = currentTime - lastTime;
        lastTime = currentTime;

        // Handle events
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                quit = true;
            }
        }

        // Read keyboard state
        const Uint8* keyboardState = SDL_GetKeyboardState(NULL);

        SDL_Color color = { 255, 255, 255 }; // white text color
        SDL_Surface* Textsurface = TTF_RenderText_Solid(font, puffer, color);
        SDL_Texture* Texttexture = SDL_CreateTextureFromSurface(renderer, Textsurface);
        SDL_Rect dstrect = {50, 50, Textsurface->w, Textsurface->h };
        SDL_FreeSurface(Textsurface);

        /*
        // Spawn a new obstacle every second
        static int obstacleTimer = 0;
        obstacleTimer += deltaTime;
        if (obstacleTimer > 1000) {
            obstacleTimer = 0;
            id++;
            int x = SCREEN_WIDTH-100;
            int y = SCREEN_HEIGHT-200;
            int w = 50;
            int h = 50;
            Obstacle obstacle(x, y, w, h, renderer);
            obstacles.push_back(obstacle);
        }
        */

        // Update character animation
        if (move == true) { character.updateAnimation(); }

        // Handle movement
        if (keyboardState[SDL_SCANCODE_A]) {
            xVel = -MOVEMENT_SPEED;
        }
        else if (keyboardState[SDL_SCANCODE_D]) {
            xVel = MOVEMENT_SPEED;
        }
        else {
            xVel = 0;
        }

        // Handle jumping
        static bool isJumping = false;
        if (keyboardState[SDL_SCANCODE_SPACE] && !isJumping) {
            isJumping = true;
            yVel = -JUMP_SPEED;
        }

        // Update character position
        xPos = 200; // += xVel;
        yPos += yVel;
        if (xPos > BORDER) { xPos = BORDER; }
        if (xPos < 0) { xPos = 0; }

        // Apply gravity
        if (yPos < BOTTOM - 58) {
            yVel += 1; // apply gravity
        }
        else {
            yVel = 0;
            isJumping = false;
        }

        // Clear screen
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);
 
        // Render background
        SDL_RenderCopy(renderer, backgroundTexture, NULL, NULL);
        SDL_RenderCopy(renderer, Texttexture, NULL, &dstrect);

        // Render obstacles
        for (auto& obstacle : obstacles) {
            obstacle.move();
            obstacle.draw(renderer);
        }
        /*
        // Remove obstacles that are off screen
        for (int i = 0; i < obstacles.size(); i++) {
           if (obstacles[i].isOffScreen()) {
                obstacles.erase(obstacles.begin() + i);
           }
        }
        */

        // Render character
        character.render(renderer, xPos, yPos);

        // Update Screen
        SDL_RenderPresent(renderer);

        frameCount++;
        Uint32 frameTime = SDL_GetTicks() - frameStart; // Milliseconds
        Sekunden = SDL_GetTicks() - FPS_Start;

        if (Sekunden > 1000) {
            sprintf_s(puffer, "Frames: %d", frameCount);
            frameCount = 0;
            FPS_Start = SDL_GetTicks();
        }

        // control fps
        if (frameTime < sollzeit) { SDL_Delay(sollzeit - frameTime); }
    }
    SDL_DestroyTexture(backgroundTexture);
    SDL_DestroyTexture(textTexture);
    TTF_CloseFont(font);


    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}


I filled the obstable-rect with black (Rectfill) and then there's only a black square, but no image. I also tried to use only one obstacle, it worked then. but as I tried with vector, it seems to display the wrong texture/pointer/whatever.

genpfault
  • 51,148
  • 11
  • 85
  • 139
  • 3
    `vector` -- Your `Obstacle` class does not have proper copy semantics and violates the [rule of 3](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three), thus cannot be safely put into a vector. The vector class will be performing copying and assignment internally, and `Obstacle` lacks a proper user-defined copy constructor and assignment operator. – PaulMcKenzie May 05 '23 at 12:47
  • `{ Obstacle o1(300, BOTTOM - 50, 50, 50, renderer); Obstacle o2 = o1; }` -- For example, you will see that this very simple code causes all sorts of problems once the `{ }` is exited. So the issue really has nothing to do with vector. – PaulMcKenzie May 05 '23 at 12:51
  • So - instead of the vector - I should only this code and it will be fixed? I will try! :-) – Coding Dallarius May 05 '23 at 13:03
  • Again, this isn't a vector issue. – PaulMcKenzie May 05 '23 at 13:06
  • The problem is your `Obstacle` class. Either define a copy constructor and assignment operator, or at least delete the defaults. – john May 05 '23 at 13:07
  • Since I haven't used SDL, you could do as @john stated (`delete` the copying and assignment), and instead implement a move constructor and move assignment operator for `Obstacle`. Then maybe that will work for vector. Maybe that is the better option for you, since what does it mean to copy an `Obstacle`? – PaulMcKenzie May 05 '23 at 13:09
  • I was afraid of it - classes need to be rethought.... thanks so far. I just wanted several obstacles the player needs to jump over. – Coding Dallarius May 05 '23 at 13:14
  • This was much easier in python..... :-( – Coding Dallarius May 05 '23 at 13:17
  • @CodingDallarius -- Hopefully you see the problem. If an Obstacle is assigned to another Obstacle, the pointer members also get copied. When an Obstacle is destroyed, your destructor `SDL_DestroyTexture(ObjTexture);` will destroy the `ObjTexture` that is being pointed to. But the second object will also call `SDL_DestroyTexture(ObjTexture);` with the same `ObjTexture`, causing a double-deletion error. So better to give your Obstacle move-semantics, and turn off copying and assignment, so that the compiler can ensure your code is doing neither of those operations. – PaulMcKenzie May 05 '23 at 13:24
  • @CodingDallarius There's no doubt that python is an easier language than C++. – john May 05 '23 at 15:29
  • My Python is quite good, I learned it from books - so I hoped I could learn c++ from ChatGPT. I learned the hard way, it is a bad teacher for c++ (with phython it is better!). So the only thing left is to learn c++ with books again .... :-( thanks at all for the help - I think I will close it and start from scratch. – Coding Dallarius May 05 '23 at 18:19

0 Answers0