Entity-Component-System (ECS)
Updated: Oct 16, 2024
ECS is the central interface to the Spatial SDK runtime. ECS is composed of entities, components, and systems. In Spatial SDK, components and entities constitute the entirety of our data model. Entities are essentially just IDs, represented as long integers, that group your components. Within these entities, a set of APIs allows access to information about the components, but the only data that resides directly on the entity is the long integer itself.
Components and attributes
Components are reusable data units, a little bit like rows in a traditional SQL table. They hold primitives, called attributes, and each component attached to an entity represents a piece of the data model. Components are declared in the code as structures of attributes.
Entities, components, and attributes form a nested hierarchy, where entities contain groups of components, and components contain lists of attributes.
- Attributes: These are the lowest level of data storage and contain things like integers and maps.
- Components: These reusable data containers hold attributes of various Spatial SDK primitive data types.
- Entities: These are wrappers around components and can represent things.
If you go through Spatial SDK’s tutorials, you’ve already encountered several components in previous sections:
- A panel component to showcase the Android UI
- A mesh component to render a cube.
- A transform component to describe the cube’s location.
These components hold data and allow you to modify an entity.
Consider the following code:
This code creates an entity. However, this entity won’t perform any actions or display anything in the scene. The entity exists, but is invisible to the user and does not impact the scene’s functionality. To make the entity perform a function, you must give it data. This is where reusable components come into play.
Add data to the entity code above by using a Mesh
component:
Entity.create(
Mesh(Uri.parse("baseball_cap.glb")),
)
Behind the scenes, the Mesh
component stores the URI you passed in. The Spatial SDK codebase defines the Mesh
component as follows:
class Mesh(
mesh: Uri = Uri.parse("about:blank"),
hittable: MeshCollision = MeshCollision.LineTest,
defaultShaderOverride: String = "",
defaultSceneOverride: Int = -1,
) : ComponentBase() {
var meshInternal by StringAttribute(R.string.mesh, this, mesh?.toString())
var mesh: Uri
get() {
return Uri.parse(meshInternal)
}
set(value) {
meshInternal = value.toString()
}
var hittable by EnumAttribute(R.string.mesh_hittable, this, MeshCollision::class.java, hittable)
var defaultShaderOverride by
StringAttribute(R.string.mesh_default_shader_override, this, defaultShaderOverride)
override fun typeID(): Int {
return Mesh.id
}
companion object : ComponentCompanion {
override val id = R.id.mesh_class
override val createDefaultInstance = { Mesh() }
}
}
This class uses attribute parameters to store and retrieve data from the internal data model. This allows you to store and retrieve the meshUri
you passed in. However, it doesn’t provide functionality. To see how adding a Mesh
component to the entity causes the system to display the entity as a 3D model in the scene, you need to understand systems.
When you are finished with an Entity
, regardless of the type (Panel
, Mesh
, or anything else), you can use the following code:
Spatial SDK’s systems will clean up your destroyed entities.
Components assign data to an entity, but they don’t provide functionality. To add functionality, we need to connect systems to our entities.
Spatial SDK has a rendering system that checks every entity in the scene. If an Entity has a Mesh
component attached, the SDK retrieves the file at the meshUri
and renders it in the scene. The rendering system is large, but this pseudo-code demonstrates how it works:
class RenderingSystem(): System {
// Systems are run by calling the execute function
override fun execute() {
// Get every Entity with a Mesh Component
Query.where{ has(Mesh.id) }.forEach { entity ->
val mesh = entity.getComponent<Mesh>()
val meshUri = mesh.meshUri
scene.renderGLTF(meshUri)
}
}
}
Systems are the encapsulation of on-tick functionality for Spatial SDK. For example, you can extend the code above to check if the entity has a Transform
component attached. If so, Spatial SDK will adjust the location where it renders the model based on the data stored in the Transform
component.