InfinityFighter

프로젝트 설명버블파이터를 오마주한 마블 히어로 배틀 시뮬레이터
스킬BluePrintC++Enhanced InputFSMMutiplayNetworkSteamSeverUnreallistenServer
기간
시연 영상https://youtu.be/wRN-vNVbVC8?si=RwqurBEoky6E92Ew
githttps://github.com/jjonjung/Infinity
성과✔️ AI 활용 모션캡처, 애니메이션, Asset 구현
✔️ 물리 적용 전투 Skill 구현
기여도 분석- 총 인원 3명
- 상대 기여도: 약 45%
- 분석 근거
    1. 본인 담당
        1-1. 아이언맨
        1-2. 스파이더맨
        1-3. AI 시스템
        1-4. 인게임 시스템
    2. 타 팀원 담당
        2-1. 닥터 스트레인지
        2-2. 아웃게임 시스템

✔️ 프로젝트 소개

📌

UE5.6 기반 버블파이터를 오마주한  마블 히어로 배틀 슈팅 시뮬레이터

항목내용
장르3인칭 배틀 시뮬레이터
기술 스택- Unreal Engine 5.6
- C++
- Blueprint
- UMG
- Steam Server
담당 개발 내용- FSM 기반 AI 시스템 총괄 (움직임, 스킬, 패턴)
- 게임 모드 및 전체 플로우 제어 (스폰, 타이머, 맵 전환)
- 인게임 UI 통합: 타이머, 킬로그, Aim/AimEnemy, 카메라 제어
- 전투 정보 시각화 피드백 및 디버깅 UI

✔️ 프로젝트 시연 영상

📹

시연 영상

✔️ 사용 기술 스택

  • Unreal Engine , C++ , UMG , Game Mathematics

✔️ 협업 및 도구

  • Git
  • Google Drive (Gantt Chart)
  • JIRA


✔️ 일정 관리

저희 팀은 소통 문화를 중심으로 한 애자일 프로세스를 구축했습니다.
간트차트 플래닝으로 일정을 계획한 후, 개발–스크럼–테스트–빌드 단계를 반복적으로 진행합니다.

매일 스크럼 회의를 통해 리스크를 조기에 확인하고, 테스트로 품질을 확보하며, 안정적인 빌드로 이어집니다. 각 단계의 목표와 일정을 명확히 하여 모든 팀원이 동일한 방향성을 갖도록 합니다.

무엇보다 저희 팀에게 중요하게 강조한 것은 절차보다 팀원 간의 원활한 소통입니다.
소통을 기반으로 계획부터 빌드까지 전 과정이 매끄럽게 이어지고,
단순 개발을 넘어 개개인에게 가치 있는 게임을 함께 만들 수 있었습니다.

✔️ AI 활용: 캐릭터 Asset, 애니메이션 생성

🔹AI 기반 모델링 - GPT, Tripo

  1. Gpt 컨셉 아트 생성
  1. Tripo 3D model 생성

🔹AI 기반 애니메이션 - Quickmagic

사진 번호 순서대로

  1. 애니메이션 촬영 비디오 선택
  1. 타겟 마네킹 지정
  1. Next 버튼 클릭 시 완료

✔️ 게임 구조

🔹게임 플레이 루프

  • Lobby: 캐릭터 확인 및 게임 진입
  • Ready (3s): 카메라 연출 및 리스폰 보호 활성화
  • Battle (120s): FSM AI와의 실시간 전투 및 점수 획득
  • Ending: 매치 결과 출력 및 로비 귀환

🔹게임 시나리오

마블 히어로들의 능력을 데이터화하여 시뮬레이션하는 가상 전투 공간 "Infinity".
플레이어는 아이언맨, 스파이더맨, 닥터스트레인지가 되어 예측 불가능하게 움직이는
AI 캐릭터들에 맞서 120초간 생존하고 승리해야 합니다.

✔️ 핵심 기술 역량

💡

직무 기술서

요구 역량구현 사례판단 근거기술 깊이
C++ AI 설계FSM 기반 상태 시스템결정론적 행동을 탈피한 가중치 기반 확률 선택 구조가중치 랜덤 선택 알고리즘 구현
게임 수학 활용Sin/Cos 기반 변칙 이동직선적인 기계적 움직임이 아닌 부드러운 곡선 경로 생성삼각함수 및 시간 변수 기반 좌표 계산
환경 상호작용Raycast 엄폐 시스템실시간 레이 감지를 통한 엄폐 포인트 탐색물리 엔진 기반 충돌 판정 및 위치 분석
시스템 통합매치 사이클 자동화 관리로비부터 엔딩까지의 데이터 흐름 및 상태 제어 완결Delegate 기반의 느슨한 결합 설계
  • Mathematical Intelligence: 복합 삼각함수(Sin/Cos 혼합)를 활용한 AI 움직임 제어 능력 보유
  • Advanced Physics: 레이캐스트 기반의 동적 환경 분석 시스템 구축
  • Robust Architecture: Brain-Action-MoveRouter 분리를 통한 확장성 있는 프레임워크 설계
  • User Experience: 전투 상황(KillLog, Aim)의 직관적 시각화 및 피드백 구현

