본문 바로가기
Study/Unreal

[Unreal/정리] 애니메이션_Jump

by generous_jeans 2020. 10. 13.

[ 애니메이션 ]

- 애니메이션 몽타주 - 

(언리얼 에디터) 콘텐츠 브라우저 -> Player -> 우클릭_애니메이션-애니메이션 몽타주 : "MTGWukongAttack"

// 보라색 에셋 : 몽타주

// 일반 시퀀스들과 다루는 방식이 다름. 

 

(언리얼 에디터-MTGWukongAttack) 몽타주-DefalutGroup DefalutSlot-슬롯매니저

(언리얼 에디터-MTGWukongAttack) 애님 슬롯 매니저-그룹 추가 : "Skill"

(언리얼 에디터-MTGWukongAttack) 애님 슬롯 매니저-슬롯 추가 : "Skill1" 

(언리얼 에디터-MTGWukongAttack) 몽타주-DefalutGroup DefalutSlot-슬롯이름 : "Skill.Skill1

 

(언리얼 에디터-MTGWukongAttack) 에셋 브라우저 : "Primary_Melee_A_Slow" DefalutGroup DefaultSlot에 드래그

(언리얼 에디터-MTGWukongAttack) Primary_Melee_A_Slow -> 디테일-애니메이션 세그먼트-재생속도 : "2.0"

(언리얼 에디터-MTGWukongAttack) 에셋 브라우저 : "Primary_Melee_B_Slow" DefalutGroup DefaultSlot에 드래그

(언리얼 에디터-MTGWukongAttack) Primary_Melee_B_Slow-> 디테일-애니메이션 세그먼트-재생속도 : "2.0"

// 연속 모션을 만들 수 있음. 

 

(언리얼 에디터-BPWukongAnim) AnimGraph -> 우클릭_"Defult Slot"

(언리얼 에디터-BPWukongAnim) Defult Slot -> 디테일-세팅-Slot Name  : "Skil.Skill1"

// Ground->Skill1->최종 애니메이션 포즈로 연결된 경우, 

// 특정 몽타주 포즈를 작동시키라고 명령을 하지 않으면 원본 포즈가 작동됨.

// 특정 몽타주 포즈를 작동시키라고 명령을 하면 원본 포즈는 무시하고 몽타주 포즈가 동작이 됨. 

 

(언리얼 에디터) 세팅 -> 프로젝트 세팅 -> 엔진-입력 -> 액션 매핑 : "QuickSlot1" "1"

(언리얼 에디터) 세팅 -> 프로젝트 세팅 -> 엔진-입력 -> 액션 매핑 : "QuickSlot2" "2"

(언리얼 에디터) 세팅 -> 프로젝트 세팅 -> 엔진-입력 -> 액션 매핑 : "QuickSlot3" "3"

 

(C++-WukongAnim.h)

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "PlayerBaseAnim.h"
#include "WukongAnim.generated.h"

UENUM(BlueprintType, Meta = (Bitflags)) // enum문을 사용할 때 사용.
enum class EWukongAnim : uint8  // uint8로 사용하겠다고 설정. 
{
    Idle,
    Run,
    Attack,
    Hit,
    Jump
};

UCLASS()
class PROJECT1005_API UWukongAnim : public UPlayerBaseAnim
{
    GENERATED_BODY()
		
public:
    UWukongAnim();

protected:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true", Bitmask, BitmaskEnum = "EWukongAnim"))
    uint8 AnimType;

    UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true", Bitmask, BitmaskEnum = "EWukongAnim"))
    UAnimMontage* Skill1Montage;

public:
    // UAnimInstance에 가상함수로 정의되어 있으므로 재정의 하여 사용할 수 있음. 
    virtual void NativeInitializeAnimation();  // 초기화될 때 사용. 
    virtual void NativeUpdateAnimation(float DeltaSeconds);  // 매 프레임마다 생성. 

public:
    virtual void InheritMoveStopNotify();
    virtual void InheritAttackEndNotify();
    virtual void InheritInIdleNotify();
    virtual void InheritNormalAttack();
	
public:
    void Skill1();
};

(C++-WukongAnim.cpp)

