I'm pretty sure this is not unexpected at all, I'm probably misunderstanding something. I'm trying to call one of the overloaded constructors as such:
SDL_RenderCopy(SDL_Renderer*, SDL_Texture*, SDL_Rect*, SDL_Rect*);
The problem came when I made a static method in a class to retrieve SDL_Rect pointers as such:
static SDL_Rect* getRectangle(rect r) {
SDL_Rect rectangle{ r.x, r.y, r.w, r.h };
return &rectangle;
}
So the call is like:
SDL_Rect* r = MyClass::getRectangle(srcRect);
SDL_Rect* r2 = MyClass::getRectangle(destRect);
SDL_RenderCopy(renderer, texture, r, r2);
They are all pointers and are returning coherent values, but for some reason I don't understand, the rectangles that I'm fetching from my class when passed to SDL, are not scaling according to the values of the rectangles. But if I change my static method to return a copy of SDL_Rect, everything works as expected, as such:
static SDL_Rect getRectangle(rect r) {
SDL_Rect rectangle{ r.x, r.y, r.w, r.h };
return rectangle;
}
And the call:
SDL_Rect r = Video::getRectangle(srcRect);
SDL_Rect r2 = Video::getRectangle(destRect);
SDL_RenderCopy(renderer, texture, &r, &r2);
The problem is in your function getRectangle():
static SDL_Rect* getRectangle(rect r) {
SDL_Rect rectangle{ r.x, r.y, r.w, r.h };
return &rectangle;
}
You are returning the address of an object, rectangle, that has automatic storage duration. Therefore, the object doesn't exist after the control returns from the function.
You may want to allocate an SDL_Rect on the heap and return its address instead:
static SDL_Rect* getRectangle(rect r) {
return new SDL_Rect{ r.x, r.y, r.w, r.h };
}
Related
I have this function:
void Texture::render(int x, int y, int w, int h, SDL_Renderer *&renderer, double angle, SDL_Point* center, SDL_RendererFlip flip)
{
// Set a destination value to -1 to keep the current value
if (x < 0) { x = rect.x; }
if (y < 0) { y = rect.y; }
if (w < 0) { w = rect.w; }
if (h < 0) { h = rect.h; }
// Create destination rectangle
SDL_Rect dstRect = { x, y, w, h };
// Render to screen
SDL_RenderCopyEx(renderer, texture, &rect, &dstRect, angle, center, flip);
}
It works. It creates an image of the correct size at the location I want. But I want to add a chunk of code where it resizes the texture itself to be the size given in the destRect.
So, anyone who finds this and reads the conversation I had with Nelfeal in the comments will see that I had a misunderstanding of how SDL_RenderCopyEx works. There's no need to resize the texture. If you need to do something like that, you can just use the dstRect when you copy it.
Actually, as far as I can find, there isn't a method to resize the actual texture itself. I'm sure one exists, but it's definitely not something people are commonly using. Which is usually a sign that it's a bad idea.
I've tweaked my code to try and simplify it, for anybody who's trying to do something similar to me:
void render(SDL_Renderer *&renderer, SDL_Rect *dstRect=NULL, SDL_Rect &srcRect=NULL, double angle=0.0, SDL_Point* center=NULL, SDL_RendererFlip flip=SDL_FLIP_NONE);
void Texture::render(SDL_Renderer *&renderer, SDL_Rect *dstRect, SDL_Rect *srcRect, double angle, SDL_Point* center, SDL_RendererFlip flip)
{
// Check to see if a destination was provided
bool check = false;
if (dstRect == NULL)
{
check = true;
dstRect = new SDL_Rect();
dstRect->x = 0;
dstRect->y = 0;
dstRect->w = SCREEN_WIDTH;
dstRect->h = SCREEN_HEIGHT;
}
// Check to see if the entire texture is being copied
if (srcRect == NULL) { srcRect = ▭ }
// Render to screen
SDL_RenderCopyEx(renderer, texture, srcRect, dstRect, angle, center, flip);
// Free dstRect
if (check) delete dstRect;}
And it looks like this when using the function:
bgTex.render(renderer);
blobTex.render(renderer, &blobDstRect);
I am maing a small game using SFML, anyway, my issue is that when rendering the sprite, and moving with float values. The sprite has a white background that 1 pixel one whichever side is moving gets shown.
Here is my Spritesheet class:
Spritesheet::Spritesheet(std::string t) {
this->texture.loadFromFile(t);
this->sprite.setTexture(this->texture);
}
sf::Sprite Spritesheet::getSprite(int x, int y, int width, int height) {
sf::Sprite spt;
spt.setTexture(this->texture);
spt.setTextureRect(sf::IntRect(x, y, width, height));
return spt;
}
void Spritesheet::setSprite(std::string t) {
this->texture.loadFromFile(t);
this->sprite.setTexture(this->texture);
}
And then the player class which is the class that draws the sprite:
Player::Player(int x, int y) {
// Some other code
this->spritesheet.setSprite("./res/img/tiles.png");
this->sprite = this->spritesheet.getSprite(48, 48, 16, 16);
this->sprite.setPosition(x, y);
this->sprite.scale(4, 4);
}
// Further down
void Player::render(RenderWindow& g) {
g.draw(this->sprite);
}
I have also tried using the sprite function setColor but that changes the texture color aswell.
I've had a problem similar to this before.
To fix it I had added a clock before the display so that I could be sure that the display was finished before I could display it again.
sf::Clock clock;
while (clock.getElapsedTime().asMilliseconds() < 100);
clock.restart();
maybe its FPS, i had a similar issue when i did not set a max fps, try window.setFramerateLimit(60); maybe it would fix it
I have this image:
I want to generate the set of sprites, each 32x32 in size. How can I do this with C++, no library used.
Texture class:
class Texture
{
protected:
// We cannot create or copy base class texture objects
// We will only ever have pointers or references to base
// class texture objects used in our program (and these
// will refer to derived class textures
Texture() = default;
Texture(Texture const &) = default;
Texture & operator=(Texture const &) = default;
virtual void LoadFromFile(std::string const & strFileName) = 0;
virtual void LoadFromResource(unsigned int rid) = 0;
public:
virtual ~Texture(){}
// virtual Rect const & GetBounds() const = 0;
virtual int Width() const = 0;
virtual int Height() const = 0;
};
using TEXTURE_PTR = std::shared_ptr<Texture>;
Sprite class:
class Sprite
{
private:
virtual void draw_impl(Canvas & c) = 0;
TEXTURE_PTR m_pTexture;
protected:
// Ensure that Sprite objects can only be constructed by derived classes
explicit Sprite(TEXTURE_PTR pt = nullptr,POINT2f const & p = { 0, 0 });
// All Sprite objects have a position state variable
POINT2f m_position;
// Sprite objects can only be copied by derived class objects
Sprite(const Sprite&) = default;
Sprite& operator=(const Sprite&) = default;
public:
virtual ~Sprite(){}
void OnDraw(Canvas & c);
void SetPosition(POINT2f const & pos);
POINT2f const & GetPosition() const;
void SetTexture(TEXTURE_PTR pt);
};
AND i create the sprite this way:
TEXTURE_PTR pLightning = std::make_shared<Texture>("resource//Lightning.bmp", RGB(255, 0, 255));
std::shared_ptr<Sprite> pSpark = std::make_shared<Sprite>(pLightning);
How can I generate 9 sprites from the above image with this method?
Edit
I come up with these code but still doesn't work
class WinTexture : public Texture
{
protected:
HBITMAP m_hbmImage;
HBITMAP m_hbmMask;
BITMAP m_bmParam;
virtual void LoadFromResource(UINT rid);
virtual void LoadFromFile(std::string const & strFileName);
void CreateMask(DWORD dwTransparent);
public:
// Construct from Windows Resource
WinTexture(UINT uid, COLORREF dwTransparent);
// Constructor from file load
WinTexture(std::string const & strFilename, COLORREF dwTransparent);
//Contruct from other Texture
WinTexture(std::shared_ptr<WinTexture> wt, int xStart,int yStart, int w, int h);
virtual ~WinTexture();
// Inherited interface
// virtual Rect const & GetBounds() const;
virtual int Width() const;
virtual int Height() const;
HBITMAP ImageHandle() const;
HBITMAP MaskHandle() const;
};
with this, I want to make a constructor to create from other WinTexture:
WinTexture::WinTexture(std::shared_ptr<WinTexture> wt, int xStart, int yStart, int w, int h)
: Texture(), // as above
m_hbmImage(NULL),
m_hbmMask(NULL) {
HDC hdcMem1 = CreateCompatibleDC(0);
HDC hdcMem2 = CreateCompatibleDC(0);
m_hbmImage = CreateBitmap(w, h, 1, 1, NULL);
//m_hbmImage = CreateCompatibleBitmap(hdcMem2, 1, 1);
SelectObject(hdcMem1, wt->ImageHandle());
SelectObject(hdcMem2, m_hbmImage);
BitBlt(hdcMem2, xStart, yStart, w, h,hdcMem1, 0, 0, SRCCOPY);
BitBlt(hdcMem1, xStart, yStart, w, h, hdcMem2, 0, 0, SRCINVERT);
//SaveDC(hdcMem2);
DeleteDC(hdcMem1);
DeleteDC(hdcMem2);
CreateMask(RGB(0, 0, 0));
}
EDIT
Currently, I have created this class from Sprite:
class TexturedSprite : public Sprite
{
private:
TEXTURE_PTR m_pTexture;
virtual void draw_impl(Canvas & c);
protected:
public:
explicit TexturedSprite(TEXTURE_PTR pt = nullptr, POINT2f pos = { 32, 32});
explicit TexturedSprite(int xStart,int yStart, int w, int h,TEXTURE_PTR pt = nullptr, POINT2f pos = { 32, 32 });
virtual ~TexturedSprite(){}
void SetTexture(TEXTURE_PTR pt);
};
I can't figure out how to implement the second constructor, to copy a part of input texture (pt):
TexturedSprite::TexturedSprite(int xStart, int yStart, int w, int h, TEXTURE_PTR pt , POINT2f pos )
:Sprite(pos)
{
HDC hdcMem1 = CreateCompatibleDC(0);
HDC hdcMem2 = CreateCompatibleDC(0);
//How to assign values to DC?
BitBlt(hdcMem1, 32, 32, w, h, hdcMem2, xStart, yStart, SRCCOPY);
DeleteDC(hdcMem1);
DeleteDC(hdcMem2);
}
At least as I read things right now, your basic intent is to load the texture, then create the individual sprites by copying 32x32 pixel pieces of the texture into the individual sprites. Unless you intend to manipulate the sprites from separate threads (which strikes me as unlikely) I'd avoid doing that.
Instead, I'd take note of a couple of the last parameters you supply to BitBlt:
BitBlt(hdcMem2, xStart, yStart, w, h,hdcMem1, 0, 0, SRCCOPY);
The 0, 0 just before the SRCCOPY specify the location in the source bitmap to use as the starting point of the BitBlt.
This lets you load the texture once, and just use that single texture for all the sprites it contains. Drawing an individual sprite at a particular location only requires that you specify the X, Y coordinates of that sprite within the source bitmap (specifically, the top, left-hand corner of that sprite) and draw a 32x32 chunk starting from there. If you want to define individual sprite objects, you can certainly do that, but each just needs to store something like a shared_ptr to the loaded texture, and the X, Y coordinates of its piece of the texture.
class Sprite {
shared_ptr<Texture> tex;
int x, y;
public:
Sprite(shared_ptr<Texture> t, int x, int y) tex(t), x(x), y(y) {}
void draw(HDC dc, int dest_x, int dest_y) {
BitBlt(dc, dest_x, dest_y, 32, 32, *tex, x, y, SRCCOPY);
}
};
If you really don't want to use any libraries you Need to manually decode the BMP file. Look at this Wikipedia entry for more Information about the structure of the BMP file Format.
I have a 7 x 7 matrix of probes that deliver a signal representing the level measured at that point in the area under investigation. I can draw a matric of 7 x 7 rectangles in SDL but I was unable to update the color.
I have a class
class Probe
{
public:
Probe();
~Probe();
void SetColor(int x);
void Set_id(int x, int y, int z);
void Level_id(void);
private:
int OldColor;
int Xcord;
int Ycord;
SDL_Rect rect; //This does not keep the rect that I create?
};
//outside all curly brackets {} I have
Probe Level[7][7];
//I initialize each instance of my class
Level[x][y].Set_id(x, y, z); and I use x and y to create and position the rectangle
// Defininlg rectangles
SDL_Rect rect = {BLOCK_W * Xcord, BLOCK_H * Ycord, BLOCK_W, BLOCK_H};
/*I get what I expected, by the way z sets the color and it is incremented so each rectangle has a different color. */
//I used function SetColor, it failed untill I regenerated rect
//in the SetColor function. I have
private:
SDL_Rect rect;
//It is private why do I need to re-create the SDL_Rect rect?
//Why is it not stored like
private:
int Xcord; // !!? Regards Ian.
If I understand correctly, your class looks something like this:
class Probe {
public:
Probe();
~Probe();
void SetColor(int x);
void Set_id(int x, int y, int z) {
SDL_Rect rect = {BLOCK_W * Xcord, BLOCK_H * Ycord, BLOCK_W, BLOCK_H};
}
private:
int OldColor;
int Xcord;
int Ycord;
SDL_Rect rect;
};
and you're wondering why the "rect" variable won't save as the private one, and how you change the color when drawing an SDL_Rect?
First of all, your "rect" isn't getting saved because you are defining a new "rect" variable in your function. What you should do is either of these:
rect = {BLOCK_W * Xcord, BLOCK_H * Ycord, BLOCK_W, BLOCK_H};
or
rect.x = BLOCK_W * Xcord;
rect.y = BLOCK_H * Ycord;
rect.w = BLOCK_W;
rect.h = BLOCK_H;
And to change the color of the rendering (assuming you are using the SDL_RenderDrawRect function, as that is the only one I know of that you can draw SDL_Rects with), you simply call:
SDL_SetRenderDrawColor(int r, int g, int b, int a);
right before you call the SDL_RenderDrawRect function (every time).
If you do not do this, and call the SetRenderDrawColor function somewhere else, your color will change to that color.
I have a problem with my SDL program. My goal is to make a dot move along a line. I have all the coordinates saved in a data file. So I just wanted to read them from the file and display the dot at the right position.
The dot class (which is named linefollower) looks like this.
class Linefollower
{
private:
int x, y;
char orientation;
public:
//Initializes the variables
Linefollower();
void set(int m_x, int m_y, char m_orietnation);
void show();
char get_orientation();
};
Linefollower::Linefollower()
{
x = 0;
y = 0;
orientation = 'E';
}
void Linefollower::set(int m_x, int m_y, char m_orientation)
{
x = m_x;
y = m_y;
orientation = m_orientation;
}
void Linefollower::show()
{
//Show the linefollower
apply_surface(x, y, linefollower, screen );
}
char Linefollower::get_orientation()
{
return orientation;
}
The apply_surface function.
void apply_surface( int x, int y, SDL_Surface * source, SDL_Surface* destination)
{
//Temporary rectangle to hold the offsets
SDL_Rect offset;
//Get the offsets
offset.x = x;
offset.y = y;
//Blit the surface
SDL_BlitSurface( source, NULL, destination, &offset);
}
The loop which ought to display the animation looks like this.
//While the user hasn't quit
while( quit == false )
{
//Apply the surface to the screen
apply_surface( 0, 0, image, screen );
fin.read((char*) &my_linefollower, sizeof my_linefollower);
if(my_linefollower.get_orientation() == 'Q')
break;
my_linefollower.show();
//Upadate the screen
if( SDL_Flip( screen ) == -1 )
{
return 1;
}
SDL_Delay(200);
}
Now I was expecting, that I get a moving dot on the screen, but the only thing which I got is the background (image) for a few seconds untill the if(my_linefollower.get_orientation() == 'Q')
break; was true. What do I do wrong?
PS: I guess it is worth noticing that I am a beginner in SDL and I took most of the code from a tutorial. Learning it exactly would be a waste of time for me, since it is unlikely that I am going to use it again any time soon.
First, you should change your offset in apply_surface to something like this:
SDL_Rect offset = { x, y, 0, 0 };
SDL_Rect doesn't have a constructor to set your members to 0 by default, so you get uninitialized memory for your width and height.
Also, you should check what linefollower contains, if it's a valid SDL_Surface. Removing your file reading code and manually controlling a Linefollower will allow you to easily find where the error comes from.
Use a debugger to validate your x and y coordinates.
Other than that, you code should work, although your window will be unresponsive because you're not pumping events through SDL_PollEvent.