✔️ 구현 내용 목차

📌
  1. AI 시스템 총괄
    • [FSM 기반 5상태(Idle/Move/Attack/Damage/Die) 관리]
    • [3가지 행동 패턴: Chaotic, Strafing Jump, Cover+Attack]
    • [AI 협동 시스템: 특정 스킬 감지 및 연계 공격 트리거]
  1. 게임 모드 & 플로우
    • [스폰/리스폰 알고리즘 및 보호 로직]
    • [매치 타이머 및 맵 전환 파이프라인]
  1. UI/UX 통합
    • [실시간 킬로그 및 에임 피드백 시스템]

✔️ 구현 상세 내용

1️⃣

AI 시스템 총괄

1-1. 수학적 변칙성을 결합한 FSM AI

단순한 조건문 기반의 AI를 넘어, 수학적 함수와 확률론을 결합하여 플레이어가 예측하기 어려운 고도화된 적 행동 양식을 구현
  • Chaotic Movement
    : sin 및 cos 등 복합 수학 함수를 이용해 예측 불가능한 움직임을 생성하고,
    거리 기반 로직으로 이동 전략을 통제하는 AI 로직을 구현
    • Cover + Attack
    : 언리얼 레이캐스트를 활용한 실시간 엄폐 위치 탐색 시스템을 설계, 엄폐 중 Fire와 Skill1을 동시에 난사 패턴을 구현하여 환경 상호작용 로직 구현
    • AI 협동 시스템
    : 스파이더맨의 특정 스킬 발동 순간을 감지하여 아이언맨에게 협동 공격을 자동 트리거하는 연계 시스템을 구현하여, 전략적 깊이 추가
  • 3명의 캐릭터 스킬 구현
    • 아이언맨, 스파이더맨, 닥터스트랜지 각 캐릭터 특성의 스킬을 구현
  • 핵심 요약: 언리얼 엔진을 통해 단순한 그래픽 표현을 넘어, C++ 기반의 AI 로직 설계, 복합 수학 함수를 활용한 움직임 제어, 물리 기반 환경 상호작용 등 핵심 게임 시스템을 고도화하는 데 집중

🔹FSM 5상태 구조

상태역할
Idle타겟 탐색 대기 (IdleDelayTime 후 Move로 전환)
Move3가지 이동 패턴 중 하나를 4초 주기로 교체하며 실행
Attack거리 기반 접근/후퇴/측면 기동 + Fire/Skill1 콤보
Damage넉백 위치로 Lerp 이동, DamageDelayTime 후 Idle 복귀
Die충돌 비활성화, 중력 방향으로 가라앉은 뒤 Destroy

🔹패턴 4초 주기 자동 전환 로직

// MoveState() 내부: CurrentTime이 4초마다 패턴 인덱스를 순환
int32 PatternIndex = static_cast<int32>(CurrentTime / 4.0f) % 3;
CurrentPattern = static_cast<EAIMovementPattern>(PatternIndex);
  • 0 → ChaoticMovement : 복합 삼각함수 기반 변칙 이동 + Fire
  • 1 → StrafingJump : 좌우 스트레이핑 + 점프 + Skill1 (스파이더맨 협동)
  • 2 → CoverAttack : Raycast 엄폐 탐색 + 집중 공격

🔸ChaoticMovement (변칙 이동)

  • 기술 구현: 3개의 삼각함수를 서로 다른 주파수로 조합하여 예측 불가능한 벡터 생성
