Unreal - 파쿠르 잡고 올라가기

2025. 10. 31. 03:37·Unreal 프로젝트 다이어리/두번째 프로젝트

캐릭터의 팔이 닿을수 있는 거리면 난간을 잡고 올라가기 기능을 제작하였습니다.

ParkourComponent형식으로 컴포넌트 형식으로 모듈화하여 제작하였습니다

 

이전에 만들었던 파쿠르 컴포넌트에서

라인트레이서를 쏴 해당 물체의 높이와 폭을 구해줍니다

 

파쿠르 컴포넌트를 제작한뒤 라인트레이서를 쏴 구하는 값들은 이와 같습니다 

.h 
AActor* hitObstracle;
float obstacleHeight; //z
float obstacleWidth; //x
float obstracleDepth; //y
float obstacleTopZ;

.cpp
UStaticMeshComponent* mesh = hitActor->FindComponentByClass<UStaticMeshComponent>();
if (!mesh)
	return;
hitObstracle = hitActor;

FVector extent = mesh->Bounds.BoxExtent;
obstacleHeight = extent.Z * 2.f; // 높이 = Z 반폭 * 2 -> 전체높이
obstacleWidth = extent.X * 2.f; // 폭 = X 반폭 * 2 -> x축 가로 기준 폭
obstacleTopZ = hitActor->GetActorLocation().Z + extent.Z; 
obstracleDepth = extent.Y * 2.f;

 

캐릭터에 파쿠르 추적 화살표 타입을 달아주었습니다

.h
//파쿠르 추적 화살표 타입
UENUM(BlueprintType)
enum class EParkourArrowType : uint8
{
	Center = 0, Up, Down, Left, Right, Max,
};

.cpp
void AMainCharacter::InitParkourSystem()
{
    arrowGroup = CreateDefaultSubobject<USceneComponent>(TEXT("ArrowGroup"));
    arrowGroup->SetupAttachment(GetCapsuleComponent());

    for (int32 i = 0; i < (int32)EParkourArrowType::Max; i++)
    {
        FString name = StaticEnum<EParkourArrowType>()->GetNameStringByIndex(i);
        FName compName(*name);

        arrows[i] = CreateDefaultSubobject<UArrowComponent>(compName);
        arrows[i]->SetupAttachment(arrowGroup);

        switch ((EParkourArrowType)i)
        {
        case EParkourArrowType::Center:
            arrows[i]->ArrowColor = FColor::Red;
            break;

        case EParkourArrowType::Up:
            arrows[i]->ArrowColor = FColor::Green;
            arrows[i]->SetRelativeLocation(FVector(0, 0, 30));
            break;

        case EParkourArrowType::Down:
            arrows[i]->ArrowColor = FColor::Blue;
            arrows[i]->SetRelativeLocation(FVector(0, 0, -80));
            break;

        case EParkourArrowType::Left:
            arrows[i]->ArrowColor = FColor::Magenta;
            arrows[i]->SetRelativeLocation(FVector(0, -30, 0));
            break;

        case EParkourArrowType::Right:
            arrows[i]->ArrowColor = FColor::Magenta;
            arrows[i]->SetRelativeLocation(FVector(0, 30, 0));
            break;
        }
    }

    parkourComp = CreateDefaultSubobject<UCParkourComponent>(TEXT("CParkourComponent"));

}

 

그리고 모션워핑 플러그인을 설치해줍니다

 

플레이어 윗 방향에서 쏘는 라인트레이서이며

캐릭터가 손으로 잡을 그랩 포인트를 계산합니다 장애물의 윗면 혹은 모서리의 좌표입니다

또한 장애물과의 높이 차이를 계산합니다

(캐릭터가 올라가야하는 수직 높이)

.h
AActor* hitObstracle_Climb;
FVector climbGrabPoint;
float distanceToObstacle;
float zDiff;

.cpp
void UCParkourComponent::CheckTrace_Up()
{
	LineTrace(EParkourArrowType::Up);

	const FHitResult& hit = hitResults[(int32)EParkourArrowType::Up];
	if (!hit.bBlockingHit)
	{
		hitObstracle_Climb = nullptr;
		climbGrabPoint = FVector::ZeroVector;
		return;
	}

	hitObstracle_Climb = hit.GetActor();
	climbGrabPoint = hit.ImpactPoint;

	zDiff = obstacleTopZ - ownerCharacter->GetActorLocation().Z;
	distanceToObstacle = FVector::Dist(ownerCharacter->GetActorLocation(), climbGrabPoint);
	
}

 

 

