Unreal - 콤보 공격

2025. 8. 22. 18:50·Unreal 프로젝트 다이어리/두번째 프로젝트

 

플레이어의 애니메이션을 담당할 DataTable 구조체를 위해

부모클래스를 없음 으로 선택하고 c++클래스를 제작해줍니다

 

데이터테이블에 액션타입, 사용애니메이션, 다음콤보공격으로 넘어가기위한 최대 입력시간

그리고 콤보의 순서인덱스값 등을 선언해주었습니다

#pragma once

#include "CoreMinimal.h"
#include "CharacterAnimDataTable.generated.h"

UENUM(BlueprintType)
enum class ECharacterAction : uint8
{
    BasicAttack UMETA(DisplayName = "Basic Attack"),
    Dodge       UMETA(DisplayName = "Dodge"),
    AirAttack   UMETA(DisplayName = "Air Attack")
};

USTRUCT(BlueprintType)
struct FCharacterAnimDataTable : public FTableRowBase
{
	GENERATED_BODY()

public:

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    ECharacterAction actionType;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    UAnimMontage* usingAnimation;

    // 다음 콤보 공격으로 넘어가기 위한 최대 입력 시간 (초)
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    float comboInputTime;

    //activate only basicattack
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bShowComboNumber", ClampMin = "0"))
    int32 comboIndexes;

    // EditCondition용 bool 변수 (실제 값)
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (HideInDetailPanel))
    bool bShowComboNumber;

};

 

데이터테이블의 각 액션타입에 맞춰서 데이터테이블의 모든 행에서 comboIndexes 변수 순서대로 오름차순 정렬하여

데이터테이블에서 설정한 comboIndexes 순서대로 콤보가 진행되도록 정렬해주었습니다

TArray<FCharacterAnimDataTable*> comboRows;
TArray<FCharacterAnimDataTable*> airComboRows;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DataTable")
UDataTable* animDataTable;
void AMainCharacter::initializeComboRows()
{
    if (!animDataTable) return;

    TArray<FCharacterAnimDataTable*> allRows;
    animDataTable->GetAllRows(TEXT("ComboAnimTable"), allRows);

    // 기존 배열 초기화
    comboRows.Empty();
    airComboRows.Empty();

    for (FCharacterAnimDataTable* row : allRows)
    {
        if (!row) continue;

        if (row->actionType == ECharacterAction::BasicAttack)
        {
            comboRows.Add(row);
        }
        else if (row->actionType == ECharacterAction::AirAttack)
        {
            airComboRows.Add(row);
        }
    }

    // 오름차순 정렬: comboIndexes 기준
    comboRows.Sort([](const FCharacterAnimDataTable& a, const FCharacterAnimDataTable& b) 
        {
        return a.comboIndexes < b.comboIndexes;
        });

    airComboRows.Sort([](const FCharacterAnimDataTable& a, const FCharacterAnimDataTable& b) 
        {
        return a.comboIndexes < b.comboIndexes;
        });
}

 

데이터테이블의 값에따라 콤보순서, 다음콤보까지의 시간, 콤보애니메이션을 고려한 콤보 함수를 만들었습니다

 

이렇게 설계하면 콤보를 추가하고싶거나 애니메이션을 바꾸고싶으면

별도의 하드코딩없이 유지보수가 쉽도록 설계가 가능합니다

void AMainCharacter::PlayBasicAttackCombo()
{
    if (GetCharacterMovement()->IsFalling()) return;
    if (comboRows.Num() == 0) return;

    bIsJumpAllowed = false;

    // 처음 입력 또는 콤보가 끊긴 상태
    if (!bCanNextCombo)
    {
        currentComboIndex = 0;
        bCanNextCombo = true;
        //다음 애니메이션 넘어갈떄의 제한
        bCanReceiveInput = false;
    }
    else
    {
        if (!bCanReceiveInput) return;
        // comboInputTime 안에 재입력 → 다음 콤보
        currentComboIndex++;
        if (currentComboIndex >= comboRows.Num())
        {
            currentComboIndex = 0; // 마지막 콤보 후 다시 처음
        }
        bCanReceiveInput = false;
    }

    FCharacterAnimDataTable* row = comboRows[currentComboIndex];
    if (!row || !row->usingAnimation) return;

    currentMontage = row->usingAnimation;
    PlayAnimMontage(currentMontage);

    GetWorld()->GetTimerManager().ClearTimer(th_comboAttackTimerHandle);
    GetWorld()->GetTimerManager().SetTimer(th_comboAttackTimerHandle,this,&AMainCharacter::ResetCombo,row->comboInputTime,false
    );
    
}

 

