Engine Structure?

Started by
11 comments, last by Norman Barrows 10 years, 7 months ago

I'm building a simple 2D Engine, and I would like some input on my idea for the engine structure and also possibly other ideas that may be better.

Currently I have a header file which is used everywhere in my engine. It defines things that are universal such as struct Point or struct Image and other things such as enums and all that jazz.

Next I have an Engine class that's a singleton. It houses everything that is my engine and is the main interface through which you work with.

The final pieces are the important components that relate to different sections of the engine. They are their own class and can be accessed through gets in my Engine class. Here are some examples.

VideoInterface

  • Handles rendering.

ObjectInterface

  • Deals with the objects and instances

There is a lot more that is yet implemented such as input, networking, logic, sound, etc.. etc.

Here is an example of my main.cpp


Engine* g_pEngine;
ObjectInterface* g_pObjectInterface;
VideoInterface* g_pVideoInterface;

void MyStepFunc( double dTickStartTime )
{
    printf( "Render Speed: %fFPS\nGame Speed: %fFPS\n\n", g_pVideoInterface->GetFPS(), g_pEngine->GetGameSpeed() );
}

int main()
{
    //Set up Engine
    g_pEngine = Engine::GetEngine();
    g_pObjectInterface = g_pEngine->GetObjectInterface();
    g_pVideoInterface = g_pEngine->GetVideoInterface();
    g_pEngine->SetStepFunc( &MyStepFunc );

    //Engine initialization
    g_pEngine->Init( 640, 480, 640, 480, (char*)"Test Engine" );

    unsigned int uiObject = g_pObjectInterface->CreateObject();
    g_pObjectInterface->SetObjectTexture( (char*)"test.png", uiObject, 64, 64 );
    g_pObjectInterface->CreateAnimation( uiObject, 2 );
    g_pObjectInterface->AddAnimationFrame( uiObject, 0, Point( 0, 0 ), Point( 32, 64 ) );
    g_pObjectInterface->AddAnimationFrame( uiObject, 1, Point( 32, 64 ), Point( 64, 64 ) );

    unsigned int uiInstance = g_pObjectInterface->CreateInstance( uiObject );
    g_pObjectInterface->SetPosition( uiInstance, CluPoint( 0, 0 ) );

    //Engine main game loop
    g_pEngine->GameLoop();

    g_pEngine->Drop();

    delete g_pEngine;

    return -1;
}

What it does right now is create the game with a window of x and y size, x and y resolution, and a name. It then creates an object and creates an animation and then creates an instance of that object and set's the instances position.

g_pEngine->SetStepFunc( &MyStepFunc ); allows the setting of hooks. This hook is called every game step( 1/60th of a second ).

I just wanted input on if this was a good way of doing it, or if there is a better way that would allow more control by game developers. For example I can see just hooks being a limiting factor so I may try to code with polymorphism in mind and allower setter functions for the various interfaces.

Advertisement

My very first design had exactly this, an omnipotent engine singleton (except mine was called Kernel but the idea was the same). At some point I realized that in my engine everything calling everything and everything depending on everything. Later I learned that this is called "spaghetti code", but back then I was just thinking "what a mess".

Long story short, I started reading about it and discovered dependency injection as an alternative to singletons, which ultimately is just a fancy way of saying "just make an instance and pass it around". This looked worse than the singleton at first, because suddenly my empty constructors had to take multiple engine systems as parameters, but in the end this was actually more useful and readable. The systems are better off being decoupled and the dependencies are made explicit.

I just want to share my engine design


engine.initialize(640,480,32,UNLIMITED_FPS,....);

Graphic_object go;

int texid=texture_storage.load("blabla.png",mipmap,CLAMP,....);//all tekstures store in one place, load texture than its not loaded yet, or return already loaded texture id

go.set_texture(texid);
//several options for graphic object
go.set_blend(...);
go.set_shader(...);
go.set_filter(...);
go.set_fog(...);
...

go.start(); // clear object vertex array
//draw object. becuose object can be sprite , animated sprite, circle, bunch of polygons and etc
//object thread as sprite
go.sprite.set_texture coords(...);
go.sprite.set_colors();
go.sprite.set_origin(...);

go.sprite.add(...);    //renders sprite to go vertex array

go.line.set_width(...);
go.line.set_colors(...);
go.line.add(vec1,vec2);  //renders textured line to go vertex array

//now go vertex array is filled time to render that array to screen
go.render();


//exiting program or level or game state

go.kill();
texture_storage.kill();

how about that design??

My very first design had exactly this, an omnipotent engine singleton (except mine was called Kernel but the idea was the same). At some point I realized that in my engine everything calling everything and everything depending on everything. Later I learned that this is called "spaghetti code", but back then I was just thinking "what a mess".

Long story short, I started reading about it and discovered dependency injection as an alternative to singletons, which ultimately is just a fancy way of saying "just make an instance and pass it around". This looked worse than the singleton at first, because suddenly my empty constructors had to take multiple engine systems as parameters, but in the end this was actually more useful and readable. The systems are better off being decoupled and the dependencies are made explicit.

That's exactly what I worried about. Actually before this I was passing a pointer that pointed to the main engine to all of the components, and the engine had all the components as a member variable allowing components to call other component functions with their pointer to the main engine. I thought the singleton was a better idea since you'll only ever have one engine running at a time and it got rid of the mess.