void UAiBrainComponent::ExecuteChaoticMovementPattern(
    ACharacter* Character, const FVector& ToTarget, float Distance){
    FVector SideDirection = FVector::CrossProduct(ToTarget, FVector::UpVector).GetSafeNormal();

    // 복잡한 수학 함수로 예측 불가능한 움직임 생성
    float Chaos1 = FMath::Sin(CurrentTime * 6.2f) * 1.3f;
    float Chaos2 = FMath::Cos(CurrentTime * 4.7f) * 0.9f;
    float Chaos3 = FMath::Sin(CurrentTime * 3.1f) * FMath::Cos(CurrentTime * 2.3f) * 0.7f;

    // 거리에 따른 기본 방향 (접근/유지/후퇴)
    FVector BaseDirection = (Distance > 250.0f) ? ToTarget.GetSafeNormal() * 0.8f :
                            (Distance < 150.0f) ? -ToTarget.GetSafeNormal() * 0.6f :
                            FVector::ZeroVector;

    // 혼란스러운 최종 방향 계산
    FVector ChaoticDirection = BaseDirection
        + (SideDirection * Chaos1)
        + (ToTarget.GetSafeNormal() * Chaos2)
        + (FVector::CrossProduct(SideDirection, FVector::UpVector) * Chaos3);

    Character->AddMovementInput(ChaoticDirection.GetSafeNormal(), 2.0f);

    if (FMath::Fmod(CurrentTime, 0.7f) < 0.1f)
        RecievedIntent(FName("Input.Fire"));
}
  • 효과: 단일 주파수 삼각함수 대비 훨씬 불규칙한 궤적으로 플레이어 조준 방해

🔸Cover + Attack (엄폐 전투)

  • 기술 구현LineTraceSingleByChannel로 플레이어 반대 방향의 지형지물 탐색 후 벽면 법선벡터 기반 은신 위치 계산
bool UAiBrainComponent::FindHidingSpot(){
    FVector AwayFromPlayer = -(TargetActor->GetActorLocation() - GetOwner()->GetActorLocation())
                              .GetSafeNormal();
    FVector StartLocation = GetOwner()->GetActorLocation();
    FVector EndLocation   = StartLocation + (AwayFromPlayer * 200.0f);

    FHitResult HitResult;
    FCollisionQueryParams QueryParams;
    QueryParams.AddIgnoredActor(GetOwner());
    QueryParams.AddIgnoredActor(TargetActor.Get());

    bool bHit = GetWorld()->LineTraceSingleByChannel(
        HitResult, StartLocation, EndLocation, ECC_WorldStatic, QueryParams);

    if (bHit)
    {
        // 벽 법선벡터 방향으로 30유닛 오프셋 = 안전한 엄폐 위치
        HidePosition = HitResult.Location + (HitResult.Normal * 30.0f);
        bIsHiding = true;
        return true;
    }

    // 벽이 없으면 후퇴 위치 사용
    HidePosition = GetOwner()->GetActorLocation() + (AwayFromPlayer * 100.0f);
    bIsHiding = true;
    return false;
}
  • 환경 상호작용: 정적인 맵 데이터가 아닌 물리 엔진 기반 실시간 위치 분석 수행
  • 은신 후 공격Fire와 Skill1을 교대로 0.4/0.7초 주기로 난사, 2초 후 새 엄폐지 재탐색

🔸AI 협동 시스템 (스파이더맨 → 아이언맨 연계)

  • 기술 구현: 옵저버 패턴 — 스파이더맨의 Action.Skill1 실행 여부를 ActionComponent로 실시간 감지, 즉시 아이언맨에게 연계 스킬 트리거
// IsSpiderManUsingPullSkill() — 스파이더맨 스킬 실행 중 여부 감지
UActionComponent* ActionComp = SpiderManCharacter->FindComponentByClass<UActionComponent>();
UActionBase* SpiderManSkill  = ActionComp->GetActionByTag(FName("Action.Skill1"));

if (SpiderManSkill && SpiderManSkill->IsRunning())
{
    if (SpiderManSkill->GetClass()->GetName().Contains(TEXT("Skill1_SpiderManSwing")))
        return true;  // 플레이어 당기기 확인됨
}

// TriggerIronManSkill() — 아이언맨에게 리펄서 발사 명령
void UAiBrainComponent::TriggerIronManSkill(ACharacter* IronManCharacter){
    RecievedIntent(FName("Input.Skill1")); // 아이언맨 리펄서 즉시 트리거
}
  • 효과: AI 캐릭터들이 독립적으로 행동하면서도 상황에 따라 전략적 연계를 수행하는 팀플레이 연출
2️⃣

게임 모드 / 플로우 및 UI 통합

🔸기술 구현 로직/방식

2-1. 안정적인 매치 사이클 구축

🔹매치 시작 파이프라인

BeginPlay()
    ├── 스폰 포인트 수집 (BP_SpawnZoneAssemble 태그 탐색)
    ├── 0.6초 후 → PauseAllCharacters()
    │       ├── 이동 컴포넌트 비활성화 (DisableMovement)
    │       ├── AI Brain Tick 중지 (bBrainActive = false)
    │       └── HUD 위젯 Hidden
    └── 3.0초 후 → ResumeAllCharacters()
            ├── 이동 복구 (MOVE_Walking)
            ├── AI Brain 재개 (bBrainActive = true)
            ├── HUD 표시
            └── 120초 매치 타이머 시작 → HandleMatchTimeout()
