Dependency Injection in a game engine

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Dependency Injection in a game engine

      Hi guys!
      I'd like to talk about DI in game engine architecture. Main question is should this technique be used or not?
      Basically everything breaks down to the choice:
      • Exposing game services as singletons and then access them inside various classes. (I don't distinguish here between global or OOP singleton as they basically the same.)
        pros
        - only singleton header is included
        - works fast
        - pretty well-known pattern
        cons
        - tightly coupled code
        - hidden dependencies between classes
        - google says that it's hard to do unit testing with it
        - seems like people are abandoning this concept lately

      • Exposing services as service locator pattern.
        Same as the singleton except that game services are abstracted away and hidden into the container and we query the container instead of asking each service for its singleton. (Seems like this requires singleton of the locator though.) Sounds like more robust but slower solution.

      • Using construction dependency injection to pass the services interfaces everywhere we need them.
        pros
        - code isn't coupled anymore
        - best flexibility and portability
        - explicit dependency in the class constructor definition
        cons
        - alot harder to prototype (requires much more time)
        - now constructors are bloated with interfaces (maybe there's a solution to this?)
        - harder to read for those who don't like fancy pattern thing

      This is it. What do you guys think? How all those global services (i.e. logger, process manager, event manager, renderer etc etc) should be organized? What's used in the serious industry? Share your thoughts!
      Looking for a job!
      My LinkedIn Profile
    • RE: Dependency Injection in a game engine

      First, I'm not sure I agree with all of your pro's and con's here. But that aside, there isn't one single answer. It completely depends on what you're trying to achieve. You can also use a bit of a hybrid.

      Originally posted by devast3d
      • Exposing game services as singletons and then access them inside various classes. (I don't distinguish here between global or OOP singleton as they basically the same.)
        pros
        - only singleton header is included
        - works fast
        - pretty well-known pattern
        cons
        - tightly coupled code
        - hidden dependencies between classes
        - google says that it's hard to do unit testing with it
        - seems like people are abandoning this concept lately



      How does this create coupled code? It just requires the CPP where you're using the singleton to include it. You'd have that dependency anyway. Besides, you could hide it behind an interface if you wanted to, which would allow you to easily swap out the singleton. That would allow you to have another logger, for example.

      I also disagree with google's notion that it's hard to do unit testing. Again, I think you can use a singleton pattern that hides it behind an interface so you could implement the unit test version of that interface.

      People aren't abandoning it, it's still a very commonly used pattern. We use it quite a bit on The Sims, and it's present in pretty much every project I've worked on.


      • Exposing services as service locator pattern.
        Same as the singleton except that game services are abstracted away and hidden into the container and we query the container instead of asking each service for its singleton. (Seems like this requires singleton of the locator though.) Sounds like more robust but slower solution.



      This is mostly the same pattern as above. I don't think I'd implement it as a locator exactly. I'd probably create a hash map or something.



      • Using construction dependency injection to pass the services interfaces everywhere we need them.
        pros
        - code isn't coupled anymore
        - best flexibility and portability
        - explicit dependency in the class constructor definition
        cons
        - alot harder to prototype (requires much more time)
        - now constructors are bloated with interfaces (maybe there's a solution to this?)
        - harder to read for those who don't like fancy pattern thing



      I disagree here. The code is just as coupled as in the singleton pattern above. I'm not convinced it gives you better flexibility or portability, and I think that having explicit dependencies in the constructor is a con, not a pro.

      If you found this pattern somewhere, I think it's being misrepresented. If you have a single, global object that represents an interface into a system, you should use the singleton pattern. Whether or not this is hidden behind an abstract interface class doesn't really matter. In fact, this is exactly how the logger works in GCC. The Logger namespace just defines the interface; the actual class itself lives in the CPP file and is completely hidden from the use. You could even write a new CPP file that implements that same interface. Doing this by passing around a Logger pointer or something would be awful. There's no advantage to this if all you're after is singleton-like behavior. In fact, there's no reason to hang onto a singleton interface at all.

      Now, if you have multiple implementations of an interface, that's a reason to pass around the interface. For example, say I had several loggers and I wanted different systems to handle logging in different ways. Maybe everything logs to the console by default, but I can "promote" a system at run-time to use a different log. That's when I would have the major systems each hold onto a pointer to an abstract Logger interface. But then, by definition, it is no longer a singleton.


      This is it. What do you guys think? How all those global services (i.e. logger, process manager, event manager, renderer etc etc) should be organized? What's used in the serious industry? Share your thoughts!


      Again, it totally depends on the system. To be more specific, I'd make the logger a singleton. In my game engine, the process manager is not a singleton at all. I have two process managers, one for system-level processes and another for logic-level processes. This makes things like implementing pause or tearing down a level much easier. I have a single event manager as a global variable, but my event manager is a bit more complex then the one in the book and includes a lot of template memory allocator stuff. I might move this to another place or split it up like the process manager, but I haven't needed to. My Scene class is passed around to various systems, which each hold onto a pointer to it. The reason I do this is because my original design called for multiple Scene objects depending on what I was doing (each layer would have been a different scene object). I could (and should) refactor it so that I'm using more of a singleton pattern but I haven't gotten around to it. This would be trivial to do, it's just not on the top of my priority list.

      In the industry, we use singletons when they're appropriate. For example, on The Sims, we have a module called Services that holds all of our singleton objects. When you register a service, you typically add a getter to the Services module and add it to the initialization so that your service is loaded. All services inherit from the Service class, which is an abstract interface (and the only thing the Services module sees). To use a service, I just call the appropriate getter, so calling into the AI system might look like this:

      Source Code

      1. Affordance* pBestAffordance = Services::GetAiService()->FindBestAction(pSim, pAiRequest, FULL_AI);


      The calling code is no more or less coupled than if I stored an AiService pointer, and this is much cleaner. That third parameter defines the AI strategy. Another way to implement this would be to have a different service for each strategy. In that case, it might be better to store off a pointer to the interface.

      Hope that helps.

      -Rez
    • RE: Dependency Injection in a game engine

      First, I'm not sure I agree with all of your pro's and con's here. But that aside, there isn't one single answer. It completely depends on what you're trying to achieve. You can also use a bit of a hybrid.

      Well, I'm only trying to find the truth so I may be wrong :) I'm doing a lot of googling and reading lately about this topic. And opinions vary drastically!

      How does this create coupled code? It just requires the CPP where you're using the singleton to include it. You'd have that dependency anyway. Besides, you could hide it behind an interface if you wanted to, which would allow you to easily swap out the singleton. That would allow you to have another logger, for example.

      Coupling here is in the sense of modularity. It's easier (at least theoretically) to extract a module which depends on the interface rather then on the singleton, although you will need to take the interface with you and provide an implementation. Hiding behind an interface seems like a good idea!

      I also disagree with google's notion that it's hard to do unit testing

      I don't really use unit testing, however I've read the article which shown the example where to test the module you need to set up several singleton calls beforehand and this may be hard for the new user of the system who don't really know the work-flow.

      People aren't abandoning it, it's still a very commonly used pattern.

      There are TONS of discussions about singleton vs DI on the Internet nowadays! Most of them tends to the conclusion that singleton is outdated (note: most of those talks are non game related though! that's why I needed your advice.)

      If you have a single, global object that represents an interface into a system, you should use the singleton pattern. Whether or not this is hidden behind an abstract interface class doesn't really matter. In fact, this is exactly how the logger works in GCC. The Logger namespace just defines the interface; the actual class itself lives in the CPP file and is completely hidden from the use. You could even write a new CPP file that implements that same interface.

      If I understand right this will only allow compile-time variance using preprocessor switchers. (i.e. one logger for one platform, another - for other platform, which platform is used is defined by #define tokens). I don't know if it's good way to do portability as I haven't participated in such large projects.

      Doing this by passing around a Logger pointer or something would be awful.

      This is what actually bothers me and the main disadvantage of the DI method! Passing the common interface down the class tree is an overkill for me!

      There's no advantage to this if all you're after is singleton-like behavior. In fact, there's no reason to hang onto a singleton interface at all.

      You mean that instead of using class calls we can use function inside namespaces? This actually raises another debate: when to use singletons and when to use namespaces?

      In the industry, we use singletons when they're appropriate.

      This is very important bit of the answer and I can finally calm down a bit and use it as well :)

      Can you explain your services a little bit more? As I understand you have singleton or namespace which holds all the global managers in the game. Why do we need to inherit from the base interface? To store all the items inside the map? Why can't we just forward declare the service classes and then add variable alongside with getter/setter for the certain service? This way we will avoid casting from the base interface and avoid querying the map. As I see from your example Services::GetAiService() should return casted version of the service already (and I don't think you include header of the AiService or other service to the Services header as it will create 'morass of includes' as book states somewhere).

      Thanks for the answer, you're really helping me with the problems which blows my mind for a very long time!
      Looking for a job!
      My LinkedIn Profile
    • Regarding Rez's point about having a singleton of an interface, remember that a singleton is effectively just a class wrapper around a pointer. There's nothing to stop some place in your code from calling StartLoggingToFile() and that updates the pointer to refer to a FileLogger instance instead of a ConsoleLogger instance.

      As to your point about casting the interface down, if anywhere that is using the singleton relies on casting it down, then either your interface is incomplete, or that singleton shouldn't be an interface in the first place, as the whole point of having it as an interface is that you can swap concrete classes without changing anything.

      As for pros/cons on singletons, I would imagine the reason Google says they are difficult to unit test is because they are global state. If you look at the post on Functional Programming, the article and Rez both mention how having a function that has fixed input and predictable output is much more testable. In the case of unit testing with a singleton, you need to ensure the global state is in the correct state before unit testing your method (assuming it makes use of the singleton). This doesn't mean it can't be done, it's just more work and more error prone.

      James
    • RE: Dependency Injection in a game engine

      Originally posted by devast3d
      Coupling here is in the sense of modularity. It's easier (at least theoretically) to extract a module which depends on the interface rather then on the singleton, although you will need to take the interface with you and provide an implementation. Hiding behind an interface seems like a good idea!


      I disagree with this. I think you can get the same amount of decoupling whether you use a singleton or not. For example, I have a World interface class along with three or four different world implementations. There is only ever a single world, so it's done with a singleton. To get the world, I call World::Get(), which will return a World interface pointer to the current world. Either way, I'm only including the World interface. No one knows which instance it is. This is the same amount of coupling you'd get if you passed in a World interface pointer. It's just as easy to extract in either case.


      I don't really use unit testing, however I've read the article which shown the example where to test the module you need to set up several singleton calls beforehand and this may be hard for the new user of the system who don't really know the work-flow.

      We use unit tests here, which include testing various services.


      There are TONS of discussions about singleton vs DI on the Internet nowadays! Most of them tends to the conclusion that singleton is outdated (note: most of those talks are non game related though! that's why I needed your advice.)


      I disagree that singleton is outdated. I think it can overused at times, which causes people to swing the pendulum in the other direction. Still, the old saying holds true: there are as many approaches to a problem as there are engineers.


      If I understand right this will only allow compile-time variance using preprocessor switchers. (i.e. one logger for one platform, another - for other platform, which platform is used is defined by #define tokens). I don't know if it's good way to do portability as I haven't participated in such large projects.


      This is really the only way to do portability. If you're building for another platform, you have to build a new executable anyway. The trick is to make as few changes as possible to do it. That's why you use interfaces. For example, I have a RenderSkin project that has a bunch of abstract interfaces to various graphical primitives, like textures, vertex buffers, etc. With a simple compile flag, I can swap in Direct3D or another renderer. This is what we did on Rat Race. Rather than having hundreds of #ifdef's everywhere, you just have a few.

      Doing this by passing around a Logger pointer or something would be awful.

      This is what actually bothers me and the main disadvantage of the DI method! Passing the common interface down the class tree is an overkill for me!


      You mean that instead of using class calls we can use function inside namespaces? This actually raises another debate: when to use singletons and when to use namespaces?


      What do you mean by singleton? Singleton is a concept and a pattern, but there are many implementations. One implementation is to use namespaced functions. Another is to use static classes. A third is to use an interface with a couple of static functions. The exact implementation you use really depends on your use-case and personal style. I go back and forth.


      Can you explain your services a little bit more? As I understand you have singleton or namespace which holds all the global managers in the game. Why do we need to inherit from the base interface? To store all the items inside the map? Why can't we just forward declare the service classes and then add variable alongside with getter/setter for the certain service? This way we will avoid casting from the base interface and avoid querying the map. As I see from your example Services::GetAiService() should return casted version of the service already (and I don't think you include header of the AiService or other service to the Services header as it will create 'morass of includes' as book states somewhere).

      All services inherit from Service because they're owned by the ServiceManager, which is in charge of initializing and destroying all the services. You never want to allocate anything at the global scope because it's non-deterministic when they are actually instantiated. The ServiceManager lets us control the order of creation and destruction as well as send messages to all services. Things like Setup(), Start(), Stop(), Save(), Load(), etc.


      Thanks for the answer, you're really helping me with the problems which blows my mind for a very long time!

      No worries. :)

      -Rez
    • Originally posted by rickvanprim
      As for pros/cons on singletons, I would imagine the reason Google says they are difficult to unit test is because they are global state. If you look at the post on Functional Programming, the article and Rez both mention how having a function that has fixed input and predictable output is much more testable. In the case of unit testing with a singleton, you need to ensure the global state is in the correct state before unit testing your method (assuming it makes use of the singleton). This doesn't mean it can't be done, it's just more work and more error prone.

      This is exactly what I was thinking about singletons and unit testing!


      I disagree with this. I think you can get the same amount of decoupling whether you use a singleton or not. For example, I have a World interface class along with three or four different world implementations. There is only ever a single world, so it's done with a singleton. To get the world, I call World::Get(), which will return a World interface pointer to the current world. Either way, I'm only including the World interface. No one knows which instance it is. This is the same amount of coupling you'd get if you passed in a World interface pointer. It's just as easy to extract in either case.

      Here we go! This was actually starting point of my confusion. Interfaces having static method Get() which returns singleton. (Same way as event manager is done in the book after which my googling has begun) Coming from the .net world when I saw this my mind was blown. How an interface can contain any data at all? Maybe I'm picking on terms, but in my mind interface is just a contract with no any implementation or data.

      In case when an interface contains static Get() (and maybe Set() or constructors' parameter bool setAsGlobal) method this become more or less trivial. But is this proper way to do? (I imagine some non game software engineers could disagree.)

      This is really the only way to do portability. If you're building for another platform, you have to build a new executable anyway. The trick is to make as few changes as possible to do it. That's why you use interfaces. For example, I have a RenderSkin project that has a bunch of abstract interfaces to various graphical primitives, like textures, vertex buffers, etc. With a simple compile flag, I can swap in Direct3D or another renderer. This is what we did on Rat Race. Rather than having hundreds of #ifdef's everywhere, you just have a few.

      I was thinking about this lately. Exactly in terms of rendering classes. Let's presume we have

      Source Code

      1. class ITexture { ... };

      interface which defines abstract methods. Then we can have DirectxTexture and OpenGLTexture which both derive from the ITexture interface. The obvious desire it to substitute one implementation over another using simplest way possible. The only way I came up with is to create TextureFactory and ask it to construct a texture. The factory itself is initialized with proper implementation in the engine init. Same thing goes to other classes, e.g. model, buffer...

      I can't see how we can use compile-time switchers with pure interfaces without going to some complicated patterns. (Create an interface, create three texture classes: Texture, DXTexture, OGLTexture. Inside Texture class put the member which will hold either DX or OGL implementation depending on the #defines. Then implement Texture methods via that internal member. Only use Texture class in the code. Dont use Dx or OGL classes. I think I saw this somewhere, however I don't remember the name of the pattern.)

      Maybe you have better ideas? :)



      You mean that instead of using class calls we can use function inside namespaces? This actually raises another debate: when to use singletons and when to use namespaces?

      What do you mean by singleton? Singleton is a concept and a pattern, but there are many implementations. One implementation is to use namespaced functions. Another is to use static classes. A third is to use an interface with a couple of static functions. The exact implementation you use really depends on your use-case and personal style. I go back and forth.

      Yeah, I was asking about the concept.
      - namespace methods: NameSpace::Foo()
      - static methods inside class: MyClass::Foo()
      - class methods with static instance exposed: MyClass::Instance()->Foo()
      - interface methods with static instance exposed (source of my confusion as said above): IMyClass::Instance()->VFoo()

      They way I always used in C# is third variant. Second way was not recommended as less flexible. And C# simply hasn't 1st and 4th methods. Yet again I ask for your advice: what's your experience says about when to use which?

      All services inherit from Service because they're owned by the ServiceManager, which is in charge of initializing and destroying all the services. You never want to allocate anything at the global scope because it's non-deterministic when they are actually instantiated. The ServiceManager lets us control the order of creation and destruction as well as send messages to all services. Things like Setup(), Start(), Stop(), Save(), Load(), etc.

      Aha, central service manager. Sounds interesting! If you have some hidden GCC chapter on this or source code examples I'd be very appreciated :D
      Looking for a job!
      My LinkedIn Profile
    • I also come from the .NET world and have spent 7 years working on various web applications. In that time I've completely moved away from using singleton's or service locators (except for cases where they provide performance improvements) mostly because I like classes to explicitly require their dependencies be passed through their constructors allowing me to easily get an idea of what an implementation will be doing. This article was kind of the turning point for me (and caused quite a stir with his followers)...

      blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/

      ...but is in regards to business applications. It seems though that game developers prefer to have dependencies stored in a global scope. Is the only disadvantage of DI in game development the extra time it takes to pass dependencies down the object graph? (If so, then it's like Rez said there's as many ways to do things as there are engineers. Some peeps think an empty constructors is cleaner while I personally think the constructor is nothing but an implementation detail and passing in concrete implementations of dependencies is cleaner. To each his own...) Good topic thanks guys!
      -bullgoose
    • Originally posted by devast3d
      In case when an interface contains static Get() (and maybe Set() or constructors' parameter bool setAsGlobal) method this become more or less trivial. But is this proper way to do? (I imagine some non game software engineers could disagree.)


      Yep, in C++ there is no actual interface. There's abstract base classes (any class that contains pure virtual functions) and regular classes.

      The exact implementation of a singleton really depends on the purpose. For example, this is how my World class does it:

      Source Code

      1. class World
      2. {
      3. static World* s_pSingleton;
      4. public:
      5. // singleton functions
      6. static void Set(World* pWorld);
      7. static void Destroy(void);
      8. static World* Get(void) { return s_pSingleton; }
      9. // The rest of the class is here.... it's all pure virtual functions
      10. };
      Display All


      As you can see, the World holds onto its own singleton pointer. The world is spun up from the Application::Init() function, which is responsible for initializing the entire game. That block looks like this:

      Source Code

      1. // create the world
      2. _LOG("Startup", "Initializing World");
      3. World* pWorld = CreateWorld();
      4. _CHECK(pWorld);
      5. World::Set(pWorld);
      6. int worldRenderStage = ApplicationSettings::GetValueAsInt("video", "worldRenderStage");
      7. if (!pWorld->Init(worldRenderStage, m_pScene))
      8. return false;


      The world is instantiated and then the singleton pointer is set. Note the CreateWorld() function. This is basically a factory method (or template method pattern, depending on your point of view) that creates the appropriate world object. The idea is that CreateWorld() is a virtual function on the Application class that's implemented in the game-specific subclass. The default implementation looks like this:

      Source Code

      1. World* Application::CreateWorld(void)
      2. {
      3. return new NullWorld;
      4. }


      It returns a stub world. The subclass implementation might look like this:

      Source Code

      1. World* EternalFarmApp::CreateWorld(void)
      2. {
      3. return new SimpleTileWorld;
      4. }


      This is the only place where the world implementation class is referenced. Everything else goes through the interface.

      Using this method, I can easily have different implementations of World. To change one, all I need to do is change that one line of code in the game-specific application class. I can gate it on an #ifdef if I want, or on a configuration file, or anything else. It's just that one place that needs to change. I can actually replace it if I want by calling the static Destroy() function and then recreating it (though in practice I never do that).

      To further the example, here's the next block of code from Application::Init(), which actually loads the world:

      Source Code

      1. // load a world if we have an initial one
      2. pTempString = ApplicationSettings::GetValueAsStringPtr("startup", "initialWorld");
      3. if (pTempString)
      4. m_pWorld->LoadWorld(pTempString->c_str());


      The idea of this block of code is that an initial world file can be set in the configuration file. If it is, it will be loaded automatically. Note the call to ApplicationSettings::GetValueAsStringPtr(). ApplicationSettings is another singleton. In my engine, there's the concept of a core configuration file that is guaranteed to live next to the EXE. This file defines the base engine settings for things like graphics, logging, directories, etc. Anyway, ApplicationSettings is a singleton that manages this data. Here is that singleton in it's entirety:

      Source Code

      1. #include <string>
      2. namespace ApplicationSettings
      3. {
      4. bool Init(const char* filename);
      5. void Destroy(void);
      6. void SetInitialLoggerDisplayFlags(void);
      7. int GetValueAsInt(const std::string& section, const std::string& key, const int defaultVal = 0);
      8. unsigned long GetValueAsUnsignedLong(const std::string& section, const std::string& key, const unsigned long defaultVal = 0);
      9. unsigned short GetValueAsUnsignedShort(const std::string& section, const std::string& key, const unsigned short defaultVal = 0);
      10. double GetValueAsDouble(const std::string& section, const std::string& key, const double defaultVal = 0);
      11. bool GetValueAsBool(const std::string& section, const std::string& key, const bool defaultVal = false);
      12. std::string* GetValueAsStringPtr(const std::string& section, const std::string& key, std::string* defaultVal = NULL);
      13. }
      Display All


      This is the second method of singleton implementation. It's nice and simple. The data itself is in the CPP file:

      Source Code

      1. static INIData s_settings; // this is a class I wrote for loading and processing INI files


      This is nice because it completely hides that detail. I don't even have to #include INIData.h in the header, which helps reduce header dependency. Of course, the disadvantage is that I can't subclass a namespace.

      So, to answer the question, I tend to use the first pattern when I want an interface with multiple implementations and the second pattern when I don't need multiple implementations. The Logger is a great example of the latter, while the World is a great example of the former.

      A lot of people don't like singletons. I hear a lot of common arguments. One of the most common is that it makes programs hard to test. This is completely untrue. Remember that default NullWorld implementation I return? That just has the methods stubbed out. This is no different than passing the interface in directly; you're either passing in a stub interface or using a stub implementation in your singleton. It's functionally the same.

      Another argument I get a lot is that singletons break class reuse because it makes dependencies harder to track. This may be more philosophical, but I'll tell you my counterpoints. First, the idea that classes are inherently reusable is incorrect. How often have you just copied and pasted a mid-level class without anything else and had it work? Even when you're doing dependency injection, you STILL can't easily reuse a mid- or high-level class. Low-level classes like Vec3 or Matrix are pretty easy to reuse, but even those may rely on other classes. No, we don't reuse classes. We reuse SYSTEMS. That's the key difference. The memory pool I discuss in the coding tidbits chapter is a single system, but it is multiple classes. The idea is not to reduce dependencies between classes, it's to reduce dependencies between systems.

      In recent post, I wrote about the various layers I use to architect my games. I have a utility layer for low-level stuff, a toolbox layer, an engine layer, a mix-in layer, and the game layer. Each layer has specific rules about dependencies and what it's allowed to depend on. I never, ever break these rules. Now, does my WorldFootprint (engine layer) ever actually talk to my Logic class (also engine layer)? Probably not. That's not the question I need to ask. The real question is, are they ALLOWED to talk to each other. The answer to this question is yes, because the engine can depend on itself. That means they are effectively coupled. What about my resource system? That's on the toolbox layer, so no, it's not. The toolbox layer can ONLY depend on the utility layer. It can't depend on any other project in the toolbox layer or above it. That means I could spin up a new project right now and only include my utility layer and the resource system and it would build and run without any code changes at all. THAT'S effective code reuse. I'm not going to be able to reuse WorldFootprint outside of the engine layer because it's too far in. It's a component that handles the actor's footprint in the world, so it talks to my world system, the component system, the object system, etc. And it's allowed to. If I want to make a fully reusable agnostic system, I do so and push it down to the toolbox or utility layer.

      The game layer is even less reusable. I can reuse the entire engine for every project, but the game project is rewritten every time. Oh, I copy & paste patterns, but it's all basically new.

      My point in all this is that people are adding large amounts of complexity and/or annoyance just get around the notion of "singletons are bad" and that they somehow can reuse those classes. How often have you actually done that in your life? Unless it's truly a fundamentally core class, the chances are good that you won't be reusing pieces of your engine outright. You'll likely reuse the whole thing, or modify it a bit.

      (( more in next post ))

    • I was thinking about this lately. Exactly in terms of rendering classes. Let's presume we have

      Source Code

      1. class ITexture { ... };

      interface which defines abstract methods. Then we can have DirectxTexture and OpenGLTexture which both derive from the ITexture interface. The obvious desire it to substitute one implementation over another using simplest way possible. The only way I came up with is to create TextureFactory and ask it to construct a texture. The factory itself is initialized with proper implementation in the engine init. Same thing goes to other classes, e.g. model, buffer...

      I can't see how we can use compile-time switchers with pure interfaces without going to some complicated patterns. (Create an interface, create three texture classes: Texture, DXTexture, OGLTexture. Inside Texture class put the member which will hold either DX or OGL implementation depending on the #defines. Then implement Texture methods via that internal member. Only use Texture class in the code. Dont use Dx or OGL classes. I think I saw this somewhere, however I don't remember the name of the pattern.)

      Maybe you have better ideas? :)


      I use a factory for my render skin very much like you described. Most of the graphics stuff is hidden away behind the main interface and is managed automatically, but I do have things for textures and stuff. If I were to implement an OpenGL version, I would implement each of the RenderSkin interfaces using OpenGL and conditionally compile/link in the appropriate libs. Visual Studio lets you do this pretty easily.


      Aha, central service manager. Sounds interesting! If you have some hidden GCC chapter on this or source code examples I'd be very appreciated :D


      It's worth noting that I was just pulling simple examples and showing them in C++ for simplicity. In reality, they are implemented in Python, which lets us get away with much more loose type checking. So when I call services.autonomy_service(), it returns the appropriate service. The services are in a list, but we also dynamically add attributes to the class (Python lets you change the actual class and/or functions at run-time) to make things easier. There's one place where they're all instantiated, but there's no casting. Python doesn't care much about type, so it's all duck-typing. If I get the autonomy service, I know that there's a function on it called find_best_action() which returns the interaction that Sim wants to perform. If I get a different service, I probably shouldn't call find_best_action().

      If I were implementing this in C++, I probably wouldn't use this pattern. I prefer singletons over service locators. Really, all the ServiceManager does is provides a single place for all the services to exist. "services" is little more than a namespace due to all the dynamically added stuff. If I were doing this in C++, I would use the singleton patterns described above for these types of things.

      To bullgoose's point, this is not a performance thing. I prefer singletons in cases where they are applicable. I don't like storing random pointers on classes that don't own those pointers if I can avoid it. What happens if I store the World pointer on every system that needed access to the world and then I decided to destroy the world and rebuild it? I'd probably have to write an event or something so that those systems could be updated. This is error prone and adds even more engineering overhead. Now in addition to passing the pointer to constructor, customers of this system need to know that they have to register for an event and how to handle it. That's a pain in the ass for little-to-no gain. In a singleton, there is one owner and it's very obvious. The alternative is to use a shared_ptr and give out weak_ptr's to everyone except the owner, which is exactly what I did for actors and components. But again, I see no real gain here.

      Still, to each their own. :)

      -Rez
    • I'll probably have to re-read those posts a couple more times to get everything, but 2 points you made that I really like are...


      No, we don't reuse classes. We reuse SYSTEMS. That's the key difference.


      ...and I especially love this one...


      I don't like storing random pointers on classes that don't own those pointers if I can avoid it. What happens if I store the World pointer on every system that needed access to the world and then I decided to destroy the world and rebuild it? I'd probably have to write an event or something so that those systems could be updated. This is error prone and adds even more engineering overhead.


      ...because I think that's the key difference between games and business / web applications. In a web app each class might hold a pointer to the same dependency in most cases, but these objects only exist over the lifetime of a single HTTP request (for the most part) and are never swapped out or explicitly destroyed (for the most part). Games on the other hand continually run and I could see the scenario you describe happening quite a bit. I'm really glad this came up as this gives me a lot to think about...

      Thanks
      -bullgoose

      The post was edited 1 time, last by bullgoose311 ().