Depth with Textures Issues

Hello guys. I want to integrate depth with my textures. My idea is to have a global vector that will contain all the textures I wanna render, and depending on how large or small the depth is associated with each texture, it will render it lastly / firstly to convey a depth in my 2D game. I’m using C++ if it matters, but I’ve developed code and I can’t comprehend why it’s not working. Would anyone be willing to take a glance at my code?

Also, yes it is incredibly messy and inconsistent, but I plan to fix that soon.

#ifndef TEXTURE
#define TEXTURE

//libraries to include
#include <SDL.h>
#include <SDL_image.h>
#include
#include

#include
#include

/*
You have to make a texture an object if you want to move a texture across the screen and stuff.

The functions in this texture class is basically what you’re gonna get without making it an object (of the object class)

*/

class texture {

private:

//textures counter
static int texturesExist;

//the texture
SDL_Texture* textureImage;

//texture dimensions
int w;
int h;

//texture coordinates
int x;
int y;

//texture depth and alpha 
int depth; //bigger number is closer / rendered last while lower number rendered first.
Uint8 alpha; //from 0-255. We use Uint8 because since the integer is set to only be 8 bits, it can only go from 0 to 255, which is prefect in this case.

//animation stuff
bool isAnimated = false; //image by default is not animated
int numberOfFrames = 0;
int animationSpeed = 100; //milliseconds

public:

//default constructor
texture();

//default constructor
texture(int tempDepth, int tempAlpha);

//argument constructor for animations
texture(int tempNumberOfFrames, int tempAnimationSpeed, int tempDepth, int tempAlpha);

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

//deallocates texture
void free();

//adds the sprite to the vector to render
void displaySprite(int tempX, int tempY);



//Renders texture at given coordinates
void displaySpriteInTextureDepth(int tempX, int tempY);

//Getter Functions
int getW() { return w; }
int getH() { return h; }
int getX() { return x; }
int getY() { return y; }
int getDepth() { return depth; }
int getAlpha() { return alpha; }
SDL_Texture* getSprite() { return textureImage; }

//Setter Functions
void setW(int temp) { w = temp; }
void setH(int temp) { h = temp; }
void setX(int temp) { x = temp; }
void setY(int temp) { y = temp; }
void setDepth(int temp) { depth = temp; }
bool setAlpha(Uint8 temp);// { alpha = temp; }
void setTexture(std::string path) { free(); loadFromFile(path); }
void setSprite(SDL_Texture* temp) { textureImage = temp; }

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

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

//deconstructor
~texture();





//// Add a static vector to store textures
//static std::vector<texture> textureDepth;
//
//
//// Define a custom comparison function for sorting by order
//static bool CompareOrderByDepth(texture& a, texture& b) {
//	return a.depth < b.depth;
//}
//
//// Static function to render all textures in the correct order
//static void RenderTextures() {
//	// Sort textures by order
//	//std::sort(textureDepth.begin(), textureDepth.end(), CompareOrderByDepth);
//
//	//// Render the textures in the correct order
//	//for (texture& tex : textureDepth) {
//	//	tex.putTextureIn_textureDepth(tex.getX(), tex.getY());
//	//}
//
//	if (textureDepth.empty()) {
//
//		std::cout << "textureDepth IS empty\n";
//
//	}
//	else {
//
//		std::cout << "textureDepth is NOT empty\n";
//
//	}
//
//	// Render the textures in the correct order
//	for (int i = 0; i < textureDepth.size(); i++) {
//		textureDepth[i].putTextureIn_textureDepth(textureDepth[i].getX(), textureDepth[i].getY());
//		std::cout << "textureDepth[" << i << "] x value: " << textureDepth[i].getX() << "\n";
//		std::cout << "textureDepth[" << i << "] y value: " << textureDepth[i].getY() << "\n";
//
//	}
//
//	//debug
//	for (int i = 0; i < textureDepth.size(); i++) {
//		std::cout << textureDepth[i].getSprite() << "\n";
//	}
//
//}

};

// Add a static vector to store textures
std::vector textureDepth;

void texture::displaySprite(int tempX, int tempY) {

x = tempX;
y = tempY;

// Add the created texture to the static vector
textureDepth.push_back(*this);

}

// Define a custom comparison function for sorting by order
bool compareDepths(texture& a, texture& b) {
return a.getDepth() < b.getDepth();
}

// Static function to render all textures in the correct order
void RenderTextures() {
// Sort textures by order
std::sort(textureDepth.begin(), textureDepth.end(), compareDepths);

//debug
if (textureDepth.empty()) {
	std::cout << "textureDepth IS empty\n";
}
else {
	std::cout << "textureDepth is NOT empty\n";
}

// Render the textures in the correct order
for (int i = 0; i < textureDepth.size(); i++) {
	textureDepth[i].displaySpriteInTextureDepth(textureDepth[i].getX(), textureDepth[i].getY());
	std::cout << "textureDepth[" << i << "] x value: " << textureDepth[i].getX() << "\n";
	std::cout << "textureDepth[" << i << "] y value: " << textureDepth[i].getY() << "\n";

}

//debug
for (int i = 0; i < textureDepth.size(); i++) {
	std::cout << textureDepth[i].getSprite() << "\n";
}

}

