5 ms·
DOD has simple solutions for this, too. A popular one is to group your entities by "archetype"- you have two arrays of Point2Ds, one for those without a Z coor
by Rusky 6y ago
DOD has simple solutions for this, too.
A popular one is to group your entities by "archetype"- you have two arrays of Point2Ds, one for those without a Z coordinate and one for those with a Z coordinate. Now if you want all Point2Ds, you loop over both arrays; if you want all Point3Ds, you just loop over one. (This generalizes cleanly to larger numbers of "components.")
In some ways, this is actually quite a bit easier to use than OOP composition+inheritance. You don't need to build your compositions up front as classes or types- you just build them directly as values, throw them all into your archetype system that manages the arrays, and query them however you like. It feels similar to a relational database.
- dragontamer 6y ago> A popular one is to group your entities by "archetype"- you have two arrays of Point2Ds, one for those without a Z coordinate and one for those with a Z coordinate. Now if you want all Point2Ds, you loop over both arrays; if you want all Point3Ds, you just loop over one. (This generalizes cleanly to larger numbers of "components.") But now your Point2D code needs to be aware of the Point3D code. Violating the open-closed principle (which is an OOP principle, but one that's extremely useful in software engineering). You NEVER want to change working code. That's the fundamental basis of the open-closed principle. Figuring out how to extend the code to new functionality ("open to extension"), WITHOUT risking any breaks on old code ("closed to modification"), is core to software engineering principles. Any "solution" that relies upon changing Point2D is null-and-void to my software engineering brain. You cannot build libraries or reusable code if your solution is "rewrite the old code to support new functionality".
- Rusky 6y ago> But now your Point2D code needs to be aware of the Point3D code. Not at all! You write the archetype management code once, and then the Point2D code just asks it for Point2Ds, nothing else, and that doesn't change no matter how you use Point2D elsewhere.
- dragontamer 6y agoI'm having difficulty understanding how your proposal works. In the beginning, there was one struct-of-arrays, the Point2D x and y arrays. int Point2D_x[]; int Point2D_y[]; We also have a number of functions that use these two arrays. Point2D_foo(Point2D_index), Point2D_bar(Point2D_index). Then, later, we discovered the need of Point3D code. Leading to the creation of one more struct-of-arrays (or 3-more arrays). int Point3D_x[]; int Point3D_y[]; int Point3D_z[]; Point3D_foo(), Point3D_bar(). Theres a 3rd function, Point3D_baz() that is specific to 3d code, but foo() and bar() are defined to be identical as the 2d versions. I haven't really figured out what parameters these 3d-versions of foo, bar, and baz would be like. All "old" Point2D code was written only knowing about the first two arrays (Point2D_x and Point2D_y). The new Point3D code can be written knowing about all 5 arrays. But I'm having difficulty seeing how you extend the old Point2D code to work on the new Point3d Arrays in the DOD case.
- Rusky 6y agoHere's a library in Rust that works this way: https://docs.rs/hecs/ https://docs.rs/hecs/ You don't just have bare arrays sitting there. You have a separate part of your program that is responsible for managing them, playing a role similar to a relational database. Now, to be clear, DOD/ECS/whatever doesn't mean "change all your Point2D methods to take an index instead of a `this` pointer." That gains you nothing on its own. If your `foo` and `bar` are just working with individual Point2Ds, then you can just write them that way- hand them a `Point2D* p` or `int* x, int* y` or something. It's when you're working with large collections of these things, which exist as pieces of larger (conceptual) entities, that the arrays become important. And you can't know what that looks like until you have a particular operation in mind- are you stepping a physics simulation, or computing a shortest path, etc? Now the trick is to write those larger-scale bulk operations in terms of queries, instead of direct access to the arrays. If your pathfinding algorithm is 2D, you can run it on all Point2Ds with a query for x and y components. If you later add a z component to some of your entities that already have x and y components, the pathfinding algorithm will keep working and just ignore the z component.
- 6y ago
- HelloNurse 6y agoExcuse me? If it's called Point2D, it must have two coordinates. The type should be called Point and it should have an arbitrary number of coordinates (which seems a bit silly, since a Point2D and a Point3D are different types, as are points and vectors that have exactly the same representation)
- Rusky 6y agoI'm just running with the original example, contrived as it may be. Nobody here is actually talking about anything specific to points of any dimension.
- dragontamer 6y agoAnd I appreciate your efforts. Thanks.