Transfering OOP knowledge to ECS/DOTS

Hi all,

Apologies for the noobie question in quite an advanced part of the forum but I’m a little anxious about this so thought best to ask. I’m currently about 4 months into learning C# and Unity, and having a great time. I’m still a total beginner but I’m starting to understand how it works. I’ve been smashing through courses and have some more advanced ones lined up, however with all this talk of the death of OOP and ECS being far superior I’m starting to worry I’m going to be learning outdated material. Should I be throwing myself into ECS, and understanding how to construct system of data or should I continue upon my current path or progessively more challenging OOP courses? In terms of creating methods and funcionality how worlds apart are OOP and ECS? Again sorry if this is nooby question but I’m finally following my dreams of gamedev and want to make sure I’m on the best path.

This is one of the most important lessons to learn as a software developer: is the new bright and shiny framework/pattern/language/library going to solve a problem facing you right now, or is it going to distract you from completing something? I’ve been programming professionally for over 20 years and I’m still learning that lesson. There is always a newer, shinier tool that promises to solve all your problems, but there are no silver bullets.

Obviously we want to learn new tools and techniques, but we’ve also got code to deliver. Is there something lacking in MonoBehaviour that is preventing you from achieving your goal? Is it something that ECS will solve?

For me, ECS lets me write code that I find easier to reason about. That’s with 2 decades of OOP behind me. Others seem to struggle with it. A lot depends on how your mind works. If you are really serious about becoming a true professional, I’d suggest completing your OOP path (keeping in mind you’re never finished learning), and then go and do the same thing in ECS/DOTS. Compare the results. See what’s similar and what’s different. You’re going to learn a lot more that way than somebody telling you which path to take, at least in this case. Both paths are fine.

I wouldn’t worry about making sure you’ve picked the “right” path. I cannot honestly say anything I’ve done with programming over the years was a complete waste and didn’t teach me something.

One of my favorite sayings: good judgement comes from wisdom. Wisdom comes from experience. Experience comes from poor judgement.

6 Likes

The point where ECS (theoretically) widely adopted and florishes maybe way after 2022+ where higher level API of DOTS would be available… I think by then you will still get to use plenty of OOP. And even after you may still use OOP together because you may know weakness of ECS pattern where OOP was more straightforward. Mostly things that do not need performance in the first place, then OOP wins because you get self documenting code that is fast to write and teach. OOP is very good for human/your team. You can also setup the scene in OOP then with the conversion workflow they became ECS at runtime, which you will need both knowledges to get the right shape of ECS data to counters OOP’s performance weakness.

2 Likes

ECS and OOP are both just tools in your toolbox, one may be a better fit for certain problems over the other, and you may find that you vastly prefer one over the other.

In my experience (entirely self-taught) OOP makes it MUCH easier to write horrible code. That doesn’t make it inherently worse, maybe just harder to use right. And there are definitely still problems that would be a nightmare to solve in ECS while in OOP they are trivial and perform no worse, so why go through the headache of trying to force the solution into ECS?

There’s a great blog post - inspired by someone from Unity no less - that shows why we should be skeptical when people make statements like “OOP is dead”.

2 Likes

You can simulate polymorphism in ECS. Say you have a component called AimComponent. It has a float3 value aimPosition. AimComponent will act as an interface, somewhat akin to

public interface AimComponent
{
     void GetAimPosition();
}

An interface needs concrete implementations. In the ECS case, we “implement” ways of writing to AimComponent by having different pairs of systems and components act in unison to write to Aim. Two examples are AimFromFoeSystem and FoeComponent, and AimFromInputSystem and InputComponent. The first pair represents aiming at an enemy - useful for AI behavior. The second pair represents aiming at an inputted location - appropriate for player-defined aiming. The systems will write to AimComponent from their respective “implementation” components. Ensure polymorphic behavior by writing authoring components which group either AimComponent and FoeComponent, or AimComponent and InputComponent, together. Now if you want an Aim implementation that listens for player input, just attach an AimFromInputAuthoring to the GameObject. You can attach other authorings whose components read from AimComponent, but the implementation of how to aim is abstracted away.