As for the spaghetti code i'm at a loss. I thought I was avoiding useless dependencies with my code which so far seems to be the case. Including a certain interface would only bring with it the declerations needed by that interface and nothing "extra" should be used, or atleast that's what it's been kept to so far with such little work done.

@serumas
Your code alone doesn't really show the engine structure, but it gives some small insight at how it may be organized. It seems quite similar to mine in which you have a component based design. Graphics_Object just deals with everything that is of the graphical nature of your game like my VideoInterface would.

What you are avoiding is not the dependencies, but rather making them explicit. If you have a method somewhere calling Engine.getRenderer().update(), you still have a dependency, it's just not explicit from the method's signature. This is actually worse IMHO because you cannot immediately tell what the dependencies are without reading all your methods. If you use some external software to walk your code and build a dependency graph, it will probably look like a tangled ball.

What I do now is only hook up the components that really need to talk to each other, or put a component somewhere above both which knows about both and hooks up their input and output. So, for instance, the ModelFactory gets a reference to the TextureCache in its constructor, because in order to load a model it needs to also fetch the corresponding textures (the TextureCache will get the texture on a cache hit, or make a call to its TextureFactory on a cache miss). But the ModelFactory cannot call Engine.getRenderer().update() because that's not its responsibility. The main loop on the other hand knows about the World and the Renderer and can do something like renderer.submitChunks(world.getVisibleChunks()). And the Engine/Kernel object simply no longer exists anywhere.

I can't say that I've nailed it 100% but it's been working out better for me. Sometimes I use DI frameworks to hook up the dependencies automatically but normally it's not a big deal to wire them by hand.

Just a re-iteration of some above posts because it's so important. Avoid Public Global Static Objects. Don't use them because its easy. It is going to make life really hard when there is a flat hierarchal structure and anything can access/call anything else. Pass in your managers by reference, it will save you a lot of headache in the long run.

If your code is getting too interdependent or tightly coupled consider using an eventing system. This allows someone higher up on the dependency tree to wire up event handlers and the sub-systems will operate independently of one another. Also take a look at using a component based structure to lighten the hierarchy.

Just a re-iteration of some above posts because it's so important. Avoid Public Global Static Objects. Don't use them because its easy. It is going to make life really hard when there is a flat hierarchal structure and anything can access/call anything else. Pass in your managers by reference, it will save you a lot of headache in the long run.

I've considered doing this when I take my current engine and rework it from scratch producing a more sleeker product( right now it's clunky without a central idea guiding it's development ).

If your code is getting too interdependent or tightly coupled consider using an eventing system. This allows someone higher up on the dependency tree to wire up event handlers and the sub-systems will operate independently of one another. Also take a look at using a component based structure to lighten the hierarchy.

I've read that using callbacks or hooks can also work to help decoupling which is what I was originally planning to use. Do you know if an eventing system is the better choice and where I can read up about how it works?

Point one: Why are you making an engine? To be honest it doesn't seem like you've actually made a lot of these components before because you don't seem to understand the coupling and dependencies between them, that may just be me. Personally I've learned what I have about engines by trying to make a -game- because each time you make a game you add a little more to your "engine" and start to see the interconnections that everything requires.

Point two: Dependencies don't just disappear, you just hide them in order to pretend you aren't actually using them, which is silly. If your little car object needs to check input or register itself to receive input with some input subsystem then you are going to -have- to reference that object in code. That makes it a dependency and the only real difference between swinging around singletons and actually passing in something you can grab objects from is that you are pretty much obfuscating the fact you are using it.

Point three: Why are you ever using printf in C++, cout does the exact same thing except it will happily munch up and present any kind of data you throw at it.

Point four: Why does anything you have made expect a character pointer that you are CASTING from a string literal. I kind of am starting to feel like you're trying to put a C blanket over C++.

Point five: Using one header file all over your program is a bad idea and almost completely defeats the purpose of header files because everytime you change a single letter in it you're going to be recompiling your entire program. If you need to declare information like enums either place them in the header they are relevant to or if they are generic, place them in small header files where they are declared within namespaces. More is less.

Point six: Your judicious use of singletons within singletons really confuses me.


I'm building a simple 2D Engine, and I would like some input on my idea for the engine structure and also possibly other ideas that may be better.
I don't know man... it's very high level. The main is very complicated in my opinion this isn't yet an engine but rather your own acceleration structure. It seems there's not enough automation.

Anyway, people have spent some lines on how singletons are bad. I don't fully appreciate their reasonings but I agree with their conclusions: drop the singleton pattern. Engines don't work in that way if not for the amount of data-pulling involved algorithm convergence and such. If we have the need to have two independant simulations we really need to have two engine instances (whatever they will operate efficiently is another question).

Besides, I think this is a start. The only other thing I want to point out is that I suggest to just have a basic interface like EngineCallbacks instead of passing around function pointers (or lambdas for that matter). Inheritance is just a better formed core concept in my opinion and if you design the interfaces correctly it comes at a very affordable price supposing it isn't optimized away in the first place.

Previously "Krohm"

I think you would profit from looking at this lecture about API design: http://www.mollyrocket.com/9438

Avoid coupling, callbacks, frameworks, engines where you have to fill in predefined gaps and get limited in what you can do; layered structures where a library/subsystem only depends on lower layers and none at same level or higher are a little better; best is if you have components which depend on nothing else and you feed in input data and output data into each by the higher level logic which connects everything and you dont need to change everything when you want to change one subsystem.

This topic is closed to new replies.

Advertisement