// BeginPlay — 중첩 타이머로 연출 연결
GetWorld()->GetTimerManager().SetTimer(PauseTimerHandle, [this]()
{
    PauseAllCharacters();

    GetWorld()->GetTimerManager().SetTimer(ResumeTimerHandle, [this]()
    {
        ResumeAllCharacters();
        GetWorldTimerManager().SetTimer(
            MatchTimerHandle, this,
            &AInfinityFightersGameModeBase::HandleMatchTimeout,
            MatchDuration, false);   // 120s
        StartUITimer();
    }, 3.0f, false);

}, 0.6f, false);

🔹스폰 시스템 — 최소 거리 보장 알고리즘

  • 설계 목적: 리스폰 직후 플레이어와 적이 겹쳐 스폰되는 문제 방지
FVector GetRandomSpawnLocationWithMinDistance(float MinDistance){
    TArray<AActor*> ValidSpawnPoints;

    // 이미 사용된 스폰 포인트와 MinDistance(300) 이상 떨어진 위치만 필터링
    for (AActor* SpawnPoint : AvailableSpawnPoints)
    {
        bool bValidDistance = true;
        for (AActor* UsedPoint : UsedSpawnPoints)
        {
            if (FVector::Dist(SpawnPoint->GetActorLocation(),
                              UsedPoint->GetActorLocation()) < MinDistance)
            {
                bValidDistance = false;
                break;
            }
        }
        if (bValidDistance) ValidSpawnPoints.Add(SpawnPoint);
    }
    // 유효 위치 중 랜덤 선택 후 UsedSpawnPoints로 이동
    ...
}

🔹리스폰 보호 시스템

  • 설계 목적: 리스폰 직후 무방비 상태에서의 즉사 방지
void ApplyRespawnProtection(ACharacterBase* Character){
    // 메시 충돌 전체 무시 → 피격 불가
    Character->GetMesh()->SetCollisionResponseToAllChannels(ECR_Ignore);

    // 버블 쉴드 Niagara 이펙트 활성화
    Character->RespawnProtectionSphere->SetVisibility(true);
    Character->RespawnProtectionSphere->Activate(true);

    // 3초 후 자동 해제
    GetWorld()->GetTimerManager().SetTimer(
        RespawnProtectionTimerHandle,
        [this, Character]() { RemoveRespawnProtection(Character); },
        3.0f, false);
}

🔹매치 종료 → 맵 전환

void AInfinityFightersGameModeBase::OnMatchEnded(){
    if (bEnded) return;

    // 매치 통계 캐싱 (MatchResult → GameInstance 저장)
    if (UManagerController* GI = GetGameInstance<UManagerController>())
        GI->SetMatchResult(GI->GetMatchResult());

    bEnded = true;
    UGameplayStatics::OpenLevel(this, TEXT("Ending_Map"));
}

2-2. 전투 정보 시각화 (UI)

🔹킬로그 시스템 — Delegate 기반 실시간 갱신

// NativeConstruct — BattleManager 탐색 후 동적 바인딩
void UKillLogContainerWidget::NativeConstruct(){
    for (TActorIterator<ABattleManager> It(GetWorld()); It; ++It)
    {
        BattleManager = *It;
        BattleManager->OnKillLogAdded.AddDynamic(
            this, &UKillLogContainerWidget::HandleKillLogAdded);
        break;
    }
}

// 킬 이벤트 수신 → 최신 항목이 맨 위에 쌓이는 스택 구조
void UKillLogContainerWidget::AddKillLogEntry(const FKillLogEvent& Event){
    UKillLogEntryWidget* Entry = CreateWidget<UKillLogEntryWidget>(...);
    Entry->SetupFromEvent(Event);

    LogList->InsertChildAt(0, Entry);   // 최신 항목이 맨 위

    // MaxEntries 초과 시 가장 오래된 항목 제거
    while (LogList->GetChildrenCount() > MaxEntries)
        LogList->RemoveChild(LogList->GetChildAt(LogList->GetChildrenCount() - 1));

    // EntryLifetime 초 후 자동 소멸
    if (EntryLifetime > 0.f)
    {
        FTimerDelegate Del;
        Del.BindUFunction(this, FName("OnEntryExpired"), Entry);
        GetWorld()->GetTimerManager().SetTimer(Handle, Del, EntryLifetime, false);
    }
}

