공부/Unreal

24.07.24

월러비 2024. 7. 29. 13:01

플레이어 : 파쿠르 컴포넌트 생성

  • 머리, 중앙, 좌, 우, 발, 하단(땅) 에 ArrowComponent를 쏴서 충돌된 오브젝트를 탐색하고 어떤 조건을 만족해서 ‘파쿠르 동작’을 실행하는지 파악한다.
    • 파쿠르 가능한지 확인
    • 어떤 파쿠르 동작을 사용할 조건을 만족했는지 확인한다.

파쿠르 화살표 타입 정의 및 사용

  • ENUM 타입 자료형 지정을 하지 않은 상태라면 TEnumAsByte<열거형 자료형>으로 선언해야한다.
    • EDrawDebugTrace
  • 다른 클래스의 ‘열거형 자료형’을 사용하기 위해서는 해당 클래스의 헤더파일을 선언해야한다.
    • 열거형 자료형은 전방선언이 불가능하기 때문이다.
  • Enum에 작성한 요소들을 C에서 ‘문자열’로 바꾸는것은 불가능하다.
    • C는 자료형을 변수로 다루는 기능이 없기 떄문이다.
      • 하지만, ‘언리얼’에서는 가능하다. ⇒ 언리얼 C++에는 리플렉션 기능이 포함 되어있기 때문이다.
    • Enum의 요소 하나하나도 자료형이다.
  • 제대로 썼는데 오류나는 경우 중 하나 : 헤더 include를 안해서 일어나는 경우가 있다.
  • 화살표 컴포넌트는 따로 위치지정을 안하면 기본 위치는 몸의 중심에서 정면방향으로 뻗는다.
  • 헤더 파일 - UENUM 메크로를 사용해서 블루프린트에도 공개가 되는 ‘열거형 자료형’과 어떤 화살표 상태인지 지정하고 선언한다. - ‘탐지 거리’ 변수 선언 - 플레이어 헤더 파일 - 생성한 화살표 컴포넌트들을 모을 씬 컴포넌트 자료형의 ‘화살표 그룹’ 변수 선언 - 화살표 컴포넌트의 ‘화살표들’ 배열 변수를 ‘파쿠르 화살표 타입’ 열거형 갯수로 선언 - 플레이어 cpp 파일 - 컴포넌트 생성 : ‘화살표 그룹’ - 열거형 자료형인 ‘화살표 타입’ 갯수만큼 반복 - ‘열거형 자료형 변수로 가져오기’ 함수를 호출하고 그안의 ‘지정한 순번의 요소를 문자열로 바꾸기’ 함수를 호출하여 문자열 변수에 ‘파쿠르 화살표 타입’ 이름을 저장한다. - 가져온 이름으로 ‘화살표 컴포넌트’를 생성하여 변수에 저장한다. - 스위치 문을 이용해서 들어온 ‘파쿠르 화살표 타입’의 숫자를 받아 조건을 확인한다. - ‘중앙 화살표’라면 ‘화살표들’ 배열에서 지정된 순번을 받아 색을 바꾼다. : 발, 왼쪽, 오른쪽 - ‘땅 화살표 컴포넌트’는 회전값도 바꾼다.
//header
UENUM(BlueprintType)
enum class EParkourArrowType : uint8
{
	//몸 중앙, 머리, 발, 왼쪽, 오른쪽, 땅 확인
	Center = 0, Head, Foot, Left, Right, Land, Max, 
};

private:
	UPROPERTY(EditAnywhere, Category = "Trace")
		float TraceDistance = 600; //선을 쏠 거리

//플레이어 header
UPROPERTY(VisibleAnywhere)
		class USceneComponent* ArrowGroup;

UPROPERTY(VisibleAnywhere)
	class UArrowComponent* Arrows[(int32)EParkourArrowType::Max];
	
//플레이어 cpp
for (int32 i = 0; i < (int32)EParkourArrowType::Max; i++)
{
	//GetNameStringByIndex : 숫자를 넣으면 그것에 맞는 '문자열'을 반환한다.
	FString name = StaticEnum<EParkourArrowType>()->GetNameStringByIndex(i);
	CHelpers::CreateComponent<UArrowComponent>(this, &Arrows[i], FName(name), ArrowGroup);

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

		//머리나 발을 놓는 이유 : 머리 위에 또는 발 밑에 아무것도 없으면 이동을 못하니까
		case EParkourArrowType::Head:
			Arrows[i]->ArrowColor = FColor::Green;
			Arrows[i]->SetRelativeLocation(FVector(0, 0, 100));
			break;

		case EParkourArrowType::Foot:
			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;

		//땅은 땅을 딛는것을 확인하기 위해 사용한다.
		case EParkourArrowType::Land:
			Arrows[i]->ArrowColor = FColor::Yellow;
			Arrows[i]->SetRelativeLocation(FVector(200, 0, 100));
			Arrows[i]->SetRelativeRotation(FRotator(-90, 0, 0));
			break;
	}
}