// Fill out your copyright notice in the Description page of Project Settings.


#include "WukongAnim.h"
#include "PlayerWukong.h"

UWukongAnim::UWukongAnim()  // 생성자.
{
    BaseAnimType = &AnimType;
    NextAttackEnable = true;  // Wukong에서는 가능. 
    NextAttackMax = 5;

    // 몽타주 에셋 읽음, 
    static ConstructorHelpers::FObjectFinder<UAnimMontage> Skill1Asset(TEXT("AnimMontage'/Game/Player/MTGWukongAttack.MTGWukongAttack'"));

    if (Skill1Asset.Succeeded())
        Skill1Montage = Skill1Asset.Object;
}

void UWukongAnim::NativeInitializeAnimation()  // 초기화될 때 사용. 
{
    Super::NativeInitializeAnimation();
}

void UWukongAnim::NativeUpdateAnimation(float DeltaSeconds)  // 매 프레임마다 생성.
{
    Super::NativeUpdateAnimation(DeltaSeconds);

    // 언리얼에서는 Cast를 사용하여 형변환을 해야 함. 
    APlayerWukong* pOwner = Cast<APlayerWukong>(TryGetPawnOwner()); // 이 애니메이션 컴포넌트를 가지고 있는 폰을 얻어오는 함수. 

    if (IsValid(pOwner))  // IsValid : U오프젝트에 대한 유효성 검사를 해줌. 제대로 된 것인지 확인을 해줘서 더 안전함. 
    {
        UCharacterMovementComponent* pMovement = pOwner->GetCharacterMovement();  // Movement를 얻어옴. 

        if (IsValid(pMovement))
        {
        }
    }
}

void UWukongAnim::InheritMoveStopNotify()
{

}

void UWukongAnim::InheritAttackEndNotify()
{

}

void UWukongAnim::InheritInIdleNotify()
{

}

void UWukongAnim::InheritNormalAttack()
{

}

void UWukongAnim::Skill1()
{
    if (!Montage_IsPlaying(Skill1Montage))
        Montage_Play(Skill1Montage);
}

(C++-PlayerWukong.h)

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "PlayerCharacter.h"
#include "PlayerWukong.generated.h"

/**
 * 
 */
UCLASS()
class PROJECT1005_API APlayerWukong : public APlayerCharacter
{
    GENERATED_BODY()
	
public:
    // Sets default values for this character's properties
    APlayerWukong();

protected:
    // UPROPERTY : 사용하지 않으면 언리얼 에디터에 공개가 되지 않음. 내부적인 변수로 사용할 수 있음.
    //             언리얼 에디터에 연결하여 사용하려면 변수를 선언하기 전에 프로퍼티로 변수의 속성을 지정해줘야 함.
    // Category : Camera로 설정. 
    // VisibleAnywhere : 다 보이지만 편집을 할 수 없음.
    // EditAnywhere : 에디터에서도 편집 가능.
    UPROPERTY(Category = Camera, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
    UCameraComponent* Camera;

    UPROPERTY(Category = Camera, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
    USpringArmComponent* Arm;

    class UWukongAnim* WukongAnim;  // 포인터 변수 선언. 

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:
    // Called every frame
    virtual void Tick(float DeltaTime) override;

    // Called to bind functionality to input
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

private:
    void MoveFront(float fScale);  // 앞뒤로 이동.
    void MoveSide(float fScale);  // 좌우로 이동.
    void RotationZ(float fScale);  // 회전.

    void JumpKey();  // 점프.
    void NormalAttack();  // 공격.

    void QuickSlot1();
    void QuickSlot2();
    void QuickSlot3();
};

(C++-PlayerWukong.cpp)

// Fill out your copyright notice in the Description page of Project Settings.


#include "PlayerWukong.h"
#include "WukongAnim.h" 

// Sets default values
APlayerWukong::APlayerWukong()
{
    // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

    Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));  // 카메라 컴포넌트 생성. 생성자에서 생성해야 함. 
    Arm = CreateDefaultSubobject<USpringArmComponent>(TEXT("Arm"));  // Sprint Arm 컴포넌트 생성.

