Feet IK(Inverse Kinematics)는 캐릭터의 발이 지면에 정확하게 위치하도록 하는 기술이다. 캐릭터 발의 위치를 조정할 수 있는 가상 본(virtual bone)을 생성한 후 사용한다. Animation Blueprint에 Two Bone IK을 사용하여 발의 위치를 정확하게 조정한다. 이를 위해 Foot IK 노드에는 목표 위치(Target Location)와 목표 회전(Target Rotation)을 입력하여 이 목표 위치와 회전은 발이 지면에 어떻게 위치해야 하는지를 정의한다.

 

목차

     

     


     

     

     
    Plugins
     
      Weapon
     
        Resource
     
          Icon128.png
    weapon_thumbnail_icon.png
     
        Source
     
          Weapon  
          SWeaponCheckBoxes.h .cpp
    SWeaponDetailsView.h .cpp

    SWeaponDoActionData.h .cpp
    SWeaponEquipmentData.h .cpp
    SWeaponHitData.h .cpp

    SWeaponLeftArea.h .cpp
    Weapon.Build.cs
    WeaponAssetEditor.h .cpp
    WeaponAssetFactory.h .cpp
    WeaponCommand.h .cpp 
    WeaponContextMenu.h .cpp
    WeaponModule.h .cpp
    WeaponStyle.h .cpp 
     
       

     

     
    Source
      U2212_06
        Characters
        CAnimInstance.h .cpp
    CEnemy.h .cpp 
    CPlayer.h .cpp
    ICharacter.h .cpp
        Components
        CFeetComponent.h .cpp 생성
    CMontagesComponent.h .cpp 
    CMovementComponent.h .cpp 
    CStateComponent.h .cpp
    CStatusComponent.h .cpp  
    CWeaponComponent.h .cpp 
        Notifies
        CAnimNotifyState_BeginAction.h .cpp
    CAnimNotifyState_BowString.h .cpp
    CAnimNotify_CameraShake.h .cpp 
    CAnimNotify_End_Parkour
    CAnimNotifyState_EndAction.h .cpp
    CAnimNotify_EndState.h .cpp
    CAnimNotifyState.h .cpp
    CAnimNotifyState_CameraAnim.h .cpp
    CAnimNotifyState_Collision.h .cpp 
    CAnimNotifyState_Combo.h .cpp
    CAnimNotifyState_Equip.h .cpp
    CAnimNotifyState_SubAction.h .cpp
        Parkour
        CParkourComponent.h .cpp
        Utilities
        CHelper.h
    CLog.h .cpp
        Weapons
        CArrow.h .cpp
    CAura.h .cpp
    CCamerModifier.h .cpp
    CGhostTrail.h .cpp
    CRotate_Object.h .cpp
    CThornObject.h .cpp
    CAnimInstance_Bow.h .cpp
    CAttachment_Bow.h .cpp
    CDoAction_Around.h .cpp
    CDoAction_Bow.h .cpp
    CDoAction_Combo.h .cpp
    CDoAction_Warp.h .cpp
    CSubAction_Around.h .cpp
    CSubAction_Bow.h .cpp
    CSubAction_Fist.h .cpp
    CSubAction_Hammer.h .cpp
    CSubAction_Sword.h .cpp
    CDoAction_Warp.h .cpp

    CAttachment.h .cpp
    CDoAction.h .cpp
    CEquipment.h .cpp
    CSubAction.h .cpp
    CWeaponAsset.h .cpp
    CWeaponStructures.h .cpp
        Global.h
    CGameMode.h .cpp
    U2212_06.Build.cs
        U2212_06.uproject
     

     

     

     

     

    파쿠르

     

     


     

    CParkourComponent.h

     

    CParkourComponent.h

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    #include "Components/ActorComponent.h"
    #include "Kismet/KismetSystemLibrary.h"//DrawDebug를 포함하는 헤더
    #include "Engine/DataTable.h"//DataTable를 다루는 헤더
    #include "CParkourComponent.generated.h"
    
    //파쿠르를 위해 추적할 화살표 타입
    UENUM(BlueprintType)
    enum class EParkourArrowType : uint8
    {
    	Center = 0, Ceil, Floor, Left, Right, Land, Max,
    };
    
    //파쿠르 동작 타입
    UENUM(BlueprintType)
    enum class EParkourType : uint8
    {
    	Climb = 0, Fall, Slide, Short, Normal, Wall, Max,
    };
    
    USTRUCT(BlueprintType)
    struct FParkourData : public FTableRowBase
    {
    	GENERATED_BODY()
    
    public:
    	UPROPERTY(EditAnywhere)
    		EParkourType Type;
    
    	UPROPERTY(EditAnywhere)
    		UAnimMontage* Montage;//파쿠르 타입에 따라 Play할 몽타주
    
    	UPROPERTY(EditAnywhere)
    		float PlayRatio = 1;//Play 속도
    
    	UPROPERTY(EditAnywhere)
    		FName SectionName;//몽타주에 Section을 주면 해당 Section부터 재생. 이를 위한 변수
    
    	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;//파쿠르 DataTable
    
    private:
    	UPROPERTY(EditAnywhere, Category = "Trace")
    		float TraceDistance = 600;//파쿠르 Line Trace가 적용되는 거리
    
    	UPROPERTY(EditAnywhere, Category = "Trace")
    		TEnumAsByte<EDrawDebugTrace::Type> DebugType;
    
    	UPROPERTY(EditAnywhere, Category = "Trace")
    		float AvailableFrontAngle = 15;//파쿠르 수행 제한각도
    
    public:
    	FORCEINLINE bool IsExecuting() { return Type != EParkourType::Max; }
    
    public:	
    	UCParkourComponent();
    
    protected:
    	virtual void BeginPlay() override;
    
    public:	
    	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
    
    private:
    	void LineTrace(EParkourArrowType InType);
    
    private:
    	//Arrow로 쏜 LineTrace를 체크하는 함수들
    	void CheckTrace_Center();
    	void CheckTrace_Ceil();
    	void CheckTrace_Floor();
    	void CheckTrace_LeftRight();
    	void CheckTrace_Land();
    
    private:
    	bool Check_Obstacle();
    
    public:
    	//파쿠르 수행 함수
    	void DoParkour(bool bLanded = false);
    	void End_DoParkour();
    
    private:
    	bool Check_ClimbMode();
    	void DoParkour_Climb();
    	void End_DoParkour_Climb();
    	
    	bool Check_FallMode();
    	void DoParkour_Fall();
    	void End_DoParkour_Fall();
    	
    	bool Check_SlideMode();
    	void DoParkour_Slide();
    	void End_DoParkour_Slide();
    
    	//Short, Normal, Wall
    	bool Check_ObstacleMode(EParkourType InType, FParkourData& OutData);
    	void DoParkour_Obstacle(EParkourType InType, FParkourData& InData);
    	void End_DoParkour_Obstacle();
    
    private:
    	TMap<EParkourType, TArray<FParkourData>> DataMap;//TMap에 Key와 Key를 넣으면 배열이 리턴된다.		
    
    private:
    	class ACharacter* OwnerCharacter;
    	class UArrowComponent* Arrows[(int32)EParkourArrowType::Max];
    
    	FHitResult HitResults[(int32)EParkourArrowType::Max];//Arrows 마다 충돌 결과를 저장할 배열 변수
    
    private:
    	AActor* HitObstacle;
    	FVector HitObstacleExtent;
    	float HitDistance;
    	float ToFrontYaw;
    
    private:
    	EParkourType Type = EParkourType::Max;//현재 수행중인 파쿠프 타입. 기본값을 Max로 설정하여 아무것도 하지 않는 타입을 기본값으로 만들어준다.
    
    private:
    	bool bFalling;
    
    private:
    	AActor* BackupObstacle;//장애물을 정보를 잠시 담는 변수
    };

    인라인 함수 추가

    • FORCEINLINE bool IsExecuting() { return Type != EParkourType::Max; }

     

    함수 추가

    • void CheckTrace_Land();

     

    내려오기 파쿠르 함수 추가

    • bool Check_FallMode();
    • void DoParkour_Fall();
    • void End_DoParkour_Fall();

     

    Slide 파쿠르 함수 추가

    • bool Check_SlideMode();
    • void DoParkour_Slide();
    • void End_DoParkour_Slide();

     

    Obstacle 파쿠르 함수 추가

    • bool Check_ObstacleMode(EParkourType InType, FParkourData& OutData);
    • void DoParkour_Obstacle(EParkourType InType, FParkourData& InData);
    • void End_DoParkour_Obstacle();

     

    변수 추가

    • bool bFalling;
      • 공중에서 떨어지는지 여부를 판단하는 bool 변수
    • AActor* BackupObstacle;
      • 장애물을 정보를 잠시 담는 변수

     

     

     

     

    CParkourComponent.cpp

    더보기
    #include "Parkour/CParkourComponent.h"
    #include "Global.h"
    #include "GameFramework/Character.h"
    #include "GameFramework/CharacterMovementComponent.h"
    #include "Components/ArrowComponent.h"
    #include "Components/CMovementComponent.h"
    
    //#define LOG_UCParkourComponent
    
    void FParkourData::PlayMontage(class ACharacter* InCharacter)
    {
    	//파쿠르 동작 중에는 FixedCamera를 켜서 카메라가 움직이지 않게 해준다.
    	if (bFixedCamera)
    	{
    		UCMovementComponent* movement = CHelpers::GetComponent<UCMovementComponent>(InCharacter);
    
    		if (!!movement)
    			movement->EnableFixedCamera();
    	}
    
    	InCharacter->PlayAnimMontage(Montage, PlayRatio, SectionName);
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    
    UCParkourComponent::UCParkourComponent()
    {
    	PrimaryComponentTick.bCanEverTick = true;
    
    	CHelpers::GetAsset<UDataTable>(&DataTable, "DataTable'/Game/Parkour/DT_Parkour.DT_Parkour'");//DataTable 생성.
    }
    
    void UCParkourComponent::BeginPlay()
    {
    	Super::BeginPlay();
    
    	TArray<FParkourData*> datas;//FParkourData 사용하여 파쿠르 관련 변수들을 담는 구조체 사용.
    	DataTable->GetAllRows<FParkourData>("", datas);//DataTable에서 데이터를 가져온다.
    
    	for (int32 i = 0; i < (int32)EParkourType::Max; i++)
    	{
    		TArray<FParkourData> temp;//FParkourData 배열 temp 생성.
    		for(FParkourData* data : datas)//datas를 순회하여 검색
    		{
    			if (data->Type == (EParkourType)i)//데이터의 타입 == UENUM인 EParkourType 이라면
    				temp.Add(*data);//해당 파쿠르 타입을 temp에 담는다. 
    
    			DataMap.Add((EParkourType)i, temp);//DataMap에 Key에 파쿠르 타입을 숫자로, Value에 FParkourData 배열 temp를 담는다.
    		}
    	}
    
    
    	OwnerCharacter = Cast<ACharacter>(GetOwner());//해당 클래스를 가지고 있는 GetOwner인 캐릭터(여기서는 Player) OwnerCharacter로 설정.
    
    	USceneComponent* arrow = CHelpers::GetComponent<USceneComponent>(OwnerCharacter, "ArrowGroup");//Arrows를 가져온다(=OwnerCharacter의 ArrowGroup을 가져온다).
    
    	TArray<USceneComponent*> components;//components 변수에 SceneComponent들(=Arrows)을 가져와 담는다.
    	arrow->GetChildrenComponents(false, components);
    
    	for (int32 i = 0; i < (int32)EParkourArrowType::Max; i++)
    		Arrows[i] = Cast<UArrowComponent>(components[i]);//Arrows[] 배열 변수에 components인 Arrows를 담는다.
    }
    
    void UCParkourComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
    {
    	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
    
    
    	CheckTrace_Land();//아래로 쏘는 Land Arrow의 LineTrace는 매 Tick 검사해야 한다.
    }
    
    void UCParkourComponent::LineTrace(EParkourArrowType InType)
    {
    	UArrowComponent* arrow = Arrows[(int32)InType];//Trace할 화살을 가져온다.
    	FLinearColor color = FLinearColor(arrow->ArrowColor);
    
    	FTransform transform = arrow->GetComponentToWorld();//arrow의 위치를 가져온다.
    
    	//위치를 활용하여 길이를 만든다.
    	FVector start = transform.GetLocation();
    	FVector end = start + OwnerCharacter->GetActorForwardVector() * TraceDistance;
    
    
    	TArray<AActor*> ignores;//Line Trace 시 무시할 것들을 담는 변수
    	ignores.Add(OwnerCharacter);//OwnerCharacter는 Line Trace에서 무시되게 한다.
    
    	UKismetSystemLibrary::LineTraceSingle(GetWorld(), start, end, ETraceTypeQuery::TraceTypeQuery3, false, ignores, DebugType, HitResults[(int32)InType], true, color, FLinearColor::White);//TraceTypeQuery3는 새로 만든 TraceChannel인 Parkour다.
    }
    
    void UCParkourComponent::CheckTrace_Center()
    {
    	EParkourArrowType type = EParkourArrowType::Center;
    	LineTrace(type);
    
    	const FHitResult& hitResult = HitResults[(int32)type];
    	CheckFalse(hitResult.bBlockingHit);
    
    	//StaticMesh가 충돌체가 되므로 StaticMesh를 변수로 두고 StaticMesh와의 충돌 여부를 체크한다.
    	UStaticMeshComponent* mesh = CHelpers::GetComponent<UStaticMeshComponent>(hitResult.GetActor());
    	CheckNull(mesh);
    
    
    	HitObstacle = hitResult.GetActor();
    
    	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;//충돌체까지의 거리
    
    	//충돌체에 대한 Normal을 뒤집는다. Player는 밖에서 안을 바라보기 때문에 -로 값을 뒤집는다.
    	ToFrontYaw = UKismetMathLibrary::MakeRotFromX(-hitResult.ImpactNormal).Yaw;
    
    #ifdef LOG_UCParkourComponent
    	CLog::Print(HitObstacle, 10);
    	CLog::Print(HitObstacleExtent, 11);
    	CLog::Print(HitDistance, 12);
    	CLog::Print(ToFrontYaw, 13);
    #endif //LOG_UCParkourComponent
    }
    
    void UCParkourComponent::CheckTrace_Ceil()
    {
    	LineTrace(EParkourArrowType::Ceil);
    }
    
    void UCParkourComponent::CheckTrace_Floor()
    {
    	LineTrace(EParkourArrowType::Floor);
    }
    
    void UCParkourComponent::CheckTrace_LeftRight()
    {
    	LineTrace(EParkourArrowType::Left);
    	LineTrace(EParkourArrowType::Right);
    }
    
    void UCParkourComponent::CheckTrace_Land()
    {
    	CheckFalse(OwnerCharacter->GetCharacterMovement()->IsFalling());
    
    	//최초 추락
    	CheckTrue(bFalling);
    	bFalling = true;
    
    	UArrowComponent* arrow = Arrows[(int32)EParkourArrowType::Land];//Land Arrow
    	FLinearColor color = FLinearColor(arrow->ArrowColor);
    
    	FTransform transform = arrow->GetComponentToWorld();
    	FVector start = transform.GetLocation();//Land Arrow 위치
    
    	const TArray<FParkourData>* datas = DataMap.Find(EParkourType::Fall);
    	FVector end = start + transform.GetRotation().GetForwardVector() * (*datas)[0].Extent;//Land Arrow 위치에서 Extent만큼 늘어난 거리를 end 지점으로 설정.
    
    
    	TArray<AActor*> ignores;//LineTrace를 무시할 Actor들을 위한 변수
    	ignores.Add(OwnerCharacter);//OwnerCharacter인 Player를 무시하도록 설정.
    
    	UKismetSystemLibrary::LineTraceSingle(GetWorld(), start, end, ETraceTypeQuery::TraceTypeQuery1, false, ignores, DebugType, HitResults[(int32)EParkourArrowType::Land], true, color, FLinearColor::White);
    }
    
    bool UCParkourComponent::Check_Obstacle()
    {
    	CheckNullResult(HitObstacle, false);//HitObstacle이 null이 아닌지 체크
    
    	//Arrow Center, Left, Right 모두 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);
    
    	//Arrow Center, Left, Right의 Normal이 모두 같은 방향인지 체크. 모서리인지 아닌지 판단
    	FVector center = HitResults[(int32)EParkourArrowType::Center].Normal;
    	FVector left = HitResults[(int32)EParkourArrowType::Left].Normal;
    	FVector right = HitResults[(int32)EParkourArrowType::Right].Normal;
    
    	CheckFalseResult(center.Equals(left), false);//Arrow center와 left값이 같은지 체크
    	CheckFalseResult(center.Equals(right), false);//Arrow center와 right값이 같은지 체크
    
    	//impactNormal과 player가 바라보는 사이의 각도를 구하여 AvailableFrontAngle 각도보다 작을 때 수행하게 만든다.
    	FVector start = HitResults[(int32)EParkourArrowType::Center].ImpactPoint;//Hit된 지점
    	FVector end = OwnerCharacter->GetActorLocation();//Player의 위치
    	float lookAt = UKismetMathLibrary::FindLookAtRotation(start, end).Yaw;//Player가 Hit된 위치를 바라보는 방향의 Yaw값을 구한다.
    	
    	FVector impactNormal = HitResults[(int32)EParkourArrowType::Center].ImpactNormal;//Hit된 지점의 Normal
    	float impactAt = UKismetMathLibrary::MakeRotFromX(impactNormal).Yaw;//Normal 방향벡터의 Yaw값
    
    	float yaw = abs(abs(lookAt) - abs(impactAt));//두 개의 Yaw값의 차이(=impactNormal과 player가 바라보는 사이의 각도)
    
    	CheckFalseResult(yaw <= AvailableFrontAngle, false);//AvailableFrontAngle로 설정한 각 15도 이하라면
    
    	return true;//수행한다.
    }
    
    void UCParkourComponent::DoParkour(bool bLanded)
    {
    	CheckFalse(Type == EParkourType::Max);//Max는 파쿠르가 수행중이 아닌 상황
    
    	if (bLanded && Check_FallMode())
    	{
    		DoParkour_Fall();
    
    		return;
    	}
    
    	//값 초기화
    	HitObstacle = NULL;
    	HitObstacleExtent = FVector::ZeroVector;
    	HitDistance = 0;
    	ToFrontYaw = 0;
    
    	CheckTrace_Center();
    
    	if (!!HitObstacle)//HitObstacle이 있다면(=CheckTrace_Center의 hitResult가 있다면)
    	{
    		//나머지 Arrow들로도 LineTrace 검사한다.
    		CheckTrace_Ceil();
    		CheckTrace_Floor();
    		CheckTrace_LeftRight();
    	}
    
    
    	CheckFalse(Check_Obstacle());//장애물이 있는지 체크
    
    	if (Check_ClimbMode())//올라가기 파쿠르를 수행할 조건이 된다면
    	{
    		DoParkour_Climb();//올라가기 파쿠르 수행
    
    		return;
    	}
    
    	if (Check_SlideMode())//슬라이드 파쿠르를 수행할 조건이 된다면
    	{
    		DoParkour_Slide();//슬라이드 파쿠르 수행
    
    		return;
    	}
    
    
    	FParkourData data;
    	if (Check_ObstacleMode(EParkourType::Normal, data))//Normal
    	{
    		DoParkour_Obstacle(EParkourType::Normal, data);
    
    		return;
    	}
    
    	if (Check_ObstacleMode(EParkourType::Short, data))//Short
    	{
    		DoParkour_Obstacle(EParkourType::Short, data);
    
    		return;
    	}
    
    	if (Check_ObstacleMode(EParkourType::Wall, data))//Wall
    	{
    		DoParkour_Obstacle(EParkourType::Wall, data);
    
    		return;
    	}
    }
    
    void UCParkourComponent::End_DoParkour()
    {
    	switch (Type)//현재 수행중인 EParkourType
    	{
    		case EParkourType::Climb:
    			End_DoParkour_Climb();
    			break;
    
    		case EParkourType::Fall:
    			End_DoParkour_Fall();
    			break;
    
    		case EParkourType::Slide:
    			End_DoParkour_Slide();
    			break;
    
    		case EParkourType::Short:
    		case EParkourType::Normal:
    		case EParkourType::Wall:
    			End_DoParkour_Obstacle();
    			break;
    	}
    
    	Type = EParkourType::Max;//EParkourType을 원래대로 돌려준다.
    
    	//파쿠르가 끝난 후에 FixedCamera를 꺼준다.
    	UCMovementComponent* movement = CHelpers::GetComponent<UCMovementComponent>(OwnerCharacter);
    	if (!!movement)
    		movement->DisableFixedCamera();
    }
    
    bool UCParkourComponent::Check_ClimbMode()
    {
    	CheckFalseResult(HitResults[(int32)EParkourArrowType::Ceil].bBlockingHit, false);
    
    	const TArray<FParkourData>* datas = DataMap.Find(EParkourType::Climb);//Find는 값이 아닌 포인터를 리턴한다. const를 사용해서 고칠 수 없도록 만든다.
    	CheckFalseResult((*datas)[0].MinDistance < HitDistance, false);//datas접근해서 0번의 MinDistance가 HitDistance보다 작은지 체크.
    	CheckFalseResult((*datas)[0].MaxDistance > HitDistance, false);//datas접근해서 0번의 MaxDistance가 HitDistance보다 큰지 체크.
    	CheckFalseResult(FMath::IsNearlyEqual((*datas)[0].Extent, HitObstacleExtent.Z, 10), false);//datas접근해서 0번의 Extent가 HitObstacleExtent.Z값과 같은지 체크.
    
    	return true;
    }
    
    void UCParkourComponent::DoParkour_Climb()
    {
    	Type = EParkourType::Climb;//EParkourType을 Climb으로 설정.
    
    	OwnerCharacter->SetActorLocation(HitResults[(int32)EParkourArrowType::Center].ImpactPoint);//Player를 Arrow Center가 쏜 LineTrace에 Hit된 위치로 이동시킨다.
    	OwnerCharacter->SetActorRotation(FRotator(0, ToFrontYaw, 0));//Player가 Hit된 방향을 바라보게 한다.
    	(*DataMap.Find(EParkourType::Climb))[0].PlayMontage(OwnerCharacter);//Climb 몽타주를 재생
    
    	OwnerCharacter->GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_Flying);//기어올라가야 하므로 중력을 꺼줘야한다. 그래서 MovementMode를 MOVE_Flying로 변경한다.
    }
    
    void UCParkourComponent::End_DoParkour_Climb()
    {
    	OwnerCharacter->GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_Walking);
    }
    
    bool UCParkourComponent::Check_FallMode()
    {
    	CheckFalseResult(bFalling, false);//bFalling=false면 바로 return false;
    	bFalling = false;
    
    	float distance = HitResults[(int32)EParkourArrowType::Land].Distance;
    
    	const TArray<FParkourData>* datas = DataMap.Find(EParkourType::Fall);
    	//거리 판단
    	CheckFalseResult((*datas)[0].MinDistance < distance, false);//파쿠르가 수행될 최소거리<distance면 바로 return false;
    	CheckFalseResult((*datas)[0].MaxDistance > distance, false);//파쿠르가 수행될 최대거리>distance면 바로 return false;
    
    	return true;
    }
    
    void UCParkourComponent::DoParkour_Fall()
    {
    	Type = EParkourType::Fall;
    
    	(*DataMap.Find(EParkourType::Fall))[0].PlayMontage(OwnerCharacter);
    }
    
    void UCParkourComponent::End_DoParkour_Fall()
    {
    }
    
    bool UCParkourComponent::Check_SlideMode()
    {
    	//충돌한게 슬라이드 거리 안에 들어와있는지 판단
    	CheckTrueResult(HitResults[(int32)EParkourArrowType::Floor].bBlockingHit, false);
    
    	const TArray<FParkourData>* datas = DataMap.Find(EParkourType::Slide);
    	CheckFalseResult((*datas)[0].MinDistance < HitDistance, false);
    	CheckFalseResult((*datas)[0].MaxDistance > HitDistance, false);
    
    	UArrowComponent* arrow = Arrows[(int32)EParkourArrowType::Floor];
    	FLinearColor color = FLinearColor(arrow->ArrowColor);
    
    	FTransform transform = arrow->GetComponentToWorld();
    
    	FVector arrowLocation = transform.GetLocation();//Floor Arrow 위치
    	FVector ownerLocation = OwnerCharacter->GetActorLocation();//캐릭터의 위치
    
    
    	float extent = (*datas)[0].Extent;//DataTable의 Extent 정보로 넣어준다.
    	float z = arrowLocation.Z + extent;//z = Floor Arrow의 z값 + extent 값
    
    	FVector forward = OwnerCharacter->GetActorForwardVector();//캐릭터의 전방벡터
    	forward = FVector(forward.X, forward.Y, 0);//캐릭터의 전방벡터에서 z값만 0으로 만든다.
    
    	FVector start = FVector(arrowLocation.X, arrowLocation.Y, z);//start위치 = (Floor Arrow의 X, Y 위치값)과 (Floor Arrow의 z값 + extent 값)
    	FVector end = start + forward * TraceDistance;//end위치 = start위치 + forward * TraceDistance
    
    
    	TArray<AActor*> ignores;
    	FHitResult hitResult;
    
    	UKismetSystemLibrary::BoxTraceSingle(GetWorld(), start, end, FVector(0, extent, extent), OwnerCharacter->GetActorRotation(), ETraceTypeQuery::TraceTypeQuery1, false, ignores, DebugType, hitResult, true);
    	CheckTrueResult(hitResult.bBlockingHit, false);
    
    	return true;
    }
    
    void UCParkourComponent::DoParkour_Slide()
    {
    	Type = EParkourType::Slide;
    
    	OwnerCharacter->SetActorRotation(FRotator(0, ToFrontYaw, 0));
    	(*DataMap.Find(EParkourType::Slide))[0].PlayMontage(OwnerCharacter);
    
    	BackupObstacle = HitObstacle;
    	BackupObstacle->SetActorEnableCollision(false);//장애물의 Collision을 꺼준다.
    }
    
    void UCParkourComponent::End_DoParkour_Slide()
    {
    	BackupObstacle->SetActorEnableCollision(true);//장애물의 Collision을 다시 켜준다.
    	BackupObstacle = NULL;
    }
    
    bool UCParkourComponent::Check_ObstacleMode(EParkourType InType, FParkourData& OutData)
    {
    	CheckTrueResult(HitResults[(int32)EParkourArrowType::Ceil].bBlockingHit, false);//Ceil Arrow의 LineTrace에 충돌된게 없다면 return false로 끝낸다.
    
    	const TArray<FParkourData>* datas = DataMap.Find(InType);//InType의 데이터를 넣어준다. InType은 Short, Normal, Wall 중 하나일 것이다.
    
    	for (int32 i = 0; i < (*datas).Num(); i++)
    	{
    		bool b = true;
    		b &= (*datas)[i].MinDistance < HitDistance;
    		b &= (*datas)[i].MaxDistance > HitDistance;
    		b &= FMath::IsNearlyEqual((*datas)[i].Extent, HitObstacleExtent.Y, 10);
    
    		OutData = (*datas)[i];
    		CheckTrueResult(b, true);
    	}
    
    	return false;
    }
    
    void UCParkourComponent::DoParkour_Obstacle(EParkourType InType, FParkourData& InData)
    {
    	Type = InType;//Short, Normal, Wall
    
    	OwnerCharacter->SetActorRotation(FRotator(0, ToFrontYaw, 0));
    	InData.PlayMontage(OwnerCharacter);
    
    	BackupObstacle = HitObstacle;
    	BackupObstacle->SetActorEnableCollision(false);
    }
    
    void UCParkourComponent::End_DoParkour_Obstacle()
    {
    	BackupObstacle->SetActorEnableCollision(true);
    	BackupObstacle = NULL;
    }

    헤더 추가

    • #include "Components/CMovementComponent.h"

     

    함수 정의

    • void UCParkourComponent::CheckTrace_Land();

     

    내려오기 파쿠르 함수 정의

    • bool UCParkourComponent::Check_FallMode();
    • void UCParkourComponent::DoParkour_Fall();
    • void UCParkourComponent::End_DoParkour_Fall();

     

    Slide 파쿠르 함수 정의

    • bool UCParkourComponent::Check_SlideMode();
    • void UCParkourComponent::DoParkour_Slide();
    • void UCParkourComponent::End_DoParkour_Slide();

     

    Obstacle 파쿠르 함수 정

    • bool UCParkourComponent:: Check_ObstacleMode(EParkourType InType, FParkourData& OutData);
    • void UCParkourComponent::DoParkour_Obstacle(EParkourType InType, FParkourData& InData);
    • void UCParkourComponent::End_DoParkour_Obstacle();

     


     

    CPlayer

     

    CPlayer.h

    더보기
    #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()
    
    private:
    	UPROPERTY(VisibleAnywhere)
    		class USpringArmComponent* SpringArm;
    
    	UPROPERTY(VisibleAnywhere)
    		class UCameraComponent* Camera;
    
    private:
    	UPROPERTY(VisibleAnywhere)
    		class UCWeaponComponent* Weapon;
    
    	UPROPERTY(VisibleAnywhere)
    		class UCMontagesComponent* Montages;
    
    	UPROPERTY(VisibleAnywhere)
    		class UCMovementComponent* Movement;
    
    	UPROPERTY(VisibleAnywhere)
    		class UCStateComponent* State;
    
    /** 파쿠르 */
    private:
    	UPROPERTY(VisibleDefaultsOnly)
    		class USceneComponent* ArrowGroup;//파쿠르를 위한 ArrowGroup
    
    	UPROPERTY(VisibleDefaultsOnly)
    		class UArrowComponent* Arrows[(int32)EParkourArrowType::Max];
    
    	UPROPERTY(VisibleDefaultsOnly)
    		class UCParkourComponent* Parkour;
    /** 파쿠르 */
    
    public:
    	ACPlayer();
    
    protected:
    	virtual void BeginPlay() override;
    
    public:	
    	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
    
    private:
    	UFUNCTION()
    		void OnStateTypeChanged(EStateType InPrevType, EStateType InNewType);
    
    private:
    	void OnAvoid();
    
    private:
    	void BackStep();
    
    public:
    	void End_BackStep() override;//ICharacter의 함수 오버라이드
    
    public:
    	void Click_RightButton();
    
    public:
    	void Landed(const FHitResult& Hit) override;//BP의 OnLanded C++버젼.
    };

    함수 추가

    • void Landed(const FHitResult& Hit) override;
      • 블루프린트의 OnLanded의 C++버젼이다. ACharacter에 virtual void Landed(const FHitResult& Hit)가 있다.

     

     

     

    CPlayer.cpp

    더보기
    #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/CMontagesComponent.h"
    #include "Components/CMovementComponent.h"
    #include "Components/CWeaponComponent.h"
    #include "Components/ArrowComponent.h"
    
    ACPlayer::ACPlayer()
    {
    	CHelpers::CreateComponent<USpringArmComponent>(this, &SpringArm, "SpringArm", GetMesh());
    	CHelpers::CreateComponent<UCameraComponent>(this, &Camera, "Camera", SpringArm);
    
    	CHelpers::CreateActorComponent<UCWeaponComponent>(this, &Weapon, "Weapon");
    	CHelpers::CreateActorComponent<UCMontagesComponent>(this, &Montages, "Montages");
    	CHelpers::CreateActorComponent<UCMovementComponent>(this, &Movement, "Movement");
    	CHelpers::CreateActorComponent<UCStateComponent>(this, &State, "State");
    	CHelpers::CreateActorComponent<UCParkourComponent>(this, &Parkour, "Parkour");
    
    	GetMesh()->SetRelativeLocation(FVector(0, 0, -90));
    	GetMesh()->SetRelativeRotation(FRotator(0, -90, 0));
    
    	USkeletalMesh* mesh;
    	CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character/Mesh/SK_Mannequin.SK_Mannequin'");
    	GetMesh()->SetSkeletalMesh(mesh);
    
    	TSubclassOf<UCAnimInstance> animInstance;
    	CHelpers::GetClass<UCAnimInstance>(&animInstance, "AnimBlueprint'/Game/ABP_Character.ABP_Character_C'");
    	GetMesh()->SetAnimClass(animInstance);
    
    	SpringArm->SetRelativeLocation(FVector(0, 0, 140));
    	SpringArm->SetRelativeRotation(FRotator(0, 90, 0));
    	SpringArm->TargetArmLength = 200;
    	SpringArm->bDoCollisionTest = false;
    	SpringArm->bUsePawnControlRotation = true;
    	SpringArm->bEnableCameraLag = true;
    
    	GetCharacterMovement()->RotationRate = FRotator(0, 720, 0);
    
    	CHelpers::CreateComponent<USceneComponent>(this, &ArrowGroup, "ArrowGroup", GetCapsuleComponent());//ArrowGroup을 생성하고 GetCapsuleComponent 아래로 붙여준다.
    	//파쿠르에 활용한 Arrows들을 생성한다. 
    	for (int32 i = 0; i < (int32)EParkourArrowType::Max; i++)
    	{
    		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::Ceil:
    			Arrows[i]->ArrowColor = FColor::Green;
    			Arrows[i]->SetRelativeLocation(FVector(0, 0, 100));
    			break;
    
    		case EParkourArrowType::Floor:
    			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;
    		}
    	}
    
    }
    
    void ACPlayer::BeginPlay()
    {
    	Super::BeginPlay();
    
    	Movement->OnRun(); //Movement의 기본을 Run으로 설정
    	Movement->DisableControlRotation();//Movement의 기본을 DisableControlRotation으로 설정
    
    	State->OnStateTypeChanged.AddDynamic(this, &ACPlayer::OnStateTypeChanged);
    }
    
    void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
    {
    	Super::SetupPlayerInputComponent(PlayerInputComponent);
    
    	PlayerInputComponent->BindAxis("MoveForward", Movement, &UCMovementComponent::OnMoveForward);
    	PlayerInputComponent->BindAxis("MoveRight", Movement, &UCMovementComponent::OnMoveRight);
    	PlayerInputComponent->BindAxis("VerticalLook", Movement, &UCMovementComponent::OnVerticalLook);
    	PlayerInputComponent->BindAxis("HorizontalLook", Movement, &UCMovementComponent::OnHorizontalLook);
    
    	PlayerInputComponent->BindAction("Sprint", EInputEvent::IE_Pressed, Movement, &UCMovementComponent::OnSprint);
    	PlayerInputComponent->BindAction("Sprint", EInputEvent::IE_Released, Movement, &UCMovementComponent::OnRun);
    
    	PlayerInputComponent->BindAction("Avoid", EInputEvent::IE_Pressed, this, &ACPlayer::OnAvoid);
    
    	PlayerInputComponent->BindAction("Fist", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetFistMode);
    	PlayerInputComponent->BindAction("Sword", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetSwordMode);
    	PlayerInputComponent->BindAction("Hammer", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetHammerMode);
    	PlayerInputComponent->BindAction("Warp", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetWarpMode);
    	PlayerInputComponent->BindAction("Around", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetAroundMode);
    	PlayerInputComponent->BindAction("Bow", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetBowMode);
    
    	PlayerInputComponent->BindAction("Action", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::DoAction);
    
    	//PlayerInputComponent->BindAction("SubAction", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SubAction_Pressed);
    	PlayerInputComponent->BindAction("SubAction", EInputEvent::IE_Pressed, this, &ACPlayer::Click_RightButton);
    	PlayerInputComponent->BindAction("SubAction", EInputEvent::IE_Released, Weapon, &UCWeaponComponent::SubAction_Released);
    }
    
    void ACPlayer::OnStateTypeChanged(EStateType InPrevType, EStateType InNewType)
    {
    	switch (InNewType)
    	{
    		case EStateType::BackStep: BackStep(); break;
    	}
    }
    
    void ACPlayer::OnAvoid()
    {
    	CheckFalse(State->IsIdleMode());
    	CheckFalse(Movement->CanMove());
    
    	CheckTrue(InputComponent->GetAxisValue("MoveForward") >= 0.0f);//뒷방향을 입력했다면
    
    	State->SetBackStepMode();//State을 BackStepMode로 변경한다.
    }
    
    void ACPlayer::BackStep()
    {
    	Movement->EnableControlRotation();//정면을 바라본 상태로 뒤로 뛰어야하기 때문에 EnableControlRotation으로 만들어준다.
    
    	Montages->PlayBackStepMode();//PlayBackStepMode()를 통해 몽타주 재생.
    }
    
    void ACPlayer::End_BackStep()
    {
    	Movement->DisableControlRotation();//Backstep이 끝나면 원래대로 돌려준다.
    
    	State->SetIdleMode();//Idle상태로 돌려줌.
    }
    
    void ACPlayer::Click_RightButton()
    {
    	if (Weapon->IsUnarmedMode())
    	{
    		Parkour->DoParkour();
    
    		return;
    	}
    
    	Weapon->SubAction_Pressed();
    }
    
    void ACPlayer::Landed(const FHitResult& Hit)
    {
    	Parkour->DoParkour(true);
    }

    void ACPlayer::Landed(const FHitResult& Hit)

    • Parkour->DoParkour(true);

     

     

    ※ 참고: Character.h에 있는 Landed()와 OnLanded() 함수


     

     

    Parkour 몽타주에 CAnimNotify_End_DoParkour 삽입

     

    모든 파쿠르 몽타주 끝부분에 CAnimNotify_End_DoParkour를 삽입한다.

     


     

     

    실행화면

     


     

     

     

     

     

    Feet IK

     

    참고

    2023.02.03 - [Unreal Engine/Unreal RPG Blueprint] - [Unreal] (2023.02.03) 25강 - 마우스휠 Zoom In & Out, IK, FK

     

    2023.02.06 - [Unreal Engine/Unreal RPG Blueprint] - [Unreal] (2023.02.06) 26강 - IK(Inverse Kinematics)


     

     

    CFeetComponent 생성

     

    새 C++ 클래스 - ActorComponent - CFeetComponent 생성

     

     

    CFeetComponent.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Components/ActorComponent.h"
    #include "Kismet/KismetSystemLibrary.h"
    #include "CFeetComponent.generated.h"
    
    USTRUCT(BlueprintType)
    struct FFeetData
    {
    	GENERATED_BODY()
    
    public:
    	UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Feet")
    		FVector LeftDistance; //X
    
    	UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Feet")
    		FVector RightDistance; //X
    
    	UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Feet")
    		FVector PelvisDistance; //Z
    
    	UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Feet")
    		FRotator LeftRotation;
    
    	UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Feet")
    		FRotator RightRotation;
    };
    
    UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
    class U2212_06_API UCFeetComponent : public UActorComponent
    {
    	GENERATED_BODY()
    
    private:
    	UPROPERTY(EditAnywhere, Category = "Trace")
    		TEnumAsByte<EDrawDebugTrace::Type> DrawDebug;
    
    	UPROPERTY(EditAnywhere, Category = "Trace")
    		float InterpSpeed = 50;//발이 움직일 때 보간될 때까지의 속도
    
    	UPROPERTY(EditAnywhere, Category = "Trace")
    		float TraceDistance = 50;//땅까지의 Trace 간격
    
    	UPROPERTY(EditAnywhere, Category = "Trace")
    		float OffsetDistance = 5;//땅에서 살짝 떠있는 값
    
    	UPROPERTY(EditAnywhere, Category = "Socket")
    		FName LeftSocket = "Foot_L";
    
    	UPROPERTY(EditAnywhere, Category = "Socket")
    		FName RightSocket = "Foot_R";
    
    public:
    	FORCEINLINE FFeetData GetData() { return Data; }
    
    public:	
    	UCFeetComponent();
    
    protected:
    	virtual void BeginPlay() override;
    
    public:	
    	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
    
    private:
    	void Trace(FName InName, float& OutDistance, FRotator& OutRotation);
    
    private:
    	class ACharacter* OwnerCharacter;
    
    	FFeetData Data;
    };

     

     

     

     

    CFeetComponent.cpp

    더보기
    #include "Components/CFeetComponent.h"
    #include "Global.h"
    #include "GameFramework/Character.h"
    #include "Components/SkeletalMeshComponent.h"
    #include "Components/CapsuleComponent.h"
    
    #define LOG_UCFeetComponent
    
    UCFeetComponent::UCFeetComponent()
    {
    	PrimaryComponentTick.bCanEverTick = true;
    
    }
    
    void UCFeetComponent::BeginPlay()
    {
    	Super::BeginPlay();
    
    	OwnerCharacter = Cast<ACharacter>(GetOwner());
    }
    
    void UCFeetComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
    {
    	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
    
    	float leftDistance, rightDistance;
    	FRotator leftRotation, rightRotation;
    
    	Trace(LeftSocket, leftDistance, leftRotation);
    	Trace(RightSocket, rightDistance, rightRotation);
    
    
    	float offset = FMath::Min(leftDistance, rightDistance);
    	Data.PelvisDistance.Z = UKismetMathLibrary::FInterpTo(Data.PelvisDistance.Z, offset, DeltaTime, InterpSpeed);
    
    	Data.LeftDistance.X = UKismetMathLibrary::FInterpTo(Data.LeftDistance.X, (leftDistance - offset), DeltaTime, InterpSpeed);
    	Data.RightDistance.X = UKismetMathLibrary::FInterpTo(Data.RightDistance.X, -(rightDistance - offset), DeltaTime, InterpSpeed);
    
    	Data.LeftRotation = UKismetMathLibrary::RInterpTo(Data.LeftRotation, leftRotation, DeltaTime, InterpSpeed);
    	Data.RightRotation = UKismetMathLibrary::RInterpTo(Data.RightRotation, rightRotation, DeltaTime, InterpSpeed);
    
    
    #ifdef LOG_UCFeetComponent
    	CLog::Print(Data.LeftDistance, 11);
    	CLog::Print(Data.RightDistance, 12);
    	CLog::Print(Data.PelvisDistance, 13);
    	CLog::Print(Data.LeftRotation, 14);
    	CLog::Print(Data.RightRotation, 15);
    #endif
    }
    
    void UCFeetComponent::Trace(FName InName, float& OutDistance, FRotator& OutRotation)
    {
    	FVector socket = OwnerCharacter->GetMesh()->GetSocketLocation(InName);//socket 위치(World 좌표)
    
    	float z = OwnerCharacter->GetActorLocation().Z;//캐릭터의 위치 z값
    
    	FVector start = FVector(socket.X, socket.Y, z);//추적을 시작할 start 지점
    
    	z = start.Z - OwnerCharacter->GetCapsuleComponent()->GetScaledCapsuleHalfHeight() - TraceDistance;
    	FVector end = FVector(socket.X, socket.Y, z);
    
    	TArray<AActor*> ignores;
    	ignores.Add(OwnerCharacter);
    
    	FHitResult hitResult;
    	UKismetSystemLibrary::LineTraceSingle(GetWorld(), start, end, ETraceTypeQuery::TraceTypeQuery1, true, ignores, DrawDebug, hitResult, true, FLinearColor::Green, FLinearColor::Red);//TraceTypeQuery1은 Visibility(=보이는 모두 다 충돌).
    
    	//값 초기화
    	OutDistance = 0;
    	OutRotation = FRotator::ZeroRotator;
    
    	CheckFalse(hitResult.bBlockingHit);//충돌이 안 일어나면 return false;
    
    
    	float length = (hitResult.ImpactPoint - hitResult.TraceEnd).Size();
    	OutDistance = length + OffsetDistance - TraceDistance;
    
    	float roll = UKismetMathLibrary::DegAtan2(hitResult.Normal.Y, hitResult.Normal.Z);
    	float pitch = -UKismetMathLibrary::DegAtan2(hitResult.Normal.X, hitResult.Normal.Z);
    
    	OutRotation = FRotator(pitch, 0, roll);
    }

     

     

     


     

     

    BP_CPlayer

     

     


     

    CAnimInstance

     

     

    CAnimInstance.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Animation/AnimInstance.h"
    #include "Components/CWeaponComponent.h"
    #include "Components/CFeetComponent.h"
    #include "CAnimInstance.generated.h"
    
    UCLASS()
    class U2212_06_API UCAnimInstance : public UAnimInstance
    {
    	GENERATED_BODY()
    
    protected:
        UPROPERTY(BlueprintReadOnly, Category = "Animation")
            float Speed;
    
        UPROPERTY(BlueprintReadOnly, Category = "Animation")
            float Pitch;
    
        UPROPERTY(BlueprintReadOnly, Category = "Animation")
            float Direction;
    
        UPROPERTY(BlueprintReadOnly, Category = "Animation")
            bool bBow_Aiming;
    
        //Feet IK
        UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "InverseKinemetics")
            bool bFeet;
    
        UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "InverseKinemetics")
            FFeetData FeetData;
    
    protected:
        UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Animation")
            EWeaponType WeaponType = EWeaponType::Max;
    
    public:
        void NativeBeginPlay() override;
        void NativeUpdateAnimation(float DeltaSeconds) override;
    
    private:
        UFUNCTION()
            void OnWeaponTypeChanged(EWeaponType InPrevType, EWeaponType InNewType);
    
    private:
        class ACharacter* OwnerCharacter;
        class UCWeaponComponent* Weapon;
    
    private:
        FRotator PrevRotation;
    };

    헤더 추가

    • #include "Components/CFeetComponent.h"

     

    변수 추가

    • UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "InverseKinemetics")
      bool bFeet;
    • UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "InverseKinemetics")
      FFeetData FeetData;

     

     

     

    CAnimInstance.cpp

    더보기
    #include "Characters/CAnimInstance.h"
    #include "Global.h"
    #include "GameFramework/Character.h"
    #include "Weapons/CSubAction.h"
    #include "Parkour/CParkourComponent.h"
    #include "Components/CFeetComponent.h"
    
    void UCAnimInstance::NativeBeginPlay()
    {
    	Super::NativeBeginPlay();
    
    	OwnerCharacter = Cast<ACharacter>(TryGetPawnOwner());
    	CheckNull(OwnerCharacter);
    
    	Weapon = CHelpers::GetComponent<UCWeaponComponent>(OwnerCharacter);
    	if (!!Weapon)
    		Weapon->OnWeaponTypeChange.AddDynamic(this, &UCAnimInstance::OnWeaponTypeChanged);
    }
    
    void UCAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
    {
    	Super::NativeUpdateAnimation(DeltaSeconds);
    	CheckNull(OwnerCharacter);
    
    	Speed = OwnerCharacter->GetVelocity().Size2D();
    
    	FRotator rotator = OwnerCharacter->GetVelocity().ToOrientationRotator();
    	FRotator rotator2 = OwnerCharacter->GetControlRotation();
    	FRotator delta = UKismetMathLibrary::NormalizedDeltaRotator(rotator, rotator2);
    	PrevRotation = UKismetMathLibrary::RInterpTo(PrevRotation, delta, DeltaSeconds, 25);
    	Direction = PrevRotation.Yaw;
    
    	Pitch = UKismetMathLibrary::FInterpTo(Pitch, OwnerCharacter->GetBaseAimRotation().Pitch, DeltaSeconds, 25);
    
    
    	CheckNull(Weapon);//무기가 있는지 확인
    
    	/** 파쿠르*/
    	UCParkourComponent* parkour = CHelpers::GetComponent<UCParkourComponent>(OwnerCharacter);
    	UCFeetComponent* feet = CHelpers::GetComponent<UCFeetComponent>(OwnerCharacter);
    
    	bFeet = false;
    	
    	if(Weapon->IsUnarmedMode())
    	{
    		if (!!parkour && !!feet)
    		{
    			bFeet = parkour->IsExecuting() == false;//EParkourType::Max라면 false==false가 되어 bFeet은 true, EParkourType::Max가 아닌 상황이라면 true==false가 되어 bFeet은 false.
    			FeetData = feet->GetData();//FFeetData를 넣어준다.
    		}
    		else if (!!feet)
    		{
    			bFeet = true;
    			FeetData = feet->GetData();
    		}
    	}	
    	/** 파쿠르*/
    	
    	/** 활*/
    	if (!!Weapon->GetSubAction())
    	{
    		bBow_Aiming = true;
    		bBow_Aiming &= WeaponType == EWeaponType::Bow;
    		bBow_Aiming &= Weapon->GetSubAction()->GetInAction();		
    	}
    	/** 활*/
    }
    
    void UCAnimInstance::OnWeaponTypeChanged(EWeaponType InPrevType, EWeaponType InNewType)
    {
    	WeaponType = InNewType;
    }

    헤더 추가

    • #include "Parkour/CParkourComponent.h"
    • #include "Components/CFeetComponent.h"

     

    void UCAnimInstance::NativeUpdateAnimation(float DeltaSeconds)

    • UCParkourComponent* parkour = CHelpers::GetComponent<UCParkourComponent>(OwnerCharacter);
      UCFeetComponent* feet = CHelpers::GetComponent<UCFeetComponent>(OwnerCharacter);

      bFeet = false;

      if (!!parkour && !!feet)
      {
      bFeet = parkour->IsExecuting() == false;
      FeetData = feet->GetData();
      }
      else if (!!feet)
      {
      bFeet = true;
      FeetData = feet->GetData();
      }

     


     

     

    실행화면

     

     


     

    '⭐ Unreal Engine > UE RPG Skill' 카테고리의 다른 글

    [UE] Feet IK, 마우스 Zoom, Enemy AI  (0) 2023.07.21
    [UE] 파쿠르: 벽 오르기  (0) 2023.07.19
    [UE] 파쿠르 구현하기  (0) 2023.07.18
    [UE] 화살  (0) 2023.07.13
    [UE] 활 시위에 손 맞추기  (0) 2023.07.10