3 ms·
> Its not impossible to solve this with interfaces and composition but those solutions are sub-optimal. The growing popularity of ECS and data-oriented design
by yellowapple 3y ago
> Its not impossible to solve this with interfaces and composition but those solutions are sub-optimal.
The growing popularity of ECS and data-oriented design in game engines suggests otherwise: keeping components separate from entities enables both performance enhancements and separations of concerns that are much more difficult to achieve with the traditional inheritance-based approach. To illustrate a bit:
> it is very common to have a base object such as "Node" that has some properties that every object in the scene graph must have, which all share the same implementation. For example they should all have a parent property and a collection of children, and ways to modify those properties.
You don't need subclasses for that; you just need a table of entity IDs (where both the things to render and the scene itself are entities) and parent IDs, which you can then recursively walk to get the entities you want to render:
WITH RECURSIVE entity_children AS (
SELECT id, parent FROM entities
UNION ALL
SELECT ec.id, ec.parent
FROM entity_children AS ec
JOIN entities AS e ON ec.id = e.parent
)
INSERT INTO scene_entities (scene, entity)
SELECT $scene_entity_id, id
FROM entity_children
WHERE parent = $scene_entity_id;
(Obviously you probably won't actually be running SQL queries in a game engine's rendering loop; this is just to illustrate the logic.)
Once you've got that list...
> They'll also share methods such as "render" which probably must be overridden in every subclass.
You don't need subclasses for that; you just need a table of entity IDs and things to render, which you can then query and send to the GPU:
INSERT INTO some_buffer_in_GPU_memory (entity, mesh, texture, position)
SELECT se.entity, em.mesh, et.texture, ep.position
FROM scene_entities AS se
JOIN entity_meshes AS em ON se.entity = em.entity
JOIN entity_textures AS et ON se.entity = et.entity
JOIN entity_positions AS ep ON se.entity = ep.entity
WHERE se.scene = $scene_entity_id;
(Again: you probably ain't actually using SQL for this; this is also overly simplified, since most modern game engines use all sorts of other stuff besides a mesh, texture, and position when rendering something. Note also that "em.mesh", "et.texture", and "ep.position" need not be actual meshes/textures/positions, but could instead be indices into buffers already on the GPU.)
The key advantage in both of these cases is that the parent/child data and the render data can live where they make the most sense, and can be processed by independently-running systems with minimal contention. This is critical for processing game logic in parallel - something which the game industry is learning the hard way with legacy engines that can't fully exploit multicore hardware.