    // 셀카봉 같은 느낌. 
    Arm->SetupAttachment(GetMesh());  // Arm의 부로모 Mesh를 지정.
    Camera->SetupAttachment(Arm);  // 카메라의 부모로 Arm을 지정. 

    // 애니메이션 클래스 지정. 
    static ConstructorHelpers::FClassFinder<UWukongAnim> AnimAsset(TEXT("AnimBlueprint'/Game/Player/BPWukongAnim.BPWukongAnim_C'"));

    if (AnimAsset.Succeeded())
        GetMesh()->SetAnimInstanceClass(AnimAsset.Class);

    // 점프의 속도 조절. 
    // GetCharacterMovement()->JumpZVelocity  // JumpZVelocity : 위로 올라가는 속도. 
}

// Called when the game starts or when spawned
void APlayerWukong::BeginPlay()
{
    Super::BeginPlay(); // 이 액터가 레벨에 입장할 때 배치되고 게임이 시작할 때 딱 한 번만 호출됨.

    WukongAnim = Cast<UWukongAnim>(GetMesh()->GetAnimInstance());  // WraithAnim을 받아옴. 
}

// Called every frame
void APlayerWukong::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);  // 게임이 동작되는 매프레임마다 호출되는 함수.
}

// Called to bind functionality to input
// UInputComponent : 입력받은 것을 연결해서 호출해줄 함수를 연결하는 바인딩 역할을 함. 
void APlayerWukong::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);  // 플레이어 컨트롤러가 빙의가 된 후에 호출. 

    // 언리얼은 유니코드 문자열을 사용하기 때문에 TEXT("")를 사용해야 함. 
    // this : 어떤 객체를 쓸 것인지.
    PlayerInputComponent->BindAxis(TEXT("MoveFront"), this, &APlayerWukong::MoveFront);
    PlayerInputComponent->BindAxis(TEXT("MoveSide"), this, &APlayerWukong::MoveSide);
    PlayerInputComponent->BindAxis(TEXT("RotationZ"), this, &APlayerWukong::RotationZ);

    // EInputEvent::IE_Pressed : 눌렀을 때.
    // EInputEvent::IE_Released : 눌렀다가 뗐을 때.
    PlayerInputComponent->BindAction(TEXT("Jump"), EInputEvent::IE_Pressed, this, &APlayerWukong::JumpKey);
    PlayerInputComponent->BindAction(TEXT("NormalAttack"), EInputEvent::IE_Pressed, this, &APlayerWukong::NormalAttack);

    PlayerInputComponent->BindAction(TEXT("QuickSlot1"), EInputEvent::IE_Pressed, this, &APlayerWukong::QuickSlot1);
    PlayerInputComponent->BindAction(TEXT("QuickSlot2"), EInputEvent::IE_Pressed, this, &APlayerWukong::QuickSlot2);
    PlayerInputComponent->BindAction(TEXT("QuickSlot3"), EInputEvent::IE_Pressed, this, &APlayerWukong::QuickSlot3);

}

void APlayerWukong::MoveFront(float fScale)  // 앞뒤로 이동. 
{
    // GetActorForwardVector()와 fScale을 곱함.
    // fScale이 0이면 움직이지 않고, 1이면 앞으로, -1이면 뒤로 가게 됨. 
    AddMovementInput(GetActorForwardVector(), fScale);

    if (IsValid(WukongAnim))  // AnimInstance가 있을 때.
    {
        if (fScale > 0.f)  // 앞으로. 
            WukongAnim->SetMoveDir(EDir::Front);
        else if (fScale < 0.f)  // 뒤로. 
            WukongAnim->SetMoveDir(EDir::Back);
    }
}

void APlayerWukong::MoveSide(float fScale)  // 좌우로 이동. 
{
    // GetActorRightVector()와 fScale을 곱함.
    // fScale이 0이면 움직이지 않고, 1이면 오른쪽으로, -1이면 왼쪽으로 가게 됨. 
    AddMovementInput(GetActorRightVector(), fScale);

    if (IsValid(WukongAnim))  // AnimInstance가 있을 때.
    {
        if (fScale > 0.f)  // 오른쪽으로. 
            WukongAnim->SetMoveDir(EDir::Right);
        else if (fScale < 0.f)  // 왼쪽으로. 
            WukongAnim->SetMoveDir(EDir::Left);
    } 
}