그리고 저번에 scv 연동을 하여 가지고있는 데이터테이블값을 연동시키는 함수를 만들어주었습니다

2025.10.29 - [Unreal5 프로젝트 다이어리2] - Unreal - CSV 데이터 테이블 만들기

 

Unreal - CSV 데이터 테이블 만들기

모든 에셋, 내부변수값을 직접 하드코딩하게되면 매번 할때마다 작업이 오래걸린다그래서 필자가 선택한방식은 scv파일형식으로 외부로 저장하여 엑셀로 추가 및 삭제 그리고 수정을 할수있게

lucodev.tistory.com

 

.h
public:
	void InitializeParkourDT();

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DataTable")
	UDataTable* parkDataTable;

	const FParkourDataRow* GetParkourData(FName rowName);

private:
	TMap<FName, FParkourDataRow> parkourDataMap;


.cpp
void UCParkourComponent::InitializeParkourDT()
{
	if (!parkDataTable)
		return;

	static const FString Context(TEXT("ParkourContext"));
	TArray<FName> rowNames = parkDataTable->GetRowNames();

	for (const FName& rowName : rowNames)
	{
		if (FParkourDataRow* row = parkDataTable->FindRow<FParkourDataRow>(rowName, Context))
		{
			parkourDataMap.Add(rowName, *row);
		}
	}
	
}

const FParkourDataRow* UCParkourComponent::GetParkourData(FName rowName)
{
	if (const FParkourDataRow* findRow = parkourDataMap.Find(rowName))
		return findRow;
	return nullptr; 
}

 

scv파일과 연동된 데이터테이블의 값들은 이와 같습니다

 

파쿠르의 분기를 결정하고 실행할 함수를 만들어주었습니다 해당 함수로 파쿠르를 실행합니다

일단 만들어진게 잡고올라가기밖에없으니 그쪽만 추가해주었습니다

void UCParkourComponent::TryParkour()
{
	struct FParkourCondition
	{
		TFunction<bool()> canExecute;
		TFunction<void()> execute;
	};

	TArray<FParkourCondition> conditions =
	{
		 { [this]() { return CanParkour_Climb(); }, [this]() { DoParkour_Climb(); } },
		 //{ [this]() { return CanParkour_Grip(); },  [this]() { DoParkour_Grip();  } },
		 //{ [this]() { return CanParkour_Slide(); }, [this]() { DoParkour_Slide(); } },
		 //{ [this]() { return CanParkour_ShortVault(); }, [this]() { DoParkour_ShortVault(); } },
		 //{ [this]() { return CanParkour_NormalVault(); }, [this]() { DoParkour_NormalVault(); } },
	};

	bool bParkourExecuted = false;
	for (auto& cond : conditions)
	{
		if (cond.canExecute())
		{
			cond.execute();
			bParkourExecuted = true;
			break;
		}
	}

	// 조건 만족하는 파쿠르 동작이 없으면 점프
	if (!bParkourExecuted && ownerCharacter)
	{
		ownerCharacter->Jump();
	}
}

 

climb의 조건을 체크해주는 함수입니다

내부조건과 외부조건인 데이터테이블 값과 연동하였습니다

bool UCParkourComponent::CanParkour_Climb()
{
	if (climbGrabPoint.IsZero() || !hitObstracle_Climb)
		return false;
	if (!ownerCharacter->GetCharacterMovement()->IsFalling())
		return false;

	const FParkourDataRow* row = GetParkourData("Climb");
	if (!row)
		return false;

	//기본값 zDiff 55이상 77이하
	if (zDiff >= row->minHeight && zDiff <= row->maxHeight &&
		obstacleWidth >= row->minWidth && obstacleWidth <= row->maxWidth)
		return true;

	if (distanceToObstacle >= 77)
		return false;

	return false;

}

 

 

난간의 위치를 구하고 잡아야할 그랩 위치를 계산하며 모션워핑컴포넌트에 ParkourTarget 이라는 이름으로 보내줄 함수를 제작하였습니다

