Initial HUD implementation and timer + game over
This commit is contained in:
Binary file not shown.
BIN
Content/Blueprints/BP_SpaceShooterHUD.uasset
LFS
Normal file
BIN
Content/Blueprints/BP_SpaceShooterHUD.uasset
LFS
Normal file
Binary file not shown.
@@ -6,8 +6,7 @@
|
|||||||
#include "DrawDebugHelpers.h"
|
#include "DrawDebugHelpers.h"
|
||||||
#include "Math/UnrealMathUtility.h"
|
#include "Math/UnrealMathUtility.h"
|
||||||
#include "EnemyProjectile.h"
|
#include "EnemyProjectile.h"
|
||||||
|
#include "SpaceShooterGameMode.h"
|
||||||
// Include SpaceshipPawn to access player-specific functionality
|
|
||||||
#include "SpaceshipPawn.h"
|
#include "SpaceshipPawn.h"
|
||||||
|
|
||||||
AEnemySpaceship::AEnemySpaceship()
|
AEnemySpaceship::AEnemySpaceship()
|
||||||
@@ -559,6 +558,11 @@ void AEnemySpaceship::Die()
|
|||||||
GetActorLocation()
|
GetActorLocation()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (ASpaceShooterGameMode* GameMode = Cast<ASpaceShooterGameMode>(UGameplayStatics::GetGameMode(GetWorld())))
|
||||||
|
{
|
||||||
|
GameMode->IncrementKillCount();
|
||||||
|
}
|
||||||
|
|
||||||
// Destroy the enemy
|
// Destroy the enemy
|
||||||
Destroy();
|
Destroy();
|
||||||
}
|
}
|
||||||
@@ -41,6 +41,10 @@ ASpaceShooterGameMode::ASpaceShooterGameMode()
|
|||||||
{
|
{
|
||||||
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("GameMode Constructor"));
|
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("GameMode Constructor"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KillCount = 0;
|
||||||
|
bIsGameOver = false;
|
||||||
|
RemainingTime = GameDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ASpaceShooterGameMode::StartPlay()
|
void ASpaceShooterGameMode::StartPlay()
|
||||||
@@ -60,6 +64,9 @@ void ASpaceShooterGameMode::StartPlay()
|
|||||||
{
|
{
|
||||||
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("GameMode 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)
|
void ASpaceShooterGameMode::Tick(float DeltaTime)
|
||||||
@@ -568,3 +575,47 @@ FVector ASpaceShooterGameMode::EnsureMinimumSpawnDistance(const FVector& Propose
|
|||||||
Direction.Normalize();
|
Direction.Normalize();
|
||||||
return PlayerLocation + Direction * MinimumSpawnDistance;
|
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<AActor*> 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);
|
||||||
|
}
|
||||||
@@ -44,6 +44,12 @@ public:
|
|||||||
virtual void StartPlay() override;
|
virtual void StartPlay() override;
|
||||||
virtual void Tick(float DeltaTime) 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:
|
protected:
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning")
|
||||||
TSubclassOf<class AEnemySpaceship> EnemyClass;
|
TSubclassOf<class AEnemySpaceship> EnemyClass;
|
||||||
@@ -78,6 +84,18 @@ protected:
|
|||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning|Difficulty")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning|Difficulty")
|
||||||
int32 DifficultyInterval = 30;
|
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:
|
private:
|
||||||
FTimerHandle EnemySpawnTimer;
|
FTimerHandle EnemySpawnTimer;
|
||||||
FTimerHandle DifficultyTimer;
|
FTimerHandle DifficultyTimer;
|
||||||
@@ -99,4 +117,8 @@ private:
|
|||||||
|
|
||||||
// New helper method to ensure minimum spawn distance
|
// New helper method to ensure minimum spawn distance
|
||||||
FVector EnsureMinimumSpawnDistance(const FVector& ProposedLocation, const FVector& PlayerLocation);
|
FVector EnsureMinimumSpawnDistance(const FVector& ProposedLocation, const FVector& PlayerLocation);
|
||||||
|
|
||||||
|
void EndGame();
|
||||||
|
void UpdateGameTimer();
|
||||||
|
FTimerHandle GameTimerHandle;
|
||||||
};
|
};
|
||||||
153
Source/MyProject3/SpaceShooterHUD.cpp
Normal file
153
Source/MyProject3/SpaceShooterHUD.cpp
Normal file
@@ -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<UFont> FontObj(TEXT("/Engine/EngineFonts/RobotoDistanceField"));
|
||||||
|
TextFont = FontObj.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASpaceshipPawn* ASpaceShooterHUD::GetPlayerPawn() const
|
||||||
|
{
|
||||||
|
APlayerController* PC = GetWorld()->GetFirstPlayerController();
|
||||||
|
if (PC)
|
||||||
|
{
|
||||||
|
return Cast<ASpaceshipPawn>(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<ASpaceShooterGameMode>(GetWorld()->GetAuthGameMode());
|
||||||
|
}
|
||||||
45
Source/MyProject3/SpaceShooterHUD.h
Normal file
45
Source/MyProject3/SpaceShooterHUD.h
Normal file
@@ -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;
|
||||||
|
};
|
||||||
@@ -37,6 +37,18 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable, Category = "Combat")
|
UFUNCTION(BlueprintCallable, Category = "Combat")
|
||||||
float GetShieldPercentage() const { return CurrentShield / MaxShield; }
|
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:
|
protected:
|
||||||
virtual void BeginPlay() override;
|
virtual void BeginPlay() override;
|
||||||
|
|
||||||
@@ -128,17 +140,17 @@ protected:
|
|||||||
void HandleMouseLook(const FInputActionValue& Value);
|
void HandleMouseLook(const FInputActionValue& Value);
|
||||||
|
|
||||||
// Health properties
|
// Health properties
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat|Stats")
|
||||||
float MaxHealth = 100.0f;
|
float MaxHealth = 100.0f;
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat|Stats")
|
||||||
float CurrentHealth = 100.0f;
|
float CurrentHealth = 100.0f;
|
||||||
|
|
||||||
// Shield properties
|
// Shield properties
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat|Stats")
|
||||||
float MaxShield = 100.0f;
|
float MaxShield = 100.0f;
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat|Stats")
|
||||||
float CurrentShield = 100.0f;
|
float CurrentShield = 100.0f;
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat")
|
||||||
|
|||||||
Reference in New Issue
Block a user