diff --git a/Config/DefaultInput.ini b/Config/DefaultInput.ini index c44327e..b4160e7 100755 --- a/Config/DefaultInput.ini +++ b/Config/DefaultInput.ini @@ -94,7 +94,7 @@ DoubleClickTime=0.200000 +ActionMappings=(ActionName="Shoot",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=LeftMouseButton) +ActionMappings=(ActionName="Shoot",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Gamepad_RightTrigger) +ActionMappings=(ActionName="Boost",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=RightMouseButton) -+ActionMappings=(ActionName="Boost",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Gamepad_RightShoulder) ++ActionMappings=(ActionName="Boost",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Gamepad_LeftTrigger) +AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=W) +AxisMappings=(AxisName="MoveForward",Scale=-1.000000,Key=S) +AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=D) diff --git a/Content/EnemyMarker.uasset b/Content/EnemyMarker.uasset new file mode 100755 index 0000000..3d696ce Binary files /dev/null and b/Content/EnemyMarker.uasset differ diff --git a/Content/EnemyPawn_BP.uasset b/Content/EnemyPawn_BP.uasset index 90dd6ed..a4d1d94 100755 Binary files a/Content/EnemyPawn_BP.uasset and b/Content/EnemyPawn_BP.uasset differ diff --git a/Content/EnemySpawner_BP.uasset b/Content/EnemySpawner_BP.uasset index 88cea4a..f28ad43 100755 Binary files a/Content/EnemySpawner_BP.uasset and b/Content/EnemySpawner_BP.uasset differ diff --git a/Content/GameModeBase_BP.uasset b/Content/GameModeBase_BP.uasset index ac8006b..0c52d3e 100755 Binary files a/Content/GameModeBase_BP.uasset and b/Content/GameModeBase_BP.uasset differ diff --git a/Content/PlayerPawn_BP.uasset b/Content/PlayerPawn_BP.uasset index 149e9f8..779c9fd 100755 Binary files a/Content/PlayerPawn_BP.uasset and b/Content/PlayerPawn_BP.uasset differ diff --git a/Content/TestMap.umap b/Content/TestMap.umap index d2e8aa4..c20e8b0 100755 Binary files a/Content/TestMap.umap and b/Content/TestMap.umap differ diff --git a/Content/TestMap_BuiltData.uasset b/Content/TestMap_BuiltData.uasset index c8553e3..f864527 100755 Binary files a/Content/TestMap_BuiltData.uasset and b/Content/TestMap_BuiltData.uasset differ diff --git a/Source/PresidentsBrigade/EnemySpawner.cpp b/Source/PresidentsBrigade/EnemySpawner.cpp index 3af7ed2..47d2f20 100755 --- a/Source/PresidentsBrigade/EnemySpawner.cpp +++ b/Source/PresidentsBrigade/EnemySpawner.cpp @@ -9,7 +9,10 @@ AEnemySpawner::AEnemySpawner() // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; + level_location = TUniquePtr(new LevelLocation()); + spawn_delay_s = 5.0f; + max_spawn_count = 5; } // Called when the game starts or when spawned @@ -27,6 +30,10 @@ void AEnemySpawner::BeginPlay() void AEnemySpawner::Tick(float delta_s) { Super::Tick(delta_s); + if (!level_location->is_initialized()) + { + level_location->initialize(GetWorld(), marker_class); + } spawn_wait_s += delta_s; if (next_spawn_threshold_s == 0) @@ -35,23 +42,30 @@ void AEnemySpawner::Tick(float delta_s) } else if (spawn_wait_s > next_spawn_threshold_s) { - FActorSpawnParameters spawnParameters; - spawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; - spawnParameters.Owner = this; - AAIController* controller = GetWorld()->SpawnActor( - controller_class, - GetActorLocation(), - FRotator(0,0,0), - spawnParameters - ); + const int spawn_count = random.RandRange(0, max_spawn_count); + + for (int i = 0; i < spawn_count; i++) + { + const FVector location = level_location->get_random_mark(); + FActorSpawnParameters spawnParameters; + spawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; + spawnParameters.Owner = this; + AAIController* controller = GetWorld()->SpawnActor( + controller_class, + location, + FRotator(0,0,0), + spawnParameters + ); + + ABasePawn* enemy = GetWorld()->SpawnActor( + pawn_class, + location, + FRotator(0,0,0), + spawnParameters + ); + controller->Possess(enemy); + } - ABasePawn* enemy = GetWorld()->SpawnActor( - pawn_class, - GetActorLocation(), - FRotator(0,0,0), - spawnParameters - ); - controller->Possess(enemy); spawn_wait_s = 0; next_spawn_threshold_s = spawn_delay_s + random.GetFraction(); } diff --git a/Source/PresidentsBrigade/EnemySpawner.h b/Source/PresidentsBrigade/EnemySpawner.h index 3e39e6e..7b7c5b5 100755 --- a/Source/PresidentsBrigade/EnemySpawner.h +++ b/Source/PresidentsBrigade/EnemySpawner.h @@ -6,6 +6,7 @@ #include "GameFramework/Actor.h" #include "BasePawn.h" #include "AIController.h" +#include "LevelLocation.h" #include "EnemySpawner.generated.h" UCLASS() @@ -41,8 +42,25 @@ protected: UPROPERTY(EditAnywhere, Category = "Enemy") TSubclassOf controller_class; + /** + * Enemy spawn location marker. + */ + UPROPERTY(EditAnywhere, Category = "Enemy") + UClass* marker_class; + + /** + * Max number of enemies to spawn in one cycle. + */ + UPROPERTY(EditAnywhere, Category = "Enemy") + int max_spawn_count; + private: + /** + * Level location. + */ + TUniquePtr level_location; + /** * Random number stream. */ diff --git a/Source/PresidentsBrigade/LevelLocation.cpp b/Source/PresidentsBrigade/LevelLocation.cpp new file mode 100755 index 0000000..e6992e4 --- /dev/null +++ b/Source/PresidentsBrigade/LevelLocation.cpp @@ -0,0 +1,73 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "LevelLocation.h" +#include "EngineUtils.h" +#include "Util.h" + +#define LogError(Msg) Util::log_error(TEXT("LevelLocation"), Msg) + +LevelLocation::LevelLocation() +{ +} + +LevelLocation::~LevelLocation() +{ +} + +bool LevelLocation::is_initialized() const +{ + return initialized; +} + +/** + * Initialize this class with a list of locations derived from the current world. + */ +void LevelLocation::initialize(UWorld *world, const UClass *marker_class) +{ + initialized = true; + + /* + * Find all the markers in the world. + */ + for (TActorIterator iterator(world); iterator; ++iterator) + { + AActor* actor = *iterator; + if (actor) + { + UClass* actor_class = actor->GetClass(); + + if (actor_class == marker_class) + { + locations.Add(actor->GetActorLocation()); + } + } + } + + if (locations.Num() == 0) + { + LogError("No markers"); + } + + /* + * Initialize random stream. + */ + random.Initialize(FMath::Rand()); +} + +/** + * Get world location of randomly selected mark. + * + * @return World location vector of mark. + */ +FVector LevelLocation::get_random_mark() const +{ + if (locations.Num() == 0) + { + LogError("No markers"); + return FVector(); + } + + const int index = random.RandRange(0, locations.Num() - 1); + return locations[index]; +} diff --git a/Source/PresidentsBrigade/LevelLocation.h b/Source/PresidentsBrigade/LevelLocation.h new file mode 100755 index 0000000..eb5c478 --- /dev/null +++ b/Source/PresidentsBrigade/LevelLocation.h @@ -0,0 +1,35 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + +/** + * + */ +class PRESIDENTSBRIGADE_API LevelLocation +{ +public: + LevelLocation(); + ~LevelLocation(); + + void initialize(UWorld *world, const UClass *marker_class); + FVector get_random_mark() const; + bool is_initialized() const; + +private: + /** + * True when initialized. + */ + bool initialized = false; + + /** + * Random number stream. + */ + FRandomStream random; + + /** + * List of locations. + */ + TArray locations; +}; diff --git a/Source/PresidentsBrigade/PresidentAIController.cpp b/Source/PresidentsBrigade/PresidentAIController.cpp index 261db84..ddeda21 100755 --- a/Source/PresidentsBrigade/PresidentAIController.cpp +++ b/Source/PresidentsBrigade/PresidentAIController.cpp @@ -8,6 +8,11 @@ #define LogInfo(Msg) Util::log_info(TEXT("APresidentAIController"), Msg) #define LogError(Msg) Util::log_error(TEXT("APresidentAIController"), Msg) +APresidentAIController::APresidentAIController() +{ + level_location = TUniquePtr(new LevelLocation()); +} + void APresidentAIController::BeginPlay() { Super::BeginPlay(); @@ -31,7 +36,7 @@ void APresidentAIController::Tick(float delta) const FVector offset(1.0, 1.0, 0); starting_location = GetPawn()->GetActorLocation() + offset; target_location = starting_location; - marker_locations = get_markers(); + level_location->initialize(GetWorld(), marker_class); // Update state. current_state = state_t::idle; @@ -47,7 +52,7 @@ void APresidentAIController::Tick(float delta) } else { - target_location = is_home ? get_random_mark() : starting_location; + target_location = is_home ? level_location->get_random_mark() : starting_location; MoveToLocation(target_location); current_state = state_t::moving; } @@ -80,50 +85,3 @@ void APresidentAIController::OnMoveCompleted(FAIRequestID request_id, const FPat { move_completed = true; } - - -/** - * Helper function to load all the markers in the level. - */ -TArray APresidentAIController::get_markers() const -{ - TArray locations; - for (TActorIterator iterator(GetWorld()); iterator; ++iterator) - { - AActor* actor = *iterator; - if (actor && actor != this) - { - UClass* actor_class = actor->GetClass(); - - if (actor_class == marker_class) - { - locations.Add(actor->GetActorLocation()); - } - } - } - - if (locations.Num() == 0) - { - LogError("No markers"); - } - - return locations; -} - -/** - * Helper function for returning world location of randomly selected mark. - * - * @return World location vector of mark. - */ -FVector APresidentAIController::get_random_mark() const -{ - if (marker_locations.Num() == 0) - { - LogError("No markers"); - return FVector(); - } - - const int index = random.RandRange(0, marker_locations.Num() - 1); - return marker_locations[index]; -} - diff --git a/Source/PresidentsBrigade/PresidentAIController.h b/Source/PresidentsBrigade/PresidentAIController.h index 1571fe9..36e4cff 100755 --- a/Source/PresidentsBrigade/PresidentAIController.h +++ b/Source/PresidentsBrigade/PresidentAIController.h @@ -4,6 +4,7 @@ #include "CoreMinimal.h" #include "AIController.h" +#include "LevelLocation.h" #include "PresidentAIController.generated.h" /** @@ -15,6 +16,8 @@ class PRESIDENTSBRIGADE_API APresidentAIController : public AAIController GENERATED_BODY() public: + APresidentAIController(); + virtual void Tick(float delta) override; protected: @@ -34,13 +37,10 @@ private: waiting }; - TArray get_markers() const; - FVector get_random_mark() const; - /** - * List of marker locations. + * Level location tracker. */ - TArray marker_locations; + TUniquePtr level_location; /** * Random number stream.