I struggle with relationships between game objects. (classes). Would appreciate assistance.

Hello All, first post, first day on this forum.

I’m wondering if someone could look over my rough outline of how I think a game could work.
I’ve been tinkering with game engine for around 10 years now, and not really getting anywhere
fast. I know basic syntax of python, C, C#, etc. - but when it comes time to actully applying the language
to a game - I get lost. I’ve yet to find a good book that applies a language to making a program,
rather - the books I own all seem to just teach the basics of a language.

This time around - I’d like to start from scratch with C++ and SDL2. This is about the only combination
I have not tried.

Anyway - if I can sketch out how I think a game could be made… could someone see if I am on the right track?
If it helps… I’ve been trying to make something 2D similar to Dwarf Fortress or Rim World.

  1. I think you start with a main loop… event handling, update, draw.
  2. In that loop you handle the different states ie: title screen, options screen, and then onto a main ‘play’ screen.
  3. My game objects will be classes… maptiles, npcs, mobs, items, quests, etc.
  4. My main file… has arrays… listofmaptiles , listofnpcs, listofitems., etc.

As I generate tiles, npcs, mobs, quests - any game object… I put them in the lists.
When the gamestate is ‘play’ - I then loop through the lists to conditional act upon each item. update / draw .

Bringing this all together - is how I have in the past with pygame and Godot… made games.

They have all performed horribly. They worked - but it was slow once I got 100’s of game objects deep.

I’m sure I’m not doing this right … and unfortunately - I don’t know, what I don’t know.

Can anyone critique this for me?
Am I missing a concept?
Was there a particular tutorial or textbook that helped you jump the gap between programming syntax and actually making a good program OOP structure?

Matt

Hi mattkw80,

I am in a vary similar position. I’m somewhat experienced in C++ but am also learning how to properly structure my code for an SDL game. For example as I add the audio system to my Tetris clone it blocks the rendering thread when I make a call to Mix_FadeOutMusic. I’m currently doing a lot of refactoring to try and make the game work as expected.

I think there are a few major points to take away from this. 1.) How to create a proper game loop. 2.) How to structure classes in an orderly fashion using proper inheritance. 3.) Useful patterns. 4.) An understanding of SDL and how it threads calls under the covers.

First off I would suggest learning the MVC (Model, View, Controller) pattern. It seems that separating logic in this manner is the best way to approach game programming. I’ve seen the MVC stated in a couple different ways. 1.) MVC1 2.) MVC2. Maybe someone can chime in a make a suggestion as to which MVC model is best for game programming.

The SDL Wiki seems quite useful and suggests a book called SDL Game Programming. I’ve started reading the book and worked through some of the exercises. It would probably be in my best interest to complete the book and all of the exercises. SDL Wiki, then click on books.

It sounds like your on the right track with your list above but likely needs to be a bit more detailed. A UML diagram may be able to provide the detail needed to design a proper class hierarchy. On point 2.) you are suggesting something like a State Pattern which I think is great. State Pattern.

I would suggest finding some SDL apps on GitHub and playing around with them as you read through the code. I found one called TeeWorlds but may be a bit complicated to learn from for a beginner. It seems like a fun game as I was able to build it and play it. TeeWorlds, details, github

I hope to learn more as I spend more time here in the discussion forums.

Electrosys

1 Like

Much to unpack here - and I appreciate your response.

I’ll go through all the the links you posted right now.

Thanks!

As for your performance problems, could you tell us about the code you run for every type of game-object for every frame? That’s usually where the problems come from. It could either be just an algorithmic thing, where you don’t realize that there’s a better way to do something, or you misuse SDL (calling SDL_RenderPresent() for each object), or maybe you really just do have very expensive operations you want to call many times a second.

I’m in a situation like yours, but, without the problem of slow run.
Justt a suggestion: try to show your code, is more easier when show some code, because the code shows like you thinking to solve something.

If you’re looking for programming patterns useful for game programming, I recommend http://gameprogrammingpatterns.com/ (you can read it for free on the web site)

Thanks for the reply’s all.

I can somewhat describe what I’m working on in the last issue I had, which I have actually solved since posting this.

I was making a proc-gen universe, similar to what Dwarf Fortress does. My performance
issue came from updating the GUI in Godot far too often. Every tick of time I was
updating the GUI text with information on 100+ objects. Once I stopped updating the GUI
text so often… it’s now very very fast. (Godot / GDscript ).
So that’s solved.

I appreciate the game programming patterns link… I think I have read about 1/2 of that book.
And it was very helpful. Turns out I had self dsicovered some of those patterns, but didn’t realize
what they were called. (I’ve been doing the Proto-type patter for a decade now).

So my biggest issue , and I will read through all you posted - is still the whole… how to make
objects deal with other objects when using something more bare… such as Monogame or C++ / SDL.

