초보 코린이의 성장 일지
UE4 Parkour Climb 본문
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Engine/DataTable.h"
#include "CParkourComponent.generated.h"
UENUM(BlueprintType)
enum class EParkourArrowType : uint8
{
// 추적할 화살표 Type
Center = 0, Ceil, Floor, Left, Right, Land, Max,
};
UENUM(BlueprintType)
enum class EParkourType : uint8
{
// 파쿠르 수행할 Type
Climb = 0, Fall, Slide, Short, Normal, Wall, Max,
};
USTRUCT(BlueprintType)
// FTableRowBase로 상속을 받아야 불러지는 곳에서 노출이된다.
struct FParkourData : public FTableRowBase
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
EParkourType Type;
UPROPERTY(EditAnywhere)
UAnimMontage* Montage;
UPROPERTY(EditAnywhere)
float PlayRatio = 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);
};
UCLASS()
class U2212_06_API UCParkourComponent : public UActorComponent
{
GENERATED_BODY()
private:
UPROPERTY(EditAnywhere, Category = "Data")
UDataTable* DataTable;
private:
UPROPERTY(EditAnywhere, Category = "Trace")
float TraceDistance = 600;
UPROPERTY(EditAnywhere, Category = "Trace")
TEnumAsByte<EDrawDebugTrace::Type> DebugType;
UPROPERTY(EditAnywhere, Category = "Trace")
float AvailableFrontAngle = 15;
public:
UCParkourComponent();
protected:
virtual void BeginPlay() override;
public:
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
private:
void LineTrace(EParkourArrowType InType);
private:
void CheckTrace_Center();
void CheckTrace_Ceil();
void CheckTrace_Floor();
void CheckTrace_LeftRight();
private:
// 배열이 return 되도록 map 사용
TMap<EParkourType, TArray<FParkourData>> DataMap;
private:
class ACharacter* OwnerCharacter;
class UArrowComponent* Arrows[(int32)EParkourArrowType::Max]; // 화살표 담을 곳
FHitResult HitResults[(int32)EParkourArrowType::Max]; // 화살표 충돌 결과 저장
private:
// 기본 충돌에 관한 정보들
AActor* HitObstacle;
FVector HitObstacleExtent;
float HitDistance;
float ToFrontYaw;
};
#include "Parkour/CParkourComponent.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Components/ArrowComponent.h"
void UCParkourComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// 값 초기화
HitObstacle = NULL;
HitObstacleExtent = FVector::ZeroVector;
HitDistance = 0;
ToFrontYaw = 0;
CheckTrace_Center();
// HitObstacle이 있다는건 hit가 되었다는 뜻
if (!!HitObstacle)
{
CheckTrace_Ceil();
CheckTrace_Floor();
CheckTrace_LeftRight();
}
}
void UCParkourComponent::CheckTrace_Ceil()
{
LineTrace(EParkourArrowType::Ceil);
}
void UCParkourComponent::CheckTrace_Floor()
{
LineTrace(EParkourArrowType::Floor);
}
void UCParkourComponent::CheckTrace_LeftRight()
{
LineTrace(EParkourArrowType::Left);
LineTrace(EParkourArrowType::Right);
}
1. 우선 추가된 코드로 각 화살표 위치에서 체크가 되도록 만들어준다.
2. Tick를 통해서 계속 판별해준다.
1. 충돌되는 객체에 다가가면 일정거리에서 전부위에 있는 화살표가 체크되는걸 확인할 수 있다.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Engine/DataTable.h"
#include "CParkourComponent.generated.h"
UCLASS()
class U2212_06_API UCParkourComponent : public UActorComponent
{
GENERATED_BODY()
private:
// 파쿠르 동작들이 수행될 수 있는지 전체적으로 체크해주는 용도
bool Check_Obstacle();
public:
void DoParkour(bool bLanded = false); // 뛰어 내리는지 체크
void End_DoParkour();
private:
// 현재 수행중인 파쿠르 타입
EParkourType Type = EParkourType::Max;
};
#include "Parkour/CParkourComponent.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Components/ArrowComponent.h"
bool UCParkourComponent::Check_Obstacle()
{
// HitObstacle는 중앙에서 hit된 장애물이므로, null이 아니여야 한다.
CheckNullResult(HitObstacle, false);
// 중앙, 좌, 우 hit되어야 하므로 체크.
bool b = true;
b &= HitResults[(int32)EParkourArrowType::Center].bBlockingHit;
b &= HitResults[(int32)EParkourArrowType::Left].bBlockingHit;
b &= HitResults[(int32)EParkourArrowType::Right].bBlockingHit;
CheckFalseResult(b, false);
// 중앙, 좌, 우 Normal를 해주는 이유는 모서리인지 아닌지 판별해주기 위해
FVector center = HitResults[(int32)EParkourArrowType::Center].Normal;
FVector left = HitResults[(int32)EParkourArrowType::Left].Normal;
FVector right = HitResults[(int32)EParkourArrowType::Right].Normal;
// Equals로 좌우 모서리 판별
CheckFalseResult(center.Equals(left), false);
CheckFalseResult(center.Equals(right), false);
// hit된 지점과 플레이어 위치를 가지고 두개의 방향을 구하면, 플레이어가 hit된 지점을 바라보는 각도가 나온다
FVector start = HitResults[(int32)EParkourArrowType::Center].ImpactPoint;
FVector end = OwnerCharacter->GetActorLocation();
float lookAt = UKismetMathLibrary::FindLookAtRotation(start, end).Yaw;
// hit된 지점에 각
FVector impactNormal = HitResults[(int32)EParkourArrowType::Center].ImpactNormal;
// 절대 x공간 축을 기준으로 normal 각 구하기
float impactAt = UKismetMathLibrary::MakeRotFromX(impactNormal).Yaw;
float yaw = abs(abs(lookAt) - abs(impactAt)); // 위에 두개의 각차, 각차만 비교할것이므로 절대값 씌운다.
// 수행할 수 있는 각도 안에 있나 판단, AvailableFrontAngle = 15
CheckFalseResult(yaw <= AvailableFrontAngle, false);
// 조건을 전부 패스했다면 수행
return true;
}
void UCParkourComponent::DoParkour(bool bLanded)
{
// 현재 수행중인 동작이 있다면 못하도록, Max가 수행중이 아닐때를 나타낸다 .
CheckFalse(Type == EParkourType::Max);
// 할 수 있는지 체크
CheckFalse(Check_Obstacle());
if (Check_ClimbMode())
{
DoParkour_Climb();
return;
}
}
void UCParkourComponent::End_DoParkour()
{
// 현재 수행 중인 타입들 체크 후 실행, 끝이나면 다시 타입 돌려주기
switch (Type)
{
case EParkourType::Climb:
End_DoParkour_Climb();
break;
}
Type = EParkourType::Max;
}
1. Check_Obstacle을 통해 한번에 파쿠르 동작을 수행할 수 있는지 없는지를 체크해준다.
2. 각 타입을 체크해주고, 동작 가능 여부를 체크
3. 각 체크를 통과했다면 그 동작을 실행하도록 설정.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Engine/DataTable.h"
#include "CParkourComponent.generated.h"
UCLASS()
class U2212_06_API UCParkourComponent : public UActorComponent
{
GENERATED_BODY()
private:
bool Check_ClimbMode();
void DoParkour_Climb();
void End_DoParkour_Climb();
};
#include "Parkour/CParkourComponent.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Components/ArrowComponent.h"
bool UCParkourComponent::Check_ClimbMode()
{
CheckFalseResult(HitResults[(int32)EParkourArrowType::Ceil].bBlockingHit, false);
// Map.Find가 값을 return 하는게 아닌 *를 return 하므로, const로 수정을 못하게 막아준다.
// 그래서 아래에서 받아서 사용할때도 *를 붙여서 사용
const TArray<FParkourData>* datas = DataMap.Find(EParkourType::Climb);
CheckFalseResult((*datas)[0].MinDistance < HitDistance, false);
CheckFalseResult((*datas)[0].MaxDistance > HitDistance, false);
CheckFalseResult(FMath::IsNearlyEqual((*datas)[0].Extent, HitObstacleExtent.Z, 10), false);
return true;
}
void UCParkourComponent::DoParkour_Climb()
{
Type = EParkourType::Climb;
// hit된 위치로 옮겨주고, 방향 바라보게 하기
OwnerCharacter->SetActorLocation(HitResults[(int32)EParkourArrowType::Center].ImpactPoint);
OwnerCharacter->SetActorRotation(FRotator(0, ToFrontYaw, 0));
(*DataMap.Find(EParkourType::Climb))[0].PlayMontage(OwnerCharacter); // 파쿠르 동작 실행
// 기어올라가야 하므로, 중력 꺼주기
OwnerCharacter->GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_Flying);
}
void UCParkourComponent::End_DoParkour_Climb()
{
// 착지하면 다시 Walk 모드로 변경
OwnerCharacter->GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_Walking);
}
1. Climb 동작 수행에 관하여 체크.
2. 동작에 필요한 요소인 Flying모드로 잠시 바꿔주고 다시 Walk로 돌려주기.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Components/CStateComponent.h"
#include "Characters/ICharacter.h"
#include "Parkour/CParkourComponent.h"
#include "CPlayer.generated.h"
UCLASS()
class U2212_06_API ACPlayer
: public ACharacter
, public IICharacter
{
GENERATED_BODY()
public:
void Click_RightButton();
};
#include "Characters/CPlayer.h"
#include "Global.h"
#include "CAnimInstance.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/InputComponent.h"
#include "Components/CWeaponComponent.h"
#include "Components/CMontagesComponent.h"
#include "Components/CMovementComponent.h"
#include "Components/ArrowComponent.h"
void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAction("SubAction", EInputEvent::IE_Pressed, this, &ACPlayer::Click_RightButton);
}
void ACPlayer::Click_RightButton()
{
if (Weapon->IsUnarmedMode())
{
Parkour->DoParkour();
return;
}
Weapon->SubAction_Pressed();
}
1. 오른쪽 누를때 동작이 수행될 수 있도록 모드를 체크해준다.
1. 노티파이를 하나 생성해준다.
2. End_DoParkour를 지정해줘야 Climb 동작이 끝난 후 공중에 안날라 다니게 할 수 있다.
#pragma once
#include "CoreMinimal.h"
#include "Animation/AnimNotifies/AnimNotify.h"
#include "CAnimNotify_End_Parkour.generated.h"
UCLASS()
class U2212_06_API UCAnimNotify_End_Parkour : public UAnimNotify
{
GENERATED_BODY()
public:
FString GetNotifyName_Implementation() const override;
virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;
};
#include "Notifies/CAnimNotify_End_Parkour.h"
#include "Global.h"
#include "Parkour/CParkourComponent.h"
FString UCAnimNotify_End_Parkour::GetNotifyName_Implementation() const
{
return "Parkour";
}
void UCAnimNotify_End_Parkour::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
{
Super::Notify(MeshComp, Animation);
CheckNull(MeshComp);
CheckNull(MeshComp->GetOwner());
UCParkourComponent* parkour = CHelpers::GetComponent<UCParkourComponent>(MeshComp->GetOwner());
CheckNull(parkour);
parkour->End_DoParkour();
}
1. End_DoParkour를 콜해준다.
1. 사용할 몽타주 동작에 만들어 준 노티파이를 지정해준다.
https://www.youtube.com/watch?v=CozKeYl9kRM
'언리얼' 카테고리의 다른 글
UE4 Feet IK, Behavior, HealBar (0) | 2023.07.21 |
---|---|
UE4 Parkour Final, Feet IK (0) | 2023.07.20 |
UE4 Parkour (0) | 2023.07.18 |
UE4 Bow Arrow (0) | 2023.07.13 |
UE4 Bow Aim Bend (0) | 2023.07.10 |
Comments