Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

codingfarm

펀치 - 발차기 본문

Unreal 4/기타

펀치 - 발차기

scarecrow1992 2020. 7. 22. 20:26

우클릭을 통해 발차기 기능을 추가해보자

발차기는 주먹과 다른 애니메이션 몽타주로 재생되며

주먹과는 달리 공격중 이동이 불가능하다.

 

우선 애니메이션 몽타주를 만들고 섹션을 형성해준다.

 

메쉬 스켈레톤에는 주먹 뿐만아니라 발끝에도 소켓을 추가한다.

 

이제 입력 메커니즘을 수정하자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void APunchKick06Character::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
    // Set up game play key bindings
    check(PlayerInputComponent);
    PlayerInputComponent->BindAction("Jump", IE_Pressed, this&ACharacter::Jump);
    PlayerInputComponent->BindAction("Jump", IE_Released, this&ACharacter::StopJumping);
 
    PlayerInputComponent->BindAxis("MoveForward"this&APunchKick06Character::MoveForward);
    PlayerInputComponent->BindAxis("MoveRight"this&APunchKick06Character::MoveRight);
 
    // We have 2 versions of the rotation bindings to handle different kinds of devices differently
    // "turn" handles devices that provide an absolute delta, such as a mouse.
    // "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
    PlayerInputComponent->BindAxis("Turn"this&APawn::AddControllerYawInput);
    PlayerInputComponent->BindAxis("TurnRate"this&APunchKick06Character::TurnAtRate);
    PlayerInputComponent->BindAxis("LookUp"this&APawn::AddControllerPitchInput);
    PlayerInputComponent->BindAxis("LookUpRate"this&APunchKick06Character::LookUpAtRate);
 
    // handle touch devices
    PlayerInputComponent->BindTouch(IE_Pressed, this&APunchKick06Character::TouchStarted);
    PlayerInputComponent->BindTouch(IE_Released, this&APunchKick06Character::TouchStopped);
 
    // VR headset functionality
    PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this&APunchKick06Character::OnResetVR);
 
    // attack functionality
    PlayerInputComponent->BindAction("Punch", IE_Pressed, this&APunchKick06Character::PunchAttack);
    PlayerInputComponent->BindAction("Kick", IE_Pressed, this&APunchKick06Character::KickAttack);
}
 
cs

좌클릭 입력 이벤트에 AttackPunch 함수를, 우클릭 입력 이벤트에 AttackKick 함수를 할당한다.

PunchAttack과 KickAttack 내부를 보면 각각 별개의 매개변수로 AttackInput 함수를 호출한다.

1
2
3
4
5
UENUM(BlueprintType)
enum class EAttackType : uint8 {
    MELEE_FIST            UMETA(DisplayName = "Melee - Fist"),
    MELEE_KICK            UMETA(DisplayName = "Melee - Kick")
};
cs
1
2
3
void APunchKick06Character::KickAttack() {
    AttackInput(EAttackType::MELEE_KICK);
}
cs

 

이제 AttackInput 내부를 보면 기존의 코드와 크게 달라진것을 확인 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
void APunchKick06Character::AttackInput(EAttackType AttackType)
{
    Log(ELogLevel::INFO, __FUNCTION__);
 
    if (PlayerAttackDataTable)
    {
        static const FString ContextString(TEXT("Player Attack Montage Context"));
        
        FName RowKey;
 
        const FAttachmentTransformRules AttachmentRules(EAttachmentRule::SnapToTarget, EAttachmentRule::SnapToTarget, EAttachmentRule::KeepWorld, false);
 
        switch (AttackType) {
        case EAttackType::MELEE_FIST :
            RowKey = FName(TEXT("Punch"));
            
            LeftMeleeCollisionBox->AttachToComponent(GetMesh(), AttachmentRules, "fist_l_collision");
            RightMeleeCollisionBox->AttachToComponent(GetMesh(), AttachmentRules, "fist_r_collision");
            
            IsKeyboardEnabled = true;
            IsAnimationBlended = true;
            break;
 
        case EAttackType::MELEE_KICK :
            RowKey = FName(TEXT("Kick"));
 
            LeftMeleeCollisionBox->AttachToComponent(GetMesh(), AttachmentRules, "foot_l_collision");
            RightMeleeCollisionBox->AttachToComponent(GetMesh(), AttachmentRules, "foot_r_collision");
 
            IsKeyboardEnabled = false;
            IsAnimationBlended = false;
            break;
 
        default:
            IsAnimationBlended = true;
            break;
        }
        
        AttackMontage = PlayerAttackDataTable->FindRow<FPlayerAttackMontage>(RowKey, ContextString, true);
        if (AttackMontage)
        {
            // generate  number between 1 and whatever is defined in the data table for this montage :
            int MontageSectionIndex = rand() % AttackMontage->AnimSectionCount + 1;
 
            // create a montage section
            FString MontageSection = "start_" + FString::FromInt(MontageSectionIndex);
 
            PlayAnimMontage(AttackMontage->Montage, 1.f, FName(*MontageSection));
        }
    }
}
cs

