0
\$\begingroup\$

I've learned that magic numbers are bad in code. However, should the coordinates, and rotation angles of all the sprites be stored in variables? Is the convention in game development to leave those hard-coded values as arguments to set the coordinates or the angles of the transformable?

// Create a texture to hold a graphic on the GPU
Texture textureBackground;
// Load a graphic into the texture
textureBackground.loadFromFile("graphics/background.png");
// Create a sprite
Sprite spriteBackground;
// Attach the texture to the sprite
spriteBackground.setTexture(textureBackground);
// Set the spriteBackground to cover the screen
spriteBackground.setPosition(0, 0);
// Create a tree sprite
Texture textureTree;
textureTree.loadFromFile("graphics/tree.png");
Sprite spriteTree;
spriteTree.setTexture(textureTree);
spriteTree.setPosition(810, 0);
// Prepare the bee
Texture textureBee;
textureBee.loadFromFile("graphics/bee.png");
Sprite spriteBee;
spriteBee.setTexture(textureBee);
spriteBee.setPosition(0, 800);
\$\endgroup\$
6
  • \$\begingroup\$ the real issues arise when the same value is used multiple times. suppose there would be twice 800 (rather than 800 and 810). Then those appear to be the same value, when that might be only coincidential. This makes it more difficult to consistently change the value later. Imho here this is much less a problem (unless 810 should actually be 800 + 10 or something like that), because you use the values once and i suppose Sprite has a properly named accessor for the value. Anyhow, this is purely opinion based \$\endgroup\$
    – 463035818_is_not_a_number
    Commented Jul 19, 2022 at 12:18
  • \$\begingroup\$ You could make them static constexpr in a header file and reuse that. That way you can give them clear names and reuse them in your code. E.g. static constexpr vec2d FixedBeePosition {0,800}; where vec2d is struct with an x and y value. Your set position will then be : spriteBee.setPosition(FixedBeePosition.x, FixedBeePosition.y) (or allow SetPosition to accept a vec2d directly). And you have removed "magic constants" from your code. \$\endgroup\$
    – Pepijn Kramer
    Commented Jul 19, 2022 at 12:19
  • 3
    \$\begingroup\$ This looks like a question for Code Review, since you have code that works and you're looking for feedback on coding style, not problem solving specific to game development. Be sure to be specific about what problem you perceive with this code, what alternative you propose, and what you see as the pros and cons or trade-offs of using that alternative. \$\endgroup\$
    – DMGregory
    Commented Jul 19, 2022 at 12:21
  • \$\begingroup\$ It's conventional to store such data (including the paths) in a file that is read when you need it, and not have any initialization constants in the source code. \$\endgroup\$
    – molbdnilo
    Commented Jul 19, 2022 at 13:18
  • \$\begingroup\$ I wonder that all these sprites (which seem to be objects or even actors in your game) became variables in itself. I would expect that all objects (actors) are stored in a container. (Later) this container may be loaded from files to allow multiple levels with different arrangements of these objects. No? (Of course, I would introduce a class Object, and maybe, a class Actor: public Object to bundle all the stuff for one object / actor which belongs together...) \$\endgroup\$ Commented Jul 19, 2022 at 13:26

3 Answers 3

0
\$\begingroup\$

For small games, it's okay to define these defaults locally in code directly. For anything more complex, it becomes rather unmanageable to keep it all in code.

That's when the data is moved into dedicated external files. This can be a simple JSON file that holds all the different sprites and their initial position. You'd then load this information at the beginning of a map/level/game and create the different shapes/textures/sprites/sounds.

As a side note, only really do this when your game gets bigger, because it introduces quite a bit more complexity, as your code has to be written more generically and can also consume quite a bit more effort to get the loading working smoothly. If you do achieve it though, it offers a lot of flexibility. You can modify the whole level or character with a simple edit of the config file and restarting the game or even implement some hot-reloading.

\$\endgroup\$
3
\$\begingroup\$

Storing values in variables is not the same thing as using magic numbers. The magic number anti-pattern is a direct usage of a number in the code.

For instance, compare the following code snippet:

currentHealth += 5;
if(currentHealth > 10) {
   currentHealth = 10;
}

Why are we increasing currentHealth by 5?
Why are we limiting currentHealth to a maximum of 10?
The values 5 & 10 are magic numbers - values being used directly in the code, and in this case, without a clear explanation as to why those particular values are being used.

That code should be refactored into something like this:

currentHealth += healthPotion.amount;
if(currentHealth > maxHealth) {
   currentHealth = maxHealth;
}

The second block of code is easier to read & maintain.

  • It explicitly tells us that the current health is being changed because of a potion.
  • It explicitly tells us that the potion shouldn't raise the current health past the maximum.
  • We can presumably change the value for maximum health in one location, minimize the potential for errors.

Based on the description given, it does not sound like you have a magic number problem. If you need to track a sprites coordinates and rotation angle, member variables are a very sensible way to do so.

For more information & examples regarding magic numbers, you might want to read this Stack Overflow post.

\$\endgroup\$
2
\$\begingroup\$

Please keep in mind that you're reading a book explaining a way to make a game, in SFML. You will likely end up having a working game, but not one that is ready to ship.

To make it simpler for you to "get something working", they hard-coded the values instead of having you write a whole system to load a level from files.

This is fine, though. This kind of book is often the first step in getting you going. Once you're done with it, you'll then need to expand and "fix all the problems you see with it": adapt it for multiple screen sizes, make levels that are dynamically loaded at run-time, add a second playable character, etc.


Is the convention in game development to leave those hard-coded values as arguments to set the coordinates or the angles of the transformable?

No, there is no convention.

Also, I suppose how much hard-coding there is will depend on the nature of the object.

I'm pretty certain some folks created HUDs completely by hard-coding values (coinCountLebel.position(screenSize.x() - 150, 0)) with the real desire to come back later and fix it before release, but yeah, they had to ship.

However, if you are to make a level by hard-coding your values, you'll realize that you're spending a lot of time in compiling your game just because you moved an object a few pixels.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .