Sparta Unreal 부트캠프 90일차

정찬호·2026년 4월 9일

1. 언리얼 미션 시스템 — 버그 분석

버그 1. AVGMissionSandbagLastContributor가 항상 null

SpawnRewardItems()에서 LastContributor or RewardItemClass is Missing 에러가 출력되는 문제가 발생했습니다.

원인
TakeDamage에서 StatComponent->ApplyDamageToStat()은 호출하고 있었지만, LastAttacker에 공격자를 저장하는 RegisterAttacker() 호출이 누락되어 있었습니다.
때문에 OnSandbagDefeated.Broadcast(LastAttacker)를 날려도 LastAttacker가 항상 nullptr인 상태였습니다.

// TakeDamage 내부에 추가
if (HasAuthority() && EventInstigator)
{
    if (AVGCharacterBase* AttackerCharacter = Cast<AVGCharacterBase>(EventInstigator->GetPawn()))
    {
        RegisterAttacker(AttackerCharacter);
    }
}

버그 2. Gimmick Server RPC가 무시되는 문제

레버, 제단 등 기믹에 상호작용이 전혀 동작하지 않는 문제였습니다.

원인 — Unreal Server RPC Owner 규칙
UFUNCTION(Server, Reliable) RPC는 호출하는 클라이언트가 해당 액터의 Owner여야만 서버로 전송됩니다.
AVGMissionGimmickBase는 월드에 배치된 독립 액터로, SetOwner()를 호출한 적이 없어 Owner가 nullptr입니다.
따라서 클라이언트에서 Server_Interact()를 호출해도 RPC가 조용히 무시됩니다.

// 이 구조가 문제
void AVGMissionGimmickBase::OnInteractWith(AVGCharacterBase* Interactor)
{
    if (!HasAuthority())
    {
        Server_Interact(Interactor); // Owner 없음 → 전송 안 됨
        return;
    }
}

RPC 방향별 Owner 요구사항 정리

RPC 종류호출 주체Owner 필요 여부
Server클라이언트 → 서버필요
Multicast서버 → 모든 클라이언트불필요
Client서버 → 특정 클라이언트필요

Sandbag에서 구현된 Multicast_OnDead가 정상 동작했던 이유는 서버가 호출하는 Multicast는 Owner 제약이 없기 때문이었습니다.

수정 방향
기믹 자신의 RPC 대신, Owner가 캐릭터인 EquipmentComponent의 RPC를 중계자로 활용했습니다.
EquipmentComponentServer_InteractWithActor(AActor*, AVGCharacterBase*)를 추가하고, 내부에서 IVGInteractable 인터페이스로만 통신하면 결합도를 낮출 수 있습니다.

// GimmickBase::OnInteractWith 수정
if (!HasAuthority())
{
    if (UVGEquipmentComponent* EquipComp =
        Interactor->FindComponentByClass<UVGEquipmentComponent>())
    {
        EquipComp->Server_InteractWithActor(this, Interactor);
    }
    return;
}

// EquipmentComponent::Server_InteractWithActor_Implementation
void UVGEquipmentComponent::Server_InteractWithActor_Implementation(
    AActor* TargetActor, AVGCharacterBase* Interactor)
{
    if (TargetActor->Implements<UVGInteractable>())
    {
        if (IVGInteractable::Execute_CanInteract(TargetActor, Interactor))
        {
            IVGInteractable::Execute_OnInteract(TargetActor, Interactor);
        }
    }
}

해당 RPC 추가는 EquipmentComponent 담당 팀원분께 건의하여 수정을 허가받아, 수정되었습니다.


버그 3. MissionItem 줍기 불가

AVGMissionItemBase::OnInteractWith 내부 첫 줄이 if (!HasAuthority()) return;이어서, 클라이언트에서 호출되면 바로 종료됩니다. Gimmick 때와 같은 문제였습니다.

작업을 시작하기 전 Interact 관련 리팩토링이 진행되었기에 내일 머지 받는 대로 수정을 진행할 계획입니다.


기타 코드 리뷰에서 발견한 사항

SetMissionState에서 OnMissionStateChanged 이중 Broadcast
SetMissionState 내부에서 OnRep_CurrentStateTag()를 직접 호출하고, 그 안에도 Broadcast가 있는데, SetMissionState 자체에도 Broadcast가 한 번 더 있다. 서버에서 상태 변경 시 이벤트가 두 번 발생합니다.

HandleMissionStateChangedUFUNCTION() 누락
AddDynamic으로 바인딩되는 함수에 UFUNCTION()이 없으면 런타임에 바인딩이 조용히 실패합니다.

ClearContributers에서 LastContributor 미초기화
Contributors 배열만 비우고 LastContributor = nullptr을 누락했습니다. 미션 리셋 후 보상 스폰 시 댕글링 포인터 참조 위험이 있습니다.


현재는 모두 수정이 완료되었습니다.

2. 언리얼 미션 시스템 - 코어와의 연결

Mission 달성 보상 - 시간 감소 값 반환

현재 설계상 미션을 완료되면 다음 단계까지의 남은 시간이 감소해야 합니다.

문제는 해당 부분이 구현이 되어 있지 않았다는 점입니다. 코어를 담당하는 팀원분의 요청에 맞추서 Delegate를 작성(단순 시간 감소 값을 발송하는 델리게이트입니다.)한 후 관련 로직을 작성했습니다.

