Advertisement

C++ & SDL2 - Big Graphics Bug - Any ideas welcome

Started by March 31, 2022 09:01 AM
21 comments, last by Nick72c 2 years, 8 months ago

Ahh…my enemy sprite draw routine is bugged and I can't find the problem.

For the time being the routine reads a 20y, by 76x, int array, and creates a new instance of vector, Class Enemy, enemies for each ‘1’, using the arrays index as a proxy for the map location (1080y, by 7860x).

The draw routine then cycles through each instance of vector, Class Enemy, enemies, loads the image template, sets the x & y relative to the 7680x1080, checks to see if each instance is currently in bounds (within the viewing window, and places the 50x50 enemy (cherry) sprite on renderer.

I've got std::cout's at each stage showing me the correct number of enemies, in the correct x/y relative to the map, and then in the correct x/y relative to the viewing window -

..but I'm getting bizarre results.

It only ever prints the sprites in the right location, but it never prints all the sprites. Yet, it is very consistent in the sprites it omits. Doesn't feel like an memory issue? but I just don't know what to think at this stage.

[Edit -1/04/2022 - I've removed the source code and replaced it with the link below to the the games GitHub page - including source code, installer and executables]

https://nickchantrell.github.io/Parrallax-Zoom-SDL2/

Thank you for the suggestion @fleabay

Advertisement

You could put your code on github (all of it including assets) or zip it and put it on something like mediafire.

At least put it in codetags. It is a mess otherwise.

🙂🙂🙂🙂🙂<←The tone posse, ready for action.

fleabay said:

You could put your code on github (all of it including assets) or zip it and put it on something like mediafire.

At least put it in codetags. It is a mess otherwise.

Thank you @fleabay - I'm learning all the time ?

https://nickchantrell.github.io/Parrallax-Zoom-SDL2/

I could still really do with some help understanding why my draw routine is only printing a small percentage of the enemies sprites to the screen.

I really can't see where I've gone wrong :(

I'm getting this. Is this what you are now getting? It's different from your screenshot.

ED: Ah, Nevermind. I see you can scroll past the right border. I'll look at this in a few hours.

🙂🙂🙂🙂🙂<←The tone posse, ready for action.

I looked it over. It is indeed an odd issue.

The obvious problem I see is that you are sending the Enemy constructor an image path and so each enemy is creating its own identical surface/texture to use. You really should be sending a premade surface that all the similar enemies share to the constructor. The constructor would just initialize the class parameters and not do any calculations. Best practice is “don't do any more work than is necessary in the constructor”

I don't know if this is related to your issue or not but it definitely needs attention.

🙂🙂🙂🙂🙂<←The tone posse, ready for action.

Advertisement

I thought the same while looking at the code in it's hard to read state of yesterday, but did not spot any bug.
So what i would do actually is using different images, see which ones do not show up at launch, then use debugger to follow all function calls a failing sprite is causing.
Maybe something suspicious can be found this way. Otherwise do the same for a working sprite and see if any difference shows.

Opposed to logging to the console, using the debugger lets you see what SDL is doing (or not) as well. (Eventually, if you use debug dlls, or compile it's code directly with yours.)
Learning to use the debugger is an essential time saver and super important, in case you're not yet familiar.

fleabay said:

I looked it over. It is indeed an odd issue.

The obvious problem I see is that you are sending the Enemy constructor an image path and so each enemy is creating its own identical surface/texture to use. You really should be sending a premade surface that all the similar enemies share to the constructor. The constructor would just initialize the class parameters and not do any calculations. Best practice is “don't do any more work than is necessary in the constructor”

I don't know if this is related to your issue or not but it definitely needs attention.

Thanks @fleabay

I appreciate this line of code looks expensive when there are only 1's and 0's on level1 map within the loadlevel1 function - but I designed it so that it will work when I eventually initialise 10 or 20 unique enemy sprites from the level map:

if (level1[y][x] == 1) enemies.emplace_back(true, (x * 100), (y * 50), 50, 50, 1, 0, 3, 0, "res/cherry.png");

These are all new concepts to me, so I will see if I can find a way for identical enemies objects to share surface/texture data (although I'm not immediately sure I understand how to achieve this).

Even so, do you really think improving the efficiencies in this manner would have any effect of the missing sprites? How do you see the two issues being linked?

JoeJ said:

I thought the same while looking at the code in it's hard to read state of yesterday, but did not spot any bug.
So what i would do actually is using different images, see which ones do not show up at launch, then use debugger to follow all function calls a failing sprite is causing.
Maybe something suspicious can be found this way. Otherwise do the same for a working sprite and see if any difference shows.

Opposed to logging to the console, using the debugger lets you see what SDL is doing (or not) as well. (Eventually, if you use debug dlls, or compile it's code directly with yours.)
Learning to use the debugger is an essential time saver and super important, in case you're not yet familiar.

Thank you Joel - this is unfortunate - I was rather hoping someone would spot a simple logic error or mis-use of a data type - something I could put right quickly.

I've only been using C++ and Visual Studio for a few months and haven't got my head around using the debugging tools.

I find it particularly problematic that I can't simulate the user input into the game while watching the debugger step through the code, and this combined with my general lack of understanding of the debugging tools means I tend to shy away from using it.

I have no idea what debug dll's might be, or whats meant by ‘compile it’s code directly with yours' - but I'll try to learn more about it.

If I understand you correctly, you think adding a range of additional unique sprites into the initialisation map and seeing how my code handles a variety of differing images may shed some light on this issue - I'll give this a try.

In the meantime my new game setup.exe installer on GitHub has just packed up - it works great if I run it from the desktop, but if I download it from GitHub the same setup.exe gives a range of errors (annoying as it worked fine several hours ago) - so I'll also need to look into this. It never rains but it poors ?

Nick72c said:
…but these are all new concepts to me, so I will see if I can find a way for identical enemies objects to share surface/texture data (although I'm not immediately sure I understand how to achieve this).

Even so, do you really think improving the efficiencies in this manner would have any effect of the missing sprites? How do you see the two issues being linked?

There may be no link, but while improving something, chances are you find a bug while doing so.
About efficiency, there are multiple points why this is bad (eventually, idk what applies to your case):
* Loading the same texture many times for for many sprites wastes memory.
* Switching the current texture to another texture (which is actually the same), can cause redundant state switches on GPU, which can prevent it from keeping all its cores busy.
* Even using a string to identify something is bad, because a string has variable length, so it needs to be allocated from memory which is slow. Copying the string to another string requires a loop, which also is slower than identifying your textures with simple number, e.g. an index to an array of all your textures. Numbers have constant size, copying them is a single instruction. There also is no need to resolve indirection from the string object to the actual string data, eventually causing a cache miss.

So, to fix that, you could have a std::vector (or array) of unique texture objects, where each object also has a string of it's file path.
While creating the sprites, you could iterate this vector until you find the given path string, then only store the found index in the sprite.
You pay the price of the search only once at launch or level load, so no problem. After that it's easy to get the texture for any given sprite using this index.

But this would break if you delete one of the textures. Because it would invalidate all indices to textures after the deleted one.
Thus, many people use unique IDs instead indices, with some mechanism to find some object from a given ID. This makes things easier eventually, but has a cost of resolving indirections form IDs to actual pointers, usually causing cache misses.
So this becomes an advanced topic, e.g. when designing Entity Component Systems.

For now i'd just use indices to identify textures and other things.
It's not critical as long as you just draw some two digit numbers of sprites, but you better get rid of bad habits early.
Often the software turns out slow, but no big bottleneck shows up. So you don't know what to optimize to improve it.
To prevent this situation, you try to write optimized code from the start and all the time, which rarely is real extra work.

This topic is closed to new replies.

Advertisement