Spawner - reeseschultz/godex GitHub Wiki
You can add or remove Components directly from a System using the Storage<ComponentName>
.
/// Add the Winner component to the Red team
void my_system(Query<EntityID, RedTeam> &p_query, Storage<Winner> *p_winners_storage) {
for (auto [entity, red_team] : p_query) {
p_winners_storage->insert(entity, Winner());
}
}
However, when you don't know the exact component to add or remove when you write your system, you can use a Spawner
: it allows to spawn a specific group of components that the user can specify at editor time. This feature is generally used by plugins or modules with System
s that doesn't know the component to spawn until the game starts.
Let's imagine the situation where we are creating a plugin that provides a volume; When an entity enters this volume a component (defined by the user at editor time) is added. As you can imagine, the Component
defined by the user, is unknown when we create the System
that detects the overlap.
Instead to fetch a specific Storage
, as shown in the beginning of this article, the system fetches a Spawner
:
void spawner_example(OverlapSettings* p_settings, Spawner<OverlapEventSpawner> &p_spawner) {
// Detect the overlap here an assign `entity_id`
// ...
p_spawner.insert_dynamic(p_settings->component_to_spawn, entity_id, p_settings->component_data);
}
The user can assign any Component
to the Spawner
OverlapSettings
, at editor time: for example we could add the DamageComponent
to the OverlapEventSpawner
so to allow the above System
to spawn such component.
The alternative to this approach is to fetch the Storage
from the World
Databag
:
void bad_system(OverlapSettings* p_settings, World* p_world) {
// Detect the overlap here an assign `entity_id`
// ...
StorageBase* storage = world->get_storage(p_settings->component_to_spawn);
storage->insert_dynamic(entity_id, p_settings->component_data);
}
The above approach works just fine, but it's sub-optimal since the System
will run in single thread: since it's fetching the entire World
.
When you need to add components that you know only at runtime, use the Spawner
is a much better option.
💡 The
Spawner
can only add a specific set of components: this allows Godex to correctly organize the multi-threaded pipeline dispatching.
To allow a Spawner
to spawn a specific component, it's necessary to specify that during the component definition.
In GDScript this is done by implementing the function get_spawners()
:
extends Component
func get_spawners():
return ['OverlapEventSpawner']
var damage_amount: int = 10
While in C++, it's done with the macro SPAWNERS()
:
struct Damage {
COMPONENT(Damage, DenseVectorStorage)
SPAWNERS(OverlapEventSpawner)
int damage_amount = 10;
};
Now the Damage
component can be spawned by the OverlapEventSpawner
.
If you want to create an engine feature or a plugin, you may need to define a new Spawner
so, it's possible to use the following syntax:
struct OverlapEventSpawner {
SPAWNER(OverlapEventSpawner)
};
You can fetch it using the Spawner
utility with a system:
// Way 1
void spawner_example(Spawner<OverlapEventSpawner> &p_spawner) {
// ...
Dictionary data;
data["damage"] = 10;
p_spawner.insert_dynamic(component_id, entity_id, data);
}
// Way 2
void spawner_example(Spawner<OverlapEventSpawner> &p_spawner) {
// ...
p_spawner.insert(entity_id, Damage(22));
}