Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Modular scripting approach vs traditional OOP

Discussion in 'General Discussion' started by Rienhl, Jul 31, 2018.

?

How do you approach coding your games

  1. OOP

    20 vote(s)
    39.2%
  2. ECS

    7 vote(s)
    13.7%
  3. Mix of everything

    27 vote(s)
    52.9%
Multiple votes are allowed.
  1. Rienhl

    Rienhl

    Joined:
    Nov 14, 2013
    Posts:
    42
    I've always found myself caught up between these to approaches when developing games.

    When I started out, I learned the "Unity way": attaching monobehaviours with basic functionailty in an ECS fashion to achieve complex behaviours and controls. Something like what's explained on this video by Craig Perko

    Later on, after some years working in the game dev industry, I leaned towards and MVC + OOP approach. Encapsulation, heavy class based systems, interfaces, inheritance, etc... This is what my technical leaders asked me to do, their frameworks were constructed over robust OOP systems.

    I enjoy both ways of developing a game, but I never quite understood why the modular approach is not taken into account in the professional world. Most studios I've worked at despise MonoBehaviours and Unity-ECS (not the new ECS support) and look for programmers with strong OOP, SOLID and design patterns knowledge. No technical interview has ever gotten even close to touching the subject of developing a game like Craig Perko (and other tutorial creators) suggests. It almost feels like doing it that way is entirely wrong.

    My question is, why?

    What am I overlooking about these approaches? What is it that all these experienced developers hate about ECS-style modularity on your code? What problems have they encountered that makes them completely disregard the "unity way"?

    Thanks for taking the time to read!

    PS: This was originally uploaded to the subreddit r/Unity3D but then I realized another valuable discussion might get started here (I never know which resource should I use the most). Anyways, here's the link to the reddit thread if you wish to read about what other people think about this.
     
  2. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    You can still use OOP principles with Unity's API, so I'm not exactly sure what the problem is.

    The game I'm currently making, for instance, involves tons of different weapons, so I'm structuring them around Inheritance instead of rewriting the same code for each MonoBehavior.

    I've created an abstract base Weapon class which inherits from MonoBehavior, a few abstract Weapon subclasses for weapons with specific features, and the actual weapon objects themselves are inheriting from one of these Weapon subclasses.
     
  3. Rienhl

    Rienhl

    Joined:
    Nov 14, 2013
    Posts:
    42
    There's no actual problem, just different preferences when approaching the solution.

    It's not that you CAN'T use OOP. It's more like some people stand on the ground of "don't use it because it's bad, old and enforces rigid hierarchies". So I want to hear other opinions. I'd suggest taking a look to the reddit post I provided above to understand what they mean.

    For the last 3 years I've been using Unity with a OOP approach and encountered no issues whatsoever.
     
  4. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Simply, a lot of what those tutorials outline is a bad approach to making larger games or working with larger teams.

    MonoBehaviour driven design at a certain point becomes an unmanageable mess. The tools for finding specific monobehaviours is inadequate, slow and cumbersome.
     
    one_one and Rienhl like this.
  5. Rienhl

    Rienhl

    Joined:
    Nov 14, 2013
    Posts:
    42
    I know this is a difficult question, but still... Where would you draw the line between what those tutorials outline and plain good old oop?
     
  6. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    A mix here - tool for the job. But, my code base is designed for several games, not just this one. So my eventual goal is to migrate entirely to ECS, simply because it's logically better than OOP for production. Personal preference is irrelevant to me so I am doing this regardless (although luckily I do also like the pattern).

    I am migrating rather than being all-in because ECS is not finished :)

    Once it is finished, there is no logical situation where OOP (which is an outdated concept, frankly) can trump ECS. At the moment there is still too much missing from pure ECS for it to be feasible to do a whole game in.
     
    one_one, Kiwasi and Rienhl like this.
  7. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    I would think of most online tutorials as quick start guides for hobby dev.

    I am not a huge advocate for OOP but configuration/composition via monobehaviours in editor is not an ideal method in a lot of instances. Either this requires robust custom editors or other more traditional approaches (configuration files, traditional data storage, etc) and code driven configuration.

    I don't really see it as monobehaviour vs oop, I see it as in editor configuration vs text/code configuration and text/code is just so much easier once you pass a certain scope.
     
  8. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    I'm not sure whether the question is about code structure (you can do a component based architecture with minimal inheritance purely in code, it's distinct from having serialized data), or about controlling behavior entirely through code vs using serialized data like ScriptableObjects to control behavior.

    The latter has some important advantages, such as being able to modify an app's behavior without requiring an update, but it also has disadvantages, primarily in terms of ability to debug and navigate the resulting structure.

    C# has a lot of very convenient tools for working with it. You can easily find all references to a class/method/property, you can step through it in the debugger, you can quickly jump up the call stack when investigating where something went wrong, and it has human-readable diffs in source control. Behavior that's driven by assets communicating with each other using UnityEvents has none of that (although some would be possible to build as editor extensions).

    For example:
    player.Stats.MaxHealth = config.DefaultCharacter.InventorySize; <-- Clearly visible as incorrect
    sourceProperty: {fileID: 11400000, guid: 41cf95a6a8a4e48d693cd5ad425046af, type: 2} <-- Looks fine in Git

    So while I wouldn't say data-controlled behavior is bad - in fact, it's sometimes necessary - it should be used carefully. Personally, I like to tag any methods that are called from outside the code with a custom attribute, and keep the use fairly sparing.
     
  9. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    IMO, the issue is not data driven vs non data driven - it's unity serialization vs other formats.

    I just moved a bunch of data from scriptableobject to csv a few days ago. My life got 10x easier. It's still "data" not "code", but it's data that's in a format that's not dependent on the unity editor.

    This is really the key. But some formats are extremely good at making errors clearly visible (tabular formats like csv being the absolute best imo). You can even use C# itself as serialization if you can run compiler on your target platform (just keep the c# files and compile on the fly).

    EXCEL_2018-07-31_20-14-38.png
    Laying out data in a clear, compact and orderly format like this makes it vastly easier to visually inspect for errors or make manual batch changes: "I should increase all bladed weapon damage by 10%" - easy pz in a good data format.

    I think the data format issue is one of the main ones with unity tutorials, since these almost always embed as much 'data' as possible into editor placed elements.

    Example: event triggers wired using editor serialized delegates. (this is a total f'n nightmare 100% worth avoiding unless you absolutely need to support non developers wiring up events)
     
    Rienhl and Kiwasi like this.
  10. one_one

    one_one

    Joined:
    May 20, 2013
    Posts:
    621
    First of all, as that point was made in the beginning of the thread: OOP does not exclude composition or strongly enforce deep inheritance hierarchies. In fact, 'composition over inheritance' is often touted as a rule of thumb for OOP.

    I wouldn't call OOP an outdated concept. It may be true for most areas of game programming, but I'd say it still has a solid place in business applications. Yes, ECS makes better use of modern hardware, so in that sense OOP (and it being very hard to write parallelizable code with it) could be considered outdated - but this performance consideration is often not nearly as important as the programmers understanding and being able to adequately model the problem domain. And I'd argue this is often easier with OOP than with an ECS, especially if that is what the programmers have been working with for years.

    Similarly, I do not see the clear superiority of ECS for aspects in games that require just a single instance. This is even more so the case when it involves generic libraries designed with OOP concepts. What would be the obvious benefit of writing a save-game loader in an ECS style?


    I agree that this is the main issue of this discussion. MonoBehaviour-based development is easier to learn at first and you can very quickly hack a few prototypes together. Due to its visual qualities because of the inspector, I also appreciate it for things that are closely related to graphics, e. g. level design. Once ECS gets a proper inspector integration, that last point will be moot, however.
    An important part of the MonoBehaviour approach, though, is that it's pretty damn fragile. There is, unfortunately, a large number of reasons for this:
    • Version control/working with a team sucks because merging does not necessarily go as well as you'd expect
    • Editor references can break easily. You renamed that variable but didn't set up the reference in every instance that it needs to be set? Tough luck, you've either broken your project quite obviously, or worse, subtly.
    • If somebody needs to edit the scene who didn't write the MonoBehaviour (or can not read code well, at all), how does he know which references are needed where?
    • After a couple of months, do you exactly remember how to assign all references in case something gets broken?
    And I've likely missed a couple. In a larger project, these points all become very important. The time spent on fixing these issues will very likely be higher than the time saved by the comforts of the editor. Plus, those kinds of errors are super annoying to track down and fix. You don't want super annoyed programmers.


    Except that's not how you'd define dependencies in text. Try looking into the concepts of inversion of control and dependency injection.

    I'm not sure if that's what you mean by it - but for most games, the opposite is true. Projects are more stable if less is expressed as logic/code (which can break) and more as simply data. In fact, this is the basis for an architecture like ECS: Your game entities all have components that store data. What makes a game interesting, of course, is the way the data interacts: the systems. However, that is where most of your development headaches will be coming from (at least as a programmer.) So keeping the systems part lean is a great idea, which in turn results in defining more characteristics of the game in data.
     
  11. one_one

    one_one

    Joined:
    May 20, 2013
    Posts:
    621
    Personally, I think that the ability to leverage the editor fully is a great advantage of ScriptableObjects. It's perfectly possible to write an editor extension that displays data in a table format and/or handles automated/large-scale data manipulation. And in the end, you can still serialize to JSON.

    PS: Sorry for the wall of text above.
     
    Rienhl likes this.
  12. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I should clarify that for me, OOP is outdated in the context of game design, because being able to have trouble free dependency injection, easy threaded code, best performance, determinism (networking will be so good) and the fact that your systems are not coupled, means that as the production wears on, things get easier, while in OOP land one begins to refactor with increasing frequency. Even an optimisation with OOP can lead to some pretty unwelcome refactors. ECS, so far in my experiments, is immune to it.

    So for game development, if you haven't got a robust library of ECS code built up then OOP seems so much easier at first and for prototyping. Once you do have a library built up, I can't see OOP having any actual purpose. I would like to hear arguments in favour of OOP as well, so I'll be following the thread and reading.

    One thing against ECS is that it's a bit like messing about with stream processing, you can't just say "hey guys, I need this done right now in this order" and that is a major downside for linear thinkers. If you have any shader experience this becomes a lot easier to deal with I suppose. But I'm thinking most programmers need to be able to deliver results now and ECS is about research in a Unity context.
     
    Rienhl and one_one like this.
  13. one_one

    one_one

    Joined:
    May 20, 2013
    Posts:
    621
    Agreed, and as stated in the OP, that sort of 'momentum' of well established systems/approaches is hard to take away, especially if/because OOP is likely going to keep on ruling business programming. And reactive programming is quickly gaining traction as well, being pretty much a philosophical counterpoint to ECS.
     
  14. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    If you can justify the time required to build out complex custom tool UI - like good tabular presentation/editing in a unity editor, go for it.

    I've been working solo for a long time (recently duo) - the idea of investing the time required to build out complex presentation for tooling strikes me as a horrendous waste unless you really have a large enough team of non developers to justify it.

    But let's be real - dumping tabular data into excel is just hard to beat if your users (including yourself) are skilled with spreadsheets. Modern spreadsheets are friggin amazing.
     
    Kiwasi likes this.
  15. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    I'm quite familiar with DI, and that literally is how certain data is connected together (via a simple SO linked to from both sides) in a game I'm working on, so ... try looking into making less assumptions. ;)

    This is what I'd challenge. It's rare that you can simply have "less logic" while providing the same functionality, and moving logic from code to a network of serialized objects doesn't make it any less likely to break. There are benefits to a more data-driven architecture, but it's not a panacea that makes everything easier, and "maximum data, minimum code" is not always the best choice.
     
  16. one_one

    one_one

    Joined:
    May 20, 2013
    Posts:
    621
    Fortunately, tools like Odin inspector make it much less of a pain. I do agree that in quite a few cases, manipulating data in tables can be easier/more convenient, though. But in cases where art assets need to be referenced (which you are doing in your example,) I find it much easier to mess up working with purely string-based asset paths like in a table. I suppose it's a matter of preference and situation, though.

    Sorry, it seems I misunderstood the point you were making there. Re-reading it now it seems I got the entire post wrong. My bad :oops:

    I suppose 'more data-driven' is relative. I was writing this with an earlier post in mind (from this thread, not yours, though) that talked about deep hierarchies for different weapon types - in which case 'more data-driven' definitely sounds like a good idea. You are right, of course, that it's not the cure for all software project woes and comes with its own set of issues, like the debugging issues you mentioned above. Claiming that data-driven development is the only valid/best/effective/whatever superlative type of development was never my intention.

    And sorry if I stepped on your programmer toes in that previous post :p
     
  17. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    That's an unusually reasonable response. It sounds like you've been doing this for a while.

    I tend to agree with you on string based paths, although I tend to find that devs tend to be a bit overly afraid of strings.

    I would argue that the reason my example works well is the repetition of paths. Being able to clearly see which reference is used where (and view in context of others) ends up preventing errors in practice. If those paths were each unique, it becomes more cumbersome since eyeballing the list provides no (casual) error check.

    The natural sense of "one of these things is not like the others" becomes a tool for flagging potential errors.
     
    Rienhl and one_one like this.
  18. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Someone should build a proper adapter. It would be brilliant to be able to export directly between excel recognizable formats and Unity ones.

    That might actually be a good project next time I get a weekend free.
     
    nhold and frosted like this.
  19. nhold

    nhold

    Joined:
    May 2, 2017
    Posts:
    50
    It should really just be an optional backend selection: ScriptableObjects, Excel Spreadsheets, SQL Databases or MongoDB.
     
  20. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I think Unity should stick to doing one serialization format well. Adding extras to the engine or run time seems like unneeded bloat. And as it stands the current JSON format does seem to be the best for the engines needs.

    But building a converter that can switch between them should be a fairly simple exercise.
     
  21. nhold

    nhold

    Joined:
    May 2, 2017
    Posts:
    50
    It wouldn't have to be bloat, just allow a hook into whatever serializes data to implement your own format and\or id scheme(The same for built data) and have packages for each type. If you only want the default don't install the others, or if you don't want default but want excel install the excel one and remove the default one.

    It could be seamless and bloatless if implemented correctly.
     
  22. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    No worries, the thread's moving quickly. And I know what you mean, it's definitely possible to go too far in the class hierarchy direction. Especially when there are a lot of examples around like:
    Code (csharp):
    1. class Fruit { ... } // Sure, makes sense
    2. class Apple : Fruit { ... } // Well ... maybe if this is a biology app?
    3. class GrannySmith : Apple { ... } //  ಠ_ಠ
    Re: Spreadsheets - They're pretty handy. I don't know if it requires direct integration though - the sheets have mostly been live config IME, meaning they get sent (usually as JSON) by the server at runtime. So even for built-in config that's usually JSON as well (autogenerated from the sheet).
     
    Last edited: Aug 2, 2018
    one_one likes this.
  23. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    This is absolutely absurd. Unity serialization is used for scene serialization and core unity functionality, you're saying that making it so you can change it to a sql backend should have no effect? This cannot be seamless and bloatless unless your definition of bloatless is massively bloated ;)

    OTOH if you meant some kind of optional serialization override, then those already exist via ISerializationCallbackReceiver

    https://docs.unity3d.com/Manual/script-Serialization-Custom.html
     
  24. nhold

    nhold

    Joined:
    May 2, 2017
    Posts:
    50
    You obviously didn't read what I wrote. Sorry about that. Also it seems you are also writing not in regards to bloat but in regards to effect which is different. Yes if you chose to implement and switch to a sql backend to manage your games in-editor serialization system it would have an effect (also would be interesting to see how textures\mesh etc would be done in that system) and potentially bloat (I don't know how well sql can compress\be written to binary or whatever compared to yaml).

    But the effect and bloat would happen on users choice and not affect eachother. ;);) I.e The bloat is by choice.
     
    Last edited: Aug 3, 2018
  25. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    I read what you wrote. The problem is, I don't think you have any actual understanding of what serialization is or what software bloat is.

    "Yes if you chose to implement and switch to a sql backend to manage your games in-editor serialization system it would have an effect (also would be interesting to see how textures\mesh etc would be done in that system)"

    This doesn't even make sense. Unity serialization does not "serialize" textures. Serialization is the process of taking oo in memory representation of data and converting it to a form better suited for persistence on disk. Textures and meshes already have advanced persistent formats (png, fbx, etc). Unity does not 'serialize' that data.

    Likewise, software bloat doesn't depend on if a feature is optional or not. From Wikipedia on Software Bloat:

    Actual (measurable) bloat can occur due to de-emphasising software efficiency in favour of other concerns like developer productivity, or possibly through the introduction of new layers of abstraction

    Any possible method for abstracting out the serialization process to include plugins for remote storage in a database (sql or nosql) would certainly fall under this and be "software bloat".

    No offense, but you really seem to lack a very basic understanding of what you're talking about.
     
  26. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Haha, I know about a VR game that has classes like Uzi : Firearm then they have a AR15 : Firearm with alot of the code from Uzi duplicated into AR15. We use composition over inheritance instead, but if I would have gone pure OOP I would not create subclasses for each implemented firearm :p
     
  27. nhold

    nhold

    Joined:
    May 2, 2017
    Posts:
    50
    I don't think you get it, but it still references the texture through it's metadata that would exist in the database. Potentially you store it in filestream\filetables or some custom implementation that links a meta id with an asset on file on some server. That's what I was referring to there.

    I think it's pretty clear I was referring to file size bloat from the different implementations, which would be from user choice. If you consider abstractions to be a negative bloat then I am okay with that I guess.

    None taken. Enjoy your day. :)
     
    Last edited: Aug 4, 2018