* 아래의 책의 내용을 정리한 글입니다! *
ABCharacter.h에 추가
private:
void Attack();
ABCharacter.cpp 에 추가
void AABCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent){
//~~
PlayerInputComponent->BindAction(TEXT("Attack"), EInputEvent::IE_Pressed, this, &AABCharacter::Attack);// 공격
}
//~~
void Attack() {
ABLOG_S(Warning);
}
클릭시 로그가 찍힌다면 성공!
이제 애니메이션을 넣어줍시다.
ABAnimInstance.h 에 추가
void PlayAttackMontage();
private:
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = Attack, Meta = (AllowPrivateAccess = true))
UAnimMontage* AttackMontage;
ABAnimInstance.cpp 에 수정 및 추가!
UABAnimInstance::UABAnimInstance()
{
CurrentPawnSpeed = 0.0f;
static ConstructorHelpers::FObjectFinder<UAnimMontage> ATTACK_MONTAGE(TEXT("AnimMontage'/Game/InfinityBladeWarriors/Animation/SK_Mannequin_Skeleton_Montage.SK_Mannequin_Skeleton_Montage'"));
if (ATTACK_MONTAGE.Succeeded())
{
AttackMontage = ATTACK_MONTAGE.Object;
}
}
//~~
void UABAnimInstance::PlayAttackMontage()
{
if (!Montage_IsPlaying(AttackMontage))
{
Montage_Play(AttackMontage, 1.0f);
}
}
애니메이션 블루프린트를 아래처럼 바꿔주고
ABCharacter.cpp 수정해주면
void Attack() {
auto AnimInstance = Cast<UABAnimInstance>(GetMesh()->GetAnimInstance());
if (nullptr == AnimInstance) return;
AnimInstance->PlayAttackMontage();
}
잘 움직이네요 ㅎ
델리게이트?
위에서 애니메이션을플레이 해보았을때 움직이는 상태에선 플레이 되지 않도록 만들었습니다.
여기서는 계속플레이중인지를 매번 체크하는 방식으로 확인하는데
델리게이트는 플레이가 끝난순간 폰에게 애니메이션이 끝났다고 알려주는 방식으로 좀 더 효율적인 방법입니다!
좀 더 넓게 본다면 하나의 작업을 여러 객체가 나눠서 처리할수있도록 하기위한 프로그래밍 설계방법입니다.
언리얼에서도 자주사용되며 여러 프로그램에서도 이와같은 방식을 많이 사용합니다.
하지만 c++에서는 델리케이트 시스템을 기본적으로 제공하고 있지 않기에
언리얼에서는 별도의 프레임워크를 사용합니다.
코드로도 만들어 봅시다.
일단 오류코드를 잡아주기위한
ArenaBattle.h
#define ABCHECK(Expr, ...) { if (!(Expr)) { ABLOG(Error, TEXT("ASSERTION : %s"), TEXT("'"#Expr"'")); return __VA_ARGS__;} }
ABCharacter.h
public:
virtual void PostInitializeComponents() override;
private:
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = Attack, Meta = (AllowPrivateAccess = true))
bool IsAttacking;
UPROPERTY()
class UABAnimInstance* ABAnim;
private:
UFUNCTION()
void OnAttackMontageEnded(UAnimMontage* Montage, bool bInterrupted);
ABCharacter.cpp
AABCharacter::AABCharacter()
{
//~~
IsAttacking = false;
//~~
void AABCharacter::PostInitializeComponents()
{
Super::PostInitializeComponents();
ABAnim = Cast<UABAnimInstance>(GetMesh()->GetAnimInstance());
ABCHECK(nullptr != ABAnim);
ABAnim->OnMontageEnded.AddDynamic(this, &AABCharacter::OnAttackMontageEnded);
}
//~~
void AABCharacter::Attack()
{
if (IsAttacking) return;
//~~
IsAttacking = true;
}
void AABCharacter::OnAttackMontageEnded(UAnimMontage* Montage, bool bInterrupted)
{
ABCHECK(IsAttacking);
IsAttacking = false;
}
다음은 노티파이!
특정 타이밍에 애님인스턴스에게 신호를 보내는 함수를 만들수있게 해주는 기능!
이처럼 추가후
ABAnimInstance.h
private:
UFUNCTION()
void AnimNotify_AttackHitCheck();
ABAnimInstance.cpp
void UABAnimInstance::AnimNotify_AttackHitCheck() {
ABLOG_S(Warning);
}
그리고 시작하면 각 행동마다 로그가 찍히게 됩니다!
이 기능을 이용해서 콤보공격을 만들어 봅시다!
아까 위에서 만든 몽타주 상태에서
링크 제거를 해 별개의 애님으로 만들어 줍시다.
여기서 엄청 해매서...
ABCharacter.h
class ARENABATTLE_API AABCharacter : public ACharacter
{
private:
void AttackStartComboState();
void AttackEndComboState();
private:
UPROPERTY(VisibleInstanceOnly, BluePrintReadOnly, Category = Attack, Meta = (AllowPrivateAccess = true))
bool CanNextCombo;
UPROPERTY(VisibleInstanceOnly, BluePrintReadOnly, Category = Attack, Meta = (AllowPrivateAccess = true))
bool IsComboInputOn;
UPROPERTY(VisibleInstanceOnly, BluePrintReadOnly, Category = Attack, Meta = (AllowPrivateAccess = true))
int32 CurrentCombo;
UPROPERTY(VisibleInstanceOnly, BluePrintReadOnly, Category = Attack, Meta = (AllowPrivateAccess = true))
int32 MaxCombo;
}
ABCharacter.cpp
AABCharacter::AABCharacter()
{
MaxCombo = 4;
AttackEndComboState();
}
oid AABCharacter::Attack()// 공격!
{
if (IsAttacking)
{
ABCHECK(FMath::IsWithinInclusive<int32>(CurrentCombo, 1, MaxCombo));
if (CanNextCombo)
{
IsComboInputOn = true;
}
}
else
{
ABCHECK(CurrentCombo == 0);
AttackStartComboState();
ABAnim->PlayAttackMontage();
ABAnim->JumpToAttackMontageSection(CurrentCombo);
IsAttacking = true;
}
}
void AABCharacter::OnAttackMontageEnded(UAnimMontage* Montage, bool bInterrupted)
{
ABCHECK(IsAttacking);
ABCHECK(CurrentCombo > 0);
IsAttacking = false;
AttackEndComboState();
}
void AABCharacter::PostInitializeComponents()
{
Super::PostInitializeComponents();
ABAnim = Cast<UABAnimInstance>(GetMesh()->GetAnimInstance());
ABCHECK(nullptr != ABAnim);
ABAnim->OnMontageEnded.AddDynamic(this, &AABCharacter::OnAttackMontageEnded);
ABAnim->OnNextAttackCheck.AddLambda([this]() -> void {
ABLOG(Warning, TEXT("OnNextAttackCheck"));
CanNextCombo = false;
if (IsComboInputOn)
{
AttackStartComboState();
ABAnim->JumpToAttackMontageSection(CurrentCombo);
}
});
}
void AABCharacter::AttackStartComboState() // 애님 분할!
{
CanNextCombo = true;
IsComboInputOn = false;
ABCHECK(FMath::IsWithinInclusive<int32>(CurrentCombo, 0, MaxCombo - 1));
CurrentCombo = FMath::Clamp<int32>(CurrentCombo + 1, 1, MaxCombo);
}
void AABCharacter::AttackEndComboState()
{
IsComboInputOn = false;
CanNextCombo = false;
CurrentCombo = 0;
}
ABAnimInstance.h
DECLARE_MULTICAST_DELEGATE(FOnNextAttackCheckDelegate);
DECLARE_MULTICAST_DELEGATE(FOnAttackHitCheckDelegate);
class ARENABATTLE_API UABAnimInstance : public UAnimInstance
{
//+
public:
void JumpToAttackMontageSection(int32 NewSection);
public:
FOnNextAttackCheckDelegate OnNextAttackCheck;
FOnAttackHitCheckDelegate OnAttackHitCheck;
private:
UFUCNTION()
void AnimNotify_AttackHitCheck();
UFUNCTION()
void AnimNotify_NextAttackCheck();
FName GetAttackMontageSectionName(int32 Section);
}
ABAnimInstance.cpp
void UABAnimInstance::JumpToAttackMontageSection(int32 NewSection)
{
ABCHECK(Montage_IsPlaying(AttackMontage));
Montage_JumpToSection(GetAttackMontageSectionName(NewSection), AttackMontage);
}
void UABAnimInstance::AnimNotify_AttackHitCheck()
{
OnAttackHitCheck.Broadcast();
}
void UABAnimInstance::AnimNotify_NextAttackCheck()
{
OnNextAttackCheck.Broadcast();
}
FName UABAnimInstance::GetAttackMontageSectionName(int32 Section)
{
ABCHECK(FMath::IsWithinInclusive<int32>(Section, 1, 4), NAME_None);
return FName(*FString::Printf(TEXT("Attack%d"), Section));
}
여기까지하면 클릭에 따라 콤보공격이 가능해집니다!
틀린점이 있다면 댓 달아주세요!
'언리얼 최고 > 언리얼 c++' 카테고리의 다른 글
이득우의 언리얼 C++ 정리 - 10 (아이템 상자와 무기제작) (2) | 2022.09.07 |
---|---|
이득우의 언리얼 C++ 정리 - 9 (충돌 설정과 대미지 전달) (0) | 2022.08.31 |
이득우의 언리얼 C++ 정리 - 7 (애니메이션 시스템의 설계) (0) | 2022.08.20 |
이득우의 언리얼 C++ 정리 - 6 (캐릭터의 제작과 컨트롤) (0) | 2022.08.20 |
이득우의 언리얼 C++ 정리 - 5 (폰의 제작과 조작) (0) | 2022.08.19 |
댓글