매개변수에 따라 주먹과 발차기로 나뉘는 것을 볼 수 있다.

공격 collision이 begin내에서 호출되어서 양주먹에 고정되었던 이전 버전과 달리

AttackInput내에서 공격 입력이 들어올때마다 부착지잠이 양주먹과 양발 사이를 오가는 모습을 보인다.

특히 발차기시에는 bool 변수를 통해 키보드 입력을 막아서 이동이 안되게끔하고, 애니메이션 블렌드 효과도 지웟다.

그리고 datatable에서 key로 쓰일 RowKey 또한 다르게 설정됨이 보인다.

 

그리고 RowKey를 통해 AttackMonatege에 발차기와 주먹 중 어떤 애니메이션 몽타주를 할당할지가 결정되며

마지막에 입력에 맞추어서 애니메이션이 재생되게끔 한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void APunchKick06Character::MoveForward(float Value)
{
    if ((Controller != NULL&& (Value != 0.0f) && IsKeyboardEnabled)
    {
        // find out which way is forward
        const FRotator Rotation = Controller->GetControlRotation();
        const FRotator YawRotation(0, Rotation.Yaw, 0);
 
        // get forward vector
        const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
        AddMovementInput(Direction, Value);
    }
}
 
void APunchKick06Character::MoveRight(float Value)
{
    if ( (Controller != NULL&& (Value != 0.0f) && IsKeyboardEnabled)
    {
        // find out which way is right
        const FRotator Rotation = Controller->GetControlRotation();
        const FRotator YawRotation(0, Rotation.Yaw, 0);
    
        // get right vector 
        const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
        // add movement in that direction
        AddMovementInput(Direction, Value);
    }
}
cs

MoveForward와 MoveRight는 바뀐것이 크게 없다.

발차기중이어서 IsKeyboardEnabled가 false일때는 if문 내에 접근 할 수 없어서 이동이 안되게끔 조건만 추가했다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void UAttackStartNotifyState::NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
{
    GEngine->AddOnScreenDebugMessage(-14.5f, FColor::Magenta, __FUNCTION__);    
 
    if (MeshComp != NULL && MeshComp->GetOwner() != NULL)
    {
        APunchKick06Character* Player = Cast<APunchKick06Character>(MeshComp->GetOwner());
        if (Player != NULL)
        {
            Player->AttackEnd();
            Player->SetIsKeyboardEnabled(true);
        }
    }
}
cs

그리고 다시 이동이 가능하게끔 하는것은 AttackStartNotifySTate 내부의 NotifyEnd 함수에서 설정하도록 하였다.

 

마지막으로 중요한부분이 남았다.

우리는 주먹 애니메이션 중일때 애니메이션 블렌드를 통해 하반신은 걷고 상반신만 공격 애니메이션이 재생되게 하였다.

하지만 발차기는 애니메이션 브렌드가 발생해서는 안되므로 이 점을 수정해주어야한다.

이제 애니메이션 블루프린트로 그래프를 확인하자

기존에 속력과 접지여부만을 관찰하던 기능에서

애니메이션을 블렌딩 할지 여부(Is Animation Blended)도 결정하게끔 하였다.

 

UpperBody 슬롯과 DefaultSlot 슬롯이 따로 따로 있음을 확인하라. 2개의 슬롯은 전부다 Default 스테이트 머신에서 나온 애니메이션을 그대로 받아서 2개의 분기점이 형성된다.

최종 애니메이션 포즈 까지 가야할 흐름은 하나만 가능하므로

Bool로 포즈를 블렌딩 하도록 하며 조건을 앞서 설정했던 Is Animation Blended로 삼도록 한다.

 

이렇게 해서 발차기 애니메이션의 기초를 완성시켰다.

 

 

이제 런타임 버그를 해결해보자

지금 만든 발차기 기능에는 치명적인 버그가 있다

임의의 공격 애니메이션이 끝나기 전에 다시한번 발차기를 한다면

이동을 하면서 발차기가 가능한것이다.

분명 우리는 AttackInput 함수내의 스위치문에서 IsKeyboardEnabled를 false로 만들게끔 하였다.

그래도 발차기를 눌렀을때 계속 이동이 된다는것은 대체 뭐가 문제일까?

우리는 발차기를 할때 AttackInput에서 IsKeyboardEnabled가 false로 바뀌고

AttackStartNotifyState의 NotifyEnd에서 IsKeyboardEnabled가 true로 바뀜을 상기해야한다.

 

만약 발차기 하나만 누른다면 어떻게 되는지 로그를 통해 확인하자.

입력이 들어오고 애니메이션의 시작과 끝이 정상적으로 이루어진 모습이다.

 

공격애니메이션 도중 곧바로 발차기를 한다면?

1이 첫번째 공격, 2가 발차기 입력이다.

2번의 발차기 입력에서 IsKeyboardEnabled가 false로 바뀌었을것이다.

하지만 애니메이션이 끝나기도 전에 입력이 들어오면서 PlayAnimMontage가 호출되었으므로 지금 호출되는 애니메이션은 곧바로 종료하고 새로운 애니메이션을 시작해야한다. 이때 애니메이션을 즉각 끝내기 위해서 NotifyEnd가 호출되면서 IsKeyboardEnabled가 true로 바뀌면서 MoveForward와 MoveRight 함수의 내부가 평범하게 실행되면서 발차기도중 이동이 허용되는것이다.

 

 

이를 막는 방법은 크게 2가지가 있다.

첫번째. 공격 애니메이션이 재생중일때는 다른 애니메이션의 재생을 엄격히 금지할것

하지만 강좌에서는 또다른 방법을 제시해주고있다.

AttackStartNotifyState의 Tick함수에서 발차기 애니메이션을 재생중일때만 IsKeyboardEnabled를 강제로 false로 만들게끔 했다.

1
2
3
EAttackType APunchKick06Character::GetCurrentAttack() {
    return CurrentAttack;
}
cs
1
2
3
4
5
6
7
8
9
10
11
void UAttackStartNotifyState::NotifyTick(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration) {
    if (MeshComp != NULL && MeshComp->GetOwner() != NULL)
    {
        APunchKick06Character* Player = Cast<APunchKick06Character>(MeshComp->GetOwner());
        if (Player != NULL)
        {
            if (Player->GetCurrentAttack() == EAttackType::MELEE_KICK)
                Player->SetIsKeyboardEnabled(false);
        }
    }
}
cs

Tick함수 내에서 발차기 상태일때만 IsKeyboardEnabled를 false로 바꾼다.

이렇게 하면 공격애니메이션 직후에 발차기를 해도 이동을 하지 않는다.

하지만 발차기를 연달아 할경우에 아주 조금씩은 이동을 하는것을 볼 수 있다.

 

'Unreal 4 > 기타' 카테고리의 다른 글

flight  (0) 2020.08.08
펀치 - 소리 재생  (0) 2020.07.20
펀치 - 충돌 이벤트(Hit Event)  (0) 2020.07.20
펀치 - 스켈레톤 소켓  (0) 2020.07.19
펀치 - 애니메이션 몽타주  (0) 2020.07.18
Comments