#include "SpaceShooterHUD.h" #include "SpaceShooterGameMode.h" #include "Engine/Canvas.h" #include "Engine/Font.h" #include "SpaceshipPawn.h" #include "UObject/ConstructorHelpers.h" ASpaceShooterHUD::ASpaceShooterHUD() { // Find and set the default font static ConstructorHelpers::FObjectFinder FontObj(TEXT("/Engine/EngineFonts/RobotoDistanceField")); TextFont = FontObj.Object; GameOverStartTime = 0.0f; } ASpaceshipPawn* ASpaceShooterHUD::GetPlayerPawn() const { APlayerController* PC = GetWorld()->GetFirstPlayerController(); if (PC) { return Cast(PC->GetPawn()); } return nullptr; } // Add this new function for drawing the bars void ASpaceShooterHUD::DrawStatusBars() { if (!Canvas) return; ASpaceshipPawn* PlayerPawn = GetPlayerPawn(); if (!PlayerPawn) return; // Get the canvas size const float ScreenWidth = Canvas->SizeX; const float ScreenHeight = Canvas->SizeY; // Calculate positions for the bars (top right corner) float StartX = ScreenWidth - BarWidth - BarPadding; float HealthY = BarPadding; float ShieldY = HealthY + BarHeight + BarPadding; // Get current health and shield values float HealthPercent = PlayerPawn->GetCurrentHealth() / PlayerPawn->GetMaxHealth(); float ShieldPercent = PlayerPawn->GetCurrentShield() / PlayerPawn->GetMaxShield(); // Clamp values between 0 and 1 HealthPercent = FMath::Clamp(HealthPercent, 0.0f, 1.0f); ShieldPercent = FMath::Clamp(ShieldPercent, 0.0f, 1.0f); // Draw health bar background FCanvasBoxItem HealthBG(FVector2D(StartX, HealthY), FVector2D(BarWidth, BarHeight)); HealthBG.SetColor(BackgroundBarColor); Canvas->DrawItem(HealthBG); // Draw health bar fill FCanvasBoxItem HealthFill(FVector2D(StartX, HealthY), FVector2D(BarWidth * HealthPercent, BarHeight)); HealthFill.SetColor(HealthBarColor); Canvas->DrawItem(HealthFill); // Draw shield bar background FCanvasBoxItem ShieldBG(FVector2D(StartX, ShieldY), FVector2D(BarWidth, BarHeight)); ShieldBG.SetColor(BackgroundBarColor); Canvas->DrawItem(ShieldBG); // Draw shield bar fill FCanvasBoxItem ShieldFill(FVector2D(StartX, ShieldY), FVector2D(BarWidth * ShieldPercent, BarHeight)); ShieldFill.SetColor(ShieldBarColor); Canvas->DrawItem(ShieldFill); // Draw text labels FString HealthText = FString::Printf(TEXT("Health: %.0f%%"), HealthPercent * 100); FString ShieldText = FString::Printf(TEXT("Shield: %.0f%%"), ShieldPercent * 100); // Draw text over the bars DrawText(HealthText, FLinearColor::White, StartX + 5, HealthY + 2, TextFont, 0.8f); DrawText(ShieldText, FLinearColor::White, StartX + 5, ShieldY + 2, TextFont, 0.8f); } void ASpaceShooterHUD::DrawHUD() { Super::DrawHUD(); ASpaceShooterGameMode* GameMode = GetGameMode(); if (!GameMode) return; if (!GameMode->IsGameOver()) { DrawGameplayHUD(); } else { DrawGameOverScreen(); } } void ASpaceShooterHUD::DrawGameplayHUD() { ASpaceShooterGameMode* GameMode = GetGameMode(); if (!GameMode) return; // Draw timer int32 Minutes = FMath::FloorToInt(GameMode->GetRemainingTime() / 60.0f); int32 Seconds = FMath::FloorToInt(FMath::Fmod(GameMode->GetRemainingTime(), 60.0f)); FString TimeString = FString::Printf(TEXT("Time: %02d:%02d"), Minutes, Seconds); DrawText(TimeString, FLinearColor::White, 50, 50, TextFont); // Draw kill count FString KillString = FString::Printf(TEXT("Kills: %d"), GameMode->GetKillCount()); DrawText(KillString, FLinearColor::White, 50, 100, TextFont); // Draw status bars DrawStatusBars(); } void ASpaceShooterHUD::DrawGameOverBackground() { if (!Canvas) return; // Draw a semi-transparent black background FCanvasBoxItem BoxItem(FVector2D(0, 0), FVector2D(Canvas->SizeX, Canvas->SizeY)); BoxItem.SetColor(FLinearColor(0.0f, 0.0f, 0.0f, 0.7f)); Canvas->DrawItem(BoxItem); } void ASpaceShooterHUD::DrawGameOverText(const FString& Text, const FVector2D& Position, const FLinearColor& Color, float Scale) { if (!Canvas || !TextFont) return; float TextWidth, TextHeight; GetTextSize(Text, TextWidth, TextHeight, TextFont, Scale); // Draw text shadow DrawText(Text, FLinearColor(0.0f, 0.0f, 0.0f, Color.A * 0.5f), Position.X + 3.0f, Position.Y + 3.0f, TextFont, Scale); // Draw main text DrawText(Text, Color, Position.X, Position.Y, TextFont, Scale); } void ASpaceShooterHUD::DrawGameOverScreen() { ASpaceShooterGameMode* GameMode = GetGameMode(); if (!GameMode || !Canvas) return; // Initialize GameOverStartTime if needed if (GameOverStartTime == 0.0f) { GameOverStartTime = GetWorld()->GetTimeSeconds(); } // Draw darkened background DrawGameOverBackground(); const FVector2D ViewportSize(Canvas->SizeX, Canvas->SizeY); const float CurrentTime = GetWorld()->GetTimeSeconds(); const float TimeSinceGameOver = CurrentTime - GameOverStartTime; // Calculate pulsating alpha for "GAME OVER" text float PulsatingAlpha = FMath::Lerp(PulsateMinAlpha, PulsateMaxAlpha, (FMath::Sin(TimeSinceGameOver * PulsateSpeed) + 1.0f) * 0.5f); // Calculate vertical positions float CenterY = ViewportSize.Y * 0.4f; // Slightly above center float Spacing = 60.0f * GameOverScale; // Draw "GAME OVER" with pulsating effect FLinearColor PulsatingColor = GameOverTextColor; PulsatingColor.A = PulsatingAlpha; FString GameOverText = TEXT("GAME OVER"); float TextWidth, TextHeight; GetTextSize(GameOverText, TextWidth, TextHeight, TextFont, GameOverScale); DrawGameOverText(GameOverText, FVector2D((ViewportSize.X - TextWidth) * 0.5f, CenterY), PulsatingColor, GameOverScale); // Draw stats with scaling animations const float StatsScale = 1.5f; float StatsY = CenterY + Spacing; // Time Survived int32 TotalSeconds = FMath::RoundToInt(GameMode->GetGameDuration() - GameMode->GetRemainingTime()); int32 Minutes = TotalSeconds / 60; int32 Seconds = TotalSeconds % 60; FString TimeText = FString::Printf(TEXT("Time Survived: %02d:%02d"), Minutes, Seconds); GetTextSize(TimeText, TextWidth, TextHeight, TextFont, StatsScale); DrawGameOverText(TimeText, FVector2D((ViewportSize.X - TextWidth) * 0.5f, StatsY), StatsTextColor, StatsScale); // Kill Count StatsY += Spacing * 0.7f; FString KillText = FString::Printf(TEXT("Enemies Destroyed: %d"), GameMode->GetKillCount()); GetTextSize(KillText, TextWidth, TextHeight, TextFont, StatsScale); DrawGameOverText(KillText, FVector2D((ViewportSize.X - TextWidth) * 0.5f, StatsY), StatsTextColor, StatsScale); // Calculate final score based on time and kills int32 FinalScore = GameMode->GetKillCount() * 100 + TotalSeconds * 10; StatsY += Spacing * 0.7f; FString ScoreText = FString::Printf(TEXT("Final Score: %d"), FinalScore); GetTextSize(ScoreText, TextWidth, TextHeight, TextFont, StatsScale); DrawGameOverText(ScoreText, FVector2D((ViewportSize.X - TextWidth) * 0.5f, StatsY), StatsTextColor, StatsScale); // Draw restart text with bounce effect float BounceOffset = FMath::Sin(TimeSinceGameOver * 3.0f) * 5.0f; StatsY += Spacing * 1.2f; FString RestartText = TEXT("Press R to Restart"); GetTextSize(RestartText, TextWidth, TextHeight, TextFont, StatsScale); DrawGameOverText(RestartText, FVector2D((ViewportSize.X - TextWidth) * 0.5f, StatsY + BounceOffset), RestartTextColor, StatsScale); } ASpaceShooterGameMode* ASpaceShooterHUD::GetGameMode() const { return Cast(GetWorld()->GetAuthGameMode()); }