
InfinityFighter
| 프로젝트 설명 | 버블파이터를 오마주한 마블 히어로 배틀 시뮬레이터 |
|---|---|
| 스킬 | BluePrintC++Enhanced InputFSMMutiplayNetworkSteamSeverUnreallistenServer |
| 기간 | |
| 시연 영상 | https://youtu.be/wRN-vNVbVC8?si=RwqurBEoky6E92Ew |
| git | https://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
- Gpt 컨셉 아트 생성
- Tripo 3D model 생성
🔹AI 기반 애니메이션 - Quickmagic
사진 번호 순서대로
- 애니메이션 촬영 비디오 선택
- 타겟 마네킹 지정
✔️ 게임 구조
🔹게임 플레이 루프
- 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)의 직관적 시각화 및 피드백 구현
✔️ 구현 내용 목차
- AI 시스템 총괄
- [FSM 기반 5상태(Idle/Move/Attack/Damage/Die) 관리]
- [3가지 행동 패턴: Chaotic, Strafing Jump, Cover+Attack]
- [AI 협동 시스템: 특정 스킬 감지 및 연계 공격 트리거]
- 게임 모드 & 플로우
- [스폰/리스폰 알고리즘 및 보호 로직]
- [매치 타이머 및 맵 전환 파이프라인]
- UI/UX 통합
- [실시간 킬로그 및 에임 피드백 시스템]
✔️ 구현 상세 내용
AI 시스템 총괄
1-1. 수학적 변칙성을 결합한 FSM AI
단순한 조건문 기반의 AI를 넘어, 수학적 함수와 확률론을 결합하여 플레이어가 예측하기 어려운 고도화된 적 행동 양식을 구현
- Chaotic Movement
: sin 및 cos 등 복합 수학 함수를 이용해 예측 불가능한 움직임을 생성하고,
거리 기반 로직으로 이동 전략을 통제하는 AI 로직을 구현
• Cover + Attack
: 언리얼 레이캐스트를 활용한 실시간 엄폐 위치 탐색 시스템을 설계, 엄폐 중 Fire와 Skill1을 동시에 난사 패턴을 구현하여 환경 상호작용 로직 구현
• AI 협동 시스템
: 스파이더맨의 특정 스킬 발동 순간을 감지하여 아이언맨에게 협동 공격을 자동 트리거하는 연계 시스템을 구현하여, 전략적 깊이 추가
- 3명의 캐릭터 스킬 구현
• 아이언맨, 스파이더맨, 닥터스트랜지 각 캐릭터 특성의 스킬을 구현
- 핵심 요약: 언리얼 엔진을 통해 단순한 그래픽 표현을 넘어, C++ 기반의 AI 로직 설계, 복합 수학 함수를 활용한 움직임 제어, 물리 기반 환경 상호작용 등 핵심 게임 시스템을 고도화하는 데 집중
🔹FSM 5상태 구조
| 상태 | 역할 |
|---|---|
Idle | 타겟 탐색 대기 (IdleDelayTime 후 Move로 전환) |
Move | 3가지 이동 패턴 중 하나를 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 캐릭터들이 독립적으로 행동하면서도 상황에 따라 전략적 연계를 수행하는 팀플레이 연출
게임 모드 / 플로우 및 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 위젯 표시
OOP 설계
Character Framework 설계 원칙
1. 책임 분리 (Brain-Action-MoveRouter)
- Brain (
UAiBrainComponent): FSM 의사결정 및 상태 전환 담당
- ActionRouter: 스킬 및 공격 실행 제어 (Fire / Skill1 / Jump)
- MoveRouter: 물리적 이동 및 수학적 경로 계산
- InputProxy:
EmitIntent()→ 입력 추상화 레이어 (AI/플레이어 동일 인터페이스)
- → 시스템 간 결합도를 낮추어 신규 캐릭터 및 패턴 추가 용이성 확보
CharacterBase
├── UAiBrainComponent ← 의사결정 (FSM)
├── UActionRouter ← 스킬 실행
├── UMoveRouter ← 이동 계산
├── UInputProxyComponent ← EmitIntent() 추상화
└── UActionComponent ← 실행 중 Action 쿼리 (GetActionByTag)2. DataAsset 기반 밸런싱
UAIData(DataAsset)으로 HP, 공격 범위, 이동 속도, 넉백 파워, 딜레이 타임을 외부화
- → 코드 수정 없이 에디터에서 밸런스 조정 가능
3. Delegate 기반의 느슨한 결합
- UI와 게임 로직이 직접 참조하지 않고
OnKillLogAdded델리게이트를 통해 통신하여 모듈성 강화
OnDamaged델리게이트로 전투 이벤트를 구독 방식으로 전달
트러블 슈팅
AI 움직임의 결정론적 단조로움 해소
문제 상황
: AI가 항상 일정한 패턴으로 움직여 전투의 재미가 반감됨
문제 원인
- 결정론적 상태 전환
: 단순if-else기반으로 인해 행동이 항상 예측 가능함
- 단순 경로
: 직선 위주의 이동으로 인해 기계적인 느낌이 강함
- 단일 패턴
: 모든 AI가 동일한 추격 로직을 사용하여 전투 다양성 부재
해결 방법
수학적 노이즈 + 주기적 패턴 순환 도입
- 4초 주기 패턴 자동 순환
CurrentTime / 4.0f % 3계산으로 Chaotic/Strafing/Cover 패턴을 시간 기반으로 교체
- 패턴 초기화 시
static카운터로 스폰된 AI마다 다른 패턴을 시작점으로 배정
- 복합 삼각함수 기반 곡선 이동
- 단일
Sin함수 대신 서로 다른 주파수의Sin/Cos3개를 조합
- 각 항이 독립적으로 진동하면서 예측 불가능한 리사주 곡선 형태의 경로 생성
- 단일
// 복합 삼각함수 — 각기 다른 주파수로 예측 불가능성 극대화
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);결과
- 정량적 성과
- AI 행동 패턴 20가지 이상 조합 가능 (3개 스킬 × 5개 상태)
- 플레이 테스트 만족도 80% 이상 (단조로움 해소)
- FSM 구조로 유지보수성 확보 (새 패턴 추가 용이)
- 기술적 학습
- FSM 설계 원칙 및 상태 전환 로직 구조화 능력 강화
- 게임 밸런스와 수학적 확률론의 연결 이해
- AI 디버깅 및 행동 시각화 경험








