전처리기 문법
- ‘#’이 붙고 ‘;’세미콜론이 안붙는 문법이다.
- 컴퓨터의 처리에 있어서 중심적인 처리를 수행하는 부분을 위해 ‘사전 준비적인 계산’을 행하는 문법이다.
- 컴파일 자체를 하지 않아서, 코드 자체가 실행 코드에 들어가지 않는 기법이다.
- 컴파일 자체를 하지 않아서, 코드 자체가 실행 코드에 들어가지 않는 기법이다.
- 테스트를 할때도 사용하고, 테스트가 끝나면 define 되어있는 이름에 ‘주석처리’를 하면 #ifdef로 호출된 곳을 컴파일 하지 않게 된다.
플레이어 파쿠르 예제
전처리기 문법을 이용한 출력 테스트
- define : 조건부 컴파일을 하기 위해 사용하는 이름 정의 선언이다.
- if def ~ endif : define으로 선언된 이름이 define 되어있으면 컴파일하고 아니라면 컴파일하지 않는 문법이다.
- cpp 파일 - define 이름 선언 - #ifdef ~ endif 정의 - ‘중앙 탐지 출력’ 함수를 호출한 결과가 잘 나오는지 테스트한다.
//cpp
#define LOG_UCParkourComponent
#ifdef LOG_UCParkourComponent
CLog::Print("HitObstacle : " + HitObstacle->GetName(), 1100);
CLog::Print("HitObstacleExtent : " + HitObstacleExtent.ToString(), 1101);
CLog::Print("HitDistance : " + FString::SanitizeFloat(HitDistance), 1102);
CLog::Print("ToFrontYaw : " + FString::SanitizeFloat(ToFrontYaw), 1103);
#endif // LOG_UCParkourComponent 출력 테스트
머리, 발, 좌, 우 라인 충돌 탐지 확인
- 헤더 파일 - ‘머리 탐지 확인’ 함수 선언 - cpp 파일 - ‘중앙 탐지 체크’ 함수 이후 - ‘충돌 오브젝트’가 널값이 아니라면 - ‘머리 탐지 확인’ 함수 호출 - ‘머리 탐지 확인’ 함수 정의 - ‘선 탐지’ 함수를 ‘파쿠르 화살표 타입’ 자료형의 ‘머리’ 값을 넣어 호출
//header
void CheckTrace_Head();
//cpp
if (!!HitObstacle)
{
CheckTrace_Head();
}
LineTrace(EParkourArrowType::Head);
충돌 탐지 정보 정의 -
- 캐릭터 앞의 오브젝트를 탐지했다면, 이제 파쿠르를 수행할 수 있는지를 확인해야한다.
- 기본은 중앙, 좌, 우의 라인 탐지가 이루어지면 파쿠르가 가능하다는 조건으로 진행한다.
- Normal : 면(을 이루는 삼각형의 정점)에 수직이 되는 벡터
- 헤더 파일 - ‘장애물 확인’ 함수 선언 - cpp 파일 - ‘장애물 확인’ 함수를 호출해서 장애물이 맞는지 확인한다. - ‘장애물 확인’ 함수 정의 - ‘충돌 오브젝트’가 없으면 오브젝트 확인 함수를 실행하지 않는다. - 조건을 확인하기위해 bool 변수 하나를 임시로 선언하고 true로 초기화한다. - ‘충돌 경과’ 배열에서 ‘파쿠르 화살표 타입’이 중앙일때 ‘블록 히트’ 되었는지 확인하고 저장한다. - ‘충돌 경과’ 배열에서 ‘파쿠르 화살표 타입’이 좌일때 ‘블록 히트’ 되었는지 확인하고 저장한다. - ‘충돌 경과’ 배열에서 ‘파쿠르 화살표 타입’이 우일때 ‘블록 히트’ 되었는지 확인하고 저장한다. - ‘조건 체크 임시 변수’가 false인지 확인한다. (중앙, 좌, 우 탐지 중 하나라도 false인경우 조건변수도 false로 나와서 함수 실행을 안한다.) - ’충돌 결과’의 ‘파쿠르 화살표 타입 : 중앙’의 ‘충돌 좌표’를 ‘시작’ 변수를 선언하고 저장한다. - ‘오너 캐릭터’의 ‘위치 가져오기’ 함수를 호출하고 ‘끝’ 변수를 선언해서 저장한다. - ‘충돌된 지점’ 변수와 ‘오너캐릭터 위치’를 넣은 ‘바라보는 회전값 반환’ 함수를 호출해서 ‘바라보는 각도’ 변수를 선언하고 저장한다. - ’충돌 결과’의 ‘파쿠르 화살표 타입 : 중앙’의 ‘충돌 수직 벡터’를 ‘충돌 수직’ 변수를 선언하고 저장한다. - 결과 확인
//header
private:
bool Check_Obstacle(); //올라갈 수 있는 장애물인지 확인
//cpp
//옵스타클 없으면, false로 리턴한다.
CheckNullResult(HitObstacle, false);
bool b = true; //임시 사용할 변수
b &= HitResults[(int32)EParkourArrowType::Center].bBlockingHit; //센터가 닿아야 true가 나온다.
b &= HitResults[(int32)EParkourArrowType::Left].bBlockingHit; //Left가 닿아야 true가 나온다.
b &= HitResults[(int32)EParkourArrowType::Right].bBlockingHit; //Right가 닿아야 true가 나온다.
CheckFalseResult(b, false); //b가 false면 더이상 수행하지 않는다.
//ImpactPoint : 충돌 지점
//플레이어에서 충돌 지점까지의 각도 확인
FVector start = HitResults[(int32)EParkourArrowType::Center].ImpactPoint;
FVector end = OwnerCharacter->GetActorLocation(); //플레이어 위치
float lookAt = UKismetMathLibrary::FindLookAtRotation(start, end).Yaw; //충돌 지점에서 플레이어를 바라보는 회전값이 반환된다., 즉 '내적각도'다.
//충돌 면에 대한 수직 되는 벡터 반환
FVector impactNormal = HitResults[(int32)EParkourArrowType::Center].ImpactNormal;
#ifdef LOG_UCParkourComponent
CLog::Print("lookAt : " + FString::SanitizeFloat(lookAt), 1110);
CLog::Print("impactNormal : " + impactNormal.ToString(), 1111);
#endif // LOG_UCParkourComponent 충돌 각도 확인
lookAt 설명
- lookAt은 충돌 지점에서 플레이어를 바라보는 방향이 반환된다.
- 파이 : 180도 ⇒ 게임에서 표현하는 각도 : 0 ~ 180 도
- 써야할 각도 : 360도
- 게임에서 정면은 0도, 왼쪽 -90도, 오른쪽 : 90도, 뒤 : -180 또는 180도
- 즉, 충돌지점에서 플레이어를 바라보는 각도 : - 180 또는 180 에 가까운 각도다.
- 플레이어의 위치가 좌냐 우냐에 따라 180이냐 -180이냐가 달라진다.
- 사각형의 위쪽이라면 ‘0도’에 가까워진다.
- 사각형의 좌 또는 우라면 충돌 각도는 -90도 또는 90도에 가까워진다.
충돌 탐지를 시작한 벡터와 수직 벡터의 각도 구하기
- cpp 파일 - ‘X 평면의 회전’ 함수에 ‘충돌 수직’ 변수를 넣고 ‘Yaw’ 값만 호출해서 ‘충돌 각도’ 변수를 선언해서 저장한다. ⇒ 즉, 면에서 lookAt 벡터의 각도와 면에서 수직인 각도의 차이인 ‘충동 선이 수직이 되도록 돌아야하는 각도’가 나온다.
//cpp
//Yaw 각도 (X 평면 각도)를 가져오는 것이다.
float impactAt = UKismetMathLibrary::MakeRotFromX(impactNormal).Yaw;
impactAt 설명
- 충돌 면의 수직 벡터의 X평면에서의 Yaw 값이 반환된다.
- MakeRotFromX : Yaw 각도 (X 평면 각도)를 가져오는 것이다.
- 즉, 수직각도와 ‘충돌지점에서 플레이어를 바라보는 방향’와의 각도를 구하는 것이다.
파쿠르 할 수 있는지 확인하는 모드 - 등산 가능한지 확인하고 등산 실행
- 파쿠르 실행 → 머리 화살표에 물체 탐지 : 위로 올라갈 수 있는 장애물이 있음
- 구조체 : 데이터를 저장하는 용도로 사용한다.
- 데이터 에셋으로 데이터를 부르기 위한 조건
- USTRUCT() 매크로로 선언해야한다.
- UCLASS() 위에 선언되어야한다.
- 클래스 : 데이터 저장 + 자신의 행동을 정의하는데 사용한다.
- 헤더 파일 - ‘등산 모드 체크’ 함수 선언 - ‘파쿠르 데이터’ 구조체 생성 : USTRUCT 메크로로 선언한다. - 파쿠르 타입의 ‘타입’ 변수 선언 - 애님 몽타주 자료형의 ‘몽타주’ 변수 선언 - ‘몽타주 속도’ 변수 선언 - ‘섹션 이름’ 변수 선언 - ‘파쿠르 수행 최소 거리’ 선언 - ‘파쿠르 수행 최대 거리’ 선언 - ‘부피’ 변수 선언 - ‘카메라 회전 허용’ 변수 선언 - ‘몽타주 실행’ 함수를 ‘캐릭터 변수’를 받아 선언한다. - 구조체 데이터를 받을 파쿠르 데이터 자료형의 ‘테스트 데이터’ 변수 선언 - ‘파쿠르 실행 : 등산’ 함수 선언 - cpp 파일 - 생성자에 애님 몽타주 클래스의 ‘몽타주’ 변수 선언 - 몽타주 클래스의 ‘에셋 가져오기’ 함수를 등산 몽타주의 경로를 넣어 저장한다. - ‘등산 모드 체크’ 함수정의 - ‘충돌 결과 : 파쿠르 화살표 타입 : 머리’의 ‘블록 충돌 확인’ 변수가 false라면 false를 반환한다. : 등산 모드를 실행하지 않는다. - ‘장애물 확인’ 함수 호출이 false인지 확인 이후 - ‘등산 모드 체크’ 함수 호출한 결과가 true 라면? : ‘파쿠르 실행 : 등산’ 호출 - ‘파쿠르 데이터 구조체’의 ‘몽타주 실행 함수 정의 - 캐릭터 매기변수의 ‘몽타주 애니메이션 실행’ 함수를 ‘구조체의 몽타주 변수, 속도, 세션이름’을 받아 호출한다. - ‘파쿠르 실행 : 등산’ 함수 정의 - 파쿠르 타입의 ‘타입’ 변수에 파쿠르 타입 : 등산 을 저장한다. - ‘테스트 데이터’ 변수의 ‘몽타주 실행’ 함수를 ‘오너캐릭터’를 넣어 호출해라
//header
USTRUCT(BlueprintType)
struct FParkourData : public FTableRowBase
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
EParkourType Type;
UPROPERTY(EditAnywhere)
UAnimMontage* Montage; //구조체 안에서는 '전방선언'이 안들어가도 된다.
UPROPERTY(EditAnywhere)
float PlayRate = 1; //몽타주 속도
UPROPERTY(EditAnywhere)
FName SectionName;
UPROPERTY(EditAnywhere)
float MinDistance; //약간의 거리를 두고 뛰어넘을것이니, 뛰어넘을 수 있는 최소거리를 저장한다.
UPROPERTY(EditAnywhere)
float MaxDistance;
UPROPERTY(EditAnywhere)
float Extent; //두께
UPROPERTY(EditAnywhere)
bool bFixedCamera; //카메라 회전을 할건지 여부
public:
void PlayMontage(class ACharacter* InCharacter); //어떤 캐릭터인지 받아서 실행할 몽타주를 고른다.
};
private:
UPROPERTY(EditAnywhere, Category = "Data")
UDataTable* DataTable; //위에서 헤더를 선언해서 class 전방선언 하지 않아도 된다.
private:
bool Check_ClimbMode();
void DoParkour_Climb();
//cpp
CHelpers::GetAsset<UAnimMontage>(&TestData.Montage, "AnimMontage'/Game/Characters/Montages/Parkour/Run_Climb_Montage.Run_Climb_Montage'");
//머리쪽이 없으면 올라가지 못하게한다.
CheckFalseResult(HitResults[(int32)EParkourArrowType::Head].bBlockingHit, false);
return true;
//올라갈 수 있는 곳인지 확인
if (Check_ClimbMode())
{
DoParkour_Climb();
return;
}
InCharacter->PlayAnimMontage(Montage, PlayRate, SectionName);
파쿠르 종료시 타입이 돌아오게 하기 - 노티파이 작성
- 헤더 파일 - ‘이름 정의’ 함수와 ‘이벤트 실행’ 함수 선언 - cpp 파일 - 이름 : ‘파쿠르 종료’ - 이벤트 함수 정의 - 파쿠르 관련 노티파이 함수 호출은 ‘파쿠르 컴포넌트’에서 가져온다. - ‘메쉬 컴포넌트’의 ‘오너 가져오기’ 함수를 호출해서 가져온 ‘캐릭터 : 플레이어’에 있는 ‘파쿠르 컴포넌트’를 ‘파쿠르’ 변수를 선언해서 저장한다. - ‘파쿠르’가 비어있는지 확인한다. - ‘파쿠르’에 있는 ‘파쿠르 종료’ 함수를 호출한다.
//cpp
//MeshComp->GetOwner() : 플레이어 / 결과 : 플레이어에 있는 파쿠르 컴포넌트를 찾게 된다.
UCParkourComponent* parkour = CHelpers::GetComponent<UCParkourComponent>(MeshComp->GetOwner());
CheckNull(parkour);
parkour->End_DoParkour();
- 헤더 파일 - ‘파쿠르 실행 종료’ 함수 선언 - cpp 파일 - ‘파쿠르 실행 종료’ 함수 정의 - 파쿠르 타입의 ‘타입’ 변수에 파쿠르 타입 : Max로 저장한다. : 다른일을 다시 할 수 있게 한다.
//header
void End_DoParkour_Climb();
//cpp
Type = EParkourType::Max; //일단 다른일 한다고 알린다.
애니메이션에 움직임이 있을경우 적용하는 방법
- 애니메이션에 루트모션이 있는지 확인하는 방법 : 애니메이션 파일 - 좌측 상단 캐릭터 버튼 - 애니메이션 - 루프 및 초기화 클릭
- 애니메이션 파일 - 루트 모션 - 루트 모션 활성화 체크 (비활성화한다면 루트모션 적용이 안된다.)
캐릭터 무브먼트 이동 모드 설명
- 디폴트 랜드 이동 모드 : 모드에 따라 중력의 적용을 다르게 설정하는 모드들이다.
- Walking : 표면을 걷는다.
- Flying : 중력의 적용을 없앤다.
- Custom : 하위 모드를 만들 수도 있다.
- 즉, 파쿠르 애니메이션에 있는 위로 이동하는 모션이 나오도록 하는 조건
- 루트 모션 활성화 및 초기화
- 디폴트 이동 모드에서 중력의 적용을 받지 않는 모드로 변경
코드로 올라갈 수 있게끔 조절 - 날기 모드 설정
- 헤더 파일 - ‘파쿠르 실행 종료 : 등산’ 함수 선언 - cpp 파일 - ‘파쿠르 실행 : 등산’ 함수 정의 - ‘오너 캐릭터’에 있는 ‘캐릭터 무브먼트 가져오기’ 함수를 호출하고 ‘무브먼트 모드 설정’ 함수에 ‘무브먼트 모드 :날기’ 넣어서 호출한다. - ‘파쿠르 실행 종료 : 등산’ 함수 정의 - ‘오너 캐릭터’에 있는 ‘캐릭터 무브먼트 가져오기’ 함수를 호출하고 ‘무브먼트 모드 설정’ 함수에 ‘무브먼트 모드 : 걷기’ 넣어서 호출한다. - ‘파쿠르 실행 종료’ 함수 정의 - switch에 파쿠르 타입 자료형의 ‘타입’ 변수를 넣는다. - ‘파쿠르 타입 : 등산’이라면 ‘파쿠르 실행 종료 : 등산’을 호출하고 종료한다.
//header
void End_DoParkour_Climb();
//cpp
//땅 이동 모드: 날기 로 변환
OwnerCharacter->GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_Flying);
switch (Type)
{
case EParkourType::Climb:
End_DoParkour_Climb();
break;
}
파쿠르 조건 체크 : 거리 제한 시키기
- cpp 파일 - ‘등산 모드 체크’ 함수 정의 - ‘테스트 데이터’의 ‘최소 거리’가 ‘충돌 거리’보다 적으면 안된다. - ‘테스트 데이터’의 ‘최대 거리’가 ‘충돌 거리’보다 크면 안된다. - FMath의 ‘근삿값 근처라면 같다고 간주’ 함수에 ‘테스트 데이터’의 ‘부피’ 와 ‘충돌 오브젝트 부피’의 ‘Z’가 ‘10’ 이내라면 안된다
//cpp
CheckFalseResult(TestData.MinDistance < HitDistance, false);
CheckFalseResult(TestData.MaxDistance > HitDistance, false);
CheckFalseResult(FMath::IsNearlyEqual(TestData.Extent, HitObstacleExtent.Z, 10), false);
파쿠르 조건 체크 : 플레이어와 면의 충돌 각과 면의 수직각의 오차
- ‘충돌 면의 수직 각’ - ‘플레이어가 충돌시킨 면의 각’ : 플레이어부터 수직각까지의 내각
- 헤더 파일 - ‘가능한 정면 앵글’ 변수 선언 후 초기화 - cpp 파일 - FMath의 ‘절댓값’ 함수에 ‘보는 각도’ 절댓값 - ‘수직각’ 절댓값을 넣고 호출한 결과를 ‘요’ 변수를 선언하고 저장한다. - ‘요’ 변수가 ‘가능한 정면 앵글’ 보다 작거나 같다면 실행하지 않는다.
//header
UPROPERTY(EditAnywhere, Category = "Trace")
float AvailableFrontAngle = 15; //정면 허용 각도
//cpp
//정면 허용 각이 아니면 파쿠르 못하게 하는 코드
//플레이어를 바라보는 각도 - 충돌지점 수직각
float yaw = FMath::Abs(FMath::Abs(lookAt) - FMath::Abs(impactAt));
CheckFalseResult(yaw <= AvailableFrontAngle, false);
파쿠르 할때 틀어진 몸 조절
- -hitResult.ImpactNormal : 플레이어가 바라보는 방향으로 맞추기 위해서이다.
- 캐릭터의 위치를 충돌지점으로 설정하면? : 충돌체와 캐릭터에 있는 메쉬로인해 뚫지는 않고 앞에서 멈춘다.
- cpp 파일 - ‘요 로부터의 회전’ 변수에 ‘x방향 회전자 만들기’ 함수에 ‘-수직각’의 ‘Yaw’각을 가져와 저장한다. - ‘파쿠르 실행 : 등산’ 함수 정의 - ‘오너 캐릭터’의 ‘액터 위치 세팅’ 함수에 ‘충돌결과 : 파쿠르 화살표 : 중앙’의 ‘충돌지점’으로 설정한다. - ‘오너 캐릭터’의 ‘액터 회전 세팅’ 함수에 ‘회전자’ 함수의 Yaw값에 ‘요 로부터의 회전’ 변수를 넣고 설정한다.