diff --git a/Content/Blueprints/BP_SpaceshipProjectile.uasset b/Content/Blueprints/BP_SpaceshipProjectile.uasset index 7602d82..bd3549c 100644 --- a/Content/Blueprints/BP_SpaceshipProjectile.uasset +++ b/Content/Blueprints/BP_SpaceshipProjectile.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ffdd0587cf11ff2ee82abbd7ded2e54afa6a639fa8732183a8562fbd2cba0c4 -size 32781 +oid sha256:74207b1b33c44d5561faa52028420879c4b9aeb0071e5871d68a2e41903fa141 +size 32752 diff --git a/Content/Blueprints/WBP_Crosshair.uasset b/Content/Blueprints/WBP_Crosshair.uasset new file mode 100644 index 0000000..fbe4bfa --- /dev/null +++ b/Content/Blueprints/WBP_Crosshair.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f985b27cbf728b8bd7e6e06cd96c807b3938a038f07d1ffdd9c3fdbb993e96d +size 18577 diff --git a/Content/EnvironmentAssests/8k_earth_clouds.uasset b/Content/EnvironmentAssests/8k_earth_clouds.uasset index add9a56..e0e533e 100644 --- a/Content/EnvironmentAssests/8k_earth_clouds.uasset +++ b/Content/EnvironmentAssests/8k_earth_clouds.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7251561d15f8c801a37c4aee15c545215aa749c6090c9b38a2c3b34b48861bf9 -size 11627424 +oid sha256:cfbe759af214a27e356eb2d6cfd6f0da729eeceb7e83fea31263c28b77766d71 +size 11627519 diff --git a/Content/EnvironmentAssests/8k_saturn.uasset b/Content/EnvironmentAssests/8k_saturn.uasset index 163e85d..75aa14d 100644 --- a/Content/EnvironmentAssests/8k_saturn.uasset +++ b/Content/EnvironmentAssests/8k_saturn.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5680d57ff56a3820ba84c5c68c79bdcc0b3abd4f7c69f303be3277c987fcaeb1 -size 1103833 +oid sha256:c2745e01e83aca3361c86f653f801d94af24915275e7db408de2f45acb18e64b +size 1103928 diff --git a/Content/EnvironmentAssests/8k_saturn_ring_alpha.uasset b/Content/EnvironmentAssests/8k_saturn_ring_alpha.uasset index 2b8819d..2d56c83 100644 --- a/Content/EnvironmentAssests/8k_saturn_ring_alpha.uasset +++ b/Content/EnvironmentAssests/8k_saturn_ring_alpha.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:574702809915b9d9b8b555accaf705f95ee2221d76f4c64ff31a24be2dec35b3 -size 73141 +oid sha256:6c01837c503514eeecdfab3c356b2e8974c6cbf974245ce34c4e187c4e14a7ef +size 73236 diff --git a/Content/EnvironmentAssests/8k_stars_milky_way.uasset b/Content/EnvironmentAssests/8k_stars_milky_way.uasset index 29b7152..26f0047 100644 --- a/Content/EnvironmentAssests/8k_stars_milky_way.uasset +++ b/Content/EnvironmentAssests/8k_stars_milky_way.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df505070f92055636af146d866ea6963ef59d9361c517478d312c7ae6ed4c969 -size 1910453 +oid sha256:9ad9800ef12bc683835f3ab46cae816e87c6bfa6837bf11e5bb9899634ba6404 +size 1910548 diff --git a/Content/EnvironmentLevel.umap b/Content/EnvironmentLevel.umap index b591f53..a2c4ce9 100644 --- a/Content/EnvironmentLevel.umap +++ b/Content/EnvironmentLevel.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c80f0011c90fe0631aa632470dbd576a265cabd8fe3476c01b58029c963bc39 +oid sha256:883cb8dfffa0306646e95e876235fa954a1d674eb26696d39e2e5b092cca47b9 size 751444 diff --git a/Content/Input/IA_Pause.uasset b/Content/Input/IA_Pause.uasset new file mode 100644 index 0000000..9385a86 --- /dev/null +++ b/Content/Input/IA_Pause.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eba7de52786a7093098b6d02689acdc20467d52c3ac32e1aebd5ccc3487a522d +size 1893 diff --git a/Content/Main.umap b/Content/Main.umap index bc698e8..56495fb 100644 --- a/Content/Main.umap +++ b/Content/Main.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba38cd9ef21e908abb1cda81719c5a5136b7004b72d9b8ddcbdc65960611d0cf -size 72074 +oid sha256:87546dfde9836da19f987e46383c1a97ac03c48a765968adf11f80f7c9e8eada +size 70812 diff --git a/Content/Meshes/Shape_Sphere.uasset b/Content/Meshes/Shape_Sphere.uasset new file mode 100644 index 0000000..b7a6767 --- /dev/null +++ b/Content/Meshes/Shape_Sphere.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd3392df5e6c73f28b6112308eed9e02d862e1442d37c2964c2f33f3f21f17d1 +size 38434 diff --git a/Source/MyProject3/MyProject3.Build.cs b/Source/MyProject3/MyProject3.Build.cs index 6dcebf8..aeea79f 100644 --- a/Source/MyProject3/MyProject3.Build.cs +++ b/Source/MyProject3/MyProject3.Build.cs @@ -10,7 +10,7 @@ public class MyProject3 : ModuleRules PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); - PrivateDependencyModuleNames.AddRange(new string[] { "EnhancedInput" }); + PrivateDependencyModuleNames.AddRange(new string[] { "EnhancedInput", "UMG" }); // Uncomment if you are using Slate UI // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); diff --git a/Source/MyProject3/SpaceShooterGameMode.cpp b/Source/MyProject3/SpaceShooterGameMode.cpp index 2a5a1ab..88a71f1 100644 --- a/Source/MyProject3/SpaceShooterGameMode.cpp +++ b/Source/MyProject3/SpaceShooterGameMode.cpp @@ -2,6 +2,8 @@ #include "SpaceshipPawn.h" #include "EnemySpaceship.h" #include "Kismet/GameplayStatics.h" +#include "Engine/World.h" +#include "GameFramework/PlayerController.h" ASpaceShooterGameMode::ASpaceShooterGameMode() { @@ -22,6 +24,18 @@ ASpaceShooterGameMode::ASpaceShooterGameMode() // Enable Tick() PrimaryActorTick.bCanEverTick = true; + // Initialize spawn patterns + SpawnPatterns.Add(ESpawnPattern::Random); + SpawnPatterns.Add(ESpawnPattern::Wave); + SpawnPatterns.Add(ESpawnPattern::Formation); + SpawnPatterns.Add(ESpawnPattern::Flanking); + + // Set default values + CurrentSpawnInterval = BaseEnemySpawnInterval; + CurrentPattern = ESpawnPattern::Random; + CurrentWaveCount = 0; + GameTime = 0.0f; + // Debug message if (GEngine) { @@ -35,7 +49,11 @@ void ASpaceShooterGameMode::StartPlay() // Start spawning enemies GetWorldTimerManager().SetTimer(EnemySpawnTimer, this, &ASpaceShooterGameMode::SpawnEnemy, - EnemySpawnInterval, true); + CurrentSpawnInterval, true); + + // Start difficulty scaling + GetWorldTimerManager().SetTimer(DifficultyTimer, this, &ASpaceShooterGameMode::UpdateDifficulty, + DifficultyInterval, true); // Debug message if (GEngine) @@ -47,6 +65,26 @@ void ASpaceShooterGameMode::StartPlay() void ASpaceShooterGameMode::Tick(float DeltaTime) { Super::Tick(DeltaTime); + GameTime += DeltaTime; + + // Potentially change spawn pattern based on game time + if (FMath::RandRange(0.0f, 1.0f) < 0.001f) + { + CurrentPattern = SpawnPatterns[FMath::RandRange(0, SpawnPatterns.Num() - 1)]; + if (GEngine) + { + FString PatternName; + switch (CurrentPattern) + { + case ESpawnPattern::Random: PatternName = TEXT("Random"); break; + case ESpawnPattern::Wave: PatternName = TEXT("Wave"); break; + case ESpawnPattern::Formation: PatternName = TEXT("Formation"); break; + case ESpawnPattern::Flanking: PatternName = TEXT("Flanking"); break; + } + GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Cyan, + FString::Printf(TEXT("Spawn Pattern Changed: %s"), *PatternName)); + } + } } void ASpaceShooterGameMode::SpawnEnemy() @@ -58,42 +96,412 @@ void ASpaceShooterGameMode::SpawnEnemy() // Only spawn if we haven't reached the maximum if (FoundEnemies.Num() < MaxEnemies) { - UWorld* World = GetWorld(); - if (World && EnemyClass) + switch (CurrentPattern) { - FVector SpawnLocation = GetRandomSpawnLocation(); - FRotator SpawnRotation = FRotator::ZeroRotator; - FActorSpawnParameters SpawnParams; - SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; + case ESpawnPattern::Random: + // Spawn a single enemy at a random edge location + { + UWorld* World = GetWorld(); + if (World && EnemyClass) + { + FVector SpawnLocation = GetScreenEdgeSpawnLocation(); + FRotator SpawnRotation = FRotator::ZeroRotator; + FActorSpawnParameters SpawnParams; + SpawnParams.SpawnCollisionHandlingOverride = + ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; - // Spawn using the Blueprint class instead of the C++ class directly - AEnemySpaceship* NewEnemy = World->SpawnActor(EnemyClass, SpawnLocation, - SpawnRotation, SpawnParams); + AEnemySpaceship* NewEnemy = World->SpawnActor( + EnemyClass, SpawnLocation, SpawnRotation, SpawnParams); + + if (NewEnemy) + { + RotateTowardsPlayer(NewEnemy, GetPlayerLocation()); + } + } + } + break; + + case ESpawnPattern::Wave: + SpawnEnemyWave(); + break; + + case ESpawnPattern::Formation: + SpawnEnemyFormation(); + break; + + case ESpawnPattern::Flanking: + SpawnEnemyFlanking(); + break; } } } -FVector ASpaceShooterGameMode::GetRandomSpawnLocation() +void ASpaceShooterGameMode::SpawnEnemyWave() +{ + UWorld* World = GetWorld(); + if (!World || !EnemyClass) + return; + + // Choose a random direction for the wave + float WaveAngle = FMath::RandRange(0.0f, 2.0f * PI); + FVector2D EdgeDirection(FMath::Cos(WaveAngle), FMath::Sin(WaveAngle)); + + // Get player location for facing direction + FVector PlayerLocation = GetPlayerLocation(); + + // Get screen bounds + TArray ScreenBounds = GetScreenBounds(); + float ScreenWidth = ScreenBounds[1].X - ScreenBounds[0].X; + + // Create a line of enemies perpendicular to the direction + FVector2D PerpDirection(-EdgeDirection.Y, EdgeDirection.X); + + // Spawn wave of enemies + for (int32 i = 0; i < WaveSize; i++) + { + FVector SpawnLocation; + SpawnLocation.X = PlayerLocation.X + (EdgeDirection.X * 2000.0f) + + (PerpDirection.X * (i - WaveSize / 2) * FormationSpacing); + SpawnLocation.Y = PlayerLocation.Y + (EdgeDirection.Y * 2000.0f) + + (PerpDirection.Y * (i - WaveSize / 2) * FormationSpacing); + SpawnLocation.Z = PlayerLocation.Z; + + FRotator SpawnRotation = FRotator::ZeroRotator; + FActorSpawnParameters SpawnParams; + SpawnParams.SpawnCollisionHandlingOverride = + ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; + + AEnemySpaceship* NewEnemy = World->SpawnActor( + EnemyClass, SpawnLocation, SpawnRotation, SpawnParams); + + if (NewEnemy) + { + RotateTowardsPlayer(NewEnemy, PlayerLocation); + } + } + + // Increase wave counter and possibly switch back to random + CurrentWaveCount++; + if (CurrentWaveCount >= 3) + { + CurrentPattern = ESpawnPattern::Random; + CurrentWaveCount = 0; + } +} + +void ASpaceShooterGameMode::SpawnEnemyFormation() +{ + UWorld* World = GetWorld(); + if (!World || !EnemyClass) + return; + + // Get player location + FVector PlayerLocation = GetPlayerLocation(); + + // Select a formation type (0 = V-formation, 1 = Line, 2 = Diamond) + int32 FormationType = FMath::RandRange(0, 2); + + // Choose a random approach angle + float ApproachAngle = FMath::RandRange(0.0f, 2.0f * PI); + FVector2D ApproachDir(FMath::Cos(ApproachAngle), FMath::Sin(ApproachAngle)); + + // Base spawn position far from player + FVector BaseSpawnPos = PlayerLocation + FVector(ApproachDir.X, ApproachDir.Y, 0) * 2500.0f; + + // Create formation positions + TArray FormationPositions; + + switch (FormationType) + { + case 0: // V-formation + for (int32 i = 0; i < 5; i++) + { + if (i == 0) // Leader + { + FormationPositions.Add(BaseSpawnPos); + } + else if (i % 2 == 1) // Left wing + { + FVector Offset(-ApproachDir.Y, ApproachDir.X, 0); + FormationPositions.Add(BaseSpawnPos + Offset * FormationSpacing * ((i + 1) / 2)); + } + else // Right wing + { + FVector Offset(ApproachDir.Y, -ApproachDir.X, 0); + FormationPositions.Add(BaseSpawnPos + Offset * FormationSpacing * (i / 2)); + } + } + break; + + case 1: // Line formation + for (int32 i = 0; i < 5; i++) + { + FVector2D PerpDir(-ApproachDir.Y, ApproachDir.X); + FVector Offset(PerpDir.X, PerpDir.Y, 0); + FormationPositions.Add(BaseSpawnPos + Offset * FormationSpacing * (i - 2)); + } + break; + + case 2: // Diamond formation + FormationPositions.Add(BaseSpawnPos); // Center + + FVector2D PerpDir(-ApproachDir.Y, ApproachDir.X); + + // Top + FormationPositions.Add(BaseSpawnPos + FVector(ApproachDir.X, ApproachDir.Y, 0) * FormationSpacing); + // Bottom + FormationPositions.Add(BaseSpawnPos - FVector(ApproachDir.X, ApproachDir.Y, 0) * FormationSpacing); + // Left + FormationPositions.Add(BaseSpawnPos + FVector(PerpDir.X, PerpDir.Y, 0) * FormationSpacing); + // Right + FormationPositions.Add(BaseSpawnPos - FVector(PerpDir.X, PerpDir.Y, 0) * FormationSpacing); + break; + } + + // Spawn enemies at formation positions + for (const FVector& Position : FormationPositions) + { + FRotator SpawnRotation = FRotator::ZeroRotator; + FActorSpawnParameters SpawnParams; + SpawnParams.SpawnCollisionHandlingOverride = + ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; + + AEnemySpaceship* NewEnemy = World->SpawnActor( + EnemyClass, Position, SpawnRotation, SpawnParams); + + if (NewEnemy) + { + RotateTowardsPlayer(NewEnemy, PlayerLocation); + } + } + + // Switch back to random pattern after a formation spawn + CurrentPattern = ESpawnPattern::Random; +} + +void ASpaceShooterGameMode::SpawnEnemyFlanking() +{ + UWorld* World = GetWorld(); + if (!World || !EnemyClass) + return; + + // Get player location + FVector PlayerLocation = GetPlayerLocation(); + + // Spawn enemies from multiple sides (usually 2-3 sides) + int32 NumSides = FMath::RandRange(2, 3); + float BaseAngle = FMath::RandRange(0.0f, 2.0f * PI); + + for (int32 Side = 0; Side < NumSides; Side++) + { + // Calculate angle for this side + float Angle = BaseAngle + (Side * (2.0f * PI / NumSides)); + FVector2D Direction(FMath::Cos(Angle), FMath::Sin(Angle)); + + // Spawn 1-2 enemies from this side + int32 NumEnemies = FMath::RandRange(1, 2); + + for (int32 i = 0; i < NumEnemies; i++) + { + // Add some variation to the spawn position + float OffsetAngle = Angle + FMath::RandRange(-0.3f, 0.3f); + FVector2D OffsetDir(FMath::Cos(OffsetAngle), FMath::Sin(OffsetAngle)); + + FVector SpawnLocation = PlayerLocation + FVector(OffsetDir.X, OffsetDir.Y, 0) * 2000.0f; + + FRotator SpawnRotation = FRotator::ZeroRotator; + FActorSpawnParameters SpawnParams; + SpawnParams.SpawnCollisionHandlingOverride = + ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; + + AEnemySpaceship* NewEnemy = World->SpawnActor( + EnemyClass, SpawnLocation, SpawnRotation, SpawnParams); + + if (NewEnemy) + { + RotateTowardsPlayer(NewEnemy, PlayerLocation); + } + } + } + + // Return to random spawning + CurrentPattern = ESpawnPattern::Random; +} + +FVector ASpaceShooterGameMode::GetScreenEdgeSpawnLocation() +{ + FVector PlayerLocation = GetPlayerLocation(); + TArray ScreenBounds = GetScreenBounds(); + + // Decide which edge to spawn from (0 = top, 1 = right, 2 = bottom, 3 = left) + int32 Edge = FMath::RandRange(0, 3); + + FVector SpawnLocation; + float RandomPos; + + switch (Edge) + { + case 0: // Top edge + RandomPos = FMath::RandRange(ScreenBounds[0].X, ScreenBounds[1].X); + SpawnLocation = FVector(RandomPos, ScreenBounds[0].Y - ScreenSpawnMargin, PlayerLocation.Z); + break; + + case 1: // Right edge + RandomPos = FMath::RandRange(ScreenBounds[0].Y, ScreenBounds[1].Y); + SpawnLocation = FVector(ScreenBounds[1].X + ScreenSpawnMargin, RandomPos, PlayerLocation.Z); + break; + + case 2: // Bottom edge + RandomPos = FMath::RandRange(ScreenBounds[0].X, ScreenBounds[1].X); + SpawnLocation = FVector(RandomPos, ScreenBounds[1].Y + ScreenSpawnMargin, PlayerLocation.Z); + break; + + case 3: // Left edge + RandomPos = FMath::RandRange(ScreenBounds[0].Y, ScreenBounds[1].Y); + SpawnLocation = FVector(ScreenBounds[0].X - ScreenSpawnMargin, RandomPos, PlayerLocation.Z); + break; + } + + return SpawnLocation; +} + +FVector ASpaceShooterGameMode::GetSpawnZoneLocation() +{ + // If no spawn zones are defined, return a screen edge location + if (SpawnZones.Num() == 0) + { + return GetScreenEdgeSpawnLocation(); + } + + // Filter active spawn zones + TArray ActiveZones; + float TotalWeight = 0.0f; + + for (const FSpawnZone& Zone : SpawnZones) + { + if (Zone.bActive) + { + ActiveZones.Add(Zone); + TotalWeight += Zone.SpawnWeight; + } + } + + // If no active zones, return screen edge + if (ActiveZones.Num() == 0) + { + return GetScreenEdgeSpawnLocation(); + } + + // Select a zone based on weight + float RandomWeight = FMath::RandRange(0.0f, TotalWeight); + float WeightSum = 0.0f; + + for (const FSpawnZone& Zone : ActiveZones) + { + WeightSum += Zone.SpawnWeight; + if (RandomWeight <= WeightSum) + { + // Generate random point within this zone's radius + float RandomAngle = FMath::RandRange(0.0f, 2.0f * PI); + float RandomRadius = FMath::RandRange(0.0f, Zone.Radius); + + FVector SpawnOffset( + FMath::Cos(RandomAngle) * RandomRadius, + FMath::Sin(RandomAngle) * RandomRadius, + 0.0f + ); + + return Zone.Location + SpawnOffset; + } + } + + // Fallback + return GetScreenEdgeSpawnLocation(); +} + +void ASpaceShooterGameMode::UpdateDifficulty() +{ + // Make the game harder over time by decreasing spawn interval + CurrentSpawnInterval = FMath::Max(0.5f, BaseEnemySpawnInterval * FMath::Pow(DifficultyScaling, + GameTime / DifficultyInterval)); + + // Update the timer + GetWorldTimerManager().SetTimer(EnemySpawnTimer, this, &ASpaceShooterGameMode::SpawnEnemy, + CurrentSpawnInterval, true); + + // Debug message + if (GEngine) + { + GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Yellow, + FString::Printf(TEXT("Difficulty Updated: Spawn Interval = %.2f"), CurrentSpawnInterval)); + } +} + +FVector ASpaceShooterGameMode::GetPlayerLocation() { APlayerController* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0); if (PlayerController && PlayerController->GetPawn()) { - FVector PlayerLocation = PlayerController->GetPawn()->GetActorLocation(); + return PlayerController->GetPawn()->GetActorLocation(); + } + return FVector::ZeroVector; +} - // Generate random angle - float Angle = FMath::RandRange(0.0f, 2.0f * PI); +TArray ASpaceShooterGameMode::GetScreenBounds() +{ + TArray Bounds; + FVector2D ScreenMin, ScreenMax; - // Generate random radius between min and max - float Radius = FMath::RandRange(MinSpawnRadius, MaxSpawnRadius); - - // Calculate spawn position in a circle around the player - FVector SpawnLocation; - SpawnLocation.X = PlayerLocation.X + Radius * FMath::Cos(Angle); - SpawnLocation.Y = PlayerLocation.Y + Radius * FMath::Sin(Angle); - SpawnLocation.Z = PlayerLocation.Z; - - return SpawnLocation; + // Get player controller for screen info + APlayerController* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0); + if (!PlayerController) + { + // Fallback values if no controller + Bounds.Add(FVector2D(-2000, -2000)); + Bounds.Add(FVector2D(2000, 2000)); + return Bounds; } - return FVector::ZeroVector; + // Get viewport size + int32 ViewportSizeX, ViewportSizeY; + PlayerController->GetViewportSize(ViewportSizeX, ViewportSizeY); + + // Get world location of screen corners + FVector WorldLocation, WorldDirection; + + // Top-Left corner + PlayerController->DeprojectScreenPositionToWorld(0, 0, WorldLocation, WorldDirection); + ScreenMin = FVector2D(WorldLocation.X, WorldLocation.Y); + + // Bottom-Right corner + PlayerController->DeprojectScreenPositionToWorld(ViewportSizeX, ViewportSizeY, WorldLocation, WorldDirection); + ScreenMax = FVector2D(WorldLocation.X, WorldLocation.Y); + + // Add some margin + ScreenMin.X -= 200; + ScreenMin.Y -= 200; + ScreenMax.X += 200; + ScreenMax.Y += 200; + + Bounds.Add(ScreenMin); + Bounds.Add(ScreenMax); + + return Bounds; +} + +void ASpaceShooterGameMode::RotateTowardsPlayer(AEnemySpaceship* Enemy, const FVector& PlayerLocation) +{ + if (!Enemy) + return; + + // Calculate direction to player + FVector Direction = PlayerLocation - Enemy->GetActorLocation(); + Direction.Z = 0; // Keep rotation in 2D plane + Direction.Normalize(); + + // Convert to rotation + FRotator NewRotation = Direction.Rotation(); + + // Set the enemy's rotation + Enemy->SetActorRotation(NewRotation); } \ No newline at end of file diff --git a/Source/MyProject3/SpaceShooterGameMode.h b/Source/MyProject3/SpaceShooterGameMode.h index 12f3f05..cca9724 100644 --- a/Source/MyProject3/SpaceShooterGameMode.h +++ b/Source/MyProject3/SpaceShooterGameMode.h @@ -4,6 +4,35 @@ #include "GameFramework/GameModeBase.h" #include "SpaceShooterGameMode.generated.h" +// Enum for different spawn patterns +UENUM(BlueprintType) +enum class ESpawnPattern : uint8 +{ + Random, // Randomly at screen edges + Wave, // Groups of enemies from one direction + Formation, // Specific formations (V-shape, line, etc.) + Flanking // Enemies from multiple sides simultaneously +}; + +// Struct to define a spawn point/zone +USTRUCT(BlueprintType) +struct FSpawnZone +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FVector Location; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float Radius = 100.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float SpawnWeight = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + bool bActive = true; +}; + UCLASS() class MYPROJECT3_API ASpaceShooterGameMode : public AGameModeBase { @@ -20,19 +49,48 @@ protected: TSubclassOf EnemyClass; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning") - float EnemySpawnInterval = 2.0f; + float BaseEnemySpawnInterval = 2.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning") int32 MaxEnemies = 10; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning") - float MinSpawnRadius = 1000.0f; + float ScreenSpawnMargin = 100.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning") - float MaxSpawnRadius = 2000.0f; + TArray SpawnZones; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning") + TArray SpawnPatterns; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning") + int32 WaveSize = 3; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning") + float FormationSpacing = 150.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning|Difficulty") + float DifficultyScaling = 0.95f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning|Difficulty") + int32 DifficultyInterval = 30; private: FTimerHandle EnemySpawnTimer; + FTimerHandle DifficultyTimer; + float CurrentSpawnInterval; + ESpawnPattern CurrentPattern; + int32 CurrentWaveCount; + float GameTime; + void SpawnEnemy(); - FVector GetRandomSpawnLocation(); + void SpawnEnemyWave(); + void SpawnEnemyFormation(); + void SpawnEnemyFlanking(); + FVector GetScreenEdgeSpawnLocation(); + FVector GetSpawnZoneLocation(); + void UpdateDifficulty(); + FVector GetPlayerLocation(); + TArray GetScreenBounds(); + void RotateTowardsPlayer(AEnemySpaceship* Enemy, const FVector& PlayerLocation); }; \ No newline at end of file diff --git a/Source/MyProject3/SpaceshipPawn.cpp b/Source/MyProject3/SpaceshipPawn.cpp index 8221a24..9690b11 100644 --- a/Source/MyProject3/SpaceshipPawn.cpp +++ b/Source/MyProject3/SpaceshipPawn.cpp @@ -7,6 +7,7 @@ #include "SpaceshipProjectile.h" #include "Kismet/GameplayStatics.h" #include "GameFramework/GameUserSettings.h" +#include "Blueprint/UserWidget.h" ASpaceshipPawn::ASpaceshipPawn() @@ -65,25 +66,6 @@ void ASpaceshipPawn::BeginPlay() { Super::BeginPlay(); - // Debug messages for setup verification - if (GEngine) - { - if (ProjectileClass) - GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("ProjectileClass is set")); - else - GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("ProjectileClass is NOT set")); - - if (ShootAction) - GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("ShootAction is set")); - else - GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("ShootAction is NOT set")); - - if (ProjectileSpawnPoint) - GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("ProjectileSpawnPoint is set")); - else - GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("ProjectileSpawnPoint is NOT set")); - } - // Store player controller reference PlayerControllerRef = Cast(Controller); @@ -108,6 +90,20 @@ void ASpaceshipPawn::BeginPlay() GameUserSettings->ApplySettings(false); } } + + // Create and add crosshair widget to viewport + if (CrosshairWidgetClass) + { + APlayerController* PlayerController = Cast(GetController()); + if (PlayerController) + { + CrosshairWidget = CreateWidget(PlayerController, CrosshairWidgetClass); + if (CrosshairWidget) + { + CrosshairWidget->AddToViewport(); + } + } + } } void ASpaceshipPawn::Tick(float DeltaTime) diff --git a/Source/MyProject3/SpaceshipPawn.h b/Source/MyProject3/SpaceshipPawn.h index a6adaf2..6922c78 100644 --- a/Source/MyProject3/SpaceshipPawn.h +++ b/Source/MyProject3/SpaceshipPawn.h @@ -3,6 +3,7 @@ #include "CoreMinimal.h" #include "GameFramework/Pawn.h" #include "InputActionValue.h" +#include "Blueprint/UserWidget.h" #include "SpaceshipPawn.generated.h" UCLASS() @@ -78,6 +79,12 @@ protected: UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input") class UInputAction* ShootAction; + UPROPERTY(EditDefaultsOnly, Category = "UI") + TSubclassOf CrosshairWidgetClass; + + UPROPERTY() + UUserWidget* CrosshairWidget; + // Input functions void HandleThrottleStarted(const FInputActionValue& Value); void HandleThrottleReleased(const FInputActionValue& Value);