From f732cdef5980a71f0d9e8aac86d0abe15980c0e9 Mon Sep 17 00:00:00 2001 From: aicorr Date: Wed, 16 Apr 2025 12:54:22 +0530 Subject: [PATCH] Initial HUD implementation and timer + game over --- .../Blueprints/BP_SpaceShooterGameMode.uasset | 4 +- Content/Blueprints/BP_SpaceShooterHUD.uasset | 3 + Source/MyProject3/EnemySpaceship.cpp | 8 +- Source/MyProject3/SpaceShooterGameMode.cpp | 51 ++++++ Source/MyProject3/SpaceShooterGameMode.h | 22 +++ Source/MyProject3/SpaceShooterHUD.cpp | 153 ++++++++++++++++++ Source/MyProject3/SpaceShooterHUD.h | 45 ++++++ Source/MyProject3/SpaceshipPawn.h | 20 ++- 8 files changed, 298 insertions(+), 8 deletions(-) create mode 100644 Content/Blueprints/BP_SpaceShooterHUD.uasset create mode 100644 Source/MyProject3/SpaceShooterHUD.cpp create mode 100644 Source/MyProject3/SpaceShooterHUD.h diff --git a/Content/Blueprints/BP_SpaceShooterGameMode.uasset b/Content/Blueprints/BP_SpaceShooterGameMode.uasset index ec25c37..6d16140 100644 --- a/Content/Blueprints/BP_SpaceShooterGameMode.uasset +++ b/Content/Blueprints/BP_SpaceShooterGameMode.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1ee6edc9f63cf69d3f0449ed485fbe473f0165ef00498861d19387a831f08fb -size 20146 +oid sha256:e2a6d951c7d7409fdd49634062524570b4b4e3b2428740a65e5b9348bd0f995c +size 20403 diff --git a/Content/Blueprints/BP_SpaceShooterHUD.uasset b/Content/Blueprints/BP_SpaceShooterHUD.uasset new file mode 100644 index 0000000..996664d --- /dev/null +++ b/Content/Blueprints/BP_SpaceShooterHUD.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:83266819489ade302d6ea91bc5a5a0d45461cfbe04386def5f1a98bd24740ac0 +size 21795 diff --git a/Source/MyProject3/EnemySpaceship.cpp b/Source/MyProject3/EnemySpaceship.cpp index 4817a28..eb8b83e 100644 --- a/Source/MyProject3/EnemySpaceship.cpp +++ b/Source/MyProject3/EnemySpaceship.cpp @@ -6,8 +6,7 @@ #include "DrawDebugHelpers.h" #include "Math/UnrealMathUtility.h" #include "EnemyProjectile.h" - -// Include SpaceshipPawn to access player-specific functionality +#include "SpaceShooterGameMode.h" #include "SpaceshipPawn.h" AEnemySpaceship::AEnemySpaceship() @@ -559,6 +558,11 @@ void AEnemySpaceship::Die() GetActorLocation() ); + if (ASpaceShooterGameMode* GameMode = Cast(UGameplayStatics::GetGameMode(GetWorld()))) + { + GameMode->IncrementKillCount(); + } + // Destroy the enemy Destroy(); } \ No newline at end of file diff --git a/Source/MyProject3/SpaceShooterGameMode.cpp b/Source/MyProject3/SpaceShooterGameMode.cpp index 865c9a0..3c01416 100644 --- a/Source/MyProject3/SpaceShooterGameMode.cpp +++ b/Source/MyProject3/SpaceShooterGameMode.cpp @@ -41,6 +41,10 @@ ASpaceShooterGameMode::ASpaceShooterGameMode() { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("GameMode Constructor")); } + + KillCount = 0; + bIsGameOver = false; + RemainingTime = GameDuration; } void ASpaceShooterGameMode::StartPlay() @@ -60,6 +64,9 @@ void ASpaceShooterGameMode::StartPlay() { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("GameMode StartPlay")); } + + RemainingTime = GameDuration; + GetWorldTimerManager().SetTimer(GameTimerHandle, this, &ASpaceShooterGameMode::UpdateGameTimer, 1.0f, true); } void ASpaceShooterGameMode::Tick(float DeltaTime) @@ -567,4 +574,48 @@ FVector ASpaceShooterGameMode::EnsureMinimumSpawnDistance(const FVector& Propose // Otherwise, extend the vector to meet the minimum distance Direction.Normalize(); return PlayerLocation + Direction * MinimumSpawnDistance; +} + +void ASpaceShooterGameMode::UpdateGameTimer() +{ + if (bIsGameOver) return; + + RemainingTime -= 1.0f; + + if (RemainingTime <= 0.0f) + { + EndGame(); + } +} + +void ASpaceShooterGameMode::EndGame() +{ + bIsGameOver = true; + + // Stop enemy spawning + GetWorldTimerManager().ClearTimer(EnemySpawnTimer); + + // Clear existing enemies + TArray FoundEnemies; + UGameplayStatics::GetAllActorsOfClass(GetWorld(), AEnemySpaceship::StaticClass(), FoundEnemies); + for (AActor* Enemy : FoundEnemies) + { + Enemy->Destroy(); + } + + // Pause the game + UGameplayStatics::SetGamePaused(GetWorld(), true); +} + +void ASpaceShooterGameMode::IncrementKillCount() +{ + if (!bIsGameOver) + { + KillCount++; + } +} + +void ASpaceShooterGameMode::RestartGame() +{ + UGameplayStatics::OpenLevel(this, FName(*GetWorld()->GetName()), false); } \ No newline at end of file diff --git a/Source/MyProject3/SpaceShooterGameMode.h b/Source/MyProject3/SpaceShooterGameMode.h index 2d703f1..96c7bcf 100644 --- a/Source/MyProject3/SpaceShooterGameMode.h +++ b/Source/MyProject3/SpaceShooterGameMode.h @@ -44,6 +44,12 @@ public: virtual void StartPlay() override; virtual void Tick(float DeltaTime) override; + void IncrementKillCount(); + void RestartGame(); + int32 GetKillCount() const { return KillCount; } + float GetRemainingTime() const { return RemainingTime; } + bool IsGameOver() const { return bIsGameOver; } + protected: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning") TSubclassOf EnemyClass; @@ -78,6 +84,18 @@ protected: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning|Difficulty") int32 DifficultyInterval = 30; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Game Rules") + float GameDuration = 30.0f; // 3 minutes default + + UPROPERTY(BlueprintReadOnly, Category = "Game Stats") + int32 KillCount; + + UPROPERTY(BlueprintReadOnly, Category = "Game Stats") + float RemainingTime; + + UPROPERTY(BlueprintReadOnly, Category = "Game State") + bool bIsGameOver; + private: FTimerHandle EnemySpawnTimer; FTimerHandle DifficultyTimer; @@ -99,4 +117,8 @@ private: // New helper method to ensure minimum spawn distance FVector EnsureMinimumSpawnDistance(const FVector& ProposedLocation, const FVector& PlayerLocation); + + void EndGame(); + void UpdateGameTimer(); + FTimerHandle GameTimerHandle; }; \ No newline at end of file diff --git a/Source/MyProject3/SpaceShooterHUD.cpp b/Source/MyProject3/SpaceShooterHUD.cpp new file mode 100644 index 0000000..0b3e67f --- /dev/null +++ b/Source/MyProject3/SpaceShooterHUD.cpp @@ -0,0 +1,153 @@ +#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; +} + +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::DrawGameOverScreen() +{ + ASpaceShooterGameMode* GameMode = GetGameMode(); + if (!GameMode) return; + + // Get canvas dimensions + const FVector2D ViewportSize(Canvas->SizeX, Canvas->SizeY); + + // Draw game over text + FString GameOverText = TEXT("GAME OVER"); + float TextWidth, TextHeight; + GetTextSize(GameOverText, TextWidth, TextHeight, TextFont); + + DrawText(GameOverText, FLinearColor::Red, + ViewportSize.X / 2 - TextWidth / 2, + ViewportSize.Y / 2 - TextHeight / 2, + TextFont, 2.0f); + + // Draw final score + FString ScoreText = FString::Printf(TEXT("Final Kill Count: %d"), GameMode->GetKillCount()); + GetTextSize(ScoreText, TextWidth, TextHeight, TextFont); + DrawText(ScoreText, FLinearColor::White, + ViewportSize.X / 2 - TextWidth / 2, + ViewportSize.Y / 2 + TextHeight * 2, + TextFont); + + // Draw restart instructions + FString RestartText = TEXT("Press R to Restart"); + GetTextSize(RestartText, TextWidth, TextHeight, TextFont); + DrawText(RestartText, FLinearColor::Yellow, + ViewportSize.X / 2 - TextWidth / 2, + ViewportSize.Y / 2 + TextHeight * 4, + TextFont); +} + +ASpaceShooterGameMode* ASpaceShooterHUD::GetGameMode() const +{ + return Cast(GetWorld()->GetAuthGameMode()); +} \ No newline at end of file diff --git a/Source/MyProject3/SpaceShooterHUD.h b/Source/MyProject3/SpaceShooterHUD.h new file mode 100644 index 0000000..abf7b24 --- /dev/null +++ b/Source/MyProject3/SpaceShooterHUD.h @@ -0,0 +1,45 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/HUD.h" +#include "SpaceShooterHUD.generated.h" + +UCLASS() +class MYPROJECT3_API ASpaceShooterHUD : public AHUD +{ + GENERATED_BODY() + +public: + ASpaceShooterHUD(); + virtual void DrawHUD() override; + +protected: + UPROPERTY() + UFont* TextFont; + + // Bar configuration + UPROPERTY(EditAnywhere, Category = "HUD|Bars") + float BarWidth = 200.0f; + + UPROPERTY(EditAnywhere, Category = "HUD|Bars") + float BarHeight = 20.0f; + + UPROPERTY(EditAnywhere, Category = "HUD|Bars") + float BarPadding = 10.0f; + + UPROPERTY(EditAnywhere, Category = "HUD|Bars") + FLinearColor HealthBarColor = FLinearColor(1.0f, 0.0f, 0.0f, 1.0f); // Red + + UPROPERTY(EditAnywhere, Category = "HUD|Bars") + FLinearColor ShieldBarColor = FLinearColor(0.0f, 0.5f, 1.0f, 1.0f); // Light Blue + + UPROPERTY(EditAnywhere, Category = "HUD|Bars") + FLinearColor BackgroundBarColor = FLinearColor(0.0f, 0.0f, 0.0f, 0.5f); // Semi-transparent black + +private: + void DrawGameplayHUD(); + void DrawGameOverScreen(); + void DrawStatusBars(); + class ASpaceShooterGameMode* GetGameMode() const; + class ASpaceshipPawn* GetPlayerPawn() const; +}; \ No newline at end of file diff --git a/Source/MyProject3/SpaceshipPawn.h b/Source/MyProject3/SpaceshipPawn.h index 2074378..7f6e1d1 100644 --- a/Source/MyProject3/SpaceshipPawn.h +++ b/Source/MyProject3/SpaceshipPawn.h @@ -37,6 +37,18 @@ public: UFUNCTION(BlueprintCallable, Category = "Combat") float GetShieldPercentage() const { return CurrentShield / MaxShield; } + UFUNCTION(BlueprintCallable, Category = "Stats") + float GetCurrentHealth() const { return CurrentHealth; } + + UFUNCTION(BlueprintCallable, Category = "Stats") + float GetMaxHealth() const { return MaxHealth; } + + UFUNCTION(BlueprintCallable, Category = "Stats") + float GetCurrentShield() const { return CurrentShield; } + + UFUNCTION(BlueprintCallable, Category = "Stats") + float GetMaxShield() const { return MaxShield; } + protected: virtual void BeginPlay() override; @@ -128,17 +140,17 @@ protected: void HandleMouseLook(const FInputActionValue& Value); // Health properties - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat|Stats") float MaxHealth = 100.0f; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat|Stats") float CurrentHealth = 100.0f; // Shield properties - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat|Stats") float MaxShield = 100.0f; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat|Stats") float CurrentShield = 100.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat")