Replicated

[Drag Down] 미끄러지는 길 (Physical Material + Surface type + Data Asset) 본문

언리얼 엔진/Drag Down (캡스톤 디자인)

[Drag Down] 미끄러지는 길 (Physical Material + Surface type + Data Asset)

라구넹 2025. 4. 17. 22:11

모듈화를 위해 당연히 컴포넌트로 분리

* OnLand나 CurrentFloor.HitResult에서 현재 바닥 트레이스 정보를 주긴 하는데, Physical Material을 안들고 온다

* bReturnPhysicalMaterial 설정이 기본적으로 안되어 있음

 

Project Settings -> Physics 가서 Physical  Surface 추가

 

#pragma once

#include "CoreMinimal.h"

#define CPROFILE_DDCAPSULE TEXT("DDCapsule")
#define CPROFILE_DDTRIGGER TEXT("DDTrigger")
#define CPROFILE_OVERLAPALL TEXT("OverlapAll")
#define CPROFILE_NOCOLLISION TEXT("NoCollision")
#define CCHANNEL_CSACTION ECC_GameTraceChannel1

#define SURFACE_DEFAULT SurfaceType_Default 
#define SURFACE_ICE SurfaceType1

Surface Type은 Collision 타입이랑 같이 관리한다

* SurfaceType1 이런 건 엔진이 알아서 관리하는 값.. 정확히는 ini 파일에서 관리

 

Physical Material 만들고 Surface 설정

* Character는 CharacterMovementComponent 기반으로 움직여서 여기서 Friction 설정하는 것만으로는 부족함

 

메시에다 Physical Material 설정

 

// Fill out your copyright notice in the Description page of Project Settings.


#include "ActorComponent/DDSurfaceDetectionComponent.h"
#include "PhysicalMaterials/PhysicalMaterial.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "DataAsset/DDGroundFrictionData.h"
#include "Physics/DDCollision.h"

UDDSurfaceDetectionComponent::UDDSurfaceDetectionComponent()
{
	SetIsReplicatedByDefault(true);

	static ConstructorHelpers::FObjectFinder<UDDGroundFrictionData> GroundFrictionDataRef(TEXT("/Script/DragDown.DDGroundFrictionData'/Game/Blueprint/DataAsset/DDDA_GroundFrictionData.DDDA_GroundFrictionData'"));
	if (GroundFrictionDataRef.Succeeded())
	{
		GroundFrictionData = GroundFrictionDataRef.Object;
	}
}

void UDDSurfaceDetectionComponent::BeginPlay()
{
	Super::BeginPlay();

	OwnerCharacter = Cast<ACharacter>(GetOwner());
	if (OwnerCharacter == nullptr) return;

	CharacterMovementComp = OwnerCharacter->GetCharacterMovement();
	if (CharacterMovementComp == nullptr) return;

	OwnerCharacter->OnCharacterMovementUpdated.AddDynamic(this, &UDDSurfaceDetectionComponent::OnMovementUpdated);
}


void UDDSurfaceDetectionComponent::OnMovementUpdated(float DeltaTime, FVector OldLocation, FVector OldVelocity)
{
	if (CharacterMovementComp == nullptr || GroundFrictionData == nullptr) return;

	FVector Start = OwnerCharacter->GetActorLocation();
	FVector End = Start - FVector(0.f, 0.f, 200.f);

	FHitResult Hit;  
	FCollisionQueryParams Params;
	Params.AddIgnoredActor(OwnerCharacter);
	Params.bReturnPhysicalMaterial = true; // CurrentFloor.HitResult is not support bReturnPhysicalMaterial

	if (GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Visibility, Params))
	{
		if (UPhysicalMaterial* PhysMat = Hit.PhysMaterial.Get())
		{
			EPhysicalSurface SurfaceType = UPhysicalMaterial::DetermineSurfaceType(PhysMat);

			switch (SurfaceType)
			{
			case SURFACE_ICE:
				SetMovementForFriction(SURFACE_ICE);
				break;
			default:
				SetMovementForFriction(SURFACE_DEFAULT);
				break;
			}
		}
	}
}

void UDDSurfaceDetectionComponent::SetMovementForFriction(EPhysicalSurface NewSurface)
{
	CharacterMovementComp->GroundFriction = GroundFrictionData->FrictionData[NewSurface].GroundFriction;
	CharacterMovementComp->BrakingFrictionFactor = GroundFrictionData->FrictionData[NewSurface].BrakingFrictionFactor;
	CharacterMovementComp->BrakingDecelerationWalking = GroundFrictionData->FrictionData[NewSurface].BrakingDecelerationWalking;
}

움직임에 업데이트가 있으면 발 밑 트레이스해서 피지컬 메테리얼 들고오고

GroundFriction, BrakingFrictionFactor, BrakingDecelerationWalking을 데이터 애셋에서 들고 온다

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "Engine/EngineTypes.h"
#include "DDGroundFrictionData.generated.h"

USTRUCT(BlueprintType)
struct FFrictionEntry
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float GroundFriction = 8.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float BrakingFrictionFactor = 2.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float BrakingDecelerationWalking = 2000.0f;
};

/**
 * 
 */
UCLASS()
class DRAGDOWN_API UDDGroundFrictionData : public UDataAsset
{
	GENERATED_BODY()
	
public:
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FrictionData")
	TMap<TEnumAsByte<EPhysicalSurface>, FFrictionEntry > FrictionData;
};

데이터 애셋은 EPhysicalSurface를 키로 하고(TMap에 넣기 위해 TEnumAsByte로 감쌈) FFrictionEntry를 밸류로 가지는 맵이다

 

블루프린트로 만들고 이렇게 에디터에서 바로바로 수정 가능하다

나중에 Surface 추가 시 쉽게 확장 가능하다

 

플레이어는 그냥 만들어놓으기만 하면 끝

 

 

자연스럽게 잘 미끄러지는 걸 확인 가능하다

데이터 기반 설계로 굉장히 유연하게 작동한다