Trouble with Animations

Hello everyone, there’s a lot to explain so to keep it brief, I have an animations class which is a derived class of my image class. Both classes utilize textures and the image class works flawlessly (I think lol). In my animation class, I have a clock using SDL_GetTicks() and a vector of spriteImage objects (my image class) to be made into an animation. Then, every so amount of milliseconds that go by, my animation should go to the next frame. I’m displaying the frames onto my screen using SDL_RenderCopy as one should. However, whether the animation works or not, I don’t even know because my animation isn’t even displaying on my screen, even though it works with my image class.

Any ideas? I would be more than happy to chat with someone and show them my code. I am a noob at SDL, so it could be a simple mistake or an issue with my code. I would be more than happy to explain everything in detail if required. Thank you.

Here’s the Code from my spriteImage class:

#ifndef TEXTURE
#define TEXTURE

class spriteImage {

private:

//the texture
SDL_Texture* texture;

//texture dimensions
int w;
int h;

//texture coordinates
int x;
int y;

//depth (bigger number is further away / rendered first while lower number rendered later)
int depth;

//REMEMBER! x and y coordinates are from base object class!

public:

//default constructor
spriteImage();

//argument constructor isn't necessary unless you want to create and render the object immediately! (Which is 99% of cases we don't lol).

//loads image at specified path
bool loadFromFile(std::string path);

//deallocates texture
void free();

//Renders texture at a given point
void displaySprite(int tempX, int tempY);

//Getter Functions
int getW();
int getH();
int getX();
int getY();
SDL_Texture* getSprite();

//Setter Functions
void setW(int temp);
void setH(int temp);
void setX(int temp);
void setY(int temp);
void setSprite(SDL_Texture* newSprite);

//Tile the Sprite
void tileSprite(int tempW, int tempH);

//Stretch the sprite
//void stretchSprite(int tempX, int tempY, float stretchW, float stretchH);
void stretchSprite(int stretchW, int stretchH);

//deconstructor
~spriteImage();

};

//default constructor
spriteImage::spriteImage() {

texture = NULL;
w = 0;
h = 0;
x = 0;
y = 0;

depth = 0;

}

