SDL 2: Implementing Sprite Clips

Started by
13 comments, last by Aldacron 6 years, 9 months ago

I am trying to implement sprite clips, or rendering a portion of a sprite sheet onto the screen. For now, I am least trying to get one sprite to display on the screen. Controller support and adding the other sprites when changing the character direction will come later. Here is what I have so far. 

First, the class declaration:


class World1
{
    public:
        //other stuff

        //screen resolution
        int window_width = 0;
        int window_height = 0;

        //handles player
        void renderPlayer(SDL_Renderer*&);
        //other stuff

        
    private:

        //player graphic and rectangle structs/variables/etc.
        SDL_Texture* pSpriteSheet = nullptr;
        SDL_Rect pSpriteClips[3];
        SDL_Rect* pSprite;
        SDL_Rect pBase;   
	SDL_RendererFlip sFlip = SDL_FLIP_NONE;

        //other stuff
};

Then, the constructor:


World1::World1(int SCREEN_WIDTH, int SCREEN_HEIGHT, SDL_Renderer*& renderer)
{
    window_width = SCREEN_WIDTH;
    window_height = SCREEN_HEIGHT;

    *pSpriteSheet = IMG_LoadTexture(renderer, "male_base-test-anim.gif");

    if (pSpriteSheet == nullptr)
    {
        cout << "Unable to load player Sprite sheet.";
    }

    //facing down sprite
    pSpriteClips[0].x = 14;
    pSpriteClips[0].y = 12;
    pSpriteClips[0].w = 145;
    pSpriteClips[0].h = 320;
 	//more code...
	
    //player sprite
    pBase.x = 0;
    pBase.y = 0;
    pBase.w = pSpriteClips[0].w;
    pBase.h = pSpriteClips[0].h;  
}

Then, the rendering


void World1::renderPlayer(SDL_Renderer*& renderer)
{
    //render
    SDL_RenderClear(renderer); //clears screen
    SDL_RenderCopyEx(renderer, pSpriteSheet, pSprite, &pBase, 0.0, NULL, sFlip);
    SDL_RenderPresent(renderer); //puts image on screen
}

So far, the program does not build and I obtain the following error:


C:\Users\Kevin\Documents\Codeblocks\KnightQuest\worlds.cpp|12|error: invalid use of incomplete type 'SDL_Texture {aka struct SDL_Texture}'|

Am I mistaken to assume that C++ will automatically assign a memory address to SDL_Rect* when I assign a value to it via the dereference operator? Or, perhaps there is another issue?

Advertisement
1 hour ago, windghost91 said:

*pSpriteSheet = IMG_LoadTexture(renderer, "male_base-test-anim.gif");

this is your issue.. SDL's SDL_Texture struct is designed to be treated as a pointer only.
Basically SDL dynamically allocates space in memory for that structure and returns pointer. So, you cannot dereference it. 

So basically if you created SDL_Texture *pSpriteSheet;
then to load image in it, you call:
pSpriteSheet = IMG_LoadTexture(renderer, "file.gif"); // behaves like (new)
and later when you won't need it:
SDL_DestroyTexture(pSpriteSheet); // behaves like (delete)

Hope this helps.

Your specific error has nothing to do with SDL_Rect*, and it is quite clear that it is referring to SDL_Texture.

It looks like your source code file does not #include the necessary files for the compiler to know how to use an SDL_Texture. Try #include <SDL.h>at the top of the file. There might even be a more specific file for graphics that you can use.

Also, you definitely shouldn't be dereferencing pSpritesheet like that - IMG_LoadTexture returns a pointer so you just need to assign it to pSpritesheet which is also a pointer.

Fix those 2 problems, and see how it goes. Also be aware that you're not initialising pSprite anywhere in that code.

On 7/7/2017 at 2:26 AM, tetriarch said:

this is your issue.. SDL's SDL_Texture struct is designed to be treated as a pointer only.
Basically SDL dynamically allocates space in memory for that structure and returns pointer. So, you cannot dereference it. 

So basically if you created SDL_Texture *pSpriteSheet;
then to load image in it, you call:
pSpriteSheet = IMG_LoadTexture(renderer, "file.gif"); // behaves like (new)
and later when you won't need it:
SDL_DestroyTexture(pSpriteSheet); // behaves like (delete)

Hope this helps.

Ok, thanks. I always wondered why tutorials did it that way. I tried it without the pointer, but it sounds like you need it.

On 7/7/2017 at 3:50 AM, Kylotan said:

Your specific error has nothing to do with SDL_Rect*, and it is quite clear that it is referring to SDL_Texture.

It looks like your source code file does not #include the necessary files for the compiler to know how to use an SDL_Texture. Try #include <SDL.h>at the top of the file. There might even be a more specific file for graphics that you can use.

Also, you definitely shouldn't be dereferencing pSpritesheet like that - IMG_LoadTexture returns a pointer so you just need to assign it to pSpritesheet which is also a pointer.

Fix those 2 problems, and see how it goes. Also be aware that you're not initialising pSprite anywhere in that code.

I meant SDL_Texture*. I'm not sure why I put SDL_Rect*.