1 Like

If you view ECS as an optimization technology and remember the 80/20 rule of code optimisation:

80% of your code will not need optimising.
20% of your code will, but only profiling your code when running will show you where that 20% is.

So in theory you should be able to get away with writing 80% of your code in OOP and only need to translate 20% to DOTS.

Think of DOTS as extracting the methods and data they use out of your objects and putting them into individual systems and your halfway to an ECS optimised game.

Note: The other half may get tricky.

2 Likes

Thanks for all the responses everyone! I’m new to programming in general so still trying to understand the basics. It seems like I’m overthinking it and will just crack on with the current path of courses and worry about writing “perfect code” later. When your new to something at an older age, you often worry so much about making mistakes that you forgot that’s how everyone else learned.

2 Likes

That’s a good mentality to have, but think about it this way: why make the mistakes yourself when you can learn from the mistakes of others? If you have any doubts or questions, search it up. Chances are someone in the past was in the same situation as you and has found a solution. There’s a treasure trove of experiences in these forums :slight_smile:

I disagree with OOP and DOD both being “tools in your toolbox”. They are completely orthogonal philosophies on programming. One is right and one is wrong. I’m sorry if that offends anyone (not), but OOP works against the model of the machines we program and creates highly inefficient programs. Objective fact.

Back in the day, DOD used to simply be called “programming” and OOP was the shiny new methodology that everyone picked up and ran with, and now OOP has thoroughly corrupted the minds of every single person who has learned to program in the past 20 years. Just look at the threads here… how many experienced professional programmers can’t even reason about their memory layouts or understand how their cache works?

IMO if you’re just starting out, do yourself a favor and learn how to program the right way from the get go. OOP is a fool’s errand and the great folly of the software industry.

I can see your point of view but you need to remember that OOP came from Simulation languages that allow developers to break problems down into components that match problem domain entities. And it is the job of compilers to convert code to be more machine friendly.

Actually if you converted/compiled OOP methods into systems and their data into entities then you would have an OOP to DOTS compiler. Or human friendlier domain relevant code into machine friendly DOTS code.

And even with boilerplate reduction the amount of code you need to write for DOTS systems is way more verbose than the code in an OOP method.

Except they don’t. Compilers suck. They optimize like garbage (just look at how well c++ compilers auto-vectorize), and even then, it’s all magic. OOP embraces random access and ignoring the physical model of the machine in favor of a more “real world” mental model, and at the end of the day, that’s a total failure. It’s wrong, it’s a dead end and everyone who has to write high performance code knows it – especially as clock speeds (memory clocks in particular) plateau and parallelization becomes the name of the game. That’s why Unity is making this huge effort to essentially rewrite the engine to unFUBAR the situation. it HAS to be done if they want serious performance, there is no other way.

OOP is a mind-virus and I believe it is directly to blame for the absolutely sorry state of the software industry.

Although I agree with your reasoning, you’re not considering the full picture. OOP has been so widely adopted because it is easy to learn for beginners, because it makes solving abstract problems easy.

Even if overnight everyone switched to DOTS, you think the ‘software industry’ wouldn’t be in less of a ‘sorry state’? Any field has its professionals and… unprofessionals. There will still be bad code, there will still be lag, bad practices and so on.

And never you mind that the vast majority of software doesn’t need all the performance DOTS brings to the table, but it does need quickness in implementation, manpower etc.

What you should tell the OP, who by his own admission he’s a complete noob to both concepts, is not to adopt this concept or that concept. You should tell him to try both so he can make his own decision. Any rational person will chose the path that brings the best benefit depending on what they’re dealing with. If they wanna make a cpu intensive game, they’ll choose dots. If they wanna make a text messaging app, they’ll choose oop.

3 Likes

My advice is to learn OOP for the next 6 months.

