How to get collision primitive name involved in collision?

I have few collision primitives assigned to my static mesh.
Each collision primitve has a ‘name’ property (static mesh details->collision->primitives->boxes->0->name)

During ‘Event Hit’ I would like to get a name of primitive that was involved in this collision.
Is this possible?
May be done in Blueprints od C++.

ACustomActor can be whatever actor that is testing or you could push the function into a blueprint library if needed.

Then just hook up the overlap or hit other component to the function and pass in the index if present and you will get the collision primitives name

header file

UFUNCTION(BlueprintCallable)
FString GetCollisionName(UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

cpp

FString ACustomActor::GetCollisionName(UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	if (OtherBodyIndex == -1) OtherBodyIndex = 0;

	FString foundName = "";
	if (OtherComp != nullptr && OtherBodyIndex > -1) 
	{
		
		if (UStaticMeshComponent* mesh = Cast<UStaticMeshComponent>(OtherComp))
		{
			FKShapeElem* el = mesh->GetStaticMesh()->GetBodySetup()->AggGeom.GetElement(OtherBodyIndex);
			foundName = el->GetName().ToString();
		}

		else if (UStaticMesh* mesh2 = Cast<UStaticMesh>(OtherComp))
		{
			FKShapeElem* el = mesh2->GetBodySetup()->AggGeom.GetElement(OtherBodyIndex);
			foundName = el->GetName().ToString();
		}
	}

	return foundName;
}

Works with overlaps and collisions.

You will need to include

#include "PhysicsEngine/BodySetup.h"
in the cpp file

Example box collider with name “My BOX”


In Game

and bp

4 Likes

Hi, I need something exactly like this but for MyComp. I want to obtain the name of the primitive, the element that was hit (I have a bumper in my car and want to know which element I hit) . Is this possible?

It should work no problem. Let me know if you have any trouble implementing it and I can help out if you get stuck.

You could move the function into a blueprint static library’s function that would make it accessible from any class.

2 Likes

I’ve implemented it this way, but it doesn’t seem to work. When I use your code in this case, I’m not sure how to get the element index for the hit component. I have three collision capsules — one for the front and two for the sides.

When I use my own function GetMyCollisionNameFromHit(), the Hit.MyItem is always -1. Could you help me figure out what’s going wrong?

.h

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CoreBlueprintFunctionLibrary.generated.h"

UCLASS()
class PROJECT_API UCoreBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
public:
	/** Returns the name of the collision element from the component based on the index. */
	UFUNCTION(BlueprintCallable, Category = "Collision")
	static FString GetCollisionName(UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

	UFUNCTION(BlueprintCallable, Category = "Collision")
	static FString GetMyCollisionNameFromHit(UPrimitiveComponent* SelfComp, const FHitResult& Hit);
};

.cpp


#include "CoreBlueprintFunctionLibrary.h"
#include "PhysicsEngine/BodySetup.h"

FString UCoreBlueprintFunctionLibrary::GetCollisionName(UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	if (OtherBodyIndex == -1) OtherBodyIndex = 0;

	FString foundName = "";
	if (OtherComp != nullptr && OtherBodyIndex > -1)
	{

		if (UStaticMeshComponent* mesh = Cast<UStaticMeshComponent>(OtherComp))
		{
			FKShapeElem* el = mesh->GetStaticMesh()->GetBodySetup()->AggGeom.GetElement(OtherBodyIndex);
			foundName = el->GetName().ToString();
		}

		else if (UStaticMesh* mesh2 = Cast<UStaticMesh>(OtherComp))
		{
			FKShapeElem* el = mesh2->GetBodySetup()->AggGeom.GetElement(OtherBodyIndex);
			foundName = el->GetName().ToString();
		}
	}

	return foundName;
}

FString UCoreBlueprintFunctionLibrary::GetMyCollisionNameFromHit(UPrimitiveComponent* SelfComp, const FHitResult& Hit)
{
	if (!SelfComp || Hit.MyItem < -1)
	{
		return TEXT("Invalid");
	}

	const int32 MyBodyIndex = Hit.MyItem;
	FString FoundName = TEXT("Unknown");

	if (const UStaticMeshComponent* MeshComp = Cast<UStaticMeshComponent>(SelfComp))
	{
		if (const UStaticMesh* StaticMesh = MeshComp->GetStaticMesh())
		{
			if (const UBodySetup* BodySetup = StaticMesh->GetBodySetup())
			{
				const FKShapeElem* ShapeElem = BodySetup->AggGeom.GetElement(MyBodyIndex);
				if (ShapeElem)
				{
					FoundName = ShapeElem->GetName().ToString();
				}
			}
		}
	}

	return FoundName;
}


Hi, @HiImGh0st , I think you should calculate distances between the hit location and that 3 collision capsules and choose the nearest one…

This is hard to calculate. Methinks it should just get the name of the primitive that struck. This element is not complicated, but I also have ones that consist of many.

The index is needed when multiple collisions are attached to the same static mesh, otherwise the code won’t recognize the correct collider. You have to pass in the index from the collision event based on the hit result’s element index.

simple way finding collision component by calculate distance :

void XXXXXXX::OnHit(UPrimitiveComponent *HitComponent, AActor *OtherActor,
                           UPrimitiveComponent *OtherComponent, FVector NormalImpulse, const FHitResult &Hit)
{ 
       // PawnRootDummy  is your UStaticMeshComponent

    int32 elemCnt = PawnRootDummy->GetStaticMesh()->GetBodySetup()->AggGeom.GetElementCount();

    float nearestDist = UE_FLOAT_HUGE_DISTANCE;
    FKShapeElem   * tmpElem = nullptr;

     for(int32 i = 0 ; i < elemCnt ; i ++){
          FKShapeElem   * const elem = PawnRootDummy->GetStaticMesh()->GetBodySetup()->AggGeom.GetElement(i);

         float tmpDist = FVector::Distance( elem->GetTransform().GetLocation() + PawnRootDummy->GetComponentLocation() , Hit.ImpactPoint);

         if(tmpDist < nearestDist){
             nearestDist = tmpDist;
             tmpElem = elem;
         }
     }

     if(tmpElem){
          UE_LOG(LogTemp, Warning, TEXT(" collison obj is : %s") , *tmpElem->GetName().ToString()  );
     }
}

demo:

1 Like

If I want to detect what I hit, then yes. But if I want to find out what I hit (i.e. if the player had several collisions and hit one of the walls), I don’t have that index in BP. The index element returns the number of what I hit, but I want to know what element the player hit. MyItem in C++ always returns -1.

I get this error when I try to use this code:

Error class FKShapeElem has no member GetTransform

float tmpDist = FVector::Distance(elem->GetTransform().GetLocation() + PawnRootDummy->GetComponentLocation(), Hit.ImpactPoint);

Hi, I need something exactly like this, but for MyComp. I’m trying to retrieve the name of the primitive and the specific element that was hit - for example, I have a bumper on my car and want to determine exactly which part was impacted. Is this possible?

include this header file:

#include  "PhysicsEngine/BodySetup.h"

Or, you can get the demo here

That’s not a problem. I have added that header. FKShapeElem does not have the GetTransform function. I don’t know why it works for you. Maybe you have modified that element?

Only objects such as FKSphylElem have this function.

CoreBlueprintFunctionLibrary.h

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CoreBlueprintFunctionLibrary.generated.h"

UCLASS()
class YOURGAME_API UCoreBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
	
	UFUNCTION(BlueprintCallable)
	static FString GetCollisionName(UPrimitiveComponent* OtherComp, FHitResult HitResult);
};

