I doubt that the dip in performance is anything to worry about especially when it helps with structuring your code better and there are other optimizations that you can apply that would help way more with writing efficient code.
?? Examples are provided in this post.
- Inlining
- Performance - Luau
You’re linking generic Luau docs as if that magically proves your point.
Inlining isn’t a universal “optimization button”, it depends entirely on the call pattern.
If you can’t explain how it applies to this specific context, then you’re not actually making an argument; you’re just name-dropping features you don’t understand.
100% confidence and 0% comprehension
take a look at my previous message
Your right, inlining isn’t an universal optimization, however OP asked for ways to write more efficient code and inlining is one of them.
There hasn’t been any code provided so there’s no specific context here. I’m trying to argue that the performance of single-script architecture is negligible and that’s there many other better ways to write efficient code (some of these ways are showcased in the luau docs about performance) over just avoiding single-script architecture when it could benefit the structuring of code.
OP has asked about how to structure his code well too though but yeah, your right.
This is actually hard to do when you first start programming (under five years in).
A few years from now, when you look at your old scripts, you’ll be shocked at how much needs to be improved because now you are better and understand way more than you did then. So stick to reaching for more, but understand that the new you will eventually rewrite most of it. Try not to slow your progress with things you will eventually master. Research is the key here.
Nah bro it’s way simpler than that.
Every programmer actually dies when they go to sleep. That’s why everyone codes at 3 AM, they’re literally fighting for dear life.
And when you wake up and look at your old code thinking “Who wrote this?”
That’s because it wasn’t you. That was the previous instance.
Fully verified by the International Bureau of Developer Lifecycle Management™.
This is lowkey one of the theories I have about life. That consciousness resets during sleep, and you just inherit the memories and the body of the last consciousness. Basically that you die everyday but because there is a form of continuity you don’t figure it out. This is just a theory of mine but interesting to see something similar here.
Nah, bro, it was just a joke.
As long as this funny electric current stays in your brain, you aren’t going anywhere.
It’s just the brain hoards info and compiles it, ending up stripping away all comments, it seems.
Fundamentally it is not categorically an ECS if you don’t provide an interface to query for entities, you can look this up on wikipedia or defer to the opinions of technical fellows in this field such as Sander Mertens and/or Michele Caini.
Quick history lesson for those that appreciate it, Ivan Sutherland made Sketchpad in 1963 that became the blueprint that has evolved into what we call ECS today, with the earliest example of an interface capable of querying by constraints. This was very optimal but if you read the paper on it, the querying capability was really the selling point.
I believe you are alluding to maintaining perfect SoA groups yourself instead of letting the ECS library do it for you. We can go back and forth on the practicality of this but I don’t think that is productive of our time, so I will let others who are interested chip in on this.
Edit:
Some solid points I would like to bring up:
- An ECS isn’t just a bunch of arrays, it also keeps track of entity liveliness
- You still need a generic entity abstraction. If you have 10 enemy types and you do an attack, you will have to get the
Healthcomponent from an entity of an unknown (at compile time) type. Just for this you’ll end up implementing an ECS - There are always a few components that are dynamically added/removed. No game is 100% static
- Even for small to medium sized games this becomes very cumbersome. Imagine having 100 entity kinds (not that many, most games have over a hundred archetypes) and ~100 systems (also medium sized). Are you going to manually maintain a 100x100 matrix of which systems match which entities?
If you still want to do this, I highly encourage that you pursue it in such game and tell everybody of how well it worked out for you.
Separately I would like to point out that the metatable usage is minimal in jecs, it is only used for queries for the generic iteration syntax but you can circumvent this by iterating the archetypes directly which have very well known structures, arrays! Also to hopefully soothe peoples’ fear, jecs is pretty optimal as I carefully ensure that most if not all internal functions in hot paths are inlined using compiler remarks. It also utilizes upvalues to accelerate chasing pointers.
In fact, Sander Mertens, the creator of the C library (flecs) have actually helped me immensely in writing large parts of the codebase personally which I am very grateful for, so at worst it still has great algorithmic optimizations, to the point it even beats several naively implemented ECS libraries implemented in C and Rust while competing in benchmarks with their respective languages, i.e. jecs using luau.
It is your prerogative for what you would like to use, I make no claims that it is for you, but anecdotally I think it helps me hit the ground running quickly for my own games and at a baseline is more optimal than many alternatives.
There are so many opinions out there, it’s hard to figure out; what would be a reasonably efficient and simple method? And there’s a lot of talk about ECS. Could you guys explain a little bit about it?
And thank you all for your diligent answers.
I don’t recommend learn ECS because ECS is very hard for programmers with less than 2 years of experience, for good start i recommend EDA-(Event-driven-archtecture)
Also when receive a good programming experience then you can combine EDA + ECS
I’ve been programming on Roblox for over four years. Does that mean I can start learning ECS right away?
then this changes everything, of course, study whatever you want
Here is my list of things I recommend you study:
- understand EDA and ECS fully and start using them in practice
- find some modules for corutine/“thread” pooling and use them
- Also, always use advanced network like a Packet or BridgeNet2 libraries. Don’t listen to those who advocate remote events; they’re wrong in their “native” type of thinking.
ECS stands for Entity Component System and it’s a pretty descriptive name.
You have entities (which are just containers that basically tie pieces of data(components) to it.
Components which are basically just pieces of data
Systems which are things that do work on a specific component or a specific group of components.
ECS has a few major benefits.
- Composition over inheritance makes your code easier to extend and overall reduces coupling. ECS does this basically by default since entities hold components which is composition.
- Tends to be better for parallelization
- Often is less complex logic at the data level (often harder to follow overall though)
- Really great for bulk updates. Normally all data (each component) is all stored in an array and you often process the array of components all at once. This matters less in lua(u) since you have less control over memory layout, but this does lead to natural speedups in other more direct languages like c/c++/rust because it is more optimized for how cpus actually work under the hood (lookup CPU cache for more details, but it won’t help you a ton in lua(u)). Also you often loop through combinations like everything with a “renderable” and a “transform” component for my renderer to quickly get everything it will need to draw for example. I will note that it still can help in lua(u) it’s just more indirect usually and not quite to the same level.
What it’s bad at
- It’s not any faster if you need to update a specific entity. If you need to apply a temporary speedboost for example to be ‘ecs’ style you should submit it to a system that applies speed boosts. You can if you want query for the relevant items and directly update the right components, but that’s not super ecs like and isn’t any better than other methods. Though the system it updated may still run slightly faster for it.
- Logic tends to be spread out. ECS focuses on data locality, not strictly logic locality (oop is more logic locality). This is not a hard rule, but it is a lot of what makes ECS harder to reason about and follow. You can minimize this from good structure.
- In lua(u) the major benefit of speed is lessened so part of the positives that normally are here aren’t quite as strong.
Honestly I often avoid ecs as most of the code I write would be complicated by it. The best place for it imo is inside game engines (specifically the scene subsystem) as that’s where its benefits shine the most. It can be used, and used to great affect in other places, but it’s not a silver bullet. It solves specific problems and is a tool designed for those problems. Good to know of so you can use it when you hit those problems though. It’s really great for when you have a bunch of objects you need to update a lot and can update independently or with strict known dependencies on other components. Often though scripts don’t really do that. They often target specific things at specific times, so you would mostly use it for architecture and cleanliness but there are other patterns that are often simpler and easier to reason about.
Thanks for the detailed answer. And I have a question. If so, does that mean ECS is not that important and it’s okay not to use it? If so, which one is best to use? Is OOP the way to go?
Honestly just use whatever leads let’s you write the cleanest most readable and most loosely coupled code you can without overthinking it. I personally favor an OOP flavored composition only style (you’ll never see any inheritance really from me with only a few exceptions). I like having objects be what I interface with. The most important part is just make it so that you can edit a system or ever rewrite it without breaking everything else (loose coupling). That way if you ever do make mistakes, which you will, you can update only the parts that are problematic and replace it with a better system without having to rewrite every system that depends on it too.
As for ECS it’s nice for what it is good at, but you probably don’t need it for roblox no. As for OOP, yesish? I don’t love some of traditional OOP like inheritance, but I do like grouping my objects and data together. Honestly OOP like interfaces for me are more of my style than a specific architecture (though it technically fits the architecture definition too). You can look into design patterns to know what will be useful where but realistically you’ll always use several different ones, just falling back to a familiar style when it doesn’t matter too much. I recommend starting with what you feel comfortable with and seeing how you can make it so changes don’t break everything (it will always break some things, just limit how far breaks can go). After that it’s mostly just implementation details that only matter when you are actively writing that part and won’t matter very much after assuming the code there hits perf targets.
tldr; write code that is loosely coupled and make it maintainable. The details don’t matter too much after that since if you find a better way you can just do that simply later.