티스토리 뷰

🟩 오늘의 목표

  • FWeaponStat 구조체를 활용해 데이터 기반으로 무기 종류를 확장할 수 있는 시스템을 구축한다.
  • 사격 입력 시 첫 발사 지연을 제거하고 고연사 시의 사운드 끊김 현상을 해결한다.
  • TMap을 이용해 무기 교체 시에도 탄약 데이터를 보존하고, 장전 후 자동으로 사격이 재개되는 로직을 완성한다.

🟧 1. 확장형 무기 데이터 구조 (FWeaponStat)

🟦 데이터 기반 무기 확장성 확보

데이터 테이블을 연동하여 권총, 연사 라이플, 샷건 등 새로운 무기 타입을 코드 수정 없이 추가할 수 있는 기반을 마련했다. 특히 PelletCount(산탄 수)와 BulletSpread(탄퍼짐) 멤버를 도입하여 단발 사격과 샷건 로직을 하나의 컴포넌트에서 통합 처리할 수 있게 설계했다.


🟧 2. 사격 반응성 및 리듬 보정 (StartFire, Fire)

🟦 첫 발사 지연 해소 및 연사 안정화

사격 입력 시 쿨타임을 역산하여 첫 발사가 즉시 나가도록 보정하고, 타이머 오차(Jitter)를 고려한 0.8f 허용치를 적용해 고연사 중 사운드가 끊기는 현상을 해결했다.

// 첫 발사 지연 해소 및 연사 타이머 설정
void UWeaponComponent::StartFire() {
    float CurrentTime = GetWorld()->GetTimeSeconds();
    // 쿨타임이 이미 지났다면 LastFireTime을 역산하여 즉시 발사 보장
    if (CurrentTime - LastFireTime >= CurrentStat->FireRate) {
        LastFireTime = CurrentTime - CurrentStat->FireRate;
    }

    Fire(); // 첫 발 즉시 실행
    GetWorld()->GetTimerManager().SetTimer(FireTimerHandle, this, &UWeaponComponent::Fire, CurrentStat->FireRate, true);
}

// 사격 리듬 보정 및 연타 방지
void UWeaponComponent::Fire() {
    float CurrentTime = GetWorld()->GetTimeSeconds();
    // 교체 딜레이 체크
    if (CurrentTime - LastWeaponSwitchTime < WeaponSwitchDelay) return;
    // 타이머 오차(Jitter)를 고려한 0.8f 허용치 적용 (연사 끊김 방지)
    if (CurrentTime - LastFireTime < CurrentStat->FireRate * 0.8f) return;

    // ... 사격 실행 및 LastFireTime 업데이트 ...
}

🟧 3. 무기별 독립 탄약 관리 (TMap, ChangeWeapon)

🟦 무기 교체 시 탄약 데이터 보존

TMap을 도입하여 무기를 교체하더라도 각 무기가 가졌던 이전 탄약 정보를 유지하도록 구현했다. 새로운 무기로 교체할 때 맵에서 데이터를 찾아 복구하거나, 처음 사용하는 무기라면 데이터 테이블의 MaxAmmo를 할당한다.

// TMap을 활용한 잔탄수 영구 보존
void UWeaponComponent::ChangeWeapon(FName NewWeaponName) {
    if (bIsReloading || WeaponRowName == NewWeaponName) return; // 장전 중 교체 차단

    WeaponAmmoMap.Add(WeaponRowName, CurrentAmmo); // 현재 탄약 저장

    if (FWeaponStat* NewStat = WeaponStatTable->FindRow<FWeaponStat>(NewWeaponName, TEXT(""))) {
        WeaponRowName = NewWeaponName;
        // 맵에서 데이터를 찾아 복구하거나 처음이면 최대 탄약 할당
        CurrentAmmo = WeaponAmmoMap.Contains(NewWeaponName) ? WeaponAmmoMap[NewWeaponName] : NewStat->MaxAmmo;
        
        LastWeaponSwitchTime = GetWorld()->GetTimeSeconds(); // 교체 지연 시작
        // ... 자동 사격 재개 로직 ...
    }
}

🟧 4. 장전 후 사격 자동 재개 (CompleteReload)

🟦 끊김 없는 전투 흐름 설계

장전이 완료되는 즉시 마우스 클릭 상태(bHoldingFire)를 체크하여 사격이 바로 재개되도록 로직을 연결했다. 이때 LastFireTime을 조정하여 장전 직후 쿨타임 대기 없이 바로 첫 발이 나갈 수 있게 보정했다.

void UWeaponComponent::CompleteReload() {
    CurrentAmmo = CurrentStat->MaxAmmo;
    bIsReloading = false;
    
    // 장전 직후 즉시 사격 가능하도록 쿨타임 초기화
    LastFireTime = GetWorld()->GetTimeSeconds() - CurrentStat->FireRate;

    if (bHoldingFire) StartFire(); // 마우스 유지 시 즉시 사격 재개
}

🟧 핵심 성과 및 회고

  • 버그 해결 : 장전 완료 후 사격 멈춤, 무기 교체 시 탄약 리셋, 연사 사운드 끊김 문제를 모두 해결했다.
  • 안정성 확보 : BeginPlay에서 시간 변수를 안전하게 초기화하여 쓰레기 값에 의한 오작동을 차단했다.
  • 협업 준비 : 팀원들이 로직을 쉽게 이해할 수 있도록 모든 핵심 함수에 상세 주석 작업을 완료했다.