CoreBlueprintFunctionLibrary.cpp

#include "CoreBlueprintFunctionLibrary.h"
#include "Engine/HitResult.h"
#include "PhysicsEngine/BodySetup.h"

FString UCoreBlueprintFunctionLibrary::GetCollisionName(UPrimitiveComponent* OtherComp, FHitResult HitResult)
{
	if (HitResult.ElementIndex == -1) HitResult.ElementIndex = 0;

	FString foundName = "";
	if (OtherComp != nullptr && HitResult.ElementIndex > -1)
	{

		if (UStaticMeshComponent* mesh = Cast<UStaticMeshComponent>(OtherComp))
		{
			FKShapeElem* el = mesh->GetStaticMesh()->GetBodySetup()->AggGeom.GetElement(HitResult.ElementIndex);
			GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Cyan, *el->GetName().ToString());
			foundName = el->GetName().ToString();
		}

		else if (UStaticMesh* mesh2 = Cast<UStaticMesh>(OtherComp))
		{
			FKShapeElem* el = mesh2->GetBodySetup()->AggGeom.GetElement(HitResult.ElementIndex);
			GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Cyan, *el->GetName().ToString());
			foundName = el->GetName().ToString();
		}
	}

	return foundName;
}

I understand, but this isn’t about detecting which object the player collided with — it’s about detecting which part of the player was involved in the collision.