데이터테이블에 값을 입력해주었습니다

 

애니메이션 블루프린트에서 상체하체 분리 애니메이션을 구분하기위해 사용했던 rootSlot을 사용 애니메이션에 적용해줍니다

 

컨트롤러에서 공격을 할떈 점프를 할수 없도록 애님노티파이의 조건을 걸어주었습니다

bCanReceiveInput 변수는 다음 애니메이션 넘어갈때 제한을 두는변수고

bIsJumpAllowed 변수는 애니메이션을 할때 점프를 불가능하게 제한을 두는 bool 변수입니다

void UMainCharacterAnimInstance::AnimNotify_CanNextAttack()
{
	myPawn = TryGetPawnOwner();
	if (myPawn)
	{
		mainCharacter = Cast<AMainCharacter>(myPawn);
		if (mainCharacter)
		{
			mainCharacter->bCanReceiveInput = true;
		}
	}
}

void UMainCharacterAnimInstance::AnimNotify_CanJump()
{
	myPawn = TryGetPawnOwner();
	if (myPawn)
	{
		mainCharacter = Cast<AMainCharacter>(myPawn);
		if (mainCharacter)
		{
			mainCharacter->bIsJumpAllowed = true;
		}
	}
}

 

'Unreal 프로젝트 다이어리 > 두번째 프로젝트' 카테고리의 다른 글

Unreal - HitInterface 상호작용  (0) 2025.08.23
Unreal - Hash 기반 AI/Enemy 데이터 설계하기  (0) 2025.08.23
Unreal - 무기 Draw  (0) 2025.08.19
Unreal - Layered Per Bone (상 하체 애니메이션 분리)  (0) 2025.08.17
Unreal - Foot IK  (0) 2025.08.17
'Unreal 프로젝트 다이어리/두번째 프로젝트' 카테고리의 다른 글
  • Unreal - HitInterface 상호작용
  • Unreal - Hash 기반 AI/Enemy 데이터 설계하기
  • Unreal - 무기 Draw
  • Unreal - Layered Per Bone (상 하체 애니메이션 분리)
lucodev
lucodev
커피와 노트북 그리고 개발
  • lucodev
    루코 개발테이블
    lucodev
  • 전체
    오늘
    어제
    • 분류 전체보기 (210) N
      • Unreal 프로젝트 다이어리 (107) N
        • 첫번째 프로젝트 (73)
        • 두번째 프로젝트 (34) N
      • Unreal 팁 (8)
      • Unreal 디버깅 (8)
      • C++ 프로그래머스 (52)
        • Stack,Queue (7)
        • Hash (4)
        • Heap (2)
        • Sort (5)
        • Exhaustive search (5)
        • Greedy (2)
        • BFS , DFS (7)
        • Graph (2)
        • Dynamic Programming (1)
        • C++ Math (2)
        • 기타 문제 (14)
      • C++ 백준 (4)
      • C++ 팁 (1)
      • 개인 코테 & 스타디 <비공개> (29)
        • 코드 개인보관함 (9)
        • 코딩테스트+@ (11)
        • 알고리즘 스타디 (6)
        • 알고리즘 스타디 과제 (3)
        • 비공개 (0)
  • 인기 글

  • 최근 글

  • 최근 댓글

  • 링크

  • 공지사항

  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 태그

    언리얼
    unreal 모션매칭
    unreal 시퀀스
    unreal inventory
    언리얼 motionmatching
    언리얼 상호작용
    언리얼 인벤토리
    언리얼 컷씬
    unreal 인벤토리
    언리얼 parkour
    언리얼 모션매칭
    언리얼 behaviortree
    언리얼 비헤이비어트리
    언리얼 behavior tree
    Unreal Parkour
    언리얼 ui
    언리얼 시퀀스
    unreal 파쿠르
    언리얼 파쿠르
    언리얼 프로그래스바
  • hELLO· Designed By정상우.v4.10.3
lucodev
Unreal - 콤보 공격
상단으로

티스토리툴바