How do I can handle collision for Mass Entity static meshes? I am using visualisation trait with a static mesh, I want to take a shot and in case of intersection with this static mesh I want to reduce its health from HealthFragment with PushCommand via EntityManager. But how can I get this EntityHandle or something else to change the health of a specific entity on OnComponentBeginOverlap? Or is this impossible and the collision will have to be handled yourself in Processor? How to do it correctly and efficiently within the ECS framework
[quote=“Zopich, post:1, topic:1951812, full:true, username:Zopich”]SpotifyPie
How do I can handle collision for Mass Entity static meshes? I am using visualisation trait with a static mesh, I want to take a shot and in case of intersection with this static mesh I want to reduce its health from HealthFragment with PushCommand via EntityManager. But how can I get this EntityHandle or something else to change the health of a specific entity on OnComponentBeginOverlap? Or is this impossible and the collision will have to be handled yourself in Processor? How to do it correctly and efficiently within the ECS framework
[/quote]
Hello,
Handling collisions within an Entity Component System (ECS) framework involves a thoughtful approach. Let’s break it down:
Collision Detection and ECS:
In ECS, collision detection is often separate from movement systems.
The typical flow is:
Apply movement without considering collisions.
Detect collisions.
Adjust the movement based on detected collisions.
Entity Interaction in Overlap Events:
In your case, you want to reduce an entity’s health when it intersects with a static mesh.
You can handle this in an overlap event (e.g., OnComponentBeginOverlap).
However, directly accessing an EntityHandle within an overlap event might not be straightforward due to ECS design.
Recommended Approach:
Consider the following steps:
Detect the collision in your overlap event.
Identify the specific entity (e.g., the one with reduced health) based on the context (e.g., tags, components).
Use a system (not the overlap event itself) to modify the health of the identified entity.
Systems have better control over entity interactions and can access components efficiently.
Example (Pseudo-code):
// Pseudo-code for handling collision and health reduction
fn handle_collision(entity: EntityHandle, other_entity: EntityHandle) {
if entity.has_component::<HealthComponent>() {
// Reduce health (modify HealthComponent)
let health = entity.get_component_mut::<HealthComponent>().unwrap();
health.reduce_health(10); // Example: Reduce by 10
}
}
// In your overlap event:
fn on_component_begin_overlap(event: CollisionEvent) {
let entity = event.get_entity();
let other_entity = event.get_other_entity();
handle_collision(entity, other_entity);
}
Efficiency Considerations:
Avoid heavy computations in overlap events.
Use tags or specific components to identify relevant entities efficiently.
Delegate complex logic (like health reduction) to dedicated systems.
Remember that ECS promotes separation of concerns, so handling collisions and modifying entity state should ideally happen in separate systems.
Hope this will help you.
Best regards,
cheri232cruz
Hello, I can’t consider your answer correct, because the main question is not how to process damage deal when OnBeginOverlap, but how the bullet or weapon actor should delegate damage to a specific entity. I have now come up with what seems to be a crutch, but ECS correct solution:
Fragments for my configure trait HitUnitTrait:
USTRUCT()
struct MASSSAMPLE_API FHealthFragment : public FMassFragment
{
GENERATED_BODY()
float Health = 0;
};
USTRUCT()
struct MASSSAMPLE_API FTargetFragment : public FMassFragment
{
GENERATED_BODY()
FVector TargetLocation;
};
USTRUCT(BlueprintType)
struct FOverlapStateData
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite, EditAnywhere)
TArray<int32> CurrentOverlappingEntities;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
TArray<int32> PreviousOverlappingEntities;
};
USTRUCT()
struct FOverlapStateFragment : public FMassFragment
{
GENERATED_BODY()
UPROPERTY()
FOverlapStateData OverlapStateData;
};
HitUnitProcess.h
UCLASS()
class MASSSAMPLE_API UHitUnitProcessor : public UMassProcessor
{
GENERATED_BODY()
public:
HitUnitProcessor();
protected:
virtual void ConfigureQueries() override
{
EntityQuery.AddRequirement<FTransformFragment>(EMassFragmentAccess::ReadOnly);
EntityQuery.AddRequirement<FHealthFragment>(EMassFragmentAccess::ReadWrite);
EntityQuery.AddRequirement<FOverlapStateFragment>(EMassFragmentAccess::ReadWrite);
EntityQuery.RegisterWithProcessor(*this);
}
virtual void Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) override;
private:
FMassEntityQuery EntityQuery;
};
HitUnitProcessor.cpp
void UHitUnitProcessor::Execute(
FMassEntityManager &EntityManager,
FMassExecutionContext &Context)
{
EntityQuery.ForEachEntityChunk(EntityManager, Context, [&](FMassExecutionContext& MassExecutionContext)
{
const auto TransformListView = MassExecutionContext.GetFragmentView<FTransformFragment>();
auto OverlapStateFragments = MassExecutionContext.GetMutableFragmentView<FOverlapStateFragment>();
const auto HealthFragmentsList = MassExecutionContext.GetMutableFragmentView<FHealthFragment>();
int i = 0;
for (int EntityIndex = 0; EntityIndex < MassExecutionContext.GetNumEntities(); ++EntityIndex)
{
i += 1;
const auto EntityLocation = TransformListView[EntityIndex].GetTransform().GetLocation();
FHealthFragment& HealthFragment = HealthFragmentsList[EntityIndex];
FOverlapStateFragment& OverlapStateFragment = OverlapStateFragments[EntityIndex];
TArray<FOverlapResult> OverlapResults;
FCollisionQueryParams QueryParams;
GetWorld()->OverlapMultiByChannel(
OverlapResults,
EntityLocation,
FQuat::Identity,
ECollisionChannel::ECC_GameTraceChannel1,
FCollisionShape::MakeSphere(150.0f),
QueryParams
);
TArray<int32> CurrentOverlappingEntities;
for (const FOverlapResult& Result : OverlapResults)
{
auto OverlappingComp = Result.GetComponent();
if (OverlappingComp && OverlappingComp->GetCollisionObjectType() == ECC_GameTraceChannel2)
{
int32 OverlappingEntityID = OverlappingComp->GetUniqueID();
CurrentOverlappingEntities.Add(OverlappingEntityID);
if (!OverlapStateFragment.OverlapStateData.PreviousOverlappingEntities.Contains(OverlappingEntityID))
{
int32 collchanel = static_cast<int32>(OverlappingComp->GetCollisionObjectType());
EntityManager.Defer().PushCommand<FMassDeferredSetCommand>(
[=](FMassEntityManager& Manager)
{
UE_LOG(LogTemp, Error, TEXT("%i overlaping with %i"), i, collchanel);
});
HealthFragment.Health -= 10.0f;
}
}
}
OverlapStateFragment.OverlapStateData.PreviousOverlappingEntities = MoveTemp(OverlapStateFragment.OverlapStateData.CurrentOverlappingEntities);
OverlapStateFragment.OverlapStateData.CurrentOverlappingEntities = MoveTemp(CurrentOverlappingEntities);
if (HealthFragment.Health <= 0)
{
Context.Defer().DestroyEntity(Context.GetEntity(EntityIndex));
}
}
});
}
but I think it’s a very complex calculation for an ECS execute per tick.