void APlayerWukong::RotationZ(float fScale)  // 회전. 
{
    AddControllerYawInput(fScale);
}

void APlayerWukong::JumpKey()  // 점프.
{
    Jump(); // Jump를 지원해줌.  
}

void APlayerWukong::NormalAttack()  // 공격.
{
    if (IsValid(WukongAnim))
        WukongAnim->NormalAttack();
}

void APlayerWukong::QuickSlot1()
{
    if (IsValid(WukongAnim))
        WukongAnim->Skill1();
}

void APlayerWukong::QuickSlot2()
{
	
}

void APlayerWukong::QuickSlot3()
{
	
}

// 몽타주를 실행하게 되면 몽타주 포즈를 실행하고 다시 처음 모션을 시작하게 됨. 

// 모션 처리를 해줘야 함. 

 

- Jump -

(언리얼 에디터-BPWukongAnim) AnimGraph -> Ground

(언리얼 에디터-BPWukongAnim) Ground_드래그 : "Jump" -> 디테일-Events-Always Reset on Entry : "체크"

 

// 다른 동작과 Jump 연결. 

(언리얼 에디터-BPWukongAnim) Idle->Jump 연결

(언리얼 에디터-BPWukongAnim) Jum ->Idle 연결

 

(언리얼 에디터-BPWukongAnim) Jum ->Idle-트랜지션 룰

(언리얼 에디터-BPWukongAnim) 우클릭_"Get Anim Type" -> Equal(Enum) : "Idle"

(언리얼 에디터-BPWukongAnim) Equal(Enum) : Idle와 결과 연결

 

(언리얼 에디터-BPWukongAnim) Idle->Jump-트랜지션 룰 

(언리얼 에디터-BPWukongAnim) 우클릭_"Get Anim Type" -> Equal(Enum) : "Jump"

(언리얼 에디터-BPWukongAnim) Equal(Enum) : Jump와 결과 연결

 

(언리얼 에디터-BPWukongAnim) Ground->Jump-트랜지션 룰 

(언리얼 에디터-BPWukongAnim) 우클릭_"Get Anim Type" -> Equal(Enum) : "Jump"

(언리얼 에디터-BPWukongAnim) Equal(Enum) : Jump와 결과 연결

 

// Jump 스테이트 추가. 

(언리얼 에디터-BPWukongAnim) Ground -> Jump

(언리얼 에디터-BPWukongAnim) 우클릭_"machine" -> 스테이트 머신 추가 : "Jump"

(언리얼 에디터-BPWukongAnim) Jump와 출력 애니메이션 포즈 연결

 

(언리얼 에디터-BPWukongAnim) Ground -> Jump -> Jump

(언리얼 에디터-BPWukongAnim) Entry_드래그 : "Start" -> 디테일-Events-Always Reset on Entry : "체크"

(언리얼 에디터-BPWukongAnim) Start_드래그 : "Loop" -> 디테일-Events-Always Reset on Entry : "체크"

(언리얼 에디터-BPWukongAnim) Loop_드래그 : "Land" -> 디테일-Events-Always Reset on Entry : "체크"

(언리얼 에디터-BPWukongAnim) Land_드래그 : "Recovery" -> 디테일-Events-Always Reset on Entry : "체크"

 

(언리얼 에디터-BPWukongAnim) Start->Loop-트랜지션 룰 -> 디테일-트랜지션-Automatic Rule Based.. : "체크"

(언리얼 에디터-BPWukongAnim) Land->Recovery-트랜지션 룰 -> 디테일-트랜지션-Automatic Rule Based.. : "체크"

 

// Start 모션 설정. 

(언리얼 에디터-BPWukongAnim) Ground -> Jump -> Jump -> Start

(언리얼 에디터-BPWukongAnim) 에셋 브라우저 : "Jump_Start" -> 디테일-세팅-LoopAnimation : "끔"

(언리얼 에디터-BPWukongAnim) Jump_Start와 출력 애니메이션 포즈 연결

 

// Loop 모션 설정. 

(언리얼 에디터-BPWukongAnim) Ground -> Jump -> Jump -> Loop