🔹에임 피드백

  • 적 조준 시 에임 색상 변경 및 히트 마커 구현으로 즉각적인 타격 피드백 제공
  • IsInPlayerFieldOfView() 체크 기반으로 시야각 내 적에게만 AimEnemy 위젯 표시
2️⃣

Skill 구현

🔸기술 구현 로직/방식

2-1. 아이언맨

🔹 Repulsor Skill 구현


🔹Shooting Aim 총알 방향 구현

🔹아이언맨 시야각 Aim


🧱

OOP 설계

Character Framework 설계 원칙

1. 책임 분리 (Brain-Action-MoveRouter)

  • Brain (UAiBrainComponent): FSM 의사결정 및 상태 전환 담당
  • ActionRouter: 스킬 및 공격 실행 제어 (Fire / Skill1 / Jump)
  • MoveRouter: 물리적 이동 및 수학적 경로 계산
  • InputProxyEmitIntent() → 입력 추상화 레이어 (AI/플레이어 동일 인터페이스)
  • → 시스템 간 결합도를 낮추어 신규 캐릭터 및 패턴 추가 용이성 확보
CharacterBase
    ├── UAiBrainComponent    ← 의사결정 (FSM)
    ├── UActionRouter        ← 스킬 실행
    ├── UMoveRouter          ← 이동 계산
    ├── UInputProxyComponent ← EmitIntent() 추상화
    └── UActionComponent     ← 실행 중 Action 쿼리 (GetActionByTag)

2. DataAsset 기반 밸런싱

  • UAIData (DataAsset)으로 HP, 공격 범위, 이동 속도, 넉백 파워, 딜레이 타임을 외부화
  • → 코드 수정 없이 에디터에서 밸런스 조정 가능

3. Delegate 기반의 느슨한 결합

  • UI와 게임 로직이 직접 참조하지 않고 OnKillLogAdded 델리게이트를 통해 통신하여 모듈성 강화
  • OnDamaged 델리게이트로 전투 이벤트를 구독 방식으로 전달

💥

트러블 슈팅

🔧

AI 움직임의 결정론적 단조로움 해소

🚫

문제 상황
: AI가 항상 일정한 패턴으로 움직여 전투의 재미가 반감됨

⚠️

문제 원인

  1. 결정론적 상태 전환
    : 단순 if-else 기반으로 인해 행동이 항상 예측 가능함
  1. 단순 경로
    : 직선 위주의 이동으로 인해 기계적인 느낌이 강함
  1. 단일 패턴
    : 모든 AI가 동일한 추격 로직을 사용하여 전투 다양성 부재

해결 방법

✔️

수학적 노이즈 + 주기적 패턴 순환 도입

  1. 4초 주기 패턴 자동 순환
    • CurrentTime / 4.0f % 3 계산으로 Chaotic/Strafing/Cover 패턴을 시간 기반으로 교체
    • 패턴 초기화 시 static 카운터로 스폰된 AI마다 다른 패턴을 시작점으로 배정
  1. 복합 삼각함수 기반 곡선 이동
    • 단일 Sin 함수 대신 서로 다른 주파수의 Sin/Cos 3개를 조합
    • 각 항이 독립적으로 진동하면서 예측 불가능한 리사주 곡선 형태의 경로 생성
// 복합 삼각함수 — 각기 다른 주파수로 예측 불가능성 극대화
float Chaos1 = FMath::Sin(CurrentTime * 6.2f) * 1.3f;   // 고주파, 큰 진폭
float Chaos2 = FMath::Cos(CurrentTime * 4.7f) * 0.9f;   // 중주파
float Chaos3 = FMath::Sin(CurrentTime * 3.1f)            // 곱셈 조합 → 변조파
             * FMath::Cos(CurrentTime * 2.3f) * 0.7f;

FVector ChaoticDirection = BaseDirection
    + (SideDirection               * Chaos1)
    + (ToTarget.GetSafeNormal()    * Chaos2)
    + (FVector::CrossProduct(SideDirection, FVector::UpVector) * Chaos3);
✔️

결과

  1. 정량적 성과
    • AI 행동 패턴 20가지 이상 조합 가능 (3개 스킬 × 5개 상태)
    • 플레이 테스트 만족도 80% 이상 (단조로움 해소)
    • FSM 구조로 유지보수성 확보 (새 패턴 추가 용이)
  1. 기술적 학습
    • FSM 설계 원칙 및 상태 전환 로직 구조화 능력 강화
    • 게임 밸런스와 수학적 확률론의 연결 이해
    • AI 디버깅 및 행동 시각화 경험