On Fri, Aug 2, 2019 at 5:18 PM Coda Highland wrote:
ECS is an inherently object-oriented technique,
Apparently you don't understand what ECS is.
No, I do. ECS is a lot broader than the SPECIFIC implementation style you're using. It's a paradigm, a way of organizing code and data.
the ID-based technique exists specifically to enable an object-oriented technique to be implemented in languages that don't make it easy to write object-oriented code.
ECS is not an "OOP for languages that don't have native OOP support"
I never said it was. I said that using IDs as a way of linking data structures together was. You can do OOP in non-OOP languages without using ECS.
you've scattered the contents of things around instead of keeping related information together.
Yes, from OOP's point of view ECS looks like "OMG! All the things are scattered!"
The key difference is: OOP is class-centric, ECS is "system"-centric (a "system" is a code which doesn't belong to any class).
Probably you have been programming OOP for too long, so that you have failed to unstick your brains from OOP to make a mental jump to ECS approach.
You didn't catch the main idea of ECS: all logic is being done by "systems" simultaneously for all objects,
that's why we "scatter" objects' properties instead of storing them inside objects.
You're attributing a line of reasoning to me that I don't actually believe, and I believe that stems from a misunderstanding -- but it's not me misunderstanding ECS, but rather you misunderstanding OOP. If you hear OOP and you can't think anything but "Java" (or "C++" or "C#", whatever) then you don't know what OOP is.
The definition of a "system" is code that runs continuously and operates on all entities that bear specified components, and a component is a data structure containing a fixed set of predefined properties. That means that, by the above definition of an object, the combination of a component and the systems that operate on it comprise an object.
This is the code you have written in another post, it's pure OOP objects-centric code:
> function update(dt)
> for _, entity in pairs(all_entities) do
> if entity.update then entity:update(dt) end
> for _, trait in pairs(entity) do
> if trait.update then trait:update(entity, dt) end
The code organized in such way that it doesn't have a possibility to efficiently implement an interaction between objects (for example, between a bullet and a monster).
In ECS such interactions would be implemented with a separate "system".
That's not true at all. I may have cobbled together the Lua example in a hurry, but I use an equivalent construction in my own code all the time. The bullet and the monster both have a trait (that is, a component) that describes their positions in space. A single piece of code (that is, a system) can handle every entity that's capable of being damaged by a bullet (which itself is determined by a trait/component).
I am taking advantage of an object-oriented style of programming in that loop, that's true. I never denied that. As an implementation detail for this toy example, I chose to implement systems as traits that possess an "update" method. This lets me run all of the systems with a single loop. It was convenient. In practice, I usually end up with a few different traits that systems can choose to use in order to have more fine-grained control over things like execution order.
I probably shouldn't have put an update method on the entity itself. That *is* an extension outside of the ECS formalism, and it muddies the discussion to have it there. That was a mistake on my part and I admit to it. (I don't even HAVE an update method in that position in my production code. I create a new component and attach it to the entity even if I'm only using it once.)
The part I'm actually NOT sure about is, in the canonical description of ECS, how you efficiently implement having different types of monsters react DIFFERENTLY to bullets. With traits, it's trivial: you just write another thing that provides the same trait and that has different code behind the interface. Are you supposed to switch on the object type in the is-affected-by-bullets system code? Do you have a separate system for each monster type, and you rely on each of those systems to detect that there's been a bullet interaction? Do you store an ID that's used to look up another system that provides the relevant behavior? I can grok each of these possibilities, and they'd all work (although the first one is DISGUSTING and I sincerely hope it's not the traditional answer) but I don't know what's the most common way to do it in the implementations you've worked with.
And a "trait" in your code is just an a collection of initialization functions
(yes, the only thing OOP could do well enough is to create and destroy objects, all the other logic is beyond OOP)
That's because it's just a toy example. :P I didn't want to implement some big flashy full-featured system with a varied set of behaviors just to prove a point.
That said, no. The initialization function creates an instance of a trait, but what makes it a trait is that every instance returned by the initialization function exposes a consistent interface, which I can access using the trait type. I built it this way just because it's Lua and that's the most straightforward way I know of to do it in Lua syntax.
Sorry, but what you call "object-oriented ECS" is not an ECS ;-)
It doesn't follow the canonical design of the original implementation, but that doesn't make it something that isn't ECS anymore. It still fits all of the core ideals of what ECS is supposed to do. And conversely, something following the canonical ECS design doesn't mean it's not OOP. It's just a particular way of looking at OOP. And, indeed, it's a very GOOD way to look at it.