(언리얼 에디터-BPWukongAnim) 에셋 브라우저 : "Jump_Apex" -> 디테일-세팅-LoopAnimation : "끔"

(언리얼 에디터-BPWukongAnim) Jump_Apex와 출력 애니메이션 포즈 연결

 

// Land모션 설정. 

(언리얼 에디터-BPWukongAnim) Ground -> Jump -> Jump -> Land

(언리얼 에디터-BPWukongAnim) 에셋 브라우저 : "Jump_Land" -> 디테일-세팅-LoopAnimation : "끔"

(언리얼 에디터-BPWukongAnim) Jump_Land와 출력 애니메이션 포즈 연결

 

// Recovery모션 설정. 

(언리얼 에디터-BPWukongAnim) Ground -> Jump -> Jump -> Recovery

(언리얼 에디터-BPWukongAnim) 에셋 브라우저 : "Jump_Recovery" -> 디테일-세팅-LoopAnimation : "끔"

(언리얼 에디터-BPWukongAnim) Jump_Recovery와 출력 애니메이션 포즈 연결

 

(언리얼 에디터-BPWukongAnim) Jump_Recovery

(언리얼 에디터-Jump_Recovery) 원하는 위치_노티파이 부분_우클릭_노티파이 추가-새 노티파이 : "ReturnIdle"

 

// Loop -> Land 연결. 

(언리얼 에디터-BPWukongAnim) Loop->Land-트랜지션 룰 

(언리얼 에디터-BPWukongAnim) 우클릭_"IsGround"

(언리얼 에디터-BPWukongAnim) IsGround결과와  연결

 

- Start 하는 중에 다른 모션을 할 수 없도록 설정 -

(언리얼 에디터-BPWukongAnim) AnimGraph -> Ground -> LevelStart

(언리얼 에디터-LevelStart) 원하는 위치_노티파이 부분_우클릭_노티파이 추가-새 노티파이 : "PlayerStart"

 

(C++-PlayerBaseAnim.h)

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "GameInfo.h"  // CoreMinimal.h를 GameInfo.h로 바꿈. 
#include "Animation/AnimInstance.h"
#include "PlayerBaseAnim.generated.h"

UENUM(BlueprintType, Meta = (Bitflags)) // enum문을 사용할 때 사용.
enum class EPlayerBaseAnim : uint8  // uint8로 사용하겠다고 설정. 
{
    Idle,
    Run,
    Attack,
    Hit,
    Jump
};

UCLASS()
class PROJECT1005_API UPlayerBaseAnim : public UAnimInstance
{
    GENERATED_BODY()

public:
    UPlayerBaseAnim();  // 생성자.

protected:
    uint8* BaseAnimType;

    // 방향 설정. 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true", Bitmask, BitmaskEnum = "EDir"))
    uint8 Dir;

    // 멈춰있는 지 확인. 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    bool MoveStop;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    bool AttackEnable;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    uint8 NextAttackIndex;

    UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    bool IsGround;
		
    uint8 NextAttackMax;
    bool NextAttack;  // 다음 공격할 것인지.
    bool NextAttackEnable;  // 다음 공격을 할 것인지 말 것인지.
    bool NextAttackInputEnable;

public:
    void SetMoveDir(EDir eDir)
    {
        Dir = (uint8)eDir;
    }

public:
    // UAnimInstance에 가상함수로 정의되어 있으므로 재정의 하여 사용할 수 있음. 
    virtual void NativeInitializeAnimation();  // 초기화될 때 사용. 
    virtual void NativeUpdateAnimation(float DeltaSeconds);  // 매 프레임마다 생성. 

public:
    UFUNCTION()  // 노티파이를 호출하기 위해 사용. 언리얼 함수인 것을 알려줌. 
    void AnimNotify_MoveStop();  // AnimNotify_(노티파이 이름)으로 해야함.

    UFUNCTION()
    void AnimNotify_AttackEnd();  // AttackEnd 노티파이 추가. 

    UFUNCTION()
    void AnimNotify_InIdle();

    UFUNCTION()
    void AnimNotify_NextAttackEnd();   // NextAttackEnd 노티파이 추가. 

