카테고리:

Unreal C++ - 부위 별 피격 구현

부위를 판정하고 싶은 만큼 Physical Material을 생성한다.

Physmat

Project Settings의 Physics의 Physical Surface란에 이름이 동일한 Physical Surface들을 설정한다.

EPhysicalSurface

피격 당하는 객체의 PhysicsAsset의 부위마다 Physical Material을 설정한다.

PhysicsBody
PhysicsHead

C++ 코드에서 인식할 수 있도록 부위 이름과 EPhysicalSurfaceType을 이어주는 Map을 저장할 클래스를 생성한다

//header
class CPP_PORTFOLIO_API CBodyMap
{
private:
	CBodyMap();

public:
	UPROPERTY(BlueprintReadOnly)
		static const TMap<FString, FString> Map;
};

//cpp
const TMap<FString, FString> CBodyMap::Map = {
	{"Body", "SurfaceType1"},
	{"Head", "SurfaceType2"}
};

무기와 피격 당하는 객체와의 BeginOverlap 이벤트에서 부위를 판별한다.
IIDamageable이라는 인터페이스를 상속 받았는지 확인하여 피해를 입을 수 있는 객체인지 확인 후, Overlap 이벤트에서 들어오는 InOtherBodyIndex가 -1인 경우는 Physical Material을 판별할 수 없으므로 return한다.

IIDamageable* character = Cast<IIDamageable>(InOther);

	if (character == nullptr || InOtherBodyIndex == -1)
		return;

InOtherBodyIndex를 여러번 가져오기 위해 Overlap Event를 여러번 발생시켜야 하기 때문에 Multi Overlap Event를 활성화한다.

Overlaps

InOtherBodyIndex가 -1이 아닌 경우 Physical Material이 할당된 BodyInstance를 피격 당한 객체의 USkeletalMeshComponent로 부터 확인할 수 있다.

BodyInstance의 Physical Material을 가져오면 할당된 Material의 이름을 가져올 수 있으며, CBodyMap으로 이를 FString으로 적인 SurfaceType으로 변경하고 FindObject를 통해 EPhysicalSurface로 변경하여 피격 당하는 객체에게 어느 부위를 피격 당하였는지 알린다.

//연속 피격 방지를 위한 Hitted 배열 검사
for (IIDamageable* hitted : Hitted)
	{
		if (hitted == character)
			return;
	}

	USkeletalMeshComponent* comp = Cast<USkeletalMeshComponent>(InOtherComp);

	if (!!comp)
	{
		FBodyInstance* BodyInstance = comp->GetBodyInstance(comp->GetBoneName(InOtherBodyIndex));

		if (BodyInstance != nullptr)
		{
			UPhysicalMaterial* physmat = BodyInstance->GetSimplePhysicalMaterial();

			Hitted.AddUnique(character);

			const UEnum* enumProperty = FindObject<UEnum>(ANY_PACKAGE, TEXT("EPhysicalSurface"), true);

			if(!!enumProperty)
			{
				FString str = CBodyMap::Map[physmat->GetName()];
				int32 index = enumProperty->GetIndexByNameString(str);
				GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, physmat->GetName());
				EPhysicalSurface type = EPhysicalSurface((uint8)index);

				character->TakeDamage(HitData, type);
			}
		}
	}