-
Notifications
You must be signed in to change notification settings - Fork 1
Components
A component is data associated with an entity, differentiated by its schema, a template. Or, more specifically, that template's integer id.
A schema is defined using Harmony's makeSchema
function:
const Health = makeSchema(world, formats.float64)
makeSchema
returns an integer, the id of the newly created schema.
Harmony exports a formats
object that contains multiple numeric types, e.g. uint8
, int16
, float64
, that are used to define the shape of schemas, the process of which is discussed below.
The term "native" here is misleading; as we will see, the other kind of component that Harmony can store is powered by a native, although esoteric, language feature.
Native components store plain built-in JavaScript values. This currently includes numbers and objects, and will include arrays, maps, and sets in the near future.
We have already defined a number component in the Health
component example above. Any of Harmony's numeric formats (uint, int, float) do not matter here: the component is simply stored in an array of built-in, IEEE 754 numbers.
Native object components are created by providing an object to makeSchema
, where each key is a fixed property name, and its value is either a format or a nested object conforming to the same rules:
const Attributes = makeSchema(world, {
strength: formats.uint8,
dexterity: formats.uint8,
// ...
modifiers: {
strength: formats.int8,
// ...
},
})
A component can be added to, or removed from, an entity using set
and unset
, respectively. Components are queried and mutated using queries.
Binary components are specialized components stored in a struct-of-arrays architecture. An instance of a native Attributes
component would look something like the following:
{
strength: 18,
dexterity: 14,
}
This object is stored in an array, where the value of each index of the array corresponds to the component of a unique entity:
[{ strength }, { strength }, { strength }]
A struct-of-arrays architecture looks like the following, where the values of each component property exist in their own array:
{
strength: [18, 8, 8],
dexterity: [14, 18, 8],
// ...
}
Like with the array of objects, the index of each array of numbers corresponds to a unique entity.
In Harmony, each array that constitutes a binary component is a TypedArray
– an array-like view, or proxy, to a binary data buffer called an ArrayBuffer. ArrayBuffers are very fast to read and write. They can be iterated, mutated, and serialized performantly relative to objects. However, they have many rules and drawbacks (that will not be discussed here) when compared to built-in arrays.
A binary component is defined using the makeBinarySchema
function. This function follows similar rules to native schema, although it does not support nested objects (yet):
const Attributes = makeBinarySchema(world, {
strength: formats.float64,
})
A single-array storage can also be defined binary component, almost identical to the original Health
example above:
const Health = makeBinarySchema(world, formats.float64)
Binary components are queried the same way as native components. However, due to the difference in storage, component values per-entity are read and modified a bit differently.
Reading and updating the value of a binary component for an entity inverts the accessor. Instead of reading the property intuitively (as with with native components):
attrs[i].strength++
One would need to access the property of interest first, and then access the index of the target entity:
attrs.strength[i]++