3
\$\begingroup\$

I am developing a game engine in C++ (for an idea of what kind of design I'm going for, think of ROBLOX, if you're familiar with that) and I need help with a problem I have.

So basically, the objects in a scene are called "Instances", and they can have a parent and any number of children. The problem I'm trying to solve is: what should the parent of the root Instance in the hierarchy be?

I'm considering creating a separate BaseInstance class which has children but no parent and have it serve as the "root" Instance in each scene, though I feel this could needlessly complicate things. The alternative is letting the "root" Instance be a regular Instance with a parent, but this parent is a nullptr (or something along those lines), and then just trust that the code will never try to reference the parent of the "root" Instance.

Another problem I'm facing is whether the scene itself should be the root instance or not.

Sorry if this comes across as unclear; I'm just really confused about what I'm supposed to do here or even how to explain what I'm trying to do.

\$\endgroup\$
2
  • \$\begingroup\$ I jump in just to say that object-oriented doesn't mean classes. Just in case you were confusing both concepts: the first one is a development paradigm while the second one is a pattern. Object-oriented programming existed way before classes, which are just glorified object factories. \$\endgroup\$ Dec 2, 2023 at 0:06
  • \$\begingroup\$ Are base instances usually interchangeable with instances, or are they a completely separate kind of thing? \$\endgroup\$
    – user253751
    Dec 2, 2023 at 13:06

2 Answers 2

7
\$\begingroup\$

what should the parent of the root Instance in the hierarchy be?

Nothing.


Does Instances without parent make sense?

If you, for example, might want a factory that create Instance, then set their properties, perhaps compose them in some way, and then attach them to the scene tree...

Or if you, for another example, might want to be able to remove an Instance from the scene tree, manipulate it in some way, and the reattach it to the scene tree, perhaps in a different place...

Then it make sense that an Instance can have no parent. More precisely that an Instance can either have zero or one parent, and that the parent can change in runtime.


Should the root of the scene tree be something special?

It could be an object dedicated to just being the root.

However, often the root often has other functions, such as handling where the game will be rendered to (Window, Canvas, Viewport...). Furthermore, it might make sense to have multiple of those objects (e.g. a main window with children windows).

Although, that might not apply to your engine.

I remind you that even though the scene tree has a root, there will be another code that uses it. And so it will have a reference to its root. Thus, you might have a SceneTree class (which is not an Instance and thus has no parent) that has an Instance which is considered to be the root of the scene tree.

The problem then becomes: How do you guarantee that the root of the scene tree has no parent?


Before we get to that, here is a related question: How do you guarantee that an Instance does not have two parents? That has two parts:

  • There is only one parent pointer, so it can only have one parent.
  • When you are going to add a child Instance you check if it has a parent already.

How do you guarantee that the root of the scene tree has no parent?

Well, similar logic applies to the scene tree root:

  • When you set a Instance as the root of the scene tree, you check it does not have a parent.
  • When are going to add a child Instance you check also if it is the root of the scene tree.

But I'm skipping a question...


Should you be able to change the scene tree root?

If the scene tree root is the scene root, then yes. You should be able to change it, because you should be able to change scenes. Thus, not making the scene root the scene tree root allows you create the root of the scene tree during initialization and never change it.

Thus the first point above (checking if the Instance you will set as root of the scene tree has a parent before setting it) would not be necessary.

And the second is easier (checking if an Instance is the root of the scene tree) because anybody could access the root of the scene tree via the composition root to compare.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you so much for your answer! Unfortunately, I didn't understand some of this stuff; I'll look into it more soon. \$\endgroup\$
    – AcinonX
    Dec 1, 2023 at 2:22
1
\$\begingroup\$

All this is ultimately up to you. OOP is a tool, not a rule. But if you're looking for suggestions and examples, here's what I did for mine:

In my engine library, there's an abstract Drawable type, which represents anything that can be drawn. I gave it an X and Y coordinate and a Draw_Self subprogram. I messed up at first and gave it a Sprite as well (which in turn is a type that contains a Spritesheet (also a type), origin, boundaries, etc), even though I would later decide that not everything that can be drawn needs a Sprite (for example, text boxes that I draw with a shader and a TTF rasterization library). So don't make that mistake. Having this parent lets me do things like keep track of its subtypes in arrays and lists rather than checking booleans endlessly to decide when to draw certain things.

There's an abstract Game_Object type, which inherits from Drawable and also has some boundary coordinates that define the space it physically occupies and an Execute subprogram, which it uses to actually do stuff. Change position, update animations, interact with stuff, etc. In practice, I find I normally give these an Animate subprogram as well just to keep the logic separate. Even things like trees fall into this category, so they can handle collisions and stuff. In addition to the advantages of the Drawable parent, this parent allows me to do things like check for collisions and other interactions between arbitrary Game_Object types.

There's an abstract Controllable type, which inherits from Game_Object and has the relevant control subprograms that you need for your target input types. In my case, Button_Down, Button_Up, Mouse_Down, Mouse_Up, Mouse_Hover, Mouse_Exit. This is kind of a must so that you can handle the events at an engine level and just call these functions on whatever object has the focus. It inherits from Game_Object and not Drawable directly, because anything that responds to input does something.

I also have a handful of reusable non-abstract implementations of these things as well, such as menus.

I've also made an abstract Game_State class, which I believe is similar to what you call a scene. This has Initialize, Execute, Draw and the same Input subprograms named above. These are things like the main menu, the pause menu, the world map, etc. The main engine loop keeps a list of these, picks the first item in the list, propagates inputs to the proper subprogram, runs its Execute, then its Draw. New Game_States are prepended to the list and popped LIFO when no longer needed. Each Game_State manages its own Drawables, Game_Objects and Controllables, figures out which of these to Execute and Draw when, checks for collisions, etc.

Now, I'm by no means an expert (I made all of this up as I developed it), so there are probably still a lot of ways to improve it. But more important than the specifics of what I did is the idea that your structure serves a purpose. It's not a right/wrong answer, but rather how do I organize all this functionality?

\$\endgroup\$

You must log in to answer this question.

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