I'm not fully on-board with the (between-the-lines) assessment in the article that this is somehow inferior to, or should just be a stepping stone towards a full-blown ECS though. At least not for a simple game where performance is not really a concern. My view is that a hybrid approach can actually be better than a 'real' ECS for those kinds of cases, taking the best of both worlds. In my case I don't use any inheritance, only composition, I don't have to fight OOP, but I can still write very clear and concise code to access and manipulate properties of my game objects. It may not be cache-efficient and whatnot, but that really is no concern for my simple game at all, having at most 20 to 40 lightweight entities active at the same time. Physics updates (which could drag down performance if too inefficient) are treated like in a traditional ECS, simply advancing the full physics sim for all entities completely separate from the game objects.
Looking forward to the next article!
That's what worries me to be honest. I'm right there with you that ECS is just the right way, but whenever I can't come up with good arguments against my choices I feel like I'm blinded by faith.
My main argument for why I wouldn't use ECS in a greenfield game project is that a lot of existing tools don't play nice with it. That's about it.
If memory accesses were as cheap as 40 years ago, inheritance vs. composition would be a lofty debate about elegance and architecture and programming language semantics and taste; now it has become a much more practical and detailed tradeoff between difficult design and bad performance.
To clarify for readers not familiar with the motivations here:
Memory access is in fact cheaper than it used to be, as you might expect. The concern is that it did not get cheaper nearly as fast as processors got faster, meaning the relative cost of accessing memory became much higher when you could be doing more calculations in that time instead.
Specially what allows for better performance is accessing memory in a pattern that can be predicted and prefetched. Linear access is best, because looking up a single byte in memory has approximately the same overhead as looking up a contiguous chunk of bytes.
Think Wiley E. Coyote picking up train tracks from behind himself and putting them down in front. If your memory access looks like that then you are less likely to suffer from memory access bottlenecks.
Without a component-based model, sure. But with OOP specifically (which usually just means object hierarchies) you're chasing a lot of pointers and usually (though not always) doing a lot of null checks and other branching inside loops.