-1
\$\begingroup\$

I am currently working on the rendering aspect of my game. The code is currently organized in this manner:

Classes: G_Texture:

class G_Texture
{
public:
    G_Texture();
    G_Texture(SDL_Texture*);

    ~G_Texture();

    virtual void render(SDL_Renderer*);
        //Render

    SDL_Rect dst; // Destination rectangle to render the texture

};

G_Text:

    class G_Text : public G_Texture
    {
    public:
        G_Text();
        G_Text(std::string, G_BitmapFont*);
        ~G_Text();

        G_BitmapFont*  font;
        std::string text;

        void render(SDL_Renderer* renderer);
    };


G_Image:

class G_Image : public G_Texture
{
public:
    G_Image();
    G_Image(SDL_Texture* text);
    ~G_Image();
    void render(SDL_Renderer*);

    SDL_Texture* texture; // Texture source
    SDL_Rect* clip; // Sub-section of texture to draw
    SDL_RendererFlip flip;
};

What I want to do now is add three features: 1) Dynamically changing string I want to dynamically render a changing string. If for example I have a character in my game with a continuously changing name, what approach should I take to render that changing name? Do I create a new class for dynamic string, which holds a string pointer?

2) Dynamically changing number So this is the same as (1), except now I want to update a number (int, double, etc...) into a string. Do I create a new class specifically for holding an int pointer? I feel like making all these classes wouldn't be very elegant. If I wanted to display anything other than an int I would have to make a new class...

3) Dynamically changing image width (Ex health bars) If I want to create a health bar whose size depends on the health of a character, what is the best approach. My current thought is to create another class for health bars, which would have a pointer to the character. Once again this doesn't seem very elegant or reusable, as I would have to create new classes for each different component requiring different pointers.

I apologize if this is confusing. I'm new to game development and stack exchange.

\$\endgroup\$
  • \$\begingroup\$ It would greatly help if you provided sample code so we can see the alternatives :) \$\endgroup\$ – Quentin Mar 9 '17 at 15:31
-1
\$\begingroup\$

There are two significant issues that I'd like to point out with your approach. One is separating concerns; the other is retaining state.

First, G_Texture is a resource class. It links a file in the filesystem to a representation of a texture in memory.

If you look at the example G_Texture class you have proposed, what does render do? Does it draw the whole texture at a given coordinate on the screen? Can you pick a part of the texture to render? Can you rotate or scale it? You picked some default behavior for G_Texture::render, and while you picked a reasonable and common behavior, it's arbitrary. What does it mean to render a texture, when a texture can be used so many ways?

What I'm saying is that your G_Texture class should be responsible for holding onto an SDL_Texture * and destroying it at the right time, and that's it. There are lots of ways to use a texture; don't pick one and stick it in the class and then override it in lots of confusing ways in other classes. Let other classes use a texture however they want to. Maybe they use two textures. Maybe they use a texture and a text file (a list of frames in an animation).

G_Text and G_Image, and e.g. G_DynamicString, G_DynamicNumber, G_ResizeableImage are all behavior classes. They describe special ways to draw a G_Texture. They could inherit from an interface, G_Renderable, instead of G_Texture.

I think you're finding this subclassing messy because you see that the "is-a" relationship isn't right. G_Text is not a special case of G_Texture -- rather, it uses a texture. What if your G_Text requires multiple images for all the glyphs? What if you want a G_ResizeableAnimatedImage?

A better approach is a "has-a" relationship. The classes that define behaviors have a reference (or a shared pointer, or a resource handle) to the resources that they need.

There are lots of different ways to work with textures, like most resources. If you separate resources and behaviors, by

  • making the resource "just a container" that is concerned with its own lifecycle -- handling its own creation/destruction and properties (width, height), and
  • making other classes that accept resources and know how to do things (e.g. draw text)

your code will be easier to work with in the long run.

Now, retaining state:

A game fills the screen every frame, and the contents of the screen are thrown away. The game doesn't know or care whether a string is dynamic or static; at some level, a graphics API still has to say "draw this string" every frame.

There are definitely optimizations to be made, but I don't really understand your distinction between "Text" and "DynamicString" at this point. If you have a class that knows what to do when you call setText, that covers both cases. Since you mentioned a pointer, I am guessing that you want to pull the game data in, every frame; a simplistic way to do this would be to supply a method that takes a lambda const string&(){}. That function can pull data from anywhere and you can make it take an integer, a string or whatever else from the closure.

Hope this helps.

\$\endgroup\$
  • \$\begingroup\$ Thank you, I am not sure I understand everything (I'm not exactly the most advanced programmer) "A better approach is a "has-a" relationship. The classes that define behaviors have a reference (or a shared pointer, or a resource handle) to the resources that they need. If you separate resources and behaviors your code will be easier to work with in the long run." I don't really understand this part. What do you mean by separating resources and behaviour? \$\endgroup\$ – Meuktwo Mar 9 '17 at 18:59
  • \$\begingroup\$ Elaborated on is-a (inheritance) vs has-a (composition), and tried to clarify why none of the classes you proposed are special kinds of textures. \$\endgroup\$ – Justin Mar 9 '17 at 19:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.