Jump to content
  • Advertisement
Sign in to follow this  
boogyman19946

Unity Singletons and Game Dev

This topic is 987 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've heard a long while ago that some interviewers would actually ask their candidates to list a design pattern that's not a Singleton, implying of course that, out of all the design patterns that there are, a person knowing only about the singleton must have no idea about good coding processes. As tongue-in-cheek as that is, I personally may have bought into this mantra myself. I guess the logic is that: if globals are evil and singletons are globally accessible, then sure singletons must be evil. 

 

Of course, there are genuinely good reasons not to declare global state carelessly:

 

+ They introduce a degree of unpredictability in the code, not knowing when and where the state mutates, making debugging ridiculous.

+ It tends to make code tightly coupled.

+ Non-trivial dependencies become less obvious as globals are almost never included as function arguments to anything.

 

Plenty of spaghetti can be had when globals roam rampant through the codebase. I've worked on applications like that before (mostly in Javascript), where data structures just magically get populated with data between function calls. If I can avoid making needless globals, I do, but then I understand some uses like maybe when you need a logger. Or maybe you have an Android app and you'd like a solitary socket to allow all your Activities to communicate with a server somewhere without having to jump hoops and opening multiple sockets. 

 

However, I've looked at some game libraries and engines (most prominently libGDX and Unity) and I've noticed they have no shame with exposing a lot of data structures globally. For instance, Unity's infrastructure allows me to control pretty much all aspects of the game from any script I want. I can find any object I want, modify any of its components, I have access to all the input state, etc, etc. libGDX is very similar. I can access any of the modules from wherever I want.

 

Somehow, I don't mind any of that, and I don't feel like it's making my code any more complicated. Quite the contrary. How would my code look if I had to pass all that state around through function arguments and the like? Wouldn't that make it needlessly verbose?

 

Sure, my code is tightly coupled to the engine, but if I'm really vehement about that, I can just write wrappers to interface with the third party stuff and keep that separate from my own code. It's not the end of the world yet. Unity also provides tools for making dependencies obvious, even if they aren't.

 

I don't know, maybe it's a special circumstance. I've had this talk in a class named "Comparative Programming Languages" [sic], which really should have been called "Comparing Programming Languages", and I've laid down my own arguments for thinking that global state causes more harm than good and is considered bad practice. Do you guys think games might be an exception? (I somehow feel like this topic might attract a flaming war so... pretty please no flaming? Thanks! )

Share this post


Link to post
Share on other sites
Advertisement
I don't believe it's tied to the type of program. Instead, I think it's tied to the context you work in.

It makes perfect sense to me to have a global for everything that you have exactly one time, like "world.map" or "player" (in single player games). This also holds for eg control programs. Hardware ports are unique by definition, and have literal some fixed address space in the memory map.