컴포넌트 가져오는 기능 작성

  • 헤더 파일 - 템플레이트로 작성 - 매개변수로는 액터 포인터 변수를 받는다. - 받은 액터 변수의 ‘해당 클래스의 컴포넌트 반환’ 함수를 찾을 클래스 자료형을 넣어 호출하고 지정한 자료형으로 형변환한다.

컴포넌트 가져오는 기능 작성 - 액터와 이름을 받는다.

  • 헤더 파일 - 받은 액터의 컴포넌트들 가져오기 함수를 T 형 자료형으로 호출한다. - 컴포넌트들을 저장할 배열 변수 선언 - 액터의 컴포넌트들 가져오기 - 저장한 배열을 반복한다. - 컴포넌트의 이름과 들어온 이름이 같을때 가장 먼저 걸린 첫번째 컴포넌트가 반환된다.

파쿠르 컴포넌트 - 화살표 컴포넌트들 가져오기

  • 파쿠르 컴포넌트가 있는 ‘캐릭터’를 찾아서 그 안에 있는 ‘화살표 컴포넌트’를 가져와서 조건을 읽는다.
  • 헤더 파일 - ‘파쿠르 컴포넌트’ 변수 선언 - 해당 컴포넌트를 소유한 캐릭터를 저장할 ‘캐릭터 변수’ 선언 - 화살표 컴포넌트 배열 변수 선언 : ‘파쿠르 화살표 타입’ 열거형 요소 갯수만큼 공간을 선언한다. - cpp 파일 - ‘오너 캐릭터’ 변수에 ‘오너캐릭터 가져오기’ 함수를 호출한 ‘형변환’ 함수를 ‘캐릭터’ 자료형으로 호출하고 저장한다. - 저장한 ‘오너 캐릭터’ 변수가 비었는지 확인한다. - 작성한 ‘컴포넌트 가져오기’ 함수를 ‘씬 컴포넌트’ 자료형으로 가져온 ‘오너 캐릭터’와 컴포넌트의 이름(화살표들) 넣어 호출하고 ‘씬 컴포넌트 변수’를 선언해 저장한다. : ‘화살표들 변수’ (가져온 오너캐릭터의 내부에 씬 컴포넌트 자료형 클래스를 가진 컴포넌트 즉, 배치된 ‘화살표 그룹 컴포넌트’를 찾아 가져오게 된다.) - ‘씬 컴포넌트’ 자료형의 ‘컴포넌트들’ 변수 선언 - ‘화살표들’ 변수에 있는 ‘화살표 컴포넌트’들을 가져오기 위해 ‘자식 컴포넌트들 가져오기’ 함수를 호출한다. - 확인 테스트
//header
UPROPERTY(VisibleAnywhere)
		class UCParkourComponent* Parkour;
		
//파쿠르 컴포넌트 header
private:
	class ACharacter* OwnerCharacter;
	class UArrowComponent* Arrows[(int32)EParkourArrowType::Max];
	
//파쿠르 컴포넌트 cpp
OwnerCharacter = Cast<ACharacter>(GetOwner());
CheckNull(OwnerCharacter);

//오너 캐릭터의 컴포넌트 : 파쿠르 화살표들
USceneComponent* arrows = CHelpers::GetComponent<USceneComponent>(OwnerCharacter, "Arrows");

TArray<USceneComponent*> components;
arrows->GetChildrenComponents(false, components);

for (int32 i = 0; i < (int32)EParkourArrowType::Max; i++)
{
	CLog::Log(components[i]->GetName());
}

TraceTypeQuery 새로 생성 방법

  • 프로젝트 세팅 - 엔진 - 콜리전 - Trace Channels - 새 트레이스 채널 클릭 - 이름 작성 - 기본 반응 : ignore(무시)