//loads image at specified path
bool spriteImage::loadFromFile(std::string path) {

free();

//The final texture
SDL_Texture* newTexture = NULL;

//Load image at specified path
SDL_Surface* loadedSurface = IMG_Load(path.c_str());
if (loadedSurface == NULL)
{
	printf("Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError());
}
else
{
	//Color key image
	SDL_SetColorKey(loadedSurface, SDL_TRUE, SDL_MapRGB(loadedSurface->format, 0, 0xFF, 0xFF));

	//Create texture from surface pixels
	newTexture = SDL_CreateTextureFromSurface(renderer, loadedSurface);
	if (newTexture == NULL)
	{
		printf("Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError());
	}
	else
	{
		//Get image dimensions
		w = loadedSurface->w;
		h = loadedSurface->h;
	}

	//Get rid of old loaded surface
	SDL_FreeSurface(loadedSurface);
}

//Return success
texture = newTexture;
return texture != NULL;

}

//deallocates texture
void spriteImage::free() {

if (texture != NULL) {

	SDL_DestroyTexture(texture);
	texture = NULL;
	w = 0;
	h = 0;
	x = 0;
	y = 0;

}

}

//render / display the sprite on the screen at given coordinates
void spriteImage::displaySprite(int tempX, int tempY) {

x = tempX;
y = tempY;

//set rendering space and render to the screen
SDL_Rect renderTexture = { x, y, w, h };
SDL_RenderCopy(renderer, texture, NULL, &renderTexture);

//renderQuad is turning the texture into a rectangle and then doing stuff with it.

}

//Getter Functions
int spriteImage::getW() { return w; }
int spriteImage::getH() { return h; }
int spriteImage::getX() { return x; }
int spriteImage::getY() { return y; }
SDL_Texture* spriteImage::getSprite() { return texture; }

//Setter Functions
void spriteImage::setW(int temp) { w = temp; }
void spriteImage::setH(int temp) { h = temp; }
void spriteImage::setX(int temp) { x = temp; }
void spriteImage::setY(int temp) { y = temp; }
void spriteImage::setSprite(SDL_Texture* newSprite) { texture = newSprite; }

//Tile the Sprite
void spriteImage::tileSprite(int tempW, int tempH) {

//assumes you want to do it from top left. We can fix this.
int xCoordCounter = 0;
int yCoordCounter = 0;

for (int i = 0; i < tempH; i++) {

	for (int j = 0; j < tempW; j++) {

		SDL_Rect renderTexture = { xCoordCounter, yCoordCounter, w, h };
		SDL_RenderCopy(renderer, texture, NULL, &renderTexture);

		//add to x coordinate
		xCoordCounter += w;

	}

	//reset x and add to y coordinates
	xCoordCounter = 0;
	yCoordCounter += h;

}

}

//Stretch the sprite
void spriteImage::stretchSprite(int stretchW, int stretchH) {
//void objTexture::stretchSprite(int tempX, int tempY, float stretchW, float stretchH) {

//set rendering space and render to the screen
//SDL_Rect renderTexture = { tempX, tempY, (w * stretchW), (h * stretchH) };
//SDL_RenderCopy(renderer, textureImage, NULL, &renderTexture);

w *= stretchW;
h *= stretchH;

//the only problem with this is doing anything below 0 doesn't invert it. It just doesn't show.

}

//deconstructor
spriteImage::~spriteImage() {

free();

}

#endif

And here’s the code from my spriteAnimation class:

#ifndef SPRITE_ANIMATION
#define SPRITE_ANIMATION

//base class
#include “spriteImage.h”
#include

//testing animation using a texture vector
//later make a function to convert sprite into texture vector?
spriteImage sprite1;
spriteImage sprite2;

class spriteAnimation : private spriteImage {

private:
//int frameW;
//int frameH;

std::vector<spriteImage> frames;

//the number of frames in the animation
int numberOfFrames;

//what frame is currently displayed on screen
int currentFrame;

//Duration of each frame in milliseconds
int frameDuration;

//Timestamp of the last frame
Uint32 lastFrameTime; //universal across all platforms not just Windows.

public:

/*

showFrame

pause animation

add stuff to reset to free


*/

//constructor
spriteAnimation(int tempW, int tempH, int tempFrameDuration);

//loads image at specified path
bool loadFromFile(std::string path) { spriteImage::loadFromFile(path); return false; }

//deallocates texture
void free() { spriteImage::free(); }

//Renders texture at a given point
//void displaySprite(int tempX, int tempY) { spriteImage::displaySprite(tempX, tempY); }
void displaySprite(int tempX, int tempY);

//Getter Functions (from spriteImage)
int getW() { return spriteImage::getW(); }
int getH() { return spriteImage::getH(); }
int getX() { return spriteImage::getX(); }
int getY() { return spriteImage::getY(); }
SDL_Texture* getSprite() { return spriteImage::getSprite(); }

//Getter Functions (Unique to this class)
int getCurrentFrame() { return currentFrame; }
int getFrameDuration() { return frameDuration; }
Uint32 getLastFrameTime() { return lastFrameTime; }
int getNumberOfFrames() { return numberOfFrames; }

//Setter Functions (from spriteImage)
void setW(int temp) { spriteImage::setW(temp); }
void setH(int temp) { spriteImage::setH(temp); }
void setX(int temp) { spriteImage::setX(temp); }
void setY(int temp) { spriteImage::setY(temp); }
void setSprite(SDL_Texture* newSprite) { spriteImage::setSprite(newSprite); }

//Setter Functions (Unique to this class)
void setCurrentFrame(int temp) { currentFrame = temp; }
void setFrameDuration(int temp) { frameDuration = temp; }
void setLastFrameTime(int temp) { lastFrameTime = temp; }

//Tile the Sprite
void tileSprite(int tempW, int tempH) { spriteImage::tileSprite(tempW, tempH); }

//Stretch the sprite
//void stretchSprite(int tempX, int tempY, float stretchW, float stretchH);
void stretchSprite(int stretchW, int stretchH) { spriteImage::stretchSprite(stretchW, stretchH); }

//destructor
~spriteAnimation();

};

//constructor
spriteAnimation::spriteAnimation(int tempW, int tempH, int tempFrameDuration) {

/*

There's no need to initialize the texture, depth, w, h, and alpha in 
the derived class's constructor because the base constructor is also
called during creation of derived class object.

However, it's mandatory we set the frame width and height in order
to animate correctly.


add in a loop that determines the size by dividing if you can get the
width of the entire texture.


*/

//adding frames temporarily
frames.push_back(sprite1);
frames.push_back(sprite2);

//number of frames derived from the items in frames vector
//numberOfFrames = (frames.size());
numberOfFrames = 2;

//frameDuration = tempFrameDuration; //milliseconds
frameDuration = 100; //milliseconds
lastFrameTime = 0;

//used to get frames from single linear sprite
//frameW = tempW;
//frameH = tempH;

//setting the default frame as the first one
currentFrame = 0;

}

//spriteAnimation specific functions
void spriteAnimation::displaySprite(int tempX, int tempY) {

Uint32 currentTime = SDL_GetTicks();
Uint32 elapsedTime = currentTime - lastFrameTime;

//SDL_Rect renderTexture = { tempX, tempY, 64, 64 };

//Check to see if it's time to advance to the next frame
if (elapsedTime >= frameDuration) {

	currentFrame = (currentFrame + 1) % numberOfFrames;
	lastFrameTime = currentTime;

}

frames[currentFrame].spriteImage::displaySprite(tempX, tempY);
//SDL_RenderCopy(renderer, frames[currentFrame].spriteImage::getSprite(), NULL, &renderTexture);

}

//destructor
spriteAnimation::~spriteAnimation() {}

#endif

Do you ever call loadFromFile on the frames in the spriteAnimation?

Do you ever call displaySprite on the spriteAnimation?

I don’t fully understand why spriteAnimation inherits from spriteImage but I doubt spriteAnimation::tileSprite calling spriteImage::tileSprite will do the right thing because it doesn’t use the frames or take frameDuration into account.

Another problem that I noticed is that the spriteImage objects cannot be copied properly.

When you add elements to std::vector it will sometimes copy (or move) the existing elements.

To handle copying you need to implement a copy constructor and a copy assignment operator because the default ones don’t do what you want in this case. You might also want to implement a move constructor and move assignment operator for performance reasons. See the rule of three/five.

Technically it’s enough for the type that you store in the std::vector to be moveable so if you want you could make it a “move-only type” by only defining the move operations. I suspect those are probably easier to implement because you can just “steal” the texture instead of having to come up with a way to copy the texture or share it between multiple instances.

If you don’t want the objects to be copyable or moveable at all you might want to disable the copy constructor and copy assignment operators.

class spriteImage {
	spriteImage(const spriteImage&) = delete;
	spriteImage& operator=(const spriteImage&) = delete;
	...
};

This will prevent accidental copies of spriteImage objects. If you try you’ll get a compilation error.

If the objects are not copyable or moveable it means you will have to store pointers (or smart pointers, like std::unique_ptr) in the std::vector instead.

Hello Peter. Thank you for responding. I did call both functions in my program, and honestly, I don’t know why I derived spriteAnimation from spriteImage. Also, I did not know about vectors (from your second post) so I can try to fix it and get back to you. Thank you.