Imagine the player is driving a car. The car hits a wall. I want to find out which component of the car was damaged by the collision. I’m not interested in which part of the wall was hit.

The OnHit event is inside the car Blueprint, because I’m not going to make every object the player might crash into into a separate Blueprint just to detect whether the collision was with the car’s front bumper or the side. That’s why I’m not interested in OtherComp. I care about MyComp — the component on the car that was hit.

So I don’t want to know HitResult.ElementIndex, which gives me the index of the hit element on the other object. I want to know the index of the car component that was hit.

Car actor with bumper

Bumper mesh with colliders


Okay. I’ll try to explain it differently. The player is a car whose body is a Static Mesh. The collisions are made up of multiple primitives, each with a different name. When the car (player) drives into a wall, the OnHit event is triggered in the car (player). What I want to find out is which part of the car caused the collision.

In the example you sent, the check is done from the perspective of the wall that was hit. But what I need is to check from the player’s (car’s) side — I want to know which component of the car hit something.

Is there any way to get what he is writing about?

Ok went for a hybrid approach. You can’t access the transform in the newer versions of the engine so you have to test for structs manually :frowning:

Based on the same procedure as @tootzoe1 checked via distance.


FString UCoreBlueprintFunctionLibrary::GetCollisionName(UPrimitiveComponent* OtherComp, FHitResult HitResult)
{	
	FString foundName = "";
	if (OtherComp != nullptr)
	{
		if (UStaticMeshComponent* mesh = Cast<UStaticMeshComponent>(OtherComp))
		{			
				FKShapeElem currentSmallest;
				FVector Start = HitResult.Location;
				FVector End;

				float nearestDist = UE_FLOAT_HUGE_DISTANCE;
				
				for (const FKBoxElem& Elem : mesh->GetStaticMesh()->GetBodySetup()->AggGeom.BoxElems)
				{							
					float tmpDist = FVector::Distance(Elem.GetTransform().GetLocation() + mesh->GetComponentLocation(), Start);
					if (tmpDist < nearestDist)
					{
						End = Elem.GetTransform().GetLocation();
						currentSmallest = Elem;
						nearestDist = tmpDist;
					}
				};
				for (const FKTaperedCapsuleElem& Elem : mesh->GetStaticMesh()->GetBodySetup()->AggGeom.TaperedCapsuleElems)
				{					
					float tmpDist = FVector::Distance(Elem.GetTransform().GetLocation() + mesh->GetComponentLocation(), Start);
					if (tmpDist < nearestDist)
					{		
						End = Elem.GetTransform().GetLocation();
						currentSmallest = Elem;
						nearestDist = tmpDist;
					}
				};

				for (const FKSphereElem& Elem : mesh->GetStaticMesh()->GetBodySetup()->AggGeom.SphereElems)
				{
					
					float tmpDist = FVector::Distance(Elem.GetTransform().GetLocation() + mesh->GetComponentLocation(), Start);
					if (tmpDist < nearestDist)
					{		
						End = Elem.GetTransform().GetLocation();
						currentSmallest = Elem;
						nearestDist = tmpDist;
					}
				};

				for (const FKSphylElem& Elem : mesh->GetStaticMesh()->GetBodySetup()->AggGeom.SphylElems)
				{
					
					float tmpDist = FVector::Distance(Elem.GetTransform().GetLocation() + mesh->GetComponentLocation(), Start);
					if (tmpDist < nearestDist)
					{		
						End = Elem.GetTransform().GetLocation();
						currentSmallest = Elem;
						nearestDist = tmpDist;
					}
				};

				for (const FKConvexElem& Elem : mesh->GetStaticMesh()->GetBodySetup()->AggGeom.ConvexElems)
				{					
					float tmpDist = FVector::Distance(Elem.GetTransform().GetLocation() + mesh->GetComponentLocation(), Start);
					if (tmpDist < nearestDist)
					{						
						End = Elem.GetTransform().GetLocation();
						currentSmallest = Elem;
						nearestDist = tmpDist;
					}
				};
				
				DrawDebugLine(OtherComp->GetWorld(), Start, End+ mesh->GetComponentLocation(), FColor::Cyan, true, 55555);
				foundName = currentSmallest.GetName().ToString();				
				UE_LOG(LogTemp, Warning, TEXT("Hit %s (%s)"), *foundName, *FString::SanitizeFloat(nearestDist));
		}		
	}
	return foundName;
}

1 Like