파쿠르 실행 작성

  • 파쿠르의 실행 동작은 화살표 컴포넌트로 들어온 조건을 받아서 ‘거리’에 따라 실행시킨다.
    • 먼 거리에서 파쿠르 실행 = 넘어가며 구르기
    • 가까운 거리에서 파쿠르 실행 = 휙 넘어가기
    • 오르기, 떨어지기, 슬라이딩, 짧게 넘어가기 등등…
  • 라인 트레이스는 ‘월드 좌표’를 사용한다.
    • 추적 쿼리는 새로 생성해서 넣는다.
  • 파쿠르는 물체 큰거 하나에만 충돌하고 실행할것이니 복합 충돌은 사용하지 않는다.
  • 헤더 파일 - ‘파쿠르 실행’ 함수 선언 - ‘파쿠르 타입’ 열거형 자료형 선언 - 선언한 열거형 자료형의 변수 선언하고 기본값은 Max로 놓는다. = Max : 파쿠르 이외에 뭔가를 하고있다고 인식한다. (현재 수행하는 파쿠르 수행 타입을 받을것이다.) - ‘히트된 오브젝트’ 변수 선언 - ‘장애물 두께’ 변수 선언 - ‘장애물까지의 거리’ 변수 선언 - ‘정면 바라볼때까지의 요 회전값’ 변수 선언 - ‘중앙 탐지 확인’ 함수 선언 - ‘선 탐지’ 함수를 ‘파쿠르 화살표 타입’ 변수를 받아 선언한다. - 화살표마다 충돌 결과를 저장하기 위해 히트 결과 자료형의 배열 변수를 선언하고 크기는 ‘파쿠르 화살표 타입’ 갯수로 선언한다. - cpp 파일 - ‘파쿠르 실행’ 함수 정의 - ‘현재 파쿠르 타입’ 이 파쿠르가 아닌 다른 행동을 하는지 확인 - ‘히트된 오브젝트, 오브젝트 두께, 히트된 거리, 정면까지의 요 회전값’을 초기화한다. - ‘중앙 탐지 확인’ 함수 호출 - ‘중앙 탐지 확인’ 함수 정의 - 파쿠르 화살표 타입 중 중앙을 변수 선언하고 저장한다. - ‘선 탐지’ 함수를 선언한 ‘화살표 타입’ 변수를 넣어 호출한다. - ‘선 탐지’ 함수 정의 - 게임 시작때 받아온 ‘화살표들’ 배열에서 매개변수로 받아온 ‘화살표 타입’ 변수를 int32로 변환하고 넣어 호출한 결과를 화살표 컴포넌트의 ‘화살표’ 변수를 선언하고 저장한다. - ‘화살표’ 변수의 색을 가져와 색 변수를 선언하고 저장한다. - ‘화살표’에서 ‘컴포넌트의 월드 좌표 가져오기’ 함수를 호출해서 ‘트랜스폼’ 변수를 선언하고 저장한다. - ‘시작’ 변수와 오너캐릭터의 ‘액터 전방 벡터 가져오기’ 함수에서 반환한 값에 ‘히트된 거리’ 변수를 곱해서 더한 값을 ‘끝’ 변수를 선언하고 저장한다. - ‘트랜스폼’ 변수의 ‘위치 가져오기’ 함수를 호출해서 ‘시작’ 변수를 선언하고 저장한다. - ‘추적 안함 배열’을 액터 자료형으로 선언한다. - 플레이어는 탐지되지 않도록 무시 배열에 오너캐릭터를 넣는다. - ‘선 탐지 싱글’ 함수를 ‘월드, 월드 시작 좌표, 종료 좌표, 탐지 채널3번 : 파쿠르 채널, 복합 충돌 여부, 선을 확인할 ‘드로우 디버그 타입’, 히트된 결과를 반환받을 변수, 자기 자신 충돌 제외 여부, 선 쏠 색상, 히팅된 후의 색상’을 넣어 호출한다.
//header

UENUM(BlueprintType)
enum class EParkourType : uint8
{
	Climb = 0, Fall, Slide, Short, Normal, Wall, Max, 
};

public:
	void DoParkour();
	
private:
	EParkourType Type = EParkourType::Max;
	
private:
	AActor* HitObstacle; //부딪힌 오브젝트
	FVector HitObstacleExtent; //부딪힌 오브젝트 두께
	float HitDistance; //플레이어부터 닿은 거리
	float ToFrontYaw;
	
private:
	void CheckTrace_Center();
	
private:
	void LineTrace(EParkourArrowType InType);
	
private:
	FHitResult HitResults[(int32)EParkourArrowType::Max];
	
//cpp
//부딪힌 오브젝트 데이터 초기화
HitObstacle = nullptr;
HitObstacleExtent = FVector::ZeroVector;
HitDistance = 0.0f;
ToFrontYaw = 0.0f;

//부딪힌 오브젝트 센터 체크
CheckTrace_Center();

UArrowComponent* arrow = Arrows[(int32)InType];
FLinearColor color = FLinearColor(arrow->ArrowColor);