//texturesCounter
int texture::texturesExist = 0;

//default constructor
texture::texture() {

/*
The texture is only animated if you use the argument constructor.
So, maybe make another function to load an animation?

*/

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

depth = 0;
alpha = 255;

// Add the created texture to the static vector
//textureDepth.push_back(*this);

//textures counter
texturesExist++;

}

//argument constructor
texture::texture(int tempDepth, int tempAlpha) {

/*
Assumes is the constructor has arguments, it's an animation.
If it's not an animation (just a still image), there's no reason
it should have any arguments.
*/

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

depth = tempDepth;
alpha = tempAlpha;

//animation stuff
isAnimated = false; //image by default is not animated
numberOfFrames = 0;
animationSpeed = 100; //milliseconds

// Add the created texture to the static vector
//textureDepth.push_back(*this);

//textures counter
texturesExist++;

}

//argument constructor for animation
texture::texture(int tempNumberOfFrames, int tempAnimationSpeed, int tempDepth, int tempAlpha) {

/*
Assumes is the constructor has arguments, it's an animation.
If it's not an animation (just a still image), there's no reason
it should have any arguments.
*/

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

depth = tempDepth;
alpha = tempAlpha;

//Animation Stuff
isAnimated = true;

numberOfFrames = tempNumberOfFrames;
animationSpeed = tempAnimationSpeed;

// Add the created texture to the static vector
textureDepth.push_back(*this);

//textures counter
texturesExist++;

}

//set alpha
bool texture::setAlpha(Uint8 temp) {

//temp can be from 0-255, 255 being solid and 0 being completely transparent.

if ((alpha >= 0) && (alpha <= 255)) {

	alpha = temp;
	SDL_SetTextureAlphaMod(textureImage, alpha);
	return true;

}

return false;

}

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

//freeing any images if they were already loaded
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
textureImage = newTexture;
return textureImage != NULL;

}

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

if (textureImage != NULL) {

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

}

}

void texture::displaySpriteInTextureDepth(int tempX, int tempY) {

/*
Maybe in the future, you can mofidy this function so animations also
work with sprite sheets containing other sprites. As of now, animations
must be their own sprite sheets.


*/

x = tempX;
y = tempY;

// If the sprite is animated
if (isAnimated) {
	static int currentFrame = 0;
	static Uint32 lastFrameTime = SDL_GetTicks();
	Uint32 currentTime = SDL_GetTicks();

	// Calculate the time elapsed since the last frame change
	Uint32 elapsedTime = currentTime - lastFrameTime;

	// Check if it's time to advance to the next frame
	if (elapsedTime >= animationSpeed) {
		currentFrame++;
		if (currentFrame >= numberOfFrames) {
			currentFrame = 0; // Loop back to the first frame
		}
		lastFrameTime = currentTime;
	}

	// Calculate the frame width from w (width of entire sprite)
	int frameWidth = w / numberOfFrames;

	// Calculate the source rect for the current frame
	SDL_Rect srcRect = { currentFrame * frameWidth, 0, frameWidth, h };

	// Set rendering space and render to the screen
	SDL_Rect renderTexture = { x, y, frameWidth, h };

	SDL_RenderCopy(renderer, textureImage, &srcRect, &renderTexture);
}
else {

	// Not animated, render the whole texture
	SDL_Rect renderTexture = { x, y, w, h };

	SDL_RenderCopy(renderer, textureImage, NULL, &renderTexture);

}

}

//Tile the Sprite
void texture::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++) {

		displaySprite(xCoordCounter, yCoordCounter);

		//add to x coordinate
		//if animation
		if (isAnimated) {
			xCoordCounter += (w / numberOfFrames);
		}
		else {
			xCoordCounter += w;
		}

	}

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

}

}

//Stretch the sprite
void texture::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);


/*
Make it so the parameters are the width and height you want to
scale it to and the time it will take to do so. Make it like the
move object function where it will over time increase in size, or
if you put 0, immediately.

*/


w *= stretchW;
h *= stretchH;

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

}

//deconstructor
texture::~texture() {

free();

//textures counter
texturesExist--;

}

#endif

Side quest: Stretch Sprite

For this problem, you can check if the texture width/height is below 0, then use SDL_RenderCopyEx with the correct SDL_RenderFlip value.

LMAO dude you’re awesome. You have helped me on like every post I’ve made so far. Thank you!

I should clarify that the weird issue is that if there’s 1 texture in the vector, it works fine, but when there’s two or more, it doesn’t display any textures. They are in the textureDepth vector though.

To fix the formatting issue with your code, paste the whole code into the prompt editor screen, then select all of the code snippet and hit Ctrl-e. That will wrap it in the correct code block styling.

I’m not sure if the following would work for your design or not, but it’s how I approached “2 and 1/2D” before.