My include files were not shown in the post, but they are in the file. I have SDL.h and SDL_image.h.

Ok, I was unable to find documentation on IMG_LoadTexture specifically. Thank you for the info.

pSprite was supposed to contain the current sprite being shown on the screen. Thank you for telling me that it was not initialized.


#include "game.h" //contains SDL.h, SDL_image.h, iostream, and stdio.h. Also has using namespace std.
#include "worlds.h" //class declaration

//gets window size, loads sprite sheet, and prepares initial sprite for rendering 
World1::World1(int SCREEN_WIDTH, int SCREEN_HEIGHT, SDL_Renderer*& renderer)
{
    window_width = SCREEN_WIDTH;
    window_height = SCREEN_HEIGHT;
  
    pSpriteSheet = IMG_LoadTexture(renderer, "male_base-test-anim.gif");

    if (pSpriteSheet == nullptr)
    {
        cout << "Unable to load player Sprite sheet.";
    }

    //facing down sprite
    pSpriteClips[0]->x = 14;
    pSpriteClips[0]->y = 12;
    pSpriteClips[0]->w = 145;
    pSpriteClips[0]->h = 320;

    pSprite = pSpriteClips[0]; //sets sprite clip upon game start to be pSpriteClips[0]

    //player rectangle that pSprite is rendered onto
    pBase->x = 0;
    pBase->y = 0;
    pBase->w = pSpriteClips[0]->w;
    pBase->h = pSpriteClips[0]->h;
}

//renders player to screen
void World1::renderPlayer(SDL_Renderer*& renderer)
{
    //Prepare sprite positions for rendering
    pBase->x = static_cast<int>(pPosX);
    pBase->y = static_cast<int>(pPosY);

    //render
    SDL_RenderClear(renderer); //clears screen
    SDL_RenderCopyEx(renderer, pSpriteSheet, pSprite, pBase, 0.0, NULL, sFlip);
    SDL_RenderPresent(renderer); //puts image on screen
}

I initalized pSprite and changed pSpriteClips and pBase to pointers. So far, the program builds, but it crashes at runtime. Using pointers to render has proven tricky for me. What can I do to avoid the crash? I will show more code if necessary.

When it crashes, don't you get the option to click 'Debug'? Or can you run the program in the debugger directly?

The debugger can tell you exactly which line has the problem. But without that information the crash could be anywhere in your code.

However, I can guess, having seen that you've apparently changed your code to make pSpriteClips an array of pointers to SDL_Rect instead of an array of SDL_Rects, that that will only work if you have actually dynamically allocated those SDL_Rects, and failing to do that will make it crash. This is probably the wrong approach to have taken, and your original array of SDL_Rects was better.

Pointers are a complex subject and they require that you understand exactly how they work and what they represent. Don't use them unless you need to.

I'm not very familiar with C++ but why are you passing reference to pointer SDL_Renderer*& instead of SDL_Renderer* pointer to struct?

I wouldn't use C++ stuff on SDL, such as the one mentioned on my last comment, I also think you most likely didn't include the SDL header file, or didn't set the correct compiler flags, or both.

1 hour ago, dud3 said:

I'm not very familiar with C++ but why are you passing reference to pointer SDL_Renderer*& instead of SDL_Renderer* pointer to struct?

That would make no real difference in this case. The renderer being used will be exactly the same. There is the potential to change the pointer but I doubt that is taking place. There's certainly nothing in the code to suggest it.

 

1 hour ago, dud3 said:

I wouldn't use C++ stuff on SDL, such as the one mentioned on my last comment

There's no reason not to. SDL won't suddenly break because it sees a C++ feature.

1 hour ago, Kylotan said:

There's no reason not to. SDL won't suddenly break because it sees a C++ feature.

Indeed you right, that statement is irrelevant, as I've mentioned I'm not very familiar with C++, and since SDL is written in C, I tend to write C like C++.

On 7/11/2017 at 5:35 AM, Kylotan said:

When it crashes, don't you get the option to click 'Debug'? Or can you run the program in the debugger directly?

The debugger can tell you exactly which line has the problem. But without that information the crash could be anywhere in your code.

However, I can guess, having seen that you've apparently changed your code to make pSpriteClips an array of pointers to SDL_Rect instead of an array of SDL_Rects, that that will only work if you have actually dynamically allocated those SDL_Rects, and failing to do that will make it crash. This is probably the wrong approach to have taken, and your original array of SDL_Rects was better.

Pointers are a complex subject and they require that you understand exactly how they work and what they represent. Don't use them unless you need to.

Yes, I get a screen to choose a debugger. The message sounded like a pointer error, which is what I was wondering about.

In my experience, sometimes, the compiler is able to convert SDL_Rects to pointers, but other times, it is not able to and I get an error something like "Unable to convert SDL_Rect to const SDL_Rect* for argument..." when attempting to render. This has frustrated me, so I tried to switch to pointers. That was troublesome too as you have seen.

If I were to use SDL_Rect instead of SDL_Rect*, do you know of a way to bypass this problem so that the compiler is able to make the conversion to pointers properly for SDL_RenderCopyEX?

 

 

This topic is closed to new replies.

Advertisement