I’ve been thinking about this hard since I posted this, and I think I just need to get back to
actual programming … as I have had a few ideas. I’m trying to remember that the classic EVENTS / UPDATE / DRAW
loop - is just that… and I need to slot in all my other logic around it somehow.

‘Game’ needs a Universe which needs Maps which needs terrains or tiles which can have a player and npcs and mobs which can have
items or wounds / injuries or quests or ambitions.

I’ll go through some of the suggestions here in more detail.

Was also thinking of buying this:

Programming: Principles and Practice Using C++

I can do the basic syntax of many languages all day long… it’s the bringing it all together I’m still struggling with.

  • It’s intuitive that repeated things should stay in a loop. Since video memory is highly disputed, at low level every frame will has to be rewritten, even if the game is “paused”.
    However, memory is a precious thing, since smaller data can fit into smaller and much faster CPU memories. So what happens if you don’t need a resource anymore? In a main loop for the entire game, you’ll free the resource, but will still left the pointer to it, which costs 8 bytes nowadays. Even worse, your loop will have plenty of ifs. If your generated world has trees on the 1st, and no 1 on the 2nd, you’ll be left with ‘if (world > 1) jump’. This creates branches, making compilers life harder.
    To avoid these problems, it’s better to have several loops, each 1 in separated f()s. So you’ll have word_1 f(), with its pointers to trees and resources to do so, while the word_2 f() won’t has any of that. After returning from each f(), you get rid of everything. And this is automatic, either by the compiler freeing things on stack (std::array for instance) or by the std::vector destructor - btw, only use those 2 containers types and std::string, for carrying data as “C-arrays”.

  • C++ is a great idea for games, because classes are a game changer. You throw thing in there and relax, sure that no one will put its dirty hands on it. For games, this is specially useful, since it can turn its direction wrongly, according to values in variables, that change often.
    However, OO only seems to work properly on C++. Java and C# have broken encapsulation. I believe many higher level languages too. So this is the 2nd point to chose C++. And how is it better? You remove from public domain everything that can change internal variables. Your class will become incommunicable. And as you go reading compiler errors, at each time you think again if it’s time to make an exception for that f() specifically (lazy approach). Then you add it as a ‘friend’. Later, if a bug arises, you’ll have a list of friends, that are the prime suspects. So you’ll go directly for likely the right path. This results in +90% of bugs being catch easily.
    And since you have data and f()s together, the being and the action tied, you earn as a bonus an abstraction. You can think that kind of thing as a character, an entity and so on. Just don’t abuse that using interfaces, linked lists, working on heap when could be avoided, and other bad trade-offs people use to do.

  • A golden strategy is to be “lazy”: you won’t do things in advance, but rather only when you are forced to. This results in less code for longer time, meaning more readable/maintainable code.
    So, don’t make an engine before the game. Instead, make only what is needed for the game. The code that results generic enough goes to a library of yours. Don’t use pointers, to create objects as resources from the heap, to have to delete them later. Just do the simple: create the object on the stack. Avoid all pointers, unless you are forced to use them.

  • Things to do later, for performance:

    • Pack variables into bits of only 1, if they have values small enough.
    • Turn your containers of objects into a class of containers (hashtable). The only fault of OO is performance. A class “per se” is not slow, since the compiler can dismantle it. And although containers have decent performance, objects are still heterogeneous memory, compared to an array of a core type.
    • Multithread: I’m not experienced about that. So it seems to me that a better strategy is to make the game single threaded, upgrating it later.
    • SIMD: this can lead to a blast of performance. But you have to target specific machines.
    • Static vector: std::vector over an array, to force staying on the stack.
    • SWAR techniques: std::vector in a variable, if each element is small enough.

This is entirely backwards. Features that work well get copied around and reused; there’s a reason why C++'s object model only exists in C++ whereas the entire rest of the statically-typed OO world uses a model that looks like Java and C#'s: because C++'s object model is broken from beginning to end. Almost everything about it done very, very wrong, which is why the C++ language is so full of footguns that shoot the feet of even experienced developers who’ve been working with C++ for decades.

You mention encapsulation specifically, so let’s look at that. First, C++ offers no guarantee whatsoever that says you can be “sure that no one will put its dirty hands on” your data if you encapsulate it properly, because such a guarantee can never exist in the presence of pointers. I lost track years ago of how many times I’ve had to debug some bizarre glitch that made no sense, discovered that guaranteed, encapsulated invariants were violated in some “impossible” way, and then traced it back to a memory management error stomping on good data. C# and Java don’t have this problem because they have no raw pointers. (Where pointers do exist, mainly for interop purposes, they’re heavily locked down and restricted in their use, to minimize the chances for this sort of stuff to go wrong.)