First off, just draw everything to the screen without any care as to order of things. We’ll fix that in this next step, but this works if no item/objects are in collision.
Now, determine depth when two or more items collide. If they collide, then you add them in the proper order into a final drawing vector. (Bottom item added first). The order in that vector is determined by where you say their “feet” contact the ground.
For instance: if one object’s “feet” are below a player’s feet on the screen, then that object should be drawn after the player so it appears on top.

I feel like this would save a lot of processing time if there’s more than a couple dozen items on the screen at any given time.

Y’know, I was wondering about the processing time as well, and what you are saying makes a whole lot more sense. However, it’s still bizarre my solution isn’t working correctly because all I am doing is making a vector of my class type “texture” and then just trying to renderCopy or whatever the function is called the vector items to my screen lol. I will (try to) implement your idea and get back to you. Thanks.

Okay, so I found out what the issue was with my original code. I tried implementing the system I had but with an array instead of a vector and it worked exactly how I wanted it to. However, I obviously want it to be dynamic. The only reason I bring this up is because I believe I can’t use your solution either, since the object could be overlapping with, technically, an infinite amount of textures and I can’t make an array that’s like a million items large or else that would be slow lol. So… any advice apart from what you already gave me? Am I doing something wrong, because I feel like it should just work with vectors lol, especially since it works with arrays. I was thinking of doing a linked list of my texture class, which honestly to me makes a lot of sense, but then I have to traverse my linked list everytime I want to delete a texture from the list, and I lose the memory address and etc.

No, that is weird. I use vectors all the time without issue.
It’s even stranger that it works with arrays and not with vectors, vector basically boils down to being a wrapper around a standard array.
I would have to see how you are iterating through the objects at this point. [edit - well, I actually do see an example above, and it looks fine.]

When you say it’s not working, is the program crashing, or is it just not drawing the images?

Is any of your debugging code catching anything?

No funny enough it’s not crashing or anything. It runs perfectly fine apart from the textures from the vector not displaying, but even regular textures render correctly. The Visual Studio Debugger shows the values are being pushed to the vector correctly, and this can be seen if I quickly threw together a for statement to print the memory addresses. However, I did just read on a StackOverflow post with a similar issue, but it still doesn’t mention, nor can I find anywhere that mentions that textures aren’t compatible with vectors. You also just confirmed they don’t have any issues with vectors, so I’m left confused lol. c++ - SDL - Using a std::vector with SDL_Texture does not work, Array works fine - Stack Overflow

That link you provided might actually hold the answer.
One of the comments mentions the “Rule of Three”, which is to provide a destructor, a copy constructor, and and a copy assignment constructor.
Otherwise the vector may not be creating a deep copy but a shallow copy instead.

Whenever I make a vector of objects it actually is a vector of object pointers;

std::vector <object *> myVector;
myVector.push_back(new object(p_renderer));

Which is why I avoid that issue (in this case the vector owns the entire object and keeps it alive through changes in scope). If a shallow copy loses scope without a destructor the SDL_Texture pointer gets orphaned and the data is lost (or perhaps persists as a memory leak). Also the pointer isn’t set to NULL which makes such a situation hard to test for.

May be a dumb question, but since that creates a copy of the object, doesn’t that double the amount of storage and memory taken up by the textures?

Yes and no. When your old texture loses scope it calls it’s destructor (which is why you need to implement it too), so it goes back to being usable ram, and the deep copy in the vector survives.
It only doubles the ram as long as everything remains in scope.

It’s only a guess on my part, but I think it’s a good one. You might have missed my update above where I explain that I use pointers to heap objects which avoids the issue for me. If you want to avoid pointers to objects, then the rule of 3 would likely be the answer… but it still remains a mystery to me why C style arrays would work while vectors fail.

Sounds like a plan. Thank you for all your help! I’ll let you know how it goes incase you care lol.

Of course, plus it could help someone with the same issue if they stumble across this down the road to know if it is the right fix.

Well, I’m sure it will because I GOT IT WORKING! For future reference for anyone with the same issue, just create a vector of texture pointers ( std::vector<className*> exampleVector; ) and for every new texture you create using a similar texture class, or really any object for that matter, use className* exampleObject = new className;

:+1: ! Alright!!!

I take back what I said earlier in my second post (The depth on collision recommendation), all of my games have tended toward reducing overlap and being low object density per screen, I was only concerned with a couple of NPCs and the player interacting with several trees/buildings correctly;

Your full sort method is most likely going to perform much better if things tend to stack a lot (like Vampire-Survivor or Brotato where the goal is to herd the enemies into a clump to get the best AOE damage. In those games they don’t so much care about enemies colliding with each other.). I was imagining std::sort having to iterate all objects with a small number of collisions. I now think the ability to quickly sort ten thousand enemies on a single screen is worth a small amount of extra processing in the low collision side of gaming.

In summary: use std::sort as you were if you have a lot of items colliding with each other. It’s easier to implement, it’s fast enough, and it works for most situations.