In some contexts (or programming languages), it is however highly discouraged to have globals. An Eclipse plugin should be useable more than once at the same time. Some piece of code may get distributed over different cores, etc. In such a case, a global is too limited. As a result, we make our 'global' by "ds = new DataStore();", and drag that around everywhere (or make it a member in all relevant object instances.
It's just a global though in all aspects as far imho, except it doesn't have a hard-coded address.


The big issue with globals (in whatever form, as far as I am concerned) I think, is not it being global in itself, but the fact that people will just stuff everything in it (it's there, oh this can easily be added), access it whenever they feel like it, and don't watch out when doing concurrent access.

You need some sort of agreement how the global data may be used, add some structure to its access.

Share this post


Link to post
Share on other sites

I do code in Java so I'm not entirely sure if my experience will be helpful to you.

 

You have to ask yourself, how much time do you spend actually writing new code vs refactoring it, and you may be surprised by just how much refactoring is done.  Now if you did not define data ownership, very clean and simple interfaces to your module, you will have a time consuming job to fix all the stuff you will break with renaming just a simple global variable.

If you code alone, then it is no problem at all to share all global data, you can ever put all your code into a single method :D

But if you work with a larger team, some team member may for a specific module's task, that has no dependency on the module this global is residing in, modify it and introduce very subtle bugs that you will spend days looking for.

 

It really depends on the context of what you are doing, if you are working on some core aspect of some service or application, and you are confident that the scope of the feature you are working on is fairly controllable, then by all means, share (non security relevant) global data, don't implement singletons and hard couple it.

As long as you put that functionality behind a clean and safe interface for other modules to use.

 

In my experience this is really an code architectural question on a grater scale that just the module you are working on. Define context of the module, context of the module's data (!!!!), define data immutability (!!!!!), scope of the module, define requirements (security, performance, exposure to other systems, how"core-y: it is, etc) and from that decide if it is necessary to provide a clean module API that hides all the nasty BL behind it (added value is, you can kill API implementation at any time without breaking all the other modules!), or it is sufficient to share global data and tightly couple with other systems. 

Usually additional few hours for a good API design will pay off in the future. 

 

Just my 2 cents :D

Share this post


Link to post
Share on other sites

My thoughts are that in some languages like c++, singletons can be abused because they're just forcing data to be object oriented that doesn't need to be. Most of the time where you might use a singleton in C++ a namespace with a few globals in it would do the job.

 

Globals themselves are a design decision. Personally I avoid them for the reasons you stated on your original post but they do have their use. If you have global program state, you should have a global to store it somehow. The problem is protecting the access to that global so that changes are marshalled thereby making it clear in code what is changing it and referencing it and when it's doing it. Personally I'd make it fully contained within a single file called state.cpp or world.cpp or something and only allow changes via some clearly named functions. Yeah, I said functions. You can put them in your namespaces but there's really no need to use a class for it. It's not like java where everything must be a class...

Share this post


Link to post
Share on other sites

Globals are not neccassarily evil and Singletons are not neccessarily evil either.  They are tools.  You usually use the right tool to get the job done but, sometimes you may be forced to adapt the wrong tool.

Share this post


Link to post
Share on other sites

in the end it's just a way of dodging responsibility for writing shitty code

 

This.

 

I still use singletons/globals from time to time. But invariably, whenever a project gets bigger, I end up running into a wall and have to rip all that out. Then I need to spend time thinking about how to pass state/information explicitly to the parts of code that need it. An invariably, when I do that, the code becomes cleaner, easier to maintain (if perhaps a little more verbose), more testable, more reusable, and intent becomes clearer. And that's a good thing.

 

This has happened so often in the past, that whenever I find myself adding global state, I take a step back and do things the right way.

Edited by phil_t

Share this post


Link to post
Share on other sites

I've considered the multi-threading argument. I agree that having no global-state can make it easier to work with, which alone is worth the effort to eliminate it; however, can't actual, Design Pattern-style singletons implement synchronization correctly?

 

As far as I know, and I'm by no means a Design Pattern guru, the Singleton pattern just provides a single instance of an object and global access to it, but the typical way of implementing it with the static getInstance() method pretty much prevents anyone from modifying the variable referencing the global object. Since we're dealing with an object reference, can't we encapsulate synchronization methods in the object's implementation, such as for instance using Java's "synchronized" modifiers on non-static methods? Also, if we decide to implement that same object as something other than a Singleton and pass it around to different threads, are we not facing the same synchronization issues?

 

 

However, I've looked at some game libraries and engines (most prominently libGDX and Unity) and I've noticed they have no shame with exposing a lot of data structures globally.

libGDX? You're using libGDX as a reference of some sort?

 

Take a look:

 

https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/math/Matrix4.java#L73

 

Thats exactly what it looks like. A static float[16], whats its used for? Oh nothing too important, just holding effin intermediary results in math functions.

 

What does that means? That you can't possibly  invert a matrix on two separate threads because it will fuck something up. Read that again: You can't use libGDX's math functions on more than one thread. And you know what? Its not the only static used that way in libGDX.

 

I'll just leave you with that bit of information.

 

EDIT: More on topic -> Eff singletons. All of them. I'd simply copy-paste swiftwcoder's words here.

 

 

Ok, how is that not flagged as a bug? That's total bulls***. Is there a reason not to use the stack? I don't think "performance" is even applicable to this situation.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!