Resources
Not all data in a world is associated with specific entities. This often applies to parameters (such as a time step), global state variables (such as the current time), or spatial data structures (such as a grid displaying entity positions). These data are called resources.
Defining resources
Similar to components, resources are defined via structs. That is, each resource has a specific type, and having two resources of the same type is not possible.
However, in contrast to components, the (potentially)
used resources do not need to be known at compile time
but can be dynamically added to the world. Due to
the current limitation that Mojo does not support
reflections, the resource type needs to be specified
via a unique TypeId
identifier, which in turn is
constructed from a string identifier. This requirement
is enforced via the IdentifiableCollectionElement
trait.
By convention, to avoid name clashes, the string identifier should
include the package, module, and type name of the resource.
For example, if we define a resource Time
in the module my_module
in the package my_package
,
the resource identifier would read my_package.my_module.Time
.
from larecs import World, Entity, IdentifiableCollectionElement, TypeId
@value
struct Time(IdentifiableCollectionElement):
# The type ID must be specified via an alias `id`
alias id = TypeId("larecs.resources.Time")
var time: Float64
Adding and accessing resources
Resources can be accessed and added via the resources
field
of World
. Adding a resource is done via the
resources.add
method:
# Add the `Time resource
world.resources.add(Time(0.0))
The resources
attribute also allows us to access and
change resources via get
,
get_ptr
and
set
methods resembling their
component-related counterparts of World
.
# Change a resource value via a reference
world.resources.get[Time]().time = 1.0
# Get a pointer to a resource
time_ptr = world.resources.get_ptr[Time]()
# Change the resource value via the pointer
time_ptr[].time = 2.0
The add
and the set
methods also allow to add or set multiple resources at once.
For example, consider the additional entities Temperature
and SelectedEntities
.
@value
struct Temperature(IdentifiableCollectionElement):
# The type ID must be specified via an alias `id`
alias id = TypeId("larecs.resources.Temperature")
var temperature: Float64
@value
struct SelectedEntities(IdentifiableCollectionElement):
# The type ID must be specified via an alias `id`
alias id = TypeId("larecs.resources.SelectedEntities")
var entities: List[Entity]
We can add and set them as follows:
# Add multiple resources
world.resources.add(
Temperature(20.0),
SelectedEntities(List[Entity]())
)
# Set multiple resources
world.resources.set(
Temperature(30.0),
Time(2.0)
)
In contrast to components, resources can
be “complex” types with heap-allocated memory,
as demonstrated above with SelectedEntities
.
We can use them to store arbitrary amounts of data.
# Create entities and add them to the selected entities
for i in range(10):
entity = world.add_entity(Position(i, i))
world.resources.get[SelectedEntities]().entities.append(entity)
Removing resources
One or multiple resources can be removed via the
remove
method. The existence
of a resource is checked via the has
method.
# Remove the `Time` and the `Temperature` resource
world.resources.remove[Time, Temperature]()
# Check if the `Time` resource exists
if world.resources.has[Time]():
print("Time resource exists")
else:
print("Time resource does not exist")