4

I tried to make the question title as generally applicable as possible, but I'm not certain I worded it so well. It was brought about by a very specific problem I'm having, and for the remainder of this question I'll just try to describe that.

I'm attempting to write a small game engine with graphics, physics, and collisions*. I am trying to implement each of these things separately; it is perfectly conceivable that I have a graphically displayed, physically moving object that can't collide (fire effects?) or a collision object that isn't rendered or governed by physics (invisible barrier). However, a large number of objects will need all three.

I'm pretty darn sure that I can isolate the three pieces almost completely in their own classes, with the one exception that they all need to reference the same position and angular orientation of their object.

Initially, I thought that I'd put each thing into its own class and just build each actual game object with multiple inheritance, but in light of the fact that they all need to share data I wasn't so sure that that would work.

Next option was having each object initialized with pointers to the appropriate position and orientation data, but I felt weird treating those like special members . . . I would like each class to act as ignorant as possible of the others, and I would never do the pointers if I were designing each class independently.

I could also let each class live with its own position data and have the combination class (via either friendship or getters/setters) force them all to coincide, but that also just didn't feel right.

I tried searching Google for information on what's typically done, but most everything I found was focussed on a specific part of the system and didn't say a lot about integrating everything. Am I just being too philosophical, or is there a better way to do this?

If it's important, I'm using C++.

Thanks.


* I know that implementing it myself is pointless, but it's all just for fun.

2
  • 1
    Does the phrase "policy based design" mean much to you? If not, take a look at "modern c++ design" from the 2003 era, I think it's exactly the pattern you're looking for. Commented Jun 23, 2015 at 20:34
  • It didn't a few hours ago, but now I think that you may be onto something! I'm not sure that it directly addresses my current issue; it seems to address issues in which some functionality is known by the host class to be implemented, but it's unknown how, whereas I think that I would like a host class that isn't aware of which functionalities it will implement. I still have more mulling to do, but even if it doesn't address my current quandary it's definitely something that I'll want to include in the overall design.
    – Jordan
    Commented Jun 24, 2015 at 7:50

2 Answers 2

3

Services, not Superclasses

These things are not related objects, they are behavioral aspects of game objects that may come and go as the state of the game objects change; think about them as services, not superclasses. They're not well-suited to purely compositional elements either, as the in-game behavior will likely be strongly dependent on the state of the game objects over time.

Example

A 'ghost' object may be visible and in motion but can pass through solid objects but may not be able to pass through a magical barrier and also may inflict damage when it collides with living tissue. It can only be damaged by magical weapons.

Sorry

Game programming is hard, at this level mostly due modeling complexity. Hack around to get your own ideas (if just having fun) and/or study existing game engines to see what others have done (e.g. Unreal Engine's source code)

2
  • I'm only familiar with services from client/server networking idioms, so I'm not sure that I entirely absorbed your answer, but I do think that you're right about how I need to stop thinking of those things as real objects in their own right and instead as add-ons for their host object. Specifically, I'm leaning toward my second original option (pointers) and having some way of letting the host object associate and disassociate freely with various e.g. graphical representations. Also, thanks for the Unreal source link -- it looks rather intimidating, but I will definitely give it a go.
    – Jordan
    Commented Jun 24, 2015 at 8:02
  • @Jordan: 'service' in this context is simply a helper/utility class or method that performs some calculations on behalf of other objects. Good luck! Commented Jun 24, 2015 at 19:38
0

You're naturally working your way towards entity-component systems.

enter image description here

The final step which is the hardest to take if you've been applying an OOP mindset for years, is to turn those shared components into raw data, but it is only then that you can really come to appreciate the true flexibility of the ECS. I actually stubbornly resisted doing that until a second iteration where I just made components into raw data.

The way to kind of counteract the feeling that this seems all wrong and a total violation of encapsulation and information hiding is to remember that typically only one or two systems will be accessing any given component and often just one system that actually transforms (mutates) that data. As a result it's actually not much harder, if any, to maintain intracomponent invariants. It also tends to make it easy to maintain broader, intercomponent invariants with coarse systems processing everything.

Entities are just containers of components (with one component type instance maximum per entity). Components are just raw data. Systems process entities/components and are the sole providers of functionality between these three. Systems are also generally decoupled from each other and typically do not call functions into each other. Most systems also resemble the loopy pattern of:

for each entity with these specific components:
    do something with the components

For instance, the render system above might be like this:

for each entity with pos/velocity and sprite components:
    render sprite component at pos

While the movement system might look like this:

for each entity with pos/velocity components:
    pos += velocity * time_elapsed

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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