MissionData를 관리하는 DataAsset에 ClearReduceTime 변수 추가, MissionBase에 반환함수 추가, MissionSubsytem에서 Mission이 완료될 시 델리게이트를 방송했습니다.

3. 머티리얼 오버레이 작업

TA 시간에 제작한 Voronoi 머티리얼을 아웃라인 오버레이로 활용했다.

Overlay Material이란?
원래 메시 위에 한 겹 더 씌우는 머티리얼이다. 메시의 Rendering 섹션에서 설정한다.

Overlay Material Max Draw Distance
오버레이 머티리얼이 카메라에서 얼마나 먼 거리까지 렌더링될지 제한하는 옵션이다.
상호작용 가능한 오브젝트 강조와 가까이 있을 때만 아웃라인을 표시하는 현재 요구사항에 딱 맞는 속성이다.

활용 예시

  • 가까이 있을 때만 아웃라인 표시
  • 상호작용 가능한 오브젝트 강조
  • 피격/상태이상 효과를 근거리에서만 표시
  • 불필요한 오버레이 렌더링을 줄여 성능 관리

3. 코딩 테스트 — 연속된 부분 수열의 합

코딩 테스트
프로그래머스 - 연속된 부분 수열의 합

실패 코드
수열의 시작 인덱스와 끝 인덱스를 저장해서, 현재 합이 작으면 끝 인덱스 증가, 크면 시작 인덱스를 중가시켰습니다.
맨 마지막 인덱스의 인자가 k와 동일하고 그 이전에 k와 같은 인자가 없을 수도 있으니 예외처리를 추가했습니다.

#include <string>
#include <vector>

using namespace std;

vector<int> solution(vector<int> sequence, int k) {
    vector<int> answer;
    int sum = sequence[0];
    int len = 1000000, limit = sequence.size() - 1;
    int leftIdx = 0, rightIdx = 0;
    
    while(leftIdx != limit && rightIdx != limit)
    {
        if(rightIdx < limit && sum < k)
        {
            rightIdx++;
            sum += sequence[rightIdx];
        }
        else if(leftIdx < limit && sum > k)
        {
            sum -= sequence[leftIdx];
            leftIdx++;
        }
        else if(sum == k)
        {
            if(len > rightIdx - leftIdx)
            {
                len = rightIdx - leftIdx;
                answer = {leftIdx, rightIdx};
            }
            
            if(rightIdx + 1 < sequence.size())
            {
                rightIdx++;
                sum+= sequence[rightIdx];
            }
            else
            {
                break;
            }
        }
    }
    
    if(len > 0 && sequence[limit] == k)
    {
        answer = {limit, limit};
    }
    
    return answer;
}

정답률은 85.3이었습니다.

테스트 1 〉	실패 (0.01ms, 3.68MB)
테스트 2 〉	통과 (0.01ms, 4.21MB)
테스트 3 〉	통과 (0.01ms, 4.21MB)
테스트 4 〉	통과 (0.02ms, 3.68MB)
테스트 5 〉	통과 (0.06ms, 4.21MB)
테스트 6 〉	통과 (0.07ms, 4.3MB)
테스트 7 〉	통과 (0.28ms, 5.02MB)
테스트 8 〉	통과 (0.55ms, 6.56MB)
테스트 9 〉	통과 (1.07ms, 9.79MB)
테스트 10 〉	통과 (2.66ms, 19.6MB)
테스트 11 〉	통과 (4.13ms, 36.2MB)
테스트 12 〉	통과 (4.28ms, 36.1MB)
테스트 13 〉	통과 (6.54ms, 36.1MB)
테스트 14 〉	통과 (4.53ms, 36.2MB)
테스트 15 〉	통과 (4.48ms, 36.1MB)
테스트 16 〉	실패 (3.23ms, 36.8MB)
테스트 17 〉	통과 (5.72ms, 36.8MB)
테스트 18 〉	통과 (0.01ms, 4.21MB)
테스트 19 〉	통과 (0.01ms, 3.67MB)
테스트 20 〉	통과 (0.01ms, 4.19MB)
테스트 21 〉	통과 (0.01ms, 4.21MB)
테스트 22 〉	통과 (0.01ms, 4.16MB)
테스트 23 〉	통과 (0.01ms, 4.16MB)
테스트 24 〉	통과 (4.49ms, 34.7MB)
테스트 25 〉	실패 (3.42ms, 34.6MB)
테스트 26 〉	통과 (5.03ms, 34.6MB)
테스트 27 〉	실패 (6.55ms, 34.6MB)
테스트 28 〉	통과 (4.16ms, 34.7MB)
테스트 29 〉	실패 (4.25ms, 34.7MB)
테스트 30 〉	통과 (5.88ms, 36.7MB)
테스트 31 〉	통과 (0.01ms, 4.2MB)
테스트 32 〉	통과 (0.01ms, 4.21MB)
테스트 33 〉	통과 (0.01ms, 4.14MB)
테스트 34 〉	통과 (0.01ms, 4.15MB)

내일 할 일

  • EquipmentComponentServer_InteractWithActor RPC 추가 반영 후 기믹 상호작용 검증
  • AVGMissionItemBase::OnInteractWith HasAuthority() 가드 제거 및 아이템 줍기 수정
  • 미션 이펙트 작업 시작 (Particle/Niagara)
profile
게임 개발 지망생입니다.

0개의 댓글