Search Unity

Unity best Architecture and Design Pattern

Discussion in 'General Discussion' started by asil92, Aug 8, 2016.

  1. asil92

    asil92

    Joined:
    Jul 12, 2016
    Posts:
    26
    Hello Everyone ... I would like to ask which Code architecture to follow in Unity ? i read about MVC MVVM and others ,, ? if anyone has links to follow or to read please post :)
    thanks a lot
     
  2. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,566
    The best idea is to forget about MVC, MVVM and whatever pattern you heard of and treat each in-game object as a separate self-contained system. Basically, imagine that you're writing a small robot for a robot swarm. No "managers", every object is separate entity.

    That is, unless you're making an accounting or database application in unity for some reason.

    Also, try to get a hang of unity comnponent architecture.Instead of using traditional programming approach (where everything is a class), you need to get used to the idea that everything in the scene is a GameObject (and not a subclass of a GameObject), and almost all functionality is implemented as MonoBehaviors attached to that game object.

    Minimize amount of intheritance and reduce number of classes.
    For example, if you have a duck and a soldier in your game, then do not make "Duck" and "Soldier" class, instead use one "NPC" class for both, and express differences between duck and soldier via choice of mesh and animation controller.

    Something like this.
     
  3. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I'll second robot swarm. This is the natural way that Unity is defined to work. And it requires the least effort to get a unity to work this way.

    Of course there are plenty of other alternatives. Plenty of different structures and ways to use Unity exist. Choose the one that is best suited for your project.

    And if you have no reason to choose anything else, go with the swarm.
     
    Ryiah and Perrydotto like this.
  4. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Try different ways and use the way that works best for you. It is completely reasonable to invest several weeks or even a few months just exploring different patterns and methodologies to find the one that is most productive for you.

    My approach is completely the opposite of the replies above. I've tried the Unity component-based approach and the normal approach I am used to. Greatly prefer the latter especially after simplifying it even more by throwing out the modern practices of using Interfaces, Events and so forth and instead using a highly procedural programming oriented model. Everything is greatly simplified using managers to control collections of objects. I have only one GameManager GameObject that has a monobehaviour with the standard Unity Start and Update methods on it.
     
  5. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
  6. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    Half of this is a code organisation issue, and the other half is a project organisation issue. On larger projects, these can and will conflict. There are no perfect answers, especially when Unity's serialization and version control are involved. Component oriented MonoBehaviours are the usual go to, but these are intrinsically tied to scenes and prefabs which have no end to the horror stories when it came time to checkout. Then there are ScriptableObjects, which are often overlooked as just data storage devices when they can be as functional as MonoBehaviours if some work is put in.


    @superpig The talk was great.
     
  7. asil92

    asil92

    Joined:
    Jul 12, 2016
    Posts:
    26
    thank u all my app is mixture with "game" and "non game" app and i need it to be maintainable over the time ... and i had a locally database written on json file
     
  8. ToshoDaimos

    ToshoDaimos

    Joined:
    Jan 30, 2013
    Posts:
    679
    In my current codebase I have over 200 scripts and... 3 MonoBehaviors. Unity doesn't force anything on you except that ultimately everything must be mapped to GameObjects.
     
  9. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    Interesting subject, I was going to ask about this too.

    I come from a procedural programming background so have my roots there, in particular Blitz Max where you simply use simple function calls to do various things rather than having to go through this whole object/model hierarchical structure all the time. It does however imply that everything is kind of exposed everywhere and there aren't going to be any function name clashes or any 'scope' or 'context' issues, so it isn't quite as organized, but it is easier at least for smaller stuff.

    Lately I've opened up to trying to give objects their own independent scripts more. So I try to make them self-autonomous, at least as far as them having governance over stuff that is 'local' to them, and then I might have some other manager thing that takes care of bigger-picture stuff. I really don't like the idea of having to call a 'method' just to set some value on an object though when I could set it directly, if I know what I'm doing so I try not to put too many middle-men between things, but I do understand how providing that interface keeps the external and internal parts communicating properly without letting external script break internal data consistency. I also don't really like the model of 'sending messages' and getting into all this more advanced programming stuff, delegates and reflection and all that, maybe I don't understand it but it isn't as intuitive and 'direct' for me.

    I still like to have arrays of objects where possible simply because it's fast to iterate through and jump around, and its helpful that this can be stored/serialized. I'll use arrays over lists or objects anyday. I also like the idea of multiple 'layers' of managers that kind of map to different layers of abstraction. I somewhat don't like having to do everything in a compartmentalized per-object way, ie lots of small scripts all over the place, but I do like how that ties into prefabs and being able to just instantiate something and have it function right away rather than hacking into some kind of shared pool of data.

    One thing I will say is that trying to do things more 'procedurally' has led me to some complexities that I don't really prefer, even though I prefer this programming style. Lots of structured/nested if/else statements. It can be okay, but trying in particularly to keep track of user interactions and changes in 'state' in this way is not very intuitive to follow. It's too easy to get lost determining exactly what 'state' things are in in a given piece of code based on various things that came before it, and then having to flow back up the nested hierarchy to figure it out.

    I recently took a look at Playmaker because I hoped a) it could save time and b) it would make certain things easier. But what I found was that doing really basic things like simple math was so longwinded, it would've been far quicker and easier for me (being familiar with scripting). What I DID like however was the state machine. This concept that objects are in certain states - its idle, it's moving, it's celebrating, it's attacking, whatever. And transitioning between those states (which is sort of another state really). This was what appealed about Playmaker but I didn't like the efficiency. So then I turned back to scripting and decided to try to do things more 'state-machiney' in code. And I actually found this helpful.

    I may not be implementing this in the most efficient way but basically now I am mapping functions to 'states' and then having a 'switch' statement (otherwise would be if/else) to go to the appropriate function based on the state. So then there's a single state-tracking variable (and I mean, just an integer, not some fancy text enum thing although I can see how that might help keep things flexible/easier to follow). And since this gets called each frame/update the flow comes in and basically routes the logic to run the appropriate function. I've found this to be helpful because it's clean and I don't have all these different layers of nested if/else statements mixed in with a lot of other code, where it becomes quite hard to follow exactly which state the object is in or having to do lots of other logic just to decide which piece of code to enter. Then you're kind of constantly having to test, well, if the key is down then do this and if the key is an A do that but if not do this, then do this if enough time passed and then do that if it's too soon, and so on .... it can get messy. So I guess I'm sort of separating out the 'routing' of major chunks of functionality from the functionality itself, relegating all the main processing to "state functions" or "transition functions" and keeping the main core of the program simple. It's just a matter of thinking through what all the states are and whether you need extra states in-between to set things up or cleanup or do other transition work.

    I always knew that I could program if/else nesting to deal with states before but those states weren't clearly defined and there were all kinds of exceptions needed to deal with special cases and ambiguities. So now I just have it nicely broken out and am 'thinking in states' as I structure things. Program flow is obviously different but it seems to make more sense. One downside maybe is that only one state gets called per update, which is probably ok because objects are in one state at a time, but sometimes you might want it to flow on to multiple transitions and other states all within one frame so that gets to be either spread out over multiple frames or you have to find a workaround. But still, I'm enjoying this approach more and I don't know why I didn't do it this way before because it maps better to object behaviors and how they change.

    [Edit] I just realized, this is practically like using a 'jump table', shock horror![/edit]
     
    Last edited: Aug 8, 2016
    JorgeAires, Elisson357 and Perrydotto like this.
  10. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,051
    Neat! I can't believe I haven't seen that yet.
     
  11. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    I'm subscribed to the youtube channel and even then I'm surprised I caught it. They've got a bad habit of dumping ten or fifteen Unite videos at a time, and since a good chunk of them are of the trend chasing variety, my eyes start to glaze over. I expect there are probably two or three other good talks that I didn't catch from that conference. It will probably be the next Unite by the time I get caught up.
     
    theANMATOR2b likes this.
  12. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    Anyone see any merit to programming in a kind of state-machine style?
     
    dahiyabunty1 and tatoforever like this.
  13. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Generally everything I do is within state machines. GameState at top level. TitleStates may exist even. GameplayStates may exist. Objects definitely have states. It just makes each state isolated which minimizes the amount of code running during each frame as well as making the program easier to debug, enhance, etc.

    And I handle states in a very straightforward way... a simple switch statement is all that is needed. States are just a natural way to break complex things down into simple things. Divide and Conquer.
     
  14. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    All of these developers apparently do: http://www.hutonggames.com/showcase.html

    I agree with GarBenjamin. Most behaviors that transition between discrete values are a good candidates for state machine architectures. AI states and menu states are obvious examples, but most activity in a game is like this, too.
     
    Alverik, Kiwasi and GarBenjamin like this.
  15. asil92

    asil92

    Joined:
    Jul 12, 2016
    Posts:
    26
    Even if my application contain both UI (native plugins ) as well as game ? i thought of possibility to use MVVM (http://forum.unity3d.com/threads/mvvm-databinding-and-lobby-toolkit.232526/) for GUI as i need them to have native ux in different platform ? and the Game part of the app is using normal unity's component based architecture ? Plz help
     
  16. asil92

    asil92

    Joined:
    Jul 12, 2016
    Posts:
    26
    thank u for reply :) Even if my application contain both UI (native plugins ) as well as game ? i thought of possibility to use MVVM (http://forum.unity3d.com/threads/mvvm-databinding-and-lobby-toolkit.232526/) for GUI as i need them to have native ux in different platform ? and the Game part of the app is using normal unity's component based architecture ? Plz help
     
  17. asil92

    asil92

    Joined:
    Jul 12, 2016
    Posts:
    26
    I already develop it using xamarin an d did not cover my needs so switched to unity .... what possible codes or functionality we can use if i switch to Unity ?? can i use the Core portable project which generally contains logic and not ui ?
     
  18. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    Sorry, the thread may have gotten a little off track from the intent of your original question. Go ahead and use MVVM for each menu, and maybe a state machine concept to switch between menus. If you're looking for a framework to speed up your MVVM development, that one looks fine.
     
  19. Meltdown

    Meltdown

    Joined:
    Oct 13, 2010
    Posts:
    5,822
    What works for me is the event subscriber/listener pattern, and singletons for all my global reference objects.
    Yes, some might say you shouldn't use singletons, but I find them easy and efficient to work with, and being an indie with limited time and money, I need to get code written quickly.

    Also check out this website, it goes into some good details about patterns for games...
    www.gameprogrammingpatterns.com
     
  20. Meltdown

    Meltdown

    Joined:
    Oct 13, 2010
    Posts:
    5,822
    Why would you want to minimize inheritance?
    I don't see any benefit in wrapping up a Duck and a Solider class into one NPC class, if both Duck and a Soldier have different methods.

    For instance, a Soldier will have a Shoot() method, while a Duck won't.
     
    JACLEMGO likes this.
  21. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    The general idea here would be to break functionality down smaller. Duck and soldier wouldn't exist as classes. Instead you would have a movement component and a shooting component.

    Check out this for size. It's far from the only way to develop in Unity. But it's the approach @neginfinity is describing.

     
    La_Psicopata, Ryiah and Meltdown like this.
  22. Meltdown

    Meltdown

    Joined:
    Oct 13, 2010
    Posts:
    5,822
    Ah I see it now, I misunderstood what he was getting at. Yes this is a good way of doing things as well.

    I find this is also very useful for state machines, making each state its own component.
     
  23. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,566
    Heavy focus on inheritance can be a pointless time sink. When someone is fascinated with OOP, it is possible to waste weeks gleefully redesigning object hierarchy to make it "perfect", "logical" and "beautiful". Meanwhile the project won't be improving in any way.

    Both can have method "attack". Difference between duck and soldier attack can be expressed via choice of mesh, animation, animation controller, etc. Basically, instead of expressing differences via CLASS, you express them via choice of components or via data. In the end you'll end up with smaller number of classes to babysit.

    I'm fairly sure I wrote a huge post about this somewhere in the past.
     
    NotaNaN, Martin_H and GarBenjamin like this.
  24. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    I agree about not overdoing it on the OOP programming stuff, trying to do stuff 'just because' it's the OOP way when it doesn't really need to be done. Like methods for getting and setting stuff that you could easily do directly without breaking anything.

    I also am leaning toward combining objects somewhat like the duck and the soldier. It means having some generic code in there and some cases that aren't used or where code is irrelevant, or you have to check 'if' a certain feature is to be used (toggled etc) and if so, do this, otherwise don't...which obviously is not as purely *efficient* in terms of code performance but it sure could make things EASIER to use on the front-end for configuring objects and stuff without having to have all these different customized scripts. Reusability is helpful for saving development time too.

    Its all a bit of a tradeoff depending on your priorities. Personally I try to keep things 'simple and easy' and mayby lazy and with minimal complexity/effort if I can. It might be sloppier but its what I prefer.
     
    Kiwasi and GarBenjamin like this.
  25. Wrymnn

    Wrymnn

    Joined:
    Sep 24, 2014
    Posts:
    380
    After 3 years and now on one big project, I have come to actually opposite conclusion of this :D Build game in OOP approach, like its not even unity game (No monobehaviours or gameobjects) and use gameobjects just for "rendering".

    So in theory, your game could be simulated with only one object on scene, with thousands of objects in galaxy where factions battle each other...

    I have seen this approach with BIG games like Master Of Orion, Endless Legends on Rimworld. They do not touch gameobjects or monobehaviour, only to display what is happening in scene as visuals.
     
  26. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    Always a safe guess, my friend. :p :D

    I've been pondering a massive refactoring to adopt a reactive-programming approach to much of my game. I'd actually kind of forgotten about RP until I stumbled across UniRx that ported Microsoft's Rx to Unity, and then it dawned on me how completely perfect the RP paradigm is for game programming.

    I'm using RP for my REST API requests but honestly the entire game (and probably any game) could be built from nothing but Observable stream manipulations. If you've never tried RP, give it a shot. I would suggest starting out in the javascript world with RxJS (also from Microsoft, same basic API) rather than trying to absorb the somewhat dense examples surrounding the .NET version and the UniRx asset (which is free, by the way). There are tons of examples and tutorials online but if anybody wants links, hit me up, I just walked somebody through an intro pretty recently.

    It's amazing how RP makes state-management and -transition complexities simply vanish. At first it just looks like event-driven programming, but the real power comes from LINQ-like manipulations of the streams to shape and respond to relationships between event streams over time. Just that statement alone should clarify why it's so great for games. As a bonus you generally write much, much less code, and many opportunities for logic errors simply go away.

    Once I really absorbed the concept, it was just like REST was for me for over HTTP interactions (not just for APIs) -- once it clicked, other approaches seemed (and often are) glaringly wrong or even broken. Which, of course, is why streams remain so popular a paradigm in foundational systems like *NIX.

    I'd kill for a Unity-grade game engine built around RP principles.

    (A pedantic aside: most "functional reactive programming" is just "reactive programming" -- FRP requires continuous-time capabilites a la Haskell Behaviors which are missing from nearly all imperative-oriented RP implementations. Basically people assume "functional" means FRP uses functional-programming concepts, when that's actually also true of non-F RP. An understandable error due to a confusing name dating back to FRP's origins in 1998.)
     
  27. Martin_H

    Martin_H

    Joined:
    Jul 11, 2015
    Posts:
    4,436
    Fun fact: that's one of the posts with the highest counts of words I never heard before, that I have ever read here.^^


    I think my approach is kind of similar, but (see reply above) maybe I just don't know better?

    You seriously should check out Panda BT:
    https://www.assetstore.unity3d.com/en/#!/content/33057

    It's still 100% coding, but it makes the structuring of the "flow" a whole lot easier once you get a hang of how it works.
     
    MV10 likes this.
  28. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    That occurred to me after I was done gushing about the wonders of RP.
    You must not read Zen Garden's posts. :)
     
    Martin_H likes this.
  29. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Plus one for Panda BT offering a whole new way to structure games in Unity. I've built a couple of small games using only Panda.

    How well does the approach scale to larger games?
     
  30. 00christian00

    00christian00

    Joined:
    Jul 22, 2012
    Posts:
    1,035
    Didn't watch the whole video, but to me it seem something that can get out of hand very easily and create more issues than what it solves. Singleton managers + normal monobehaviours all life.
     
  31. Martin_H

    Martin_H

    Joined:
    Jul 11, 2015
    Posts:
    4,436
    I'll let you know once the stuff I'm working on can be called "game" :D.
     
    Kiwasi and MV10 like this.
  32. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    Ooh, just found out that javascript and C# support function pointers/delegates ... which means instead of a switch/case statement and enums you can just say myvar (which is a function pointer variable) = name of function, and then just call myvar(). Like hardwiring the function calls. Clean, efficient, more easily expanded. Each time there's a state change, just assign the a new state's function to the function pointer and then there's just one function to call every frame that never changes its name. There's some more info here: https://unity3d.com/learn/tutorials/modules/intermediate/scripting/coding-practices?playlist=17117

    H
    ere is another approach using C# interfaces.. but although I can sort of understand it it seems rather more longwinded than just using function pointers... you have to create all the instances of classes just to set things up. It seems easier to just point a function pointer at a given function and call it rather than all this fancy programmatic hoohah.
    https://unity3d.com/learn/tutorials...terfaces-make-state-machine-ai?playlist=17117
     
    Last edited: Aug 26, 2016
    Deeeds and Martin_H like this.
  33. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    I don't see it as being cleaner and any easier to expand than using the switch statement. Certainly not what I would call efficient.

    Here is a breakdown of the processing happening auto-magically when using a delegate due to the layers of indirection:

    Delegate call
    • Check for nullity
    • Navigate from object pointer to array of invocations (all delegates are potentially multicast)
    • Loop over array, and for each invocation:
      • Fetch method address
      • Work out whether or not to pass the target as first argument
      • Push arguments onto stack (may have been done already - not sure)
      • Optionally (depending on whether the invocation is open or closed) push the invocation target onto the stack
      • Call method
    Interfaces are just as bad. Actually I think they are even worse with an extra step or two involved before the method is finally called.

    For reference I pulled that breakdown from StackOverflow where they had a good analysis going on.

    I get that to the end developer it just looks like a very nifty magic way to handle such things. But what is often not considered is how inefficient such things really are. And yes perhaps it won't matter. Depending on the game and the hardware running it this may not warrant any thought. But I am always thinking about these things. Magic things are generally damn inefficient things.

    For me it is far simpler, just as clean and way more efficient to simply use a switch statement. The same could be done with an if () else if () else if () structure but I find using switch is a nice compromise between making things cleaner and still highly optimized (switch is generally as fast or faster than an if tree).

    Of course, I am also working on games that need every bit of performance I can get. Things like these:




    The kind of games most folks around here are working on probably aren't quite as demanding. ;)
     
    Last edited: Aug 27, 2016
    NotaNaN, Deeeds and Martin_H like this.
  34. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Well yes. How else were you implementing a state machine?

    You can go one step further by using reflection to grab the methods from a new class. And thus have an almost totally modular state machine.
     
  35. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,566
    There's also that usual abstract base class with "exec" function... several derived instances, that are initialized, and one "AbstractState *currentState = nullptr;" somewhere. A state can set current state to something else.

    Can be much cleaner than function "pointers", because it embeds data into the state. In case of unity, serializer hates polymorphism, so it'll be less elegant.
    Also (you probably already know this) see decorator pattern (which you probably already know):
    Command pattern, State pattern and maybe Visitor pattern.
    https://en.wikipedia.org/wiki/Decorator_pattern

    Also, it is worth keeping in mind that in C# callign a delegate that references data that is not explicitly passed into it OR a delegate that is a non-static method of a class seems to generate garbage during every call (because apparently compiler needs to create an anonymous class to wrap the external data into it or something)
     
  36. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,566
    The problem here is that switch is non-extensible, while delegate is, switch is also error-prone, while delegate is less so (it limits vairables to immediate scope). Whatever madness C# does with delegate-related calls isn't present in at least some other languages, and even in case of C#, there's abstract class-based approach. Another issue with switch-case is when you need to implement multiple methods per state and then you suddenly realize you need one more. That'll require you to comb through your code potentially adding a bug somewhere.

    Anyway, just quick few cents.
     
    NotaNaN, GarBenjamin and Kiwasi like this.
  37. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    It has to be just a different way of viewing things for different people. That is all I can think of to explain it.

    I don't see how anything is simpler, more extensible than this:
    Code (CSharp):
    1. switch(eCurState)
    2. {
    3.     case PlayerStateType.Idle:
    4.         StateIdle();
    5.         break;
    6.  
    7.     case PlayerStateType.Ducking:
    8.         StateDucking();
    9.         break;
    10.  
    11.     case PlayerStateType.Jumping:
    12.         StateJumping();
    13.         break;
    14.  
    15.     case PlayerStateType.Walking:
    16.         StateWalking();
    17.         break;
    18. }
    If I later decide "oh the player needs to be able to climb and swim too" it is a simple matter to add:
    Code (CSharp):
    1.     case PlayerStateType.Climbing:
    2.         StateClimbing();
    3.         break;
    4.  
    5.     case PlayerStateType.Swimming:
    6.         StateSwimming();
    7.         break;
     
    Last edited: Aug 27, 2016
    Deeeds likes this.
  38. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,566
    1. Function body gets too large at 5+ states.
    2. Does not clearly express code intent. (I would prefer "eCurrentState.performAction();");
    3. Variables will be shared between states (increases probability of error).
    4. Since there are functions for individual states, it means the data wants to resolve into some uniform base class or structure.
    5. Extending via 3rd plugin will be harder.
    6. Trouble when there are multiple function calls per state, meaning you'll have several switch/case trees in different places.

    I would understand switch/case tree if you didn't have individual functions OR hit a severe technical limitation (that does not happen with switch/case).

    The problem here is if you, say, implement state as polymorphic object, or even as delegates, then you won't even need to write that. You'll just create methods and they'll just work. You won't need to add more values to enum, and won't need to add few lines to swtich case, and you won't need the enum itself.

    Of course, unity serializer messes things up a bit.

    Also, I suspect that it may be possible to reduce all states to a single datatype, expressing differences via value/choice of animation, but that's another story.
     
    GarBenjamin likes this.
  39. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    I see what you are saying and I used to view things pretty much the same way.

    I just don't see it that way anymore.

    Many of these things I think are open to interpretation and also of course apply to specific use cases or at least specific goals of the developer.

    For example, function body gets too large at 5 states. I don't think there is a hard limit for what is too large. I'd have no problem with a dozen or more states.

    Same for Does not clearly express content. For me (and I would certainly hope any competent programmer) looking at the switch statement with states literally spelled out such as:

    Code (CSharp):
    1.     case PlayerStateType.Climbing:
    2.         StateClimbing();
    3.         break;
    it's instantly understandable. This is relaying control to the Climbing state. The player is currently climbing something.

    3 to 6 I think are just things for specific edge cases or so forth. Because they are not things I run into.

    I've said it before... I think there is no right or wrong way only what is right for a given person. If the other way works for you and does not add needless complexity then certainly you should use it. I just found simplifying everything such as using switch statements makes development so much more productive, less errors (and less complex errors), very easy to extend with additional functionality.

    I'm not saying it is perfect. Nothing is. Just that after doing things the other way for quite a while I started getting this idea I couldn't shake that all of this stuff was just adding unnecessary complexity to my programs. And it is the kind of thing where you use one to gain a benefit and then you need to use another to fix a flaw caused by using the first one and so forth. And you end up with a chain of complexity. By throwing all of it out I no longer run into that situation at all. Literally all of my development time is focused on the actual game itself instead of managing systems, working around issues and so forth.

    Now I do find those things interesting purely from a software engineering perspective. It's fun to mess around with just for the heck of it.
     
    CorWilson and Deeeds like this.
  40. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Problem is this structure gets really difficult to manage and extend when you need StateEnter, StateExit, StatePause ect. Transitions are also not obvious using a switch structure.
     
  41. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    I handle all state transitions through a single method call. For example SetNewState(PlayerStateType eNewState) is used anytime the player's state needs to change.

    That uses a switch statement to examine the new state and performs any setup needed. Doing it this way I don't need anything else.
     
    Deeeds likes this.
  42. Martin_H

    Martin_H

    Joined:
    Jul 11, 2015
    Posts:
    4,436
    Did you take a look at the Panda BT example scripts? Or at least scroll through the docs till you see a screenshot of one:

    http://www.pandabehaviour.com/?page_id=23#Static_View

    When you have that level of conditional branching, parallel running branches and sequences, I doubt your switch statements are still as readable as your example. For simpler stuff I'm doing switch/if statements as well.
    Of course to be able to call task nodes from a BT script you need to define them in code first, but that's really easy and allows to break it down into small pieces. I really like this solution for writing AI. It's probably also great for doing gameflow tasks like checking victory conditions, or handling some UI stuff.

    My weapon class currently tries to cover every kind of ranged weapon in the game. That means I'm not having to handle only 1 state and switch from there, I have one big update loop with tons of different nested if/else statements to cover the logic behind different firemodes, ammo, etc.. Potentially that might have been easier to maintain with something else, but I'm still sticking to the if/else/switch approach, because it's light on performance and it works. I'm not trying to dogmatically shove behaviour trees into everything I do now, just because it's the latest fancy thing I've discovered. Yesterday I started writing a new class and I'm doing it similar to your way again, because everything else seems unnecessary. But for the next AI task I'll use BT again, because that just seems a whole lot easier to make it do what I want it to do.
     
    GarBenjamin and Kiwasi like this.
  43. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,566
    Regarding functions and expressiveness. Ideally I would preffer a function to fit a single screen on my computer (that'll be 20..30 lines) and function name to clearly express what it does. Switch case tree is one of the most verbose structures in language. Sure, I might change my opinion eventually, but at the moment I would try to reduce the whole switch/case tree to a single line if possible, and I definitely wouldn't want to keep typing PlayerState.SomeState every case label.


    Uh, this just looks a lot like pure C# code.
    With that level of verbosity one could just use coroutines. It is a pity that their state doesn't serialize.

    Did anyone ever implement something like unreal's behavior trees for Unity?
    https://docs.unrealengine.com/latest/INT/Engine/AI/BehaviorTrees/QuickStart/index.html
     
    GarBenjamin likes this.
  44. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    In virtually all compiled languages (and definitely in MSIL), a switch will always be faster because it compiles to a jump table instead of a conditional evaluation, though in C# this is only true if you compile to release mode (in case anybody feels like inspecting the IL). And thanks to all the looping and processing overhead we do in games, this is one of the few scenarios where the difference could actually matter to the programmer.
     
    GarBenjamin likes this.
  45. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    If-then-else which later became switch-case ;-)

    The reflection idea is interesting.
     
  46. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    I do like the simplicity of the switch-case... I only just started using this as I used to be a tried-and-true if-then-else fanboy, lol .... switch is nicer. But now that I know Unity can do function pointers, it's game on.

    One big advantage of a function pointer (delegate) is performance... the way a switch statement works is the system has to go through every 'case' and compare the value to what the variable is, and that means if your state is like the last state in the switch statement, it's got to go through all the other comparisons first to get to it before it calls a function. With a function pointer, you are literally 'hardwiring' the system to jump straight to the relevant function that matches the state, without constantly having this 'decision making' process going on every single frame. It's like you decide once (when the state is changed) what the new state will be, and you lock that in (store the function in a variable) and then from then on you just do one line of code - one function call - and the appropriate function runs. That to me seems tremendously more efficient. I know, maybe this doesn't matter depending on how heavy the code is and whether this is even worth optimizing, but in my mind I like the efficiency of it. Then instead of 50 lines of code dealing just with the switch statement and calling the right function (which, across multiple frames is redundant work), you end up with just one line of code and you're done.

    Another thing with function pointers is you can make an array of function pointers, into which you can load a whole bunch of function calls and then go through them in a little loop and just fire them off like a little 'program'. This is a very simple quick way to let you build custom 'programs' within your program at runtime. You could even write a simple language around it similar to assembly language with simple instructions performed by each function. This said, from what I gather, a delegate can actually be set up with this mini `program` because you can add multiple function pointer to it and they'll all get called (though I'm not sure about the sequence?)

    I guess you could think of a function pointer/delegate like an old fashioned 'jump table'. A jump table would be just a series of pointers to subroutines/functions in a sequence, and then you'd look up the appropriate function based on an index offset pointer.. it'd go straight to the function pointer and read it and then jump to that. Very efficient compared to making many comparisons to decide where to go.

    Also on the topic of state transitions... would I be wrong in saying that a transition is really just another state? An in-between state? So like if you start at 'idle' and you want to get to 'run', there needs to be a 'path' or connector going from one to the other... so somewhere the code needs to 'change state', but also it might want to do some setup work or something to prepare for the new state... so this would just be another state, right? like.. preparing to run. And once that function is done it should then change the state function pointer to the 'run' state.?

    P.S. I totally did not get the thing about abstract base classes and all that.
     
    Last edited: Aug 27, 2016
    Kiwasi likes this.
  47. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,566
    And that is not how switch statement works.

    As previously mentioned by @MV10 usually when switch case takes something like an enum as argument, it often constructs a jump table. Meaning an array of addresses to jump to in case a specific value is passed to the switch statement. So it is not a "if (enumValue == SomeEnum.ValueA) else if (enumValue == SomeEnum.ValueB)", but "goto jumpTable[enumValue];".

    Switch/case becomes FUN (dwarf-fortress style) when the language allows execution to fall through the switch case if there's no break statement.

    For example:
    Code (csharp):
    1.  
    2. #include <iostream>
    3.  
    4. int test(int count){
    5.     int n = (count + 7) / 8;
    6.     int result = 0;
    7.     switch (count % 8) {
    8.         case 0: do {result++;
    9.         case 7:     result++;
    10.         case 6:     result++;
    11.         case 5:     result++;
    12.         case 4:     result++;
    13.         case 3:     result++;
    14.         case 2:     result++;
    15.         case 1:     result++;
    16.             } while (--n > 0);
    17.     }
    18.     return result;
    19. }
    20.  
    21. void printArg(int val){
    22.     std::cout << val << " " << test(val) << std::endl;
    23. }
    24.  
    25. int main(int argc, char** argv){
    26.     for(int i = 0; i < 8; i++){
    27.         printArg(i);
    28.     }
    29. }
    30.  
    Output:
    Code (csharp):
    1.  
    2. 0 8
    3. 1 1
    4. 2 2
    5. 3 3
    6. 4 4
    7. 5 5
    8. 6 6
    9. 7 7
    10.  
    That's C++ code.
     
    GarBenjamin likes this.
  48. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    That's not the case with C# though, as I understand it. You're not supposed to need the breaks, but I haven't tried it and old habits die hard.
     
  49. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,566
    It requires breaks.
    https://msdn.microsoft.com/en-us/library/06tc147t.aspx

    However, you're not allowed to write code where execution would fall through from one case label to the next, meaning you can't put one case label before a do/while loop and another one in the middle of it. So, it is just lots of syntaxic sugar for nothing.... BUUUT apparently you can goto to a different case label ! ^_^.

    From the example on msdn:
    Code (csharp):
    1.  
    2.         switch (str)
    3.         {
    4.             case "1":
    5.             case "small":
    6.                 cost += 25;
    7.                 break;
    8.             case "2":
    9.             case "medium":
    10.                 cost += 25;
    11.                 goto case "1";//<<==here
    12. ..........
    13.        }
    14.  
    So it is possible to write spaghetti by design, it seems.
     
    Deeeds and GarBenjamin like this.
  50. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    C# requires you to be explicit in specifying which behavior you want. If you type in break it jumps past the case. You can use goto to get the old style fall through behavior, but you must type it explicitly.

    Unity Gems had a good example of this, if you can find an archived version of the site.
     
    Martin_H likes this.