캐릭터가 물에 들어가면 1초에 1씩 산소가 줄고, 산소가 0이 되면 체력이 2씩 줄어듦
물에서 나오면 산소가 점점 회복됨
구성 :
// 최대 산소량
UPROPERTY(EditAnywhere, Replicated, Category = "Stats")
float MaxBreath = 100.f;
// 현재 산소량 (네트워크 복제, 변경 감지 함수 지정)
UPROPERTY(ReplicatedUsing=OnRep_CurretnBreath)
float CurrentBreath = MaxBreath;
// 산소 부족 시 체력에 입는 데미지량
UPROPERTY(EditAnywhere, Replicated, Category = "Stats")
float NotEnoughBreathDmg = 2.f;
// 1초 간격 호출됨
void DecreaseBreath()
{
if(CurrentBreath > 0)
{
CurrentBreath = FMath::Max(0.f, CurrentBreath - 1.f);
OnRep_CurretnBreath(); // UI 갱신용 함수 호출
}
else
{
ApplyDamage(NotEnoughBreathDmg); // 산소 0 이면 체력 깎기
}
}
void IncreaseBreath()
{
if(CurrentBreath < MaxBreath)
{
CurrentBreath = FMath::Min(MaxBreath, CurrentBreath + 1.f);
OnRep_CurretnBreath();
}
else
{
GetWorldTimerManager().ClearTimer(BreathTimer);
}
}
void OnRep_CurretnBreath()
{
if (APlayerController* PC = Cast<APlayerController>(Controller))
{
if (ADCPlayerController* MyPC = Cast<ADCPlayerController>(PC))
{
if (UInGameHUDWidget* HUD = MyPC->GetInGameHUD())
{
HUD->SetOxygen(CurrentBreath, MaxBreath);
}
}
}
}
void ApplyDamage(float DamageAmount)
{
CurrentHP = FMath::Clamp(CurrentHP - DamageAmount, 0.f, MaxHP);
// 사망 판정 등 추가 구현 가능
}
ApplyDamage 가 호출돼 체력을 깎음ApplyDamage 함수는 체력만 다룸void UInGameHUDWidget::NativeConstruct()
{
Super::NativeConstruct();
if (APlayerController* PC = GetOwningPlayer())
{
if (AWaitingPlayerState* PS = PC->GetPlayerState<AWaitingPlayerState>())
{
if (!PS->PlayerNickname.IsEmpty())
{
SetNicknameText(PS->PlayerNickname);
}
else
{
SetNicknameText(PS->GetPlayerName());
}
PS->OnNicknameChanged.AddDynamic(this, &UInGameHUDWidget::SetNicknameText);
}
}
// 초기 체력 UI 설정 - 임시 100/100
UpdateHealthDisplayWithValues(100.0f, 100.0f);
// 초기 산소 UI 설정 - 임시 100/100
SetOxygen(100.0f, 100.0f);
}
ADCCharacter::ADCCharacter()
{
PrimaryActorTick.bCanEverTick = true;
MaxHP = 100.f;
CurrentHP = MaxHP;
MaxBreath = 100.f;
CurrentBreath = MaxBreath;
NotEnoughBreathDmg = 2.f;
// 기타 변수 초기화
}
void ADCCharacter::BeginPlay()
{
Super::BeginPlay();
// 체력 및 산소 초기화 (필요에 따라 HUD/PlayerState 등 초기화 호출 포함 가능)
CurrentHP = MaxHP;
CurrentBreath = MaxBreath;
// 예시: 물에 들어가면 BrewerTimer 타이머 시작 등 추가 설정 가능
}
void UInGameHUDWidget::SetOxygen(float CurrentOxygen, float MaxOxygen)
{
// 텍스트 출력 (예: "80 / 100")
if (OxygenText)
{
FString OxygenString = FString::Printf(TEXT("%.0f / %.0f"), CurrentOxygen, MaxOxygen);
OxygenText->SetText(FText::FromString(OxygenString));
}
// 게이지 채우기 및 색상 변경
if (OxygenBar)
{
float Percent = MaxOxygen > 0 ? CurrentOxygen / MaxOxygen : 0.f;
OxygenBar->SetPercent(Percent);
FLinearColor BarColor;
if (Percent < 0.3f) // 30% 미만: 분홍색
{
BarColor = FLinearColor(FColor::FromHex(TEXT("FF9DFB")));
}
else // 기본(30% 이상): 하늘색
{
BarColor = FLinearColor(FColor::FromHex(TEXT("6BD9FF")));
}
OxygenBar->SetFillColorAndOpacity(BarColor);
}
}

Overlay 자식 순서: ProgressBar 위, TextBlock 아래(=텍스트가 위에 뜸)
TextBlock
ProgressBar
Background Image: 둥근 관(외곽) PNG (투명+하얀 외곽/그림자)
Fill Image: 안쪽 채워지는 캡슐 PNG (테두리와 간격이 살짝 보이게 여백 포함)
➡ 이렇게 하면 배경과 채움 사이에 띠가 남아 ‘튜브 속 액체’처럼 보임!
➡ 진행률은 SetPercent() 로 갱신, 색은 위 1) 방식으로 변경
Canvas Panel 안에서는 자식 목록의 위/아래가 Z 순서
UI 배경(블러/이미지)이 버튼을 가리지 않게:
Replication 및 타이머를 이용해 동기화 및 자동 업데이트 구현
캐릭터와 UI 간 원활한 상태 전달 및 실시간 갱신 중요
HUD 위젯 생성시 임의값 100/100으로 초기화 가능
캐릭터가 물에 들어가면 타이머에 의해 DecreaseBreath() 가 1초마다 호출
CurrentBreath가 줄고 0이 되면 ApplyDamage로 체력이 줄어듦
물 밖에서는 IncreaseBreath() 로 다시 산소를 회복
OnRep_CurretnBreath()가 호출되어 HUD UI의 산소 ProgressBar를 갱신
HUD에서는 SetOxygen(CurrentBreath, MaxBreath) 함수를 통해 UI가 각각의 값을 표시