일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 게임개발
- MAC
- 언리얼엔진
- Race condition
- 유니티
- STCF
- ret2libc
- TSet
- Double free
- stride
- 게임 개발
- DP
- linear difference equation
- CTF
- Rr
- 메카님
- dirty cow
- MLFQ
- AINCAA
- 유스케이스
- frequency-domain spectrum analysis
- DSP
- 운영체제
- Security
- pdlc
- 배경 그림
- RBAC
- Unity #Indie Game
- sampling theory
- dtft
- Today
- Total
다양한 기록
[UE Game Framework] #5 Character Combo Action 본문
애니메이션 몽타주
- 몽타주(Montage) : 이미지 일부를 잘라내 한 화면에서 합성하는 회화 기법
- 애니메이션 클립을 잘라내고 합성한 후 이를 재생하는 애니메이션 기능
- 애니메이션 클립을 모아둔 다수의 섹션으로 구성
- 섹션은 연동 가능, 스크립트를 통해 원하는 섹션으로 건너뛸 수 있음
애니메이션 집어넣기
섹션 만들기
옆에 화살표 있으면 두 섹션이 연결되어 있다는 의미임
개별적으로 관리하고 싶으면 링크 제거하면 됨
수동으로 연결하려면 AnimInstance 함수 사용
1. 공격 입력이 들어올 때 첫번째 몽타주 섹션이 재생되도록 하기
인풋 액션 만들고 매핑 컨텍스트에 추가해줌 (숄더랑 쿼터 둘 다)
캐릭터 플레이어에서 액션이랑 함수 추가
액션은 레퍼런스 찾아서 넣어주면 됨
베이스에서 ComboActionMontage가 필요한데, 레퍼런스 찾는 거 솔직히 비효율적임
블루프린트에서 설정
블루프린트 들어가면 이게 있는데
이렇게 설정해주면 됨 (이래서 블루프린트에 리드 라이트 준 것)
그리고 애니메이션 블루프린트에 몽타주를 재생할 수 있는 슬롯을 주어야 함
이러면 몽타주 애니메이션이 기존 애니메이션을 덮어써서 나가게 함
몽타주가 플레이되지 않으면 기존 애니메이션이 나감
그리고 블루프린트에서 변경이 생겼으니 게임 모드에서 레퍼런스 바꿔줘야 함
공격 잘 함
* 슬롯 따로 설정해준 거 없는데, 디폴트라 괜찮은듯
콤보 공격 기획
- 콤보 정보를 저장한 데이터 애셋 생성 (ArenaBattleCombo)
- 각 콤보마다 입력을 테스트하는 프레임 지정
- 테스트 프레임 전에 입력이 들어오면 다음 몽타주 섹션으로 이어서 재생
- 테스트 프레임보다 입력이 늦으면 해당 섹션을 마저 플레이하고 종료
ABComboActionData 클래스 추가하고 데이터 애셋으로 만듦
총 네가지 속성
몽타주 섹션 이름 지정, 콤보 개수, 프레임 기준 재생 속도, 입력이 사전에 입력되었는지 감지하는 프레임 지정
베이스에서 필요한 거 넣어주고
void AABCharacterBase::ProcessComboCommand()
{
if ( CurrentCombo == 0 )
{
ComboActionBegin();
}
}
void AABCharacterBase::ComboActionBegin()
{
// Combo Started
CurrentCombo = 1;
// Movement Setting
GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_None);
// Animation Setting
const float AttackSpeedRate = 1.0f;
UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
AnimInstance->Montage_Play(ComboActionMontage, AttackSpeedRate);
FOnMontageEnded EndDelegate;
EndDelegate.BindUObject(this, &AABCharacterBase::ComboActionEnd);
AnimInstance->Montage_SetEndDelegate(EndDelegate, ComboActionMontage);
}
void AABCharacterBase::ComboActionEnd(UAnimMontage* TargetMontage, bool IsProperlyEnded)
{
ensure(CurrentCombo != 0);
CurrentCombo = 0;
GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_Walking);
}
일단 지금까진 공격 중 이동 못하게 막은 정도
콤보 액션 데이터 설정
타이머 추가
타이머를 발동 시킬 함수, 타이머가 발동되면 입력이 들어왔는지 안들어왔는지를 체크
void AABCharacterBase::ProcessComboCommand()
{
if ( CurrentCombo == 0 )
{
ComboActionBegin();
return;
}
if ( !ComboTimerHande.IsValid() ) // 타이밍을 놓쳤거나, 더 이상 진행 못하거나
{
HasNextComboCommand = false;
}
else
{
HasNextComboCommand = true;
}
}
void AABCharacterBase::ComboActionBegin()
{
// Combo Started
CurrentCombo = 1;
// Movement Setting
GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_None);
// Animation Setting
const float AttackSpeedRate = 1.0f;
UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
AnimInstance->Montage_Play(ComboActionMontage, AttackSpeedRate);
FOnMontageEnded EndDelegate;
EndDelegate.BindUObject(this, &AABCharacterBase::ComboActionEnd);
AnimInstance->Montage_SetEndDelegate(EndDelegate, ComboActionMontage);
ComboTimerHande.Invalidate();
SetComboCheckTimer();
}
void AABCharacterBase::ComboActionEnd(UAnimMontage* TargetMontage, bool IsProperlyEnded)
{
ensure(CurrentCombo != 0);
CurrentCombo = 0;
GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_Walking);
}
void AABCharacterBase::SetComboCheckTimer()
{
int32 ComboIndex = CurrentCombo - 1;
ensure(ComboActionData->EffectiveFrameCount.IsValidIndex(ComboIndex));
const float AttackSpeedRate = 1.0f;
float ComboEffectiveTime = (ComboActionData->EffectiveFrameCount[ComboIndex] /
ComboActionData->FrameRate) / AttackSpeedRate;
if (ComboEffectiveTime > 0.0f)
{
GetWorld()->GetTimerManager().SetTimer(
ComboTimerHande, this, &AABCharacterBase::ComboCheck, ComboEffectiveTime, false);
}
}
void AABCharacterBase::ComboCheck()
{
ComboTimerHande.Invalidate();
if ( HasNextComboCommand )
{
UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
CurrentCombo = FMath::Clamp(CurrentCombo + 1, 1, ComboActionData->MaxComboCount);
FName NextSection = *FString::Printf(TEXT("%s%d"), *ComboActionData->MontageSectionNamePrefix, CurrentCombo);
AnimInstance->Montage_JumpToSection(NextSection, ComboActionMontage);
SetComboCheckTimer();
HasNextComboCommand = false;
}
}
여기서 EndDelegate 걸어서 콤보 안이어지면 끝내버림
타이머 끝나면 ComboCheck 불리는데, 입력 들어온 게 있으면 다음 몽타주 섹션으로 점프
* 입력 들어오면 바로 넘어가는게 아님, 선입력 생각하면 됨
콤보 안이어져야 몽타주가 끝나니까, 그때 End 불림
'언리얼 엔진 > Unreal Game Framework' 카테고리의 다른 글
[UE Game Framework] #7 Character Stat & Widget (0) | 2025.01.05 |
---|---|
[UE Game Framework] #6 Character Attack Hit Check (0) | 2025.01.05 |
[UE Game Framework] #4 Character Animation Setting (0) | 2025.01.04 |
[UE Game Framework] #3 Character Control Setting (0) | 2025.01.03 |
[UE Game Framework] #2 Character & Input System (0) | 2025.01.03 |