Meanwhile, what they do have, which I assume is the source of your complaint about “broken encapsulation,” is reflection, a system that lets you use outside information to reach inside of a class’s internals and modify it directly without going through the public interface. And while that definitely can be used in ways that break things, there are two important points to consider. First, everyone hates reflection. It’s slow, it’s bulky and cumbersome to use, and it throws all the advantages of type safety out the window, giving you a system that’s basically “the worst of both worlds” with all of the disadvantages of both static and dynamic typing in exchange for a few of the advantages of dynamic typing. Reflection sucks, so no one uses it unless they really need to.

But second, there are times when you really need to! It’s distressingly common to run across over-encapsulated code, where someone who’s learned that “encapsulation is good” but not that “the dose makes the poison” locks away everything that they can’t personally see a good reason to make accessible within an object’s internals, and then someone with a legitimate use case needs to be able to get at that data and can’t. In times like that, reflection can be a developer’s best friend. And C++ doesn’t have it.

Can whoever this new person is please stop resurrecting months or even years-old threads.

Thanks.

"
sjr

5h

Can whoever this new person is please stop resurrecting months or even years-old threads.

Thanks’

Why? As the OP, I very much appreciate the new information. Are you one of these power tripping moderators with no real power in your life, so you get off on ruling over online forums?

Can I answer from here, via email? The message was send by <noreply@discourse.libsdl.org>, but at the bottom of it says: “Visit Topic or reply to this email to respond”. Reply or don’t reply?

(You can reply by email and it will show up in the thread, as you can see here. The link in the email is for people that want to see notifications by email but prefer to respond on the website.)

There’s no need for that.

There’s nothing wrong with reviving an old thread if there’s value in the reply (but also, it’s probably not necessary to start replying to a bunch of old threads, as it bumps them all back to the front page.)

I said that because from time to time I stumble across a video saying “OO proved harmful”. So I think “Alright, what C++ did wrong this time?”. And then they came with Java and C#. Usually a code like this:

class MyClass {
int private_var = 42;
public:
auto getter () const {return private_var;}
void setter (const int N) {private_var = N;}
};

What's the point in that setter? It's at best just a filter for some special attribution. This level of "encapsulation" even C is capable of: put a variable as static in a separate file, and access it only through the "public" f()s from that file. C++ can do much better:

class MyClass {
int private_var = 42;
public:
auto var () {return private_var;}
int operator = (const int N) {return N;}
friend auto f (MyClass &mc);
};

auto f (MyClass &mc)
{ mc.private_var = 8;} //Ok, friend.

int main () {
MyClass mc, *ptr = &mc;
// ptr->private_var = 5; //Compile error: private.
// *ptr = (MyClass) 5; //Compile error: no conversion C-style to ‘MyClass’.
*ptr = 5; //Ok, it does nothing.
f (mc);
std::cout << mc.var();
return EXIT_SUCCESS;
}

I answered that because it’s 1 of a few topics here not being technical about SDL. We should be discussing here game design, marketing, overall strategies and things like that. Or a new category should be made, and move this topic to there.

Well, if a UB caused that, it can really modify anything in the code. But for pointer, I never had that issue, even working handling alone 10k lines of complex and demanding code. I simply avoid pointers. Whenever I send an object to a f(), I do it via a reference. A common use of them is as iterators, applying an algorithm over a container. Even when I use a lambda to pass my criteria (to the algorithm), I deal with a reference, and the changing part is made by the compiler. Most changing algorithms in STL return void. So, by the const correctness principle, whenever I get an iterator as a result, it’s marked const. I usually have a ‘c’ in the variable/pointer name, discouraging me even to try modifying anything.
So I follow these steps:

  1. Apply the algorithm, getting a const iterator back.
  2. Immediately verify if the algorithm failed, in C-way.
  3. Convert that iterator to index or reference, depending on data struct format it aims.
  4. If I keep the iterator, it’s usually const in a small f(), commonly fitting in a screen.

I also don’t use this:

class MyClass {
char var;
char *resource;

I always make 'resource' as std::vector/string, if I don't know the maximum elements, and std::array or static_vector otherwise. std::array is as fast as a 1-size-only C-array.

@Atiladf: if you provide pieces of code in posts, please put them in the appropriate tags for the code, coloring the syntax. Currently, every snippet ruins the content formatting of your posts.

I know right? This is why people are hesitant to join forums and participate - there’s always that one power tripping moderator who gets off running around the beach stomping out sand castles as he finds them.

I appreciate the info and the effort everyone has contributed to this thread, it’s going to take me a while to absorb it all. But there are friendlier forums out there, so if this going to turn into a classic forum-moderator-power trip, I’ll head back to the other forums.

1 Like

I think the only moderators here are the actual SDL developers.

Anyway, like he said, replying to a bunch of old threads brings them all back to the front page. This forum is AFAIK mostly supposed to be about SDL, but there is a category for non-SDL game dev talk.