Frankly, noob questions are useful. It helps the devs step back to answer them. I am sure unity devs are discussing this very topic as they’re simplifying DOTS API so that, in 10 years, the noob question will be “can you old geezers tell me another funny story about OOP?”.
That’s my own bias, I think the choice is personal so I’ll tell you what I’ve tried and maybe something will resonate with you.

When I started programming it was in Pascal, there was no objects back then and it was so easy. I paired up with a buddy to make an iterated fractal systems explorer for uni. The task division was simple: he does the math, I do the UI. Two big code files and that’s it. We share data via two global static arrays, my buttons call his functions, as calculation progresses he increments a global iteration counter and I display a progress bar. Dead simple. Total 2000 lines. Not a bug. Not a crash. Fast as hell and the gui was instant.

A decade later I went back to programming. Everybody said Java and OOP is the way so, like a dullard, I followed the herd, hired a tutor to learn Java. I couldn’t do shit in it because brain wasn’t able to twist simple things into inheritance and the odd idea that objects contain data and also do things, plus java needs 20 different libraries so you turn into a librarian, really. I got nothing done.
Game making itch got too strong, bought a Unity license and just did things. Bugs everywhere (mine, not the engine) spaghetti code galore, aweful but it runs.
After a few protos and one shipped game where I learned saner programming from someone, keeping OOP at bay with max one level inheritance, very flat, easy.
But as things got complicated between agents, the mess of unreadable code was replaced by the question of actor, verb and object where the verb sometimes is active “player orders agent to move” and other times passive “inventory received a death event so drop everything”. Doesn’t flow. Headaches.

So I started decomposing things and turned everything into agents adding requests into a queue and systems processing the queue and doing things to agents. One direction. All active verbs. It slowly blurred into what’s called ECS. Brain at peace.

If you like that way of thinking, it can be done without DOTS.
I say that because DOTS changes a lot, also the API is very complicated and verbose, for example DefaultGameObjectInjectionWorld mean active.
Plus there is near zero debugger, you don’t really know what transforms your data and that leads to bugs.
All that compounded makes prototyping very very hard.

1 Like

You are using a very narrow definition of “efficient” and ignoring the problem domain. Not all software needs to make maximum efficient use of the computing resources. There’s the efficiency of the development team, maintainability and the ability to actually find developers who can build and maintain it.

To suggest OOP is a fool’s errand ignores the amazing software that has been built on it (and, to be fair, some “I’m amazed that even works” and “I’m amazed you thought that was a good idea to create in the first place, much less convinced an entire team of developers that it was a good idea” software). One can build a steaming mess of awful code in any style, and a careful, professional developer can build efficient, maintainable and accurate software in any style (and, yes, even in PHP if you must).

To suggest there is “one true way” to create software is doing a disservice to people entering the field. Creating software requires balancing features and performance against the skills of the people writing the code and the amount of time available or that you are willing to spend. Without having a wide array of skills and experience you won’t have the wisdom to make a good judgement about what is the correct approach for a given project.

2 Likes

If you think about it the day we have the perfect programming system is the day programming in any already ‘solved’ domain will become like working with LEGO… You will just need to link the relevant systems up to each other and hit play.

Unity with components got very close it allows assets to be shared and traded that provide the common features needed across multiple games.

But compiled OOP does not cache well as you can quickly end up with a calling/messaging/referencing mess of objects that have to talk to each other to do one simple thing e.g. UI element in HUD needs to know the ammo count; so asks player to ask current weapon to ask magazine what the ammo count is.

In theory with game atomic* systems in DOTS could you achieve LEGO like game development or would the communication between systems still need to be hand crafted?

In DOTS would you just link the UI element in the HUD to the ammo count as a Combined Data Entity e.g. (String | Int) then what happens when the player changes the weapon?

*Not fully atomic logical operators as this would just consume too much bandwidth for too little processing; remember CPUs have Code and Data caches and limited memory bandwidth that needs to be used optimally.