    UFUNCTION()
    void AnimNotify_ReturnIdle();  // jump후 idle로 돌아가는 노티파이.
    
    UFUNCTION()
    void AnimNotify_PlayerStart();  // 플레이어가 시작 모션을 끝내는 노티파이.

public:
    void NormalAttack();  // 공격. 
    void Jump();

public:
    virtual void InheritMoveStopNotify();
    virtual void InheritAttackEndNotify();
    virtual void InheritInIdleNotify();
    virtual void InheritNormalAttack();

};

(C++-PlayerBaseAnim.cpp)

// Fill out your copyright notice in the Description page of Project Settings.


#include "PlayerBaseAnim.h"
#include "PlayerCharacter.h"  // 추가.

UPlayerBaseAnim::UPlayerBaseAnim()  // 생성자.
{
    Dir = (uint8)EDir::Front;  // 기본으로  Front 설정. 

    MoveStop = true;  // 처음에는 멈춰있으므로 true.
    AttackEnable = true;  // 처음에 공격 가능. 
    NextAttackEnable = false;  // 처음에 다음 공격 불가능하게 설정. 
    NextAttack = false;
    NextAttackInputEnable = false;
    NextAttackMax = 0;
    NextAttackIndex = 0;
}

void UPlayerBaseAnim::NativeInitializeAnimation()  // 초기화될 때 사용. 
{
    Super::NativeInitializeAnimation();

    *BaseAnimType = (uint8)EPlayerBaseAnim::Idle;  
}

void UPlayerBaseAnim::NativeUpdateAnimation(float DeltaSeconds)  // 매 프레임마다 생성.
{
    Super::NativeUpdateAnimation(DeltaSeconds);

    // 언리얼에서는 Cast를 사용하여 형변환을 해야 함. 
    APlayerCharacter* pOwner = Cast<APlayerCharacter>(TryGetPawnOwner()); // 이 애니메이션 컴포넌트를 가지고 있는 폰을 얻어오는 함수. 

    if (IsValid(pOwner))  // IsValid : U오프젝트에 대한 유효성 검사를 해줌. 제대로 된 것인지 확인을 해줘서 더 안전함. 
    {
        UCharacterMovementComponent* pMovement = pOwner->GetCharacterMovement();  // Movement를 얻어옴. 

        if (IsValid(pMovement))
        {
            IsGround = pMovement->IsMovingOnGround();  // IsMovingOnGround() : 땅을 밟고 있는 지 판단.
            if (IsGround)  
            {
                // Velocity.Size() : 이동속도의 크기
                if (pMovement->Velocity.Size() > 0.f)  // 0보다 크면 움직임.
                {
                    MoveStop = false;  // 이동 중에는 false.
                    
                    NextAttackInputEnable = false;
                    NextAttackIndex = 0;
                    NextAttack = false;
                    *BaseAnimType = (uint8)EPlayerBaseAnim::Run;
                }
                else  // 0이면 움직이지 않음. 
                {
                    MoveStop = true;  // 멈췄을 때 true.
                }
            }
        }
    }
}

void UPlayerBaseAnim::AnimNotify_MoveStop()  // 노티파이.
{
    *BaseAnimType = (uint8)EPlayerBaseAnim::Idle;  // 동작이 끝나면 Idle로 돌아감. 

    InheritMoveStopNotify();
}

void UPlayerBaseAnim::AnimNotify_AttackEnd()  // 노티파이.
{
    *BaseAnimType = (uint8)EPlayerBaseAnim::Idle;  // 동작이 끝나면 Idle로 돌아감. 

    NextAttackInputEnable = false;
    NextAttackIndex = 0;
    NextAttack = false;

    InheritAttackEndNotify();
}

void UPlayerBaseAnim::AnimNotify_InIdle()  // 노티파이.
{
    AttackEnable = true;  // 공격 가능 상태로.

    InheritInIdleNotify();
}

void UPlayerBaseAnim::AnimNotify_NextAttackEnd()  // NextAttackEnd() 노티파이 추가. 
{
    if (NextAttack)
    {
        ++NextAttackIndex;

        if (NextAttackIndex >= NextAttackMax)
            NextAttackIndex = 0;
        NextAttack = false;
    }
    else
    {
        NextAttackInputEnable = false;
    }
}

