1
\$\begingroup\$

I'm trying to write a game in C++ using OpenGL. I've tried to separate my game logic and my rendering logic as much as possible, and up until now, this has worked for me. I haven't written a lot of code, and right now, I'm getting to the point where I have all my separate parts, and I'm trying to put them together.

In order to keep game and rendering logic separated, I've written them both in separate static libraries (So I have a libCore and a libRendering. There's also a third static library that contains code that is shared between the two libraries (Things like utility methods for reading files, custom exceptions, ...), this library is called libShared. Both of my libraries (libCore and libRendering) are first linked against libShared and then the game executable is linked against libCore and libRendering.

I've been using an entity-component-system (Which is part of libCore) to keep track of all my entities, now I'd like to introduce a RenderingComponent which contains all the data necessary for actually rendering an object (Things like VAO's, VBO's, Textures, Normal Maps, ...).

The problem is, that doing this would bring part of the rendering code into the game logic code. And I'm wondering what the best course of action would be here.

Should I move the ECS into libShared?

Should I just create a single libEngine that I can link my game against, instead of 3 different libraries?

Do you have any other solutions?

Note: I do realize that separation of game and rendering logic has been discussed before, my question is specifically about the architecture of the various libraries I have layed out in this question, and other peoples' opionions on it.

To clarify what I mean, here's what my CMakeLists.txt looks like (They're actually separate files, but I'll keep them together for simplicity's sake).

...

add_library(Shared ${SHARED_HEADER_FILES} ${SHARED_SOURCE_FILES})

...

add_library(Rendering ${RENDERING_HEADER_FILES} ${RENDERING_SOURCE_FILES})
target_link_libraries(Rendering Shared ${OPENGL_LIBRARIES} ${GLEW_LIBRARIES} ${GLFW_LIBRARY})

...

add_library(Core ${CORE_HEADER_FILES} ${CORE_SOURCE_FILES})
target_link_libraries(Core Shared ${LUA_LIBRARIES})

...

add_executable(Game ${GAME_HEADER_FILES} ${GAME_SOURCE_FILES} src/main.cpp)
target_link_libraries(Game Core Rendering)

Second Note: If this is better suited for Code Review or Software Engineering StackExchange, I'll happily move my question over there.

\$\endgroup\$
  • \$\begingroup\$ The question is "would game developers be better able to answer this question". If so, it belongs here. \$\endgroup\$ – Almo Aug 13 '18 at 17:54
0
\$\begingroup\$

It depends on where you want the RenderingComponent to live. If RenderingComponent is itself part of the rendering code, then you are asserting the rendering code depends on (and is thus aware of) the entity system. This means the layers of abstraction in your code should look like (A > B indicating that A is "higher level" than B; A can see and make use of B, but generally not the other way around):

Gameplay Logic > Rendering > Entity System > Shared Core Stuff

If, on the other hand, you want the RenderingComponent to be a part of the entity system, the entity system has to be aware of the rendering system to implement this. So your abstraction layers should end up like:

Gameplay Logic > Entity System > Rendering > Shared Core Stuff

Either is approach is fine (it's perfectly fine for gameplay or other high-level stuff to understand the idea that they will be drawn, you just don't want them directly handling the actual drawing; the component would contain the references to the rendering primitives required of your rendering library to draw the relevant data).

I would generally advocate for the latter approach, where whatever entity system you're using is a high-level system that may have components that just wrap lower level functionality (rendering, physics, audio, et cetera) to provide the functionality of those lower-level systems in "entity ready" form. I suspect this will also be a more direct solution for you, as opposed to the first option which sounds like it might involve moving your entity system down a few levels of abstraction and thus decoupling it from other high-level gameplay code.


As for your query about one static library versus many... that's largely up to you, it's orthogonal to the design issue (generally speaking). I would advocate for a single monolithic static library unless you seriously plan to mix-and-match the smaller static libraries. In practice I suspect you will not, and so the overhead of working in one "engine library" is smaller and more digestible for you.

\$\endgroup\$

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.