-
블루아카이브 루트슈터 팬게임 프로젝트(23) - 재장전 & 무기교체 애니메이션루트슈터 프로젝트 2024. 1. 26. 03:35
안녕하세요. 이번에는 재장전 애니메이션과 무기교체 애니메이션을 적용해 보았습니다. 이번에 사용할 기능은 애니메이션 노티파이와 델리게이트, 스테이트입니다.
PlayerCharacter.h
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDele_Dynamic); public: UPROPERTY(VisibleAnywhere, BlueprintAssignable, Category = "Event") FDele_Dynamic FDele_StartReload; //재장전 델리게이트 UPROPERTY(VisibleAnywhere, BlueprintAssignable, Category = "Event") FDele_Dynamic FDele_StartSwitch; //무기교체 델리게이트
먼저 캐릭터 클래스에 다이나믹 멀티캐스트 델리게이트를 하나 정의해주고 재장전 시 broadcast 할 델리게이트와 무기교체시 broadcast할 델리게이트를 하나씩 생성해줍니다. 저도 이 기능을 처음써보는 것이기 때문에 (언리얼 자체가 처음이지만..) 이렇게 설명하는 것이 맞는지 모르겠습니다.
void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerInputComponent); PlayerInputComponent->BindAction<TDelegate<void(int)>>(TEXT("GunSlot1"), IE_Pressed, this, &APlayerCharacter::StartSwitch, 0); PlayerInputComponent->BindAction<TDelegate<void(int)>>(TEXT("GunSlot2"), IE_Pressed, this, &APlayerCharacter::StartSwitch, 1); PlayerInputComponent->BindAction<TDelegate<void(int)>>(TEXT("GunSlot3"), IE_Pressed, this, &APlayerCharacter::StartSwitch, 2); PlayerInputComponent->BindAction(TEXT("Reload"), IE_Pressed, this, &APlayerCharacter::APlayerCharacter::StartReload); } void APlayerCharacter::StartReload() { if( gun->nowMag == int(gun->gunStat.maxMag * (1.0 + (playerStat[4] / 100.0)))) return; reloadSpeed = gun->gunStat.reloadTime * (1.0 - (playerStat[3] / 100.0)); if (FDele_StartReload.IsBound()) { GetWorldTimerManager().ClearTimer(FireRateTimerHandle); bNowReloading = true; FDele_StartReload.Broadcast(); } } void APlayerCharacter::StartSwitch(int slotNum) { if(nowEquipGun == slotNum || gunSlot[slotNum].itemType == EItemType::EIT_DEFAULT || bNowReloading || bNowSwitching) return; if(FDele_StartSwitch.IsBound()) { GetWorldTimerManager().ClearTimer(FireRateTimerHandle); nowEquipGun = slotNum; bNowSwitching = true; FDele_StartSwitch.Broadcast(); } }
이제 플레이어가 원하는 재장전 키와 무기교체 키를 등록한 후 어떤 함수를 실행할지도 바인딩 해둡니다. 재장전의 경우 R키를 눌렀을 때 StartReload 함수가 실행되게 합니다. 이때 무기의 탄창이 꽉 차있다면 재장전을 해도 의미가 없기 때문에 if문을 걸고 이를 통과하면 델리게이트가 바운드 됐는지 확인하고 브로드캐스트를 실행합니다.
무기교체 함수인 StartSwitch 또한 지금 장착하고 있는 슬롯을 또 다시 누르거나 비어있는 슬롯을 선택했을 때 반응하면 안되기 때문에 if문으로 검사해준 후 통과하면 broadcast를 합니다. bNowReloading과 bNowSwitching은 재장전 도중에 총을 발사하거나 하는 행동을 막기 위해 선언해준 상태를 표현하기 위한 bool 변수입니다.
PlayerAnimInstance.h
private: //플레이어 재장전 UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Player State", meta = (AllowPrivateAccess = "true")) bool bIsReload; // 장전시작하는가? UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Player State", meta = (AllowPrivateAccess = "true")) float reloadAnimPlaySpeed; //장전을 몇초동안 하는가? private: UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Player State", meta = (AllowPrivateAccess = "true")) bool bIsSwitch; //무기교체하는가? private: UFUNCTION() void StartReloading(); //장전이 시작했을때 호출되는 함수 // header UFUNCTION(BlueprintCallable, Category = "Reloading") void EndReloading(); UFUNCTION() void StartSwitching(); //무기교체가 시작했을때 호출 UFUNCTION(BlueprintCallable, Category = "Switching") void EndSwitching(); UFUNCTION(BlueprintCallable, Category = "Switching") void SwitchingWeapon();
UAnimInstance를 상속받은 플레이어의 애니메이션 클래스입니다. 방금 브로드 캐스트 한 신호를 이곳에서 받아 함수를 실행하게 됩니다. 위의 bool 변수들은 플레이어 상태변환을 위해 존재하는 변수입니다.
void UPlayerAnimInstance::NativeInitializeAnimation() { Super::NativeInitializeAnimation(); if(!playerPawn) playerPawn = TryGetPawnOwner(); //플레이어 할당받기 시도 if(playerPawn) player = Cast<APlayerCharacter>(playerPawn); if(player) { //이렇게 함으로써 플레이어가 StartReload를 호출하면 StartReloading또한 호출 player->FDele_StartReload.AddDynamic(this, &UPlayerAnimInstance::StartReloading); player->FDele_StartSwitch.AddDynamic(this, &UPlayerAnimInstance::StartSwitching); } }
먼저 플레이어 할당을 받으면 각각의 델리게이트가 broadcast를 할 시 어떤 함수를 호출할지 설정해 둡니다.
FDele_StartReload 가 broadcast를 하면 여기에서 StartReloading 함수를 호출하는 방식입니다.
void UPlayerAnimInstance::StartReloading() { if(player) reloadAnimPlaySpeed = 2.17 / player->reloadSpeed; bIsReload = true; } void UPlayerAnimInstance::StartSwitching() { bIsSwitch = true; }
broadcast시 실행되는 함수입니다. 재장전 또는 무기교체를 시작하면 플레이어 상태를 나타내는 bool을 true로 바꿉니다. 재장전, 무기교체를 시작하겠다는 뜻 입니다.
이렇게 애님 클래스의 bool 변수가 변하면 이제 애니메이션의 스테이트 머신에서 상태에 따라 애니메이션이 재생됩니다. bIsReload가 true라면 Reload 상태가 되어 재장전 애니메이션을 재생하고 bIsSwitch 가 true라면 무기재생 애니메이션이 재생됩니다. false라면 다시 Empty 상태로 돌아옵니다.
예시 사진으로, 재장전 상태에 들어가기 위해서는 isSwitch가 true이고 isReload가 false이어야 합니다. 둘다 true라면 동시에 두 상태에 진입하는 것이니 이상하겠죠.
무기교체 상태에 진입하면 무기교체 애니메이션을 실행해줍니다.
재장전 또한 비슷하게 구현하면 되니 생략하도록 하겠습니다.
그리고 하나 더 중요한 것이 있는데, 바로 하반신의 모션(이동 모션)과 상반신의 모션(주로 총기 조작 모션) 을 분기점을 기준으로 Blend 해주는 것 입니다. 이것은 블루프린트로 Layered blend per bone으로 해주면 됩니다, 레이어 설정의 분기필터에 추가하여 spine즉 척추쪽 스켈레톤을 기준으로 블렌딩 해주면 됩니다.
여기서 상반신의 모션은 사실 재장전과 무기교체를 안하고 있는 상태가 있을 수 있습니다. 따라서 bool에 or 연산을 해서 둘중 하나라도 애니메이션이 재생되는 상태라면 true(즉 Blend Weight를 1.0으로 설정)로 설정하게 하면 됩니다.
하나 더 중요한 것은 애니메이션에 노티파이를 설정해야 하는 것 입니다. 재장전이 거의 끝나가면 현재 탄창의 탄약이 다시 꽉 차야 합니다. 이 시점은 프로그래머(또는 디자이너)가 임의로 설정할 수 있는데 이것을 노티파이 기능으로 하는 것 입니다. 저는 맨 마지막 프레임에 재장전이 끝났다는 노티파이를 하나 추가해주었습니다.
보라색 태그는 사운드 노티파이로 애니메이션의 특정 시점에 사운드를 재생시키도록 집어넣을 수 있습니다.
무기 교체 애니메이션에서는 노티파이를 두개 사용하였습니다. 무기교체시 중간에 들고있는 무기가 바뀌어야 하므로 중간에 NewGunEuipped 노티파이를 만들고 맨끝에 애니메이션이 끝났다는 노티파이를 하나 더 설정하였습니다.
이 노티파이 시점에 무언가 함수를 실행하고 싶다면 애니메이션 블루프린트의 이벤트 그래프에서 AnimNotify_노티파이 이름 노드를 찾아서 생성 한 후 부르고 싶은 함수를 호출하면 됩니다. 이것은 C++에서도 할 수 있는 것으로 아는데, 워낙 단순하기 때문에 블루프린트에 추가하였습니다.
void UPlayerAnimInstance::EndReloading() { bIsReload = false; if (player) { player->FinishReload(); } } void UPlayerAnimInstance::EndSwitching() { bIsSwitch = false; if (player) { player->FinishSwitch(); } } void UPlayerAnimInstance::SwitchingWeapon() { if (player) { player->GunChangeTo(); } }
노티파이를 지나갈 시 호출되는 함수들입니다. 애니메이션이 끝났을 때 호출되는 함수들은 다시 상태 bool 변수들을 false로 돌려준 후 플레이어에게 동작이 끝났음을 알리는 함수들을 호출합니다.
void APlayerCharacter::FinishReload() { gun->GunReloaded(); magazineUI->InitUI(gun->nowMag, gun->gunStat.maxMag * (1.0 + (playerStat[4] / 100.0))); bNowReloading = false; } void APlayerCharacter::FinishSwitch() { bNowSwitching = false; }
무기의 재장전이 끝난다면 총에게 재장전 모션이 끝났으니 총알을 리필(?) 하라고 함수를 호출해준 후 탄창 상태 UI도 다시 초기화 해주면 됩니다. 둘다 플레이어의 상태 변수도 false로 설정해주어서 이제 재장전, 무기교체중이 아니라는 것을 알립니다.
결과물입니다. 재장전과 무기교체 애니메이션 모두 잘 돌아가는 것을 볼 수 있습니다.
'루트슈터 프로젝트' 카테고리의 다른 글
블루아카이브 루트슈터 팬게임 프로젝트(25) - 총기의 특수효과 추가 (0) 2024.01.29 블루아카이브 루트슈터 팬게임 프로젝트(24) - 무기 사운드 추가 (0) 2024.01.26 블루아카이브 루트슈터 팬게임 프로젝트(22) - 메인화면 구현 (1) 2024.01.25 블루아카이브 루트슈터 팬게임 프로젝트(21) - 게임 저장 시스템 (1) 2024.01.23 블루아카이브 루트슈터 팬게임 프로젝트(20) - 인벤토리 아이템 판매 시스템 (1) 2024.01.21