//컴포넌트의 상대좌표를 월드좌표로 가져온다.
FTransform transform = arrow->GetComponentToWorld();

FVector start = transform.GetLocation();
FVector end = start + OwnerCharacter->GetActorForwardVector() * TraceDistance;

TArray<AActor*> ignores;
ignores.Add(OwnerCharacter);

//ETraceTypeQuery::TraceTypeQuery3 : 편집 - 콜리전에서 Parkour 채널 직접 넣은거다.
UKismetSystemLibrary::LineTraceSingle(GetWorld(), start, end, ETraceTypeQuery::TraceTypeQuery3, false, ignores, DebugType, HitResults[(int32)InType], true, color, FLinearColor::White);

EParkourArrowType type = EParkourArrowType::Center;
LineTrace(type);

콜리전 프리셋 생성

  • 더 명확히 충돌되는 오브젝트를 구별하기 위해 사용한다.
  • 프로젝트 세팅 - 엔진 - 콜리전 - 새 프로파일 클릭 - 이름 지정 - 콜리전 켜짐 : Conllision Enabled - 오브젝트 타입 : WorldStatic - 설명 : 그냥 그대로 - 모든 충돌 Block으로 체크

화살표 중앙 탐지 확인

  • 히트 결과는 누를때마다 할당되고 삭제된다.
    • 이때, 너무 자주 실행되면 메모리 단편화’가 발생한다.
    • 한번 생성하고 또 생성하지 않게끈 레퍼런스(&)를 붙인다.
      • 이때, 변수를 수정하면 주소의 값까지 변하게 된다.
      • 이것을 방지하기 위해 const를 작성한다.
    • 결과 : cont FHitResult& hitResult
  • cpp 파일 - ‘중앙 탐지 확인’ 함수 정의 - ‘중돌 결과’ 배열에 선언한 ‘파쿠르 화살표 타입’ 변수를 넣고 선언하여 저장한다. - 히트 결과의 ‘블록 확인’ 함수를 호출해서 false가 나오는지 확인한다. - ‘충돌 결과’의 ‘컴포넌트 가져오기’ 함수를 ‘스테틱 메쉬 컴포넌트 자료형’으로 호출하고 스테틱 메쉬 컴포넌트 변수를 선언하고 저장한다. (결과 : 충돌 결과의 ‘메쉬 컴포넌트’ 자료형으로 된 가장 첫번쨰 컴포넌트가 가져와진다.) - 제대로 가져와졌는지 ‘메쉬’ 변수를 확인한다. - ‘충돌 결과’의 ‘액터 가져오기’ 함수를 호출해서 ‘충돌 오브젝트’에 저장한다. - ‘최소 충돌’ ‘최대 충돌’ 변수 선언 - ‘메쉬’의 ‘충돌 위치 가져오기’ 함수를 ‘최소충돌, 최대충돌’ 변수를 넣어 호출한다. - 저장한 ‘최소충돌’과 ‘최대충돌’ 위치를 계산해서 ‘x, y, z’ 변수를 선언하고 저장한다. - ‘충돌 두께’에 ‘x, y, z’를 넣는다. - ‘충돌 거리’에 ‘충돌 결과’의 ‘거리’를 저장한다.
//cpp
const FHitResult& hitResult = HitResults[(int32)type];
CheckFalse(hitResult.bBlockingHit);

//부딪힌 오브젝트의 스테틱 메쉬 컴포넌트 가져오기
UStaticMeshComponent* mesh = CHelpers::GetComponent<UStaticMeshComponent>(hitResult.GetActor());
CheckNull(mesh);

//부딪힌 오브젝트의 '액터' 가져오기
HitObstacle = hitResult.GetActor();

//충돌체 : Bound (Bounding Box : 충돌 박스)
//즉, 최초 충돌과 최대 충돌을 뺴면 '길이'가 나온다.
//부딪힌 액터의 두께 구하기
FVector minBound, maxBound;
mesh->GetLocalBounds(minBound, maxBound);

float x = FMath::Abs(minBound.X - maxBound.X);
float y = FMath::Abs(minBound.Y - maxBound.Y);
float z = FMath::Abs(minBound.Z - maxBound.Z);
HitObstacleExtent = FVector(x, y, z);

HitDistance = hitResult.Distance; //충돌 거리

 

'공부 > Unreal' 카테고리의 다른 글

24.07.26  (0) 2024.07.31
24.07.25  (0) 2024.07.31
24.07.23  (0) 2024.07.27
24.07.22  (0) 2024.07.26
24.07.19  (0) 2024.07.26