void UPlayerBaseAnim::AnimNotify_ReturnIdle()  // jump후 idle로 돌아가는 노티파이. 
{
    *BaseAnimType = (uint8)EPlayerBaseAnim::Idle;
    
    NextAttackInputEnable = false;
    NextAttackIndex = 0;
    NextAttack = false;
}

void UPlayerBaseAnim::NormalAttack()  // 공격.
{
    if (AttackEnable)
    {
        *BaseAnimType = (uint8)EPlayerBaseAnim::Attack;

        if (NextAttackEnable && NextAttackInputEnable)
        {
            NextAttack = true;
        }
        else if(!NextAttackEnable)
            AttackEnable = false;  // 공격 불가능 상태로.

        NextAttackInputEnable = true;
    }

    InheritNormalAttack();
}

void UPlayerBaseAnim::Jump()
{
    *BaseAnimType = (uint8)EPlayerBaseAnim::Jump;
}

void UPlayerBaseAnim::InheritMoveStopNotify()
{

}

void UPlayerBaseAnim::InheritAttackEndNotify()
{

}

void UPlayerBaseAnim::InheritInIdleNotify()
{

}

void UPlayerBaseAnim::InheritNormalAttack()
{

}

(C++-PlayerWukong.cpp)

// Fill out your copyright notice in the Description page of Project Settings.


#include "PlayerWukong.h"
#include "WukongAnim.h" 

// Sets default values
APlayerWukong::APlayerWukong()
{
    // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

    Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));  // 카메라 컴포넌트 생성. 생성자에서 생성해야 함. 
    Arm = CreateDefaultSubobject<USpringArmComponent>(TEXT("Arm"));  // Sprint Arm 컴포넌트 생성.

    // 셀카봉 같은 느낌. 
    Arm->SetupAttachment(GetMesh());  // Arm의 부로모 Mesh를 지정.
    Camera->SetupAttachment(Arm);  // 카메라의 부모로 Arm을 지정. 

    // 애니메이션 클래스 지정. 
    static ConstructorHelpers::FClassFinder<UWukongAnim> AnimAsset(TEXT("AnimBlueprint'/Game/Player/BPWukongAnim.BPWukongAnim_C'"));

    if (AnimAsset.Succeeded())
        GetMesh()->SetAnimInstanceClass(AnimAsset.Class);

    // 점프의 속도 조절. 
    // GetCharacterMovement()->JumpZVelocity  // JumpZVelocity : 위로 올라가는 속도. 
}

// Called when the game starts or when spawned
void APlayerWukong::BeginPlay()
{
    Super::BeginPlay(); // 이 액터가 레벨에 입장할 때 배치되고 게임이 시작할 때 딱 한 번만 호출됨.

    WukongAnim = Cast<UWukongAnim>(GetMesh()->GetAnimInstance());  // WraithAnim을 받아옴. 
}

// Called every frame
void APlayerWukong::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);  // 게임이 동작되는 매프레임마다 호출되는 함수.
}   

// Called to bind functionality to input
// UInputComponent : 입력받은 것을 연결해서 호출해줄 함수를 연결하는 바인딩 역할을 함. 
void APlayerWukong::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);  // 플레이어 컨트롤러가 빙의가 된 후에 호출. 

    // 언리얼은 유니코드 문자열을 사용하기 때문에 TEXT("")를 사용해야 함. 
    // this : 어떤 객체를 쓸 것인지.
    PlayerInputComponent->BindAxis(TEXT("MoveFront"), this, &APlayerWukong::MoveFront);
    PlayerInputComponent->BindAxis(TEXT("MoveSide"), this, &APlayerWukong::MoveSide);
    PlayerInputComponent->BindAxis(TEXT("RotationZ"), this, &APlayerWukong::RotationZ);

    // EInputEvent::IE_Pressed : 눌렀을 때.
    // EInputEvent::IE_Released : 눌렀다가 뗐을 때.
    PlayerInputComponent->BindAction(TEXT("Jump"), EInputEvent::IE_Pressed, this, &APlayerWukong::JumpKey);
    PlayerInputComponent->BindAction(TEXT("NormalAttack"), EInputEvent::IE_Pressed, this, &APlayerWukong::NormalAttack);

    PlayerInputComponent->BindAction(TEXT("QuickSlot1"), EInputEvent::IE_Pressed, this, &APlayerWukong::QuickSlot1);
    PlayerInputComponent->BindAction(TEXT("QuickSlot2"), EInputEvent::IE_Pressed, this, &APlayerWukong::QuickSlot2);
    PlayerInputComponent->BindAction(TEXT("QuickSlot3"), EInputEvent::IE_Pressed, this, &APlayerWukong::QuickSlot3);

}

