Search Unity

Starting a discussion on architecture.

Discussion in 'Scripting' started by AlanGameDev, Oct 15, 2015.

  1. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Hello there.

    I've made LOTS of stuff in Unity, something that is always a question before starting a project, is what architecture I'm going to adopt.

    Generally, I end up deciding right away by the complexity of what I'm going to do, for example, if it's going to be a 'standard' game, with few procedural stuff an not much logic, then I use the architecture that Unity itself lays out for us, with various Components for all the logic (I like the single responsibility approach, so for damage I have a component that 'receives' the damage, then calls a 'health manager' component (cached ref), and the 'health manager' component have a cached list of 'health change receiver' components that are sub-classed to have different behaviours (e.g.: 'take hit', 'die').

    The example above is a very good way to put a game together and is the intended way (I think) to work with Unity. It makes gamedev much easier and modular. It allows you to drag and drop for the win once you have all the components and assets. I see that generally others tend to have less scripts with more functionality. I don't like that. There is code around that use its own 'components' that are sub-components of the Unity's components. That quickly becomes messy and in terms of performance is not very worthy. Of course you could have a POCO for some kind of data, say, 'attacks', and have a single MonoBehaviour that have a list of various kinds of attacks, but that's about it.

    I'm not saying however that the performance of having GameObjects and various Components (or script components/monobehaviours in this context) is good. I would say it's enough for most games, and of course I know that Unity is a high-performance C++ engine and stuff, and that's why I think we should always take advantage of the tools it offers us - like the component system - to do stuff instead of rolling our own solutions which generally doesn't make a difference and sometimes performs even worse because C# in Unity isn't really fast - Mono is much slower than MS's CLR, and the Mono in Unity is very old what means it's much much slower than the current Mono which MS's CLR blows outta the water, so IMHO, sticking with the "Unity way" of doing things is the best approach for most games.

    A simplification of how things are done (just to be clear):
    • Standard GameObjects for most objects
    • Monobehaviours (aka script components) for most logic
    • For objects interaction, you use the 'physics' engine, even if only for colliders/triggers (no sim)
    • To access components on other objects you use GetComponent and variations
    This is a very decent approach and the productivity is incredibly high, in the past there was some myths, specially on IRC, that things like enabling scripts weren't performant, but from my own tests I've concluded that anything concerning the default component-based architecture is performant enough.

    So far so good.

    The problems emerge when you need something that that↑ architecture can't provide. Recently I've been playing with voxels and structural analysis (SA); in noobies terms: realtime physics for minecraft buildings/terrain, and obviously you can't use that architecture for something as massive as voxels. In fact it's bad even to use POCOs because 1.they are allocated in the heap 2.refs aren't lightweight (8 bytes on 64 bit) and 3.don't take advantage of processor cache because heap is not allocated in sequence. So, to tell the truth a clever architecture that uses Structs would be the best way to go imho. But anyway, I went with POCOs for the voxels data, and then you have to create a 'viewer' that will retrieve the data and generate the meshes in Unity, preferably in chunks.

    I've manage to obtain something usable, the SA is done on a separate thread, I'm taking advantage of atomic access not to lock the variables (I know locking do more than that, but in this case the possible problems aren't critical and the nanosecs multiplied by some million voxels are important), and the 'viewer' is smart enough to sort the chunks from near to far, and to skip generation if the deltatime is too high (0.02s in this case). The end result for the terrain itself was impressive, here are some screens so you know what I'm talking about:

    forces.png
    forces2.png
    chunk.png
    ↑ Benchmarking showed that 8vox³ chunks are the best for an average player speed.

    The numbers are inverted on some sides because the mesh is sharing the vertexes (8 vertex for a cube without neighbors), so UV mapping couldn't be correct, that won't make a difference in the end because the textures aren't going to have numbers/letters, and smoothing is going to be dealt with material.

    Basically, that approach can't take much advantage of the Unity's "intended" architecture, and you end up going with a pure OOP approach which, even when preferring composing over inheritance, ends up very different from the way the game engine actually works. Basically you're using two different paradigms and trying to make them work together.

    I've had no actual problem with that, since I've made a 'voxels manager' component that does all the abstraction and for the rest of the stuff I'm using the good ole Unity way of doing things, but while I didn't have any technical problem, it's a little bit of a pain to work like that.

    It ended up becoming so cumbersome that, I ended up going 2D, so I ported the SA and here's how it looks:
    forces2d.png

    It turned out that the 'view area', which is not only the 'visible area' by the camera, but the area where all stuff happens is quite good with 50x50 voxels, totalling in the worst case 2499 voxels on the 'interactive' area, and I found out that you can use standard GameObjects for that (I'm not targetting mobiles), and the thing still runs @ 1000 fps on my computer which is quite old (g3220, gtx550ti), and that is instantiating the objects! (no pooling). To make things even more impressive, there are blocks that are updated continuously, they represent mostly 'machines' in the game lore, and the world size is up to 8000x1000 voxels, and I managed to generate 4 million of those machines, and they update very quickly (on a separate thread), and the visible voxels, which are GameObjects they reflect the updates in real time. I'm not talking numbers here, but the performance was really impressive, and each visible voxel just reads the data from the array on Update(), what's the way Unity generally does stuff.

    The development was much more straightforward and I could take full advantage of Unity architecture. The difference was huge; I'm even using standard box2D colliders for the blocks. So far I'm extremely happy with the results.

    I would like to know what approach others are using (doesn't matter the complexity of the game), and if they are happy with it, what caveats they found, what advantages over other approach, and I would like to start an overall discussion on architecture since It's hard to find anything sane on the subject.

    Please let's not turn this thread into a freak show, noobies are welcome, trolls are not. If you've nothing valid to add please abstain from posting.
     
    manpower13 likes this.
  2. manpower13

    manpower13

    Joined:
    Dec 22, 2013
    Posts:
    140
    Hi there,

    Nice information you got there. Nice idea of a discussion.
    Most of the times I am using managers. A manager has arrays of components. Each component has his own data and his own function. The manager keeps control of it.
    The manager is only one script par subject. For example a music manager has an array of music components. The manager controls those music components.
    Sometimes one of those managers can grow pretty big. In my current game I have a ui manager. It has references to different ui objects and components. It does not look very nice in the editor anymore and I think I should seperate the big manager in smaller managers.

    That is my way of dealing with the components. It works pretty good as long as you keep attention to the size of a manager.

    (Hope this is what you consider a valid post ;))

    ~Floris Weers
     
    AlanGameDev likes this.
  3. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Thank you! Of course your post is valid! That was mostly for trolls or troublemakers.

    I had the idea of starting a thread like this not only because it's something that is always a consideration in my projects, but also because there's not much about it on the web and I see there's no consensus. I've seen a lot of code from different people and sometimes the approach is completely different.

    I've used a lot of 'managers' in the past, including static "game managers", the latter one is not a good idea most of the time, but depending on the game, I think you can find a good compromise with decent modularity. The term 'manager' is very vague though, depending on who you ask every script could be considered one, but I got the idea. For managing musics, of course a music manager is the way to go :). On a related question, what would you do if you had to access that 'music manager' from almost all GameObjects (if it were part of the game mechanics for example)? Would you use a static reference?
     
    manpower13 likes this.
  4. manpower13

    manpower13

    Joined:
    Dec 22, 2013
    Posts:
    140
    At the moment I am using static references for that, something like singletons. I have most managers on the same gameobject in the scene. This way I know there will never be a second instance so I do not have to worry about that. If I have a MessageManager for example, that shows messages on the screen, and I have a object that needs to show a message I would do something like this : MessageManager.Instance.Showmessage()
    While if I have a script that needs to call this showmessage function multiple times in a short period I would cache the MessageManager first. Everybody always said it was faster :p.

    Sometimes I do wonder whether to use the static reference or getcomponent in the following case: if a MusicManager wants to get data from a UIManager, and both components are on the same object. I could simply use the static reference, or I could get the component and save that reference.

    I guess using the static way things would be clearer.
     
  5. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Personally, I'm not a fan of Singletons like the ones you're probably used to. When I have to use statics I make a 'statics manager' that is a static class itself and makes sure to manages duplicated stuff and holds references. I find it easier to maintain and debug (which is hard).

    Regarding caching; caching is faster than using GetComponent for each call, but certainly caching a static in a local reference is not going to do any good. Statics are extremely fast. In fact, consts are static, and in managed environments, consts/statics are extremely performant and they have a 'static' memory address.

    In the case of the UIManager, in this case you'll probably want to cache a reference in the MusicManager. Unless you use the UIManager from a lot of different scripts, in this case a static/singleton pattern could be an option.
     
    Last edited: Oct 15, 2015
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I tend to think in terms of systems. It's carry over from my process engineering training, we like to think in nice neat unit operations. Data flows through systems, in a fashion not dissimilar to chemicals in pipes.

    A system can be defined as anything you can draw a box around and clearly define all the inputs and outputs. Systems can be of any size. But in practical terms the universe is too big for everyone except physicists. And a top quark is too small.
     
  7. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Thank you. The problem is that in Unity you're somewhat limited to 'plug-ins', since you don't have access to the entry point nor the classes that actually 'manages' the engine, so it's not like in a framework in which you have more liberty to make 'supervars' and stuff like that, that's why singletons are so much needed in Unity, although as mentioned above, I really don't like singletons and I use an approach that is more like a monostate. Basically a class to manage and hold static references, this way I can just call some debug helper from each getter/setter and be happy :p. I'm very used to this approach and I consider it superior to the singletons as people are used to (holding a static reference themselves).

    To be honest, the way to 'share' data across components is always something complex, I'd say that's the biggest problem Unity has, and specially when I was a noob I was completely lost. It's funny how the tutorials on accessing other stuff are so shallow. For example, let's say every bad guy needs to check which good guy is closer to them, there are many ways to achieve that, the most OOP-friendly I think would be to keep a local reference to a list of good guys, but what if the bad guys are spawning very often? Getting that reference may cost some valuable time, in this case a static is an option imho, as long as you manage it properly. Of course this example isn't very good because probably it's premature optimization, but it's just an example on how things could be confusing. I don't get surprised at all when noobies show a slow code that uses GameObject.Find() each frame, because I expect most noobies to get completely lost when it comes to accessing data from other components... or when someone asks why when you kill one baddie all of them die and then in their code they have a static for health or something like that.

    What I said above happens a lot also because generally OOP devs they frown upon any kind of 'global' that is not a constant like if it was going to open the gates of hell. In Unity you can't do much without some kind of global, be it a singleton or other static pattern.

    To some degree, in terms of strict OOP good practices, Unity does encourages some bad ones, as using Monobehaviours for everything, but so far it's been working wonders, so the C# purists shouldn't be too harsh on Unity programming. Some things that are considered bad practices they work perfectly if you know what you're doing, and sometimes you don't have a good alternative.
     
    Last edited: Oct 16, 2015
  8. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Sure you do. The Unity entry points are well defined. They are Awake, Start and OnEnable. Then there are defined call in points for every frame, object destruction, physics tick and pretty much everything else you can think of.

    Global state is a bad thing more often then not. Singletons lead to all sorts of problems down the track. Just because we are using Unity doesn't make the code magically immune. I do have some singletons, for things like game state. But even then its not strictly needed.

    Typically the systems I deal in are pretty small sets of three or four close coupled components. I'll wrap them up in there own namespace to prompt me when I'm stepping out of the system. The links in and out of the system are normally all managed through a couple of public methods.

    I also try and keep my dependencies one way. A high level system can know about a low level one. But not the other way around.

    There are a ton of effective ways to communicate between components. Here are some of them.

     
  9. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Sorry bad quoting, I'm on a phone.

    I was talking about something on a larger scale, so I could for example pass something in awake/start. Since you don't have such a possibility, most of the time you have to somehow "find" the stuff you need in these functions.

    Sure there are many good ways to communicate between components, especially when components are interacting directly with others that are on the same object or object hierarchy you want, the classical example is ray casting, getting the game object from the collider, and using get component and similars. However, I'm talking about non-obvious functionality, for example, how would you do the example of the bad guys having to get the nearest good guy? A static getnearestgoodguy(point) doesn't look a terrible idea for me. And you could "register" the references to the good guys on their start functions with another static call, and unregister on destroy/death/when disabling the alive-behaviour component.

    Unless I'm missing something terrible.
     
  10. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Nothing terrible missed. For that particular use case a static helper method is one way to approach it. But it doesn't have to be static.
     
    AlanGameDev likes this.
  11. manpower13

    manpower13

    Joined:
    Dec 22, 2013
    Posts:
    140
    Well that is an approach I have not yet heard. Sounds pretty solid!
    That 'statics' manager does it work like this:
    Static manager has public variables and in awake all those variables are assigned to the static variable. Every script that needs one of the components does not get the component itself directly, but uses the static variable in the static manager.
    Sounds good, might be something to use in my next game ;)
     
  12. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Note: That last terrible was supposed to be obvious :p... damn phones.
    Yeah, personally I wouldn't use something like that, I like my stuff more 'generic', with more separation of concerns, but that's not unacceptable imho, specially if the game is small and there are no other lists to manage.
    You highlighted a good point, that's exactly what I'm trying to discuss, I'm assuming that a noob will go with the static "GetNearestGoodGuy" function solution, but then they see that they shouldn't use statics for anything, but what's the alternative in this case? It isn't a simple solution to avoid statics in this case, and I'm sure that's why we see so much bad usage of GameObject.Find, because it seems to me that the obvious solution for the newcomers (to scripting also) would be to just make a 'manager' gameobject, attach a 'goodguysmanager' component to it, and then use Find("mymanager")/FindObjectsOfType, what's not a terrible solution per se, since it could be done only once in the Start() of the enemy and cached, but some/most noobies they tend to do that in Update() or every time they want to call a function, I don't know why. Even if it was cached, in terms of performance it's not a very good alternative to the static, specially if there are lots of objects in scene. There are better ways to find a specific gameobject.
    Google for 'Monostate'. Just don't mix it with singletons, use it to manage all your statics.
     
  13. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    It's funny, I ended up recently using something very similar to a monostate, and I think the idea works quite well. I was all proud of myself for thinking of it since I had never seen the technique used before. I've been very pleased when using them, they're easy to update, they grant you control, they feel good.

    It's like all of the up sides to a singleton without that dirty, shameful feeling. It's also very easy to change, and if you need to dig in deep - it offers enough indirection to easily replace stuff with totally different backing data.
     
  14. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Just IMO, Unity does tend to lean us into the composite design pattern, and the singleton design pattern. I've googled and youtube'd design patterns for Unity for about 20-30 hours and it's only come up with a few different alternatives. Generally I've found either people running with UUnity's component design pattern, or trying to run against it. Making things like IoC Containers (which had their own set of problems), service locator (anti-)patterns, and some just trying to make heads or tails of Unity's component design (say hello, singletons).

    From what I've seen, some of the experienced programmers on here tend to stay with Unity's design pattern for the most part (at least from what I see of the forums). I've noticed a lot take on the composite design pattern. Making a small number of mini systems, which interlink, and interact as a single entities.

    So really Unity doesn't tend to cater to a purist C# programmers perspective in some senses, however their inherent design pattern is a standard one. For me atleast coming from Unreal, where there's about 20 abstract classes to sift through before reaching the appropriate class to use, it's been a real mind bender getting used to the Unity way. Though now I understand it a little, it makes for a brilliant whiteboard to start with a fresh sheet.

    Like BoredMormon says, there's Start and Awake, there's also OnEnable, which calls several times throughout Awake and Start. The only issue is there's no pre gameObject script, and doesn't go out of their way to show the execution order of the GameObjects in the scene. Unity tends to have that hidden.

    There's ways around that, like lazy scripting, and having a setup scene, with objects to start the game before the actual game scene is loaded (using Object.DontDestroyOnLoad to keep the ones from the first scene). There's another thing I'm actually interested in to find a single entry point into the game: MonoManager. It says you can change the execution order of script. So I'm assuming if you set ONE script to be -9999 and the others at default. The awake of THAT gameObject would effectively count as an entry point? Not into the application itself, but atleast into the scene.
     
  15. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    I find software architecture quite fascinating because of the inherent conflict in creating (programming) real things (conceptual objects) with artificial things (code). I will always remember an old prof of mine who described a new programmers dilemma like this: "Programmers new to OO think programing is supposed to mimic reality, but it doesn't!"

    I'm an enterprise software developer and I have zero experience working with Unity. I occasionally hang around this forum, primarily because I find architecture discussions regarding games even more interesting than regular architecture discussions. I think the reason is primarily because creating a game is essentially "mimicking reality", even if the code doesn't.

    So here's what I started thinking about when I read your question about finding players.

    Why can't an enemy who is looking for the nearest player act more like an agent in real life? To start simply, they could make use of "sight" (ray cast a few times up to a maximum range), and "hearing" (sound effects could trigger an action on all GameObjects within a certain radius). Along the same lines, you could introduce the other senses or tools such as a radio. Enemy's could be instantiated with a "radio" object which occasionally gives updates when another agent detects the player. There is no reason the enemy's actually need to have eyes, ears or things that look like radios on their avatar. These are simply code objects which behave similar to real life objects. Nor is there any reason for the agent who receives stimulus to act or think on the data -- It's simply available if necessary.

    This allows the code to work together to solve problems without any "manager", which is essentially cheating anyways: knowing exactly where the player is through some kind of glitch in the Matrix. It also avoids the topic of singletons all together.

    In anticipation of the trolling, let me say this: ultimately, I think architecture is a balancing act between complexity and flexibility. I like to take part in discussions like these because it help me understand how different patterns and practices affect that balance in the short and long term.
     
    Kiwasi likes this.
  16. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    I think Beliar's comment is about managing the underlying engines such as controlling the physics of the world in a more hands-on way. The scripts we write in C# are essentially "interpreted" and running in a world controlled by a black box C engine.

    Think about the Voxel example. There isn't an easy way to override the terrain engine to create a destructible world. In order to achieve this, he needed to manage the terrain in C#, create objects to represent the terrain and pass those objects into the black box. This is the kind of work you would like to do in a more memory and CPU efficient way, skipping the instantiating and passing phase altogether. Another balancing act -- this is the price of abstraction.
     
    Kiwasi likes this.
  17. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Thanks. Yeah, I've been playing with UE4 recently and I must say that it's surely not as 'simple' as Unity, I mean, in Unity once you know a few new concepts and how generally things works, specially the component based architecture (and realize that GameObjects are just components holders (even Transforms)), then it gets really simple and straightforward. On the other hand however, UE4 takes advantage of specialized types for a performance improvement, in Unity there's only one kind of GameObject for everything, and they aren't very fast. I'm not saying they're slow or something, I'm perfectly OK with GameObjects and perhaps they're the best approach, but surely there's a reason why UE4 have specialized ones, the only explanation I could think is because of performance, so if you need to have an object that has a mesh in 3d space but you don't need anything else, you can just stick with a 'lesser' version instead of a fully featured GameObject. Please note that I'm just pointing that difference, at the moment I don't have a real opinion on if the extra complexity is worth.

    That 'entry point' thing was really badly worded...

    Thank you. Very good points you have there; regarding the part of "mimicking reality" - have you ever tried Python? :p. It's a 'limitless' language, it has multiple inheritance and all the other stuff (no trolling intended hehe), you don't have to deal with delegates for example, you can just pass a function without calling it to some_variable and then do some_variable(lots of parameters), the community is extremely educated and helpful, and the libraries (and there are LOTS) are mostly high-quality, even the ones you've never heard about.

    I've more than 60k lines of procedural generation (tile-based maps) in Python and everything was done by 'simulating' agents (mimicking real life), for example, I start with a barren terrain, then water starts to spring from some points, creating rivers and sometimes canyons (cell-based automation), then vegetation grows where the land is fertile, and then people start to appear, they have needs and start to create houses and stuff, so far I've absolutely no problem reproducing real-life needs and even learning.

    The only real problem of Python I've found so far is the performance. It really sucks. CPython sucks monkey balls, there are other things like PyPy out there that are impressively fast but hogs the memory, compilers like nuitka/shedskin(dead?) aren't very usable so far, and the only good way I found to speedup code considerably is converting the important parts to Cython which is extremely fast... you don't even have to change the code, pure python will work perfectly, but once you declare some c-types, I don't know if it's because it relieves the work from the CPython GC (which i assume is slow), or if it has some damn clever optimizations, but the code runs like Java, and the best part is it's native c-compiled code. Enough of off-topic! :p

    As I said, when the components are 'interacting' with other components, on the same GameObject or not, things are easy and there's no much room for multiple ways of getting references and communicating with the other components, the problem is when they aren't. The obvious way to keep track of a group of GameObjects in Unity is using Tags, and a decent solution to deal with the 'get nearest good guy' problem is to attribute a 'good guys' tag to them, otherwise you have to keep a list yourself, because you surely don't want to loop all GameObjects in scene. I assume the built-in "Tags" system it exists on a higher level than what we have access to, and that is (probably/imho) the proper way to do that if you're making a game from scratch. On a related discussion however, Unity doesn't provide for example a "score" functionality, and since you don't have access to that "higher-level" layer, statics aren't a terribly bad way to achieve that kind of functionality.

    If you were doing a game from scratch, you could have a 'score' variable that is a member of your "Main" class, and that class would then manage the game objects, tick them and stuff, in Unity you don't have access to this "Main" layer, so having a static could be a way to emulate that. That's what I was trying to say with 'entry point'. I was talking about all the logic ranging from the 'main program entry point' to the 'scripting layer'. You don't have access to all of that; please note that I'm not saying you SHOULD have, no sir, but I'm saying that some things would be obvious if you had that access, but since you don't you have to find a way to achieve that functionality using solely the 'last logic layer', that is, the 'scripting', and statics are understandable (i'm not saying justifiable) considering all of that.

    Also, we should keep in mind that the evil is in the globals. Single-instances are very common in software hehe.
     
    Last edited: Oct 16, 2015
  18. RocheB

    RocheB

    Joined:
    Oct 17, 2015
    Posts:
    1
    @Beliar: You can talk the talk but you're very wrong in some things:

    1st: 8voxels^3 is 2x2x2voxels
    2nd: Tags in Unity are a distributed subsystem. Getting by tag is slow.
    3rd: Python is not suitable for much besides web.
    4th: You need more than 2 screenshots and a few confusing words to convince me.
    5th: There's actually no good way to 'couple', be it tightly or loosely, components in different GameObjects. You are at mercy of what Unity exposes you, what is very limited, GetComponent, Find, etc..
    6th: The "evil" is in the code you don't understand. Do you understand your code?
    7th: In UE4, pawns are GameObjects. There is nothing different than that. There's no "lightweight pawn".
    8th: It seems you've no idea what an "entry point" means.

    /thread.
     
  19. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    I think I know who you are... I'm gonna ask the moderators if I can have your IP!

    You talk the talk; do you walk the walk? :D

    Ok, here we go:

    1- I'm talking about (8vox)^3 and not 8(vox^3). That was quite obvious so I didn't put the braces.

    2- I thought Unity had an actual tag system and managed lists of gamebjects for each tag, so basically FindGameObjectsWithTag would simply retrieve one of these lists maintained by Unity, if that's not the case, does Unity loop all GameObjects and compare their tags?

    3- That's not true, confirmed by a 2 seconds Google search, and/or not living in a cave in the past 10 years.

    4- see below

    5- That's part of what I'm talking about, although 'good' is very subjective and imho those functions you mentioned (and variations) are good for 95% of the cases.

    6- see below

    7- I've only player a little bit with UE4, but from what I remember there are different object types.

    4,6 and 8: Look, I'm not trying to convince anyone of anything, I'm not even an expert or something; I consider myself in an intermediate level. If that part about understanding is about statics, then my personal rule of thumb is: If you're using statics because you need, then OK, if you use them 'because of lazy', then you're doing it wrong.
    Also, the 'entry point' was badly worded, but 'entry point' is too vague, my error was in precision, not correctness.

    I'm sorry if I'm giving an arrogant impression to you.
     
    manpower13 likes this.
  20. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    I have done a bit of work with Python and, you're right, it's quite powerful. Actually, in many ways, it is too powerful. There is another trade off to be considered when choosing between static and dynamic languages. Static languages benefit from some very powerful analysis tools, which can catch some nasty, albeit usually simple, bugs.

    As for Python being suitable only for web.. That is a very strange comment for someone who wants to be taken seriously.

    Another cool language is Smalltalk. It is similar to python (or should I say Python is similar to Smalltalk) in that everything is an object and everything is mutable. Except Smalltalk goes one step further to say that everything is also a message. There are almost no language keywords -- everything is described by the framework. Meta.

    Unfortunately, Smalltalk didn't get the success it deserved because it got drowned out by Java. There are still some shops creating fully featured desktop programs running primarily on Smalltalk though. I happen to work at one :)
     
    Kiwasi likes this.
  21. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Yeah, Python is awesome, the power of having objects for everything and a fully dynamic language is invaluable. You can for example not only fully compose classes at runtime, but also inherit them, creating new types that exist only at runtime. Also, it's a true multi-paradigm language, what I really appreciate, and of course it's also easy to write asynchronous code.

    Sure there are some considerable drawbacks. I too appreciate the 'analysis' tools in 'static' language, the most basic being the compiler itself, which will complain about errors at compile-time, while most dynamic langs will simply fail at run-time, possibly in the hands of the end user. Once you're very used to dynamic languages, and of course if you also use some code analysis tools to help you, and do some extensive unit testing, there's not much of a difference. I try to be more careful with dynamic languages, for example using hasattr extensively, and other 'reflection' features. That is specially important because I do abuse the power of dynamics, but sure there's a reason why dynamite starts with 'dynami' lol :D.

    Please don't mention Java :). I really tried and wanted to like Java, there's LibGDX which is an awesome framework, but I simply can't stand it. Java feels like a language from the 80's. Just to master the collections you need a year, and there is type erasure and that stuff, not to mention that, besides the IRC being quite lovely, generally the resources on the Web are terrible. I found lots of tutorials teaching bad practices, and some quite popular libraries are also very bad, for example, I was looking for a csv library, and after failing miserably to use the most popular one, I asked on #java and they say it was crap, I was suspecting that already, specially because it takes arrays what didn't make much sense, however, they pointed me at another library, from a project called apache commons, I couldn't understand exactly if I had to keep all the other bloat, and in the end I giving up on that altogether. That functionality is a one-liner in Python (using join, lambda, and json), and to achieve something like that in Java I was struggling.

    I'm not even going to mention how Java is full of 'exceptions to the rules' or 'special cases' a la C++, sometimes it seems they were sick of developing then they just used some quick hacks to get things working.

    I'm not saying Python doesn't have flaws, unicode for example is still a problem nowadays since lots of people are still using version 2, but even with all the problems it feels a modern language. It's crazy how it's in fact older than Java.
     
  22. RBaumann

    RBaumann

    Joined:
    Nov 2, 2015
    Posts:
    1
    AlanGameDev likes this.
  23. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    Yeah, I'm hiding here because I don't have time to support my GTAV mods :(. I haven't even had time to play the game... by the way, time sure flies, I could swear I was releasing Persistance v0.0.0.1b a few weeks back. Now I realize I haven't played GTAV for 4 months, I barely finished the main story line :(.

    I surely enjoyed programming the mods though, although I'm by no means a lua programmer and I don't like the language very much.

    I receive a lot of messages from people who bought GTAV on PC only because of the Persistance mod. In my opinion GTAV left a lot of low hanging fruits. An open-world game like GTA is surely on my list of games I want to make. I'm going to grab all the low hanging fruits though :p. Unfortunately you need too much man-hours to make a game like that, even with non-pr graphics and simpler mechanics. Unfortunately it seems GTA is all about 'Online' these days.

    I'm somewhat broke at the moment, so I don't have time for hobby projects like modding, it's surely very satisfying though. Cortex Command was another game I really enjoyed modding, many years ago.

    Big studios could pay for passionate modders like us, I mean, I wouldn't care getting paid only to cover my basic expenses, because after all I would be doing what I love, and it's surely a great addition to a portfolio/résumé, and some mods they add a considerable value to the product. It's not fair that your mod is raising their sales and you receive nothing for that. If I were rich or something, then OK, and I think that's the case for most famous modders out there. I however need to pay my bills, so it doesn't make much sense working to improve GTAV sales :p.

    Thank you for your kind words. I really appreciate it. I received another kind email from Kopalov yesterday about the port of the SH mod to C++. We modders are generally awesome ;).

    By the way, how did you find me here? :eek:

    This whole conversation is completely off-topic here though. Lemme add some on-topic words:

    Generally, tightly coupled code is faster than loosely coupled like MVC architecture or other observer patterns, there are cases when tight coupling is impracticable, but sometimes the game is simple enough and the only pros of loosely coupling are reusability and extensibility. Sometimes when you're the only programmer and don't plan to add more functionality, perhaps it could make sense to go the lazy way, but I always wondered, do you guys think about these things when working on your projects, and have performance ever be considered when taking these decisions? Personally, unless the deadline is really short and the loosely coupled system would be too complex, I always use a 'observer' pattern (although generally a very simple one), specially because of my game programming background, since it is a necessity to use an approach like that when you're building an engine. Imho, tight coupling generally isn't worthy, and one should also consider that the performance hit can be cleverly distributed so you for example get references at the start, when the game is paused, then the performance hit is either nonexistent or negligible.
     
  24. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    I always start with what I consider the best abstraction and tune for performance when and only when it's an issue, or where it's something I know will be an issue.

    Systems based on composition can actually be much better performing, but Unity's architecture kind of just embraces it in name only. For example entity component systems where components are just data and are processed by 'systems', you can store components in a way that takes advantage of cpu caching, you can more easily batch work, in general you can optimize a lot of things easier.

    My experience is that a lot of performance issues are best handled indirectly, usually by just thinking about a different way to tackle the problem. That's probably where 90% of my breakthrough's have come from.

    For example voxel worlds. How much of your world is really dynamic and needs voxels? Well why not just use voxels for those specific cases? A hybrid of sorts? Or say networking where a lot of developers might spend most of their time optimizing data structures, where the best solution is find ways to not send the data to begin with, or find another way to express the problem that results in less data (sending quaternions vs sending an angle as a single float for example).

    The problem with tackling most performance challenges head on is you quickly run into diminishing returns. You only have so much stack, cpu, etc.. Large returns require changing something about the fundamental approach. So I generally stop optimizing once I reach the point where I'm not getting significant returns, and I back off and try to look at the problem fresh, trying to make as few assumptions as I can.
     
    AlanGameDev likes this.
  25. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    I agree 100%.

    Although not directly related to networking, I always use as little data as possible by habit, for example, if an object rotates only on one axis, I always work with a single float. I have no idea if this would be considered a micro-optimization, but I'm used to it, and in fact it makes the code better in my opinion. I see that a lot of coders out there they just don't care much, what sometimes results in ugly code, for example something like a "RotateAroundWorldX" taking a Vector3.

    Regarding dynamic worlds and voxels, 7 Days to Die did something along the lines of what you're talking about I guess, 'hybrid of sorts', however, worlds can be dynamic without voxels and vice-versa. In fact there are some games out there with 'dynamic' worlds that aren't made of voxels; basically the game becomes a 'level editor' with 'rules', or game mechanics :p. I quite like games like that.

    Back when PPUs were still a thing, I used to spend hours in those 'physics sandboxes', the Ageia one was quite cool to 'play' with. I like games like Phun/Algodoo too. In my opinion, we need more of these sandboxes games, but in 3D, with objectives, and good graphics. From a technical standpoint, there's nothing keeping us from doing something like that, say, the player can cut some trees and nail the wood pieces together to create cool stuff. I mean, obviously there is a limitation with these rt physics engines like physx/newton/ode/bullet/bepu/havok, since they all work with velocities not forces, so you can't build a house for example using standard physx rigidbodies and joints, it would go wobbly and probably explode for no apparent reason :p. But these days processing power is ridiculously huge even on modest gamer PCs, and in fact there are alternative rt physics engines out there that are more suitable for that kind of simulation.
     
  26. LukePammant

    LukePammant

    Joined:
    Mar 10, 2015
    Posts:
    50
    I too have been struggling with proper architecture in my games. I have been trying my damnedest to keep everything loosely coupled but hit roadblocks constantly. For example I have a PlayerMovement component that allows a player to sprint for a certain amount of time based on "stamina". I also have a PlayerAttack script that uses "stamina". Where should "stamina" be stored? Should I create a "PlayerStats" component that fires the event "StaminaChanged" that PlayerMovement and PlayerAttack listen for? StaminaChanged could be fired every frame in an update loop from PlayerStats. Would that have a major negative performance impact?

    I've managed to get all of my component to component communication working so far using ExecuteEvents.Execute but that only handles communication to a known destination (ie the current gameObject or a colliding gameObject, etc.). I'm struggling wrapping my head around how to get the UI to know about an event such as a gun firing so that it can reduce the ammo count on the HUD. Should this be done via an Event Manager like the one by Ben D'Angelo here? This is relying on a on a singleton which is what brought me to this post.

    I'm interested in hearing some solutions for where I am stuck!
     
  27. AlanGameDev

    AlanGameDev

    Joined:
    Jun 30, 2012
    Posts:
    437
    I don't see any problem in creating an independent component to hold the 'stats' and fire events when they change, however, that doesn't seem very 'orthodox' to me. In most cases the PlayerMovement and PlayerAttack components don't need to be constantly 'informed' of the stamina level, they would simply check for it when they have to change its value. A quite decent flow in my opinion is to make a function to which you pass the amount of stamina to be used and that returns a boolean informing if the action is possible.
    Personally I won't use Unity components in this case, but I don't see anything wrong in using them. Unfortunately C# doesn't support multiple inheritance, which I'm somewhat used to because I'm a Python coder too, so it forces me to think much longer on the correct approach for each situation, be it using a more complex class hierarchy, or interfaces or most likely class composing. I'm not a fan of interfaces to be honest.
    Class composing would be adequate for this case I think. You could have a player object that holds the stats and calls the methods on the 'action' classes, from which you would subclass the attack and movement classes.
    In fact there are so many ways to do that and all of them have pros and cons. Perhaps someone with more experience could add 2 cents here.

    That's surely going to have no performance impact whatsoever. Most likely the data you're using is atomic so unless you're doing literally trillions of operations per frame the performance is totally negligible.

    For ultimate flexibility, events are surely the way to go. Personally I wouldn't use an external library since plain C# events are simple enough in my opinion, but that's a matter of taste.

    For UI, I think a 'static' pattern like Singleton is in fact decent solution. You only have one instance of it anyway, and you surely want to easily access it from anywhere in your code. It's not strictly necessary though, all you have to do in fact is get a reference to the player object, once you have that it's just a matter of monitoring its stats and updating the UI accordingly. If the player fires events when the stats are changed, perhaps you may want to use that, but you'd need some solid code to connect those events and that approach is more prone to bugs. In terms of performance it's not worthy optimizing an UI too much because the impact generally is negligible, so just accessing the stats every frame is totally acceptable, and even running the whole shebang in an ugly try block to prevent nullrefexceptions :p.

    It would surely be great to have more opinions on this subject.
     
    eisenpony likes this.
  28. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    I'm with Alan on this one. Simply create a component which handles the stamina attribute and get a reference to it form other components that need to use stamina. Using a function that returns true or false if the sender receives its required stamina is a good way to go for starters. I might call it TryUseStamina:

    Code (csharp):
    1. class Stamina : Component
    2. {
    3.   private int currentStamina = 100;
    4.  
    5.   public bool TryUseStamina(int amount)
    6.   {
    7.     if (currentStamina > amount)
    8.     {
    9.       currentStamina -= amount;
    10.       return true;
    11.     }
    12.     return false;
    13.   }
    14. }
    To get the reference, use the GetComponent<> generic method from your Movement and Attack components:

    Code (csharp):
    1. class Movement : Component
    2. {
    3.  
    4.   private Stamina stamina = null;
    5.   private Stamina StaminaComponent
    6.   {
    7.     get
    8.     {
    9.       if (stamina == null)
    10.         stamina = GetComponent<Stamina>();
    11.       return stamina;
    12.     }
    13.   }
    14.  
    15.   private MovementState currentState;
    16.   public void ActivateSprint()
    17.   {
    18.     if (StaminaComponent.TryUseStamina(10))
    19.       currentState = new SprintMovement();
    20.   }
    21. }
    If you don't like having such a narrow class, you could move stamina control into a more complex type:

    Code (csharp):
    1. class PlayerStats : Component
    2. {
    3.   private int currentHealth = 100;
    4.   private int currentStamina = 100;
    5.  
    6.   public delegate void DeathHandler(GameObject sender);
    7.   public event DeathHandler Death;
    8.  
    9.   public bool TryUseStamina(int amount)
    10.   {
    11.     if (currentStamina > amount)
    12.     {
    13.       currentStamina -= amount;
    14.       return true;
    15.     }
    16.     return false;
    17.   }
    18.  
    19.   public void Damage(int amount)
    20.   {
    21.     currentHealth -= amount;
    22.     if (currentHealth <= 0)
    23.       OnDeath();
    24.   }
    25.  
    26.   private void OnDeath()
    27.   {
    28.     var deathListeners = Death;
    29.     if (deathListeners != null)
    30.       deathListeners(this.gameObject);
    31.   }
    32.  
    33. }
    Personally, I like to keep things simpler, so I would leave it as two classes. However, if you go this route, you'll want to observe the Interface Segregation rule. Split the jobs that this class does into pieces so clients are exposed to a little complexity as possible:

    Code (csharp):
    1. interface IStamina
    2. {
    3.   bool TryUseStamaina(int amount);
    4. }
    5.  
    6. interface IDamageable
    7. {
    8.   void Damage(int amount);
    9.   delegate void DeathHandler(GameObject sender);
    10.   event DeathHandler Death;
    11. }
    Now have your complex class implement both interfaces

    Code (csharp):
    1. class PlayerStats : Component, IDamageable, IStamina
    2. {
    3.   // ...
    4. }
    Now your client can ask the Unity Framework for a component that implements the correct interface:

    Code (csharp):
    1. class Movement : Component
    2. {
    3.  
    4.   private IStamina stamina = null;
    5.   private IStamina StaminaComponent
    6.   {
    7.     get
    8.     {
    9.       if (stamina == null)
    10.         stamina = GetComponent<IStamina>();
    11.       return stamina;
    12.     }
    13.   }
    14.  
    15.   private MovementState currentState;
    16.   public void ActivateSprint()
    17.   {
    18.     if (StaminaComponent.TryUseStamina(10))
    19.       currentState = new SprintMovement();
    20.   }
    21. }
    The interface protects you from exposing your true type, so you can add a different class for testing (or cheating)

    Code (csharp):
    1. class InfiniteStamina : Component, IStamina
    2. {
    3.   public bool TryUseStamina(int amount)
    4.   {
    5.     return true;
    6.   }
    7. }
     
    Last edited: Mar 5, 2016
  29. LukePammant

    LukePammant

    Joined:
    Mar 10, 2015
    Posts:
    50
    Thanks for the great replies!

    I think that I didn't come to a solution like this because I felt that having the `GetComponent<IStamina>();` line of code meant "tight coupling" which is what I've been fighting furiously to avoid. By doing this we are forcing the gameObject with the Movement component to have a Stamina component too. I supposed that coupling something to an interface really isn't coupling though?

    The event driven (ExecuteEvents/SendMessage) way of inter-component communication is only useful for setting something or calling a method on an unknown destination and expecting nothing in return. It is needlessly difficult to use the same system to get something specific from an unknown source (stamina from a stamina component) and thus dependencies are needed. I've made myself believe that dependencies are the root of all evil and I think that is where my confusion has stemmed from. Really I should be thinking that concrete dependencies are evil where as abstract dependencies are okay because they can be swapped out at will.

    I've been looking into Dependcy Injection and IOC containers such as Zenject and StrangeIOC. I might try taking on Zenject to get some better code practices into my code. My struggle with something like IOC with this stamina example is that it doesn't make sense to resolve that dependency via a container because the Stamina component will be specific to the gameObject. I plan to make this game multiplayer so Stamina will not be a singleton. Perhaps I am over engineering too early in the game?
     
    eisenpony likes this.
  30. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Quite possibly yes.

    Tight coupling on systems that are inherently related to each other isn't always a bad thing. Conceptually movement relies on having stamina. It makes sense to allow coupling here. Where coupling doesn't make sense is in unrelated systems, movement should not depend on sound.

    The main point of all these exotic design patterns and architecture is to improve developer efficiency. Simply writing GetComponent<Stanina> may have saved you more time then implementing a complex design ever will.

    Understanding YAGNI is just as important as understanding SOLID. There is a time and a place for both principles.
     
    LukePammant and eisenpony like this.
  31. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    Wikipedia does a decent job of describing coupling. IMO, having a dependency is not the same as being coupled.

    Dependencies are not really something we can get away from entirely. Even the perfect event/messaging system involves dependencies, they are just hidden a bit more. My component may not need to worry about the dependency explicitly in code, but it is still dependent upon the other components being registered correctly to handle the types of messages it might send.

    I mostly agree with this idea however I would be careful about using words like "evil" to describe anything in software. Sometimes I use a concrete dependency because a) it allows me to separate two obviously distinct responsibilities b) it keeps the interface of my component very simple c) the concrete dependency is typically very easy to read and follow and d) it's a relatively cheap refactoring to swap out a concrete dependency with an interface dependency once I need two different implementations. Once you get past the procedural and spaghetti style programming of a novice, design is more about tradeoffs than obvious black and white truths.

    A comment about messaging systems: I've heard an argument that message bus or agent discovery systems are superior to standard interface programming because they prevent null reference exceptions. Personally, I think this is a regression in disguise. NullReferenceException is actually a really good thing. It allows a sick program to tell the operator that it isn't behaving properly. Some well built top level error handling might be able to help a program recover in this situation by saving the user data and shutting down safely. In a messaging system, my components might continue sending messages that aren't being received, or are being received incorrectly. This might continue until the state of the program is really bad, such as a refusal for more memory to hold messages from the operating system. At this point, it is really really difficult to save user data and shutdown safely.

    I wish the RequireComponentAttribute that Unity provides was more flexible to allow interfaces and runtime creation. However, I still prefer FindComponent and standard interface programming over message systems. I think they are easier to read and understand by all types of programmers and also easier to troubleshoot and write correct error handling code for.

    Very true. I think inversion of control (IoC) might be overkill for a simple Unity game because Unity already provides a very decent framework for managing dependencies. However, don't confuse dependency injection with IoC; DI can allow you to create reusable code without the need for an IoC container.
     
    LukePammant and Kiwasi like this.
  32. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    Here is a good source for different design pattern http://www.dofactory.com/net/design-patterns

    How well the architecture is, depends on your design skill to pick the right "tool" for the right task. There is no pattern that will make good design for you. Cheers.
     
    LukePammant likes this.
  33. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    This is 100% wrong. Bad implementation will give you problems, that goes for any pattern design.
     
  34. Sun-Dog

    Sun-Dog

    Joined:
    Mar 23, 2009
    Posts:
    144