void UCParkourComponent::DoParkour_Climb()
{
	const FParkourDataRow* row = GetParkourData("Climb");
	if (!row || !ownerCharacter || !hitObstracle_Climb)
		return;

	ownerCharacter->GetCharacterMovement()->SetMovementMode(MOVE_Flying);
	if (row->PlayMontage)
		ownerCharacter->PlayAnimMontage(row->PlayMontage, row->playRatio);

	FVector handLoc = ownerCharacter->GetMesh()->GetBoneLocation(TEXT("hand_r"));

	const FHitResult& upHit = hitResults[(int32)EParkourArrowType::Up];
	if (!upHit.bBlockingHit)
		return;

	FVector grabPoint = upHit.ImpactPoint;
	grabPoint.Z += 5.f;
	climbGrabPoint = grabPoint;

	FVector currentRoot = ownerCharacter->GetActorLocation();
	FVector targetLocation;
	targetLocation.X = grabPoint.X;                     // X는 난간 기준
	targetLocation.Y = grabPoint.Y;                     // Y는 난간 기준
	targetLocation.Z = currentRoot.Z + (grabPoint.Z - handLoc.Z); // Z는 손 높이 기준 보정

	// 벽 바라보도록
	FRotator targetRotation = (-upHit.ImpactNormal).Rotation();

	if (motionWarpComp)
	{
		motionWarpComp->AddOrUpdateWarpTargetFromLocationAndRotation(
			FName("ParkourTarget"),targetLocation,targetRotation
		);
	}

}

 

모션워핑컴 노티파이를 추가해주고 타깃 이름을 설정해줍니다

또한 노티파이를 하나 추가하여 Climb할때 실행한 Flying모드 변경을 EndParkourClimb노티파이를 제작하여 다시 Walking으로 되돌려주었습니다 (제가 설계한 모션매칭 시스템과 연동하기위해 루트슬롯으로 변동해주었습니다)

(애니메이션은 반드시 루트 모션이어야합니다)

 

 

결과

 

 

 

'Unreal 프로젝트 다이어리 > 두번째 프로젝트' 카테고리의 다른 글

Unreal - 파쿠르 벽차기  (0) 2025.11.03
Unreal - 파쿠르 볼트(Vault)  (0) 2025.11.02
Unreal - CSV 데이터 테이블 만들기  (0) 2025.10.29
Unreal - 파쿠르 방해물 탐색  (0) 2025.10.29
Unreal - 적 타게팅  (0) 2025.10.29
'Unreal 프로젝트 다이어리/두번째 프로젝트' 카테고리의 다른 글
  • Unreal - 파쿠르 벽차기
  • Unreal - 파쿠르 볼트(Vault)
  • Unreal - CSV 데이터 테이블 만들기
  • Unreal - 파쿠르 방해물 탐색
lucodev
lucodev
커피와 노트북 그리고 개발
  • lucodev
    루코 개발테이블
    lucodev
  • 전체
    오늘
    어제
    • 분류 전체보기 (211) N
      • Unreal 프로젝트 다이어리 (108) N
        • 첫번째 프로젝트 (73)
        • 두번째 프로젝트 (35) N
      • Unreal 팁 (8)
      • Unreal 디버깅 (8)
      • C++ 프로그래머스 (52)
        • Stack,Queue (7)
        • Hash (4)
        • Heap (2)
        • Sort (5)
        • Exhaustive search (5)
        • Greedy (2)
        • BFS , DFS (7)
        • Graph (2)
        • Dynamic Programming (1)
        • C++ Math (2)
        • 기타 문제 (14)
      • C++ 백준 (4)
      • C++ 팁 (1)
      • 개인 코테 & 스타디 <비공개> (29)
        • 코드 개인보관함 (9)
        • 코딩테스트+@ (11)
        • 알고리즘 스타디 (6)
        • 알고리즘 스타디 과제 (3)
        • 비공개 (0)
  • 인기 글

  • 최근 글

  • 최근 댓글

  • 링크

  • 공지사항

  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 태그

    unreal 시퀀스
    Unreal Parkour
    언리얼 ui
    언리얼 프로그래스바
    언리얼 모션매칭
    언리얼 상호작용
    언리얼 시퀀스
    unreal inventory
    언리얼 behavior tree
    언리얼 motionmatching
    unreal 모션매칭
    언리얼
    언리얼 parkour
    언리얼 비헤이비어트리
    언리얼 컷씬
    unreal 파쿠르
    unreal 인벤토리
    언리얼 인벤토리
    언리얼 behaviortree
    언리얼 파쿠르
  • hELLO· Designed By정상우.v4.10.3
lucodev
Unreal - 파쿠르 잡고 올라가기
상단으로

티스토리툴바