void APlayerWukong::MoveFront(float fScale)  // 앞뒤로 이동. 
{
    if (!bStartAnimation)
        return;
    
    // GetActorForwardVector()와 fScale을 곱함.
    // fScale이 0이면 움직이지 않고, 1이면 앞으로, -1이면 뒤로 가게 됨. 
    AddMovementInput(GetActorForwardVector(), fScale);

    if (IsValid(WukongAnim))  // AnimInstance가 있을 때.
    {
        if (fScale > 0.f)  // 앞으로. 
            WukongAnim->SetMoveDir(EDir::Front);
        else if (fScale < 0.f)  // 뒤로. 
            WukongAnim->SetMoveDir(EDir::Back);
    }
}

void APlayerWukong::MoveSide(float fScale)  // 좌우로 이동. 
{
    if (!bStartAnimation)
        return;

    // GetActorRightVector()와 fScale을 곱함.
    // fScale이 0이면 움직이지 않고, 1이면 오른쪽으로, -1이면 왼쪽으로 가게 됨. 
    AddMovementInput(GetActorRightVector(), fScale);

    if (IsValid(WukongAnim))  // AnimInstance가 있을 때.
    {
        if (fScale > 0.f)  // 오른쪽으로. 
            WukongAnim->SetMoveDir(EDir::Right);
        else if (fScale < 0.f)  // 왼쪽으로. 
            WukongAnim->SetMoveDir(EDir::Left);
    } 
}

void APlayerWukong::RotationZ(float fScale)  // 회전. 
{
    if (!bStartAnimation)
        return;
        
    AddControllerYawInput(fScale);
}

void APlayerWukong::JumpKey()  // 점프.
{
    if (!bStartAnimation)
        return;
        
    if (IsValid(WukongAnim))
        WukongAnim->Jump();

    Jump(); // Jump를 지원해줌.  
}

void APlayerWukong::NormalAttack()  // 공격.
{
    if (!bStartAnimation)
        return;
    
    if (IsValid(WukongAnim))
        WukongAnim->NormalAttack();
}

void APlayerWukong::QuickSlot1()
{
    if (!bStartAnimation)
        return;
        
    if (IsValid(WukongAnim))
        WukongAnim->Skill1();
}

void APlayerWukong::QuickSlot2()
{
    if (!bStartAnimation)
        return;
}

void APlayerWukong::QuickSlot3()
{
    if (!bStartAnimation)
        return;
}

(C++-PlayerCharacter.h)

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "GameInfo.h"  // CoreMinimal.h를 GameInfo.h로 바꿈. 
#include "GameFramework/Character.h"
#include "PlayerCharacter.generated.h"

UCLASS()
class PROJECT1005_API APlayerCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    // Sets default values for this character's properties
    APlayerCharacter();

protected:
    bool bStartAnimation;

public:
    void StartAnimation()
    {
        bStartAnimation = true;
    }

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:	
    // Called every frame
    virtual void Tick(float DeltaTime) override;

    // Called to bind functionality to input
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

};

(C++-PlayerCharacter.cpp)

// Fill out your copyright notice in the Description page of Project Settings.


#include "PlayerCharacter.h"

// Sets default values
APlayerCharacter::APlayerCharacter()
{
    // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

    bStartAnimation = false;

}

// Called when the game starts or when spawned
void APlayerCharacter::BeginPlay()
{
    Super::BeginPlay();
	
}

// Called every frame
void APlayerCharacter::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

// Called to bind functionality to input
void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

}

 

 

댓글