Summary
ChaseComponent currently stores a direct EntityHandle to its chase target. This creates a tight coupling between the chasing entity and a specific entity instance. Instead, ChaseComponent should depend on a new BeaconComponent — a tag/marker component attached to the entity that should be chased. The ChaseSystem would then query for the beacon rather than resolving a stored handle.
Current Design
ChaseComponent holds a direct EntityHandle to the target:
class ChaseComponent {
EntityHandle target_{}; // ← direct coupling to a specific entity
public:
void setTarget(const EntityHandle& entityHandle) noexcept;
EntityHandle target() const noexcept;
};
ChaseSystem resolves this handle every frame:
const auto entityHandle = cc->target();
auto go = updateContext.find(entityHandle);
The builder DSL also wires the target via EntityHandle:
.withAi([&](auto& aib) {
aib.chasing()
.target(shipGameObject.entityHandle()) // ← EntityHandle passed at config time
.seekInterval(0.05f);
})
Problems
- Tight coupling: The chasing entity must know the exact
EntityHandle of its target at configuration time.
- Fragile on respawn: If the player entity is destroyed and re-created (e.g., from a pool), the stored handle becomes stale. Every chasing entity would need to be updated.
- Inflexible: Cannot easily switch chase targets at runtime or have multiple entities act as beacons.
Proposed Design
1. Introduce BeaconComponent
A lightweight marker component attached to the entity that should be chased:
// helios/engine/modules/ai/components/BeaconComponent.ixx
class BeaconComponent {
// Tag component — no data needed initially.
// Could later carry a beacon type/channel ID for selective chasing.
};
2. Modify ChaseComponent
Remove the EntityHandle target_ field. The chase target is no longer stored; it is discovered at runtime via BeaconComponent:
class ChaseComponent {
float cooldown_ = 0.0f;
float cooldownTimer_ = 0.0f;
bool isEnabled_ = true;
// EntityHandle target_ removed
};
3. Modify ChaseSystem
Query for the beacon entity via View instead of resolving a stored handle:
void update(UpdateContext& ctx) noexcept {
// Find beacon entity (e.g., the player)
auto beaconView = ctx.view<BeaconComponent, TranslationStateComponent, Active>().whereEnabled();
// ...use beacon position for all chasing entities
}
4. Update Builder DSL
Replace .target(entityHandle) with beacon attachment on the target entity:
// On the player (target):
playerPrefab.add<BeaconComponent>();
// On enemies (chasers) — no target handle needed:
.withAi([](auto& aib) {
aib.chasing()
.seekInterval(0.05f);
})
Affected Files
New
| File |
Description |
include/helios/engine/modules/ai/components/BeaconComponent.ixx |
New beacon marker component |
Modified
| File |
Change |
include/helios/engine/modules/ai/components/ChaseComponent.ixx |
Remove EntityHandle target_, setTarget(), target() |
include/helios/engine/modules/ai/systems/ChaseSystem.ixx |
Query BeaconComponent via View instead of resolving stored handle |
include/helios/engine/modules/ai/components/_module.ixx |
Export new BeaconComponent |
include/helios/engine/modules/ai/registry.ixx |
Register BeaconComponent with ComponentReflector |
include/helios/engine/builder/gameObject/builders/configs/ChaseConfig.ixx |
Remove .target(EntityHandle) method |
include/helios/engine/builder/gameObject/builders/AiBuilder.ixx |
Update builder API |
examples/collision_detection/main.cpp |
Replace .target(handle) with BeaconComponent on player |
Documentation
| File |
Change |
include/helios/engine/modules/ai/README.md |
Update API examples |
include/helios/engine/modules/ai/components/README.md |
Document BeaconComponent, remove setTarget() reference |
CHANGELOG.md |
Breaking change entry |
Breaking Change
This removes ChaseComponent::setTarget() / ChaseComponent::target() and the .target(EntityHandle) builder method. Mark as breaking in CHANGELOG.
Checklist
Summary
ChaseComponentcurrently stores a directEntityHandleto its chase target. This creates a tight coupling between the chasing entity and a specific entity instance. Instead,ChaseComponentshould depend on a newBeaconComponent— a tag/marker component attached to the entity that should be chased. TheChaseSystemwould then query for the beacon rather than resolving a stored handle.Current Design
ChaseComponentholds a directEntityHandleto the target:ChaseSystemresolves this handle every frame:The builder DSL also wires the target via
EntityHandle:Problems
EntityHandleof its target at configuration time.Proposed Design
1. Introduce
BeaconComponentA lightweight marker component attached to the entity that should be chased:
2. Modify
ChaseComponentRemove the
EntityHandle target_field. The chase target is no longer stored; it is discovered at runtime viaBeaconComponent:3. Modify
ChaseSystemQuery for the beacon entity via
Viewinstead of resolving a stored handle:4. Update Builder DSL
Replace
.target(entityHandle)with beacon attachment on the target entity:Affected Files
New
include/helios/engine/modules/ai/components/BeaconComponent.ixxModified
include/helios/engine/modules/ai/components/ChaseComponent.ixxEntityHandle target_,setTarget(),target()include/helios/engine/modules/ai/systems/ChaseSystem.ixxBeaconComponentvia View instead of resolving stored handleinclude/helios/engine/modules/ai/components/_module.ixxBeaconComponentinclude/helios/engine/modules/ai/registry.ixxBeaconComponentwithComponentReflectorinclude/helios/engine/builder/gameObject/builders/configs/ChaseConfig.ixx.target(EntityHandle)methodinclude/helios/engine/builder/gameObject/builders/AiBuilder.ixxexamples/collision_detection/main.cpp.target(handle)withBeaconComponenton playerDocumentation
include/helios/engine/modules/ai/README.mdinclude/helios/engine/modules/ai/components/README.mdBeaconComponent, removesetTarget()referenceCHANGELOG.mdBreaking Change
This removes
ChaseComponent::setTarget()/ChaseComponent::target()and the.target(EntityHandle)builder method. Mark as breaking in CHANGELOG.Checklist
BeaconComponentai/registry.ixxai/components/_module.ixxEntityHandle target_fromChaseComponentChaseSystemto queryBeaconComponentChaseConfigbuilder (remove.target())collision_detectionexample