Compare commits

...

3 Commits

Author SHA1 Message Date
Sara Montecino
4c88c7da5c Experimental commit -- no clue what's here 2024-01-02 19:40:23 -08:00
Sara Montecino
7ba7379699 Spawn enemies off map 2022-11-19 12:50:05 -08:00
Sara Montecino
770e33fce1 Add player2 HUD 2022-11-19 11:47:39 -08:00
24 changed files with 240 additions and 78 deletions

View File

@ -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=LeftMouseButton)
+ActionMappings=(ActionName="Shoot",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Gamepad_RightTrigger) +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=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=W)
+AxisMappings=(AxisName="MoveForward",Scale=-1.000000,Key=S) +AxisMappings=(AxisName="MoveForward",Scale=-1.000000,Key=S)
+AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=D) +AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=D)

BIN
Content/EnemyMarker.uasset Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -71,6 +71,13 @@ void ABasePawn::handle_hit(
// Update current health. // Update current health.
current_health -= damage_class_map[other_class]; current_health -= damage_class_map[other_class];
} }
handle_hit_internal(other_actor);
}
void ABasePawn::handle_hit_internal(AActor* other_actor)
{
// do nothing by default;
} }
UPawnMovementComponent* ABasePawn::GetMovementComponent() const UPawnMovementComponent* ABasePawn::GetMovementComponent() const

View File

@ -75,6 +75,8 @@ protected:
const FHitResult& hit const FHitResult& hit
); );
virtual void handle_hit_internal(AActor* other_actor);
virtual UPawnMovementComponent* GetMovementComponent() const; virtual UPawnMovementComponent* GetMovementComponent() const;
UPROPERTY(EditAnywhere, Category="Player") UPROPERTY(EditAnywhere, Category="Player")

View File

@ -4,6 +4,7 @@
#include "EnemyAIController.h" #include "EnemyAIController.h"
#include "EngineUtils.h" #include "EngineUtils.h"
#include "DrawDebugHelpers.h" #include "DrawDebugHelpers.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Util.h" #include "Util.h"
#define LogInfo(Msg) Util::log_info(TEXT("AEnemyIController"), Msg) #define LogInfo(Msg) Util::log_info(TEXT("AEnemyIController"), Msg)
@ -17,7 +18,7 @@ void AEnemyAIController::Tick(float delta_time)
{ {
case state_t::init: case state_t::init:
president = get_president(); president = get_president();
if (president) if (UKismetSystemLibrary::IsValid(president))
{ {
current_state = state_t::wait; current_state = state_t::wait;
} }
@ -31,7 +32,7 @@ void AEnemyAIController::Tick(float delta_time)
} }
break; break;
case state_t::target: case state_t::target:
if (!president) if (!UKismetSystemLibrary::IsValid(president))
{ {
current_state = state_t::init; current_state = state_t::init;
} }
@ -39,26 +40,28 @@ void AEnemyAIController::Tick(float delta_time)
{ {
FVector target_location = president->get_location(); FVector target_location = president->get_location();
const ABasePawn* local_pawn = Cast<ABasePawn>(GetPawn()); const ABasePawn* local_pawn = Cast<ABasePawn>(GetPawn());
if (local_pawn) if (UKismetSystemLibrary::IsValid(local_pawn))
{ {
FVector current_location = local_pawn->get_location(); FVector current_location = local_pawn->get_location();
starting_location = current_location;
FVector between = target_location - current_location; FVector between = target_location - current_location;
between = FVector(between.X, between.Y, 0); between = FVector(between.X, between.Y, 0);
between.Normalize(); between.Normalize();
FVector extended = (between * overshoot_multiplier) + target_location; FVector extended = (between * overshoot_multiplier) + target_location;
MoveToLocation(extended); MoveToLocation(extended);
current_state = state_t::moving; current_state = state_t::moving_attack;
} }
else else
{ {
// This will be cleaned up shortly. // This will be cleaned up shortly.
current_state = state_t::dead; current_state = state_t::dead;
} }
} }
break; break;
case state_t::moving: case state_t::moving_attack:
case state_t::moving_retreat:
if (move_completed) if (move_completed)
{ {
move_completed = false; move_completed = false;
@ -73,6 +76,15 @@ void AEnemyAIController::Tick(float delta_time)
} }
} }
void AEnemyAIController::redirect_target()
{
if (current_state == moving_attack)
{
MoveToLocation(starting_location);
current_state = moving_retreat;
}
}
void AEnemyAIController::OnMoveCompleted(FAIRequestID request_id, const FPathFollowingResult& result) void AEnemyAIController::OnMoveCompleted(FAIRequestID request_id, const FPathFollowingResult& result)
{ {
move_completed = true; move_completed = true;

View File

@ -18,6 +18,7 @@ class PRESIDENTSBRIGADE_API AEnemyAIController : public AAIController
public: public:
virtual void Tick(float delta_time) override; virtual void Tick(float delta_time) override;
void redirect_target();
protected: protected:
virtual void OnMoveCompleted(FAIRequestID request_id, const FPathFollowingResult& result) override; virtual void OnMoveCompleted(FAIRequestID request_id, const FPathFollowingResult& result) override;
@ -42,7 +43,8 @@ private:
init, init,
wait, wait,
target, target,
moving, moving_attack,
moving_retreat,
dead dead
}; };
@ -67,4 +69,9 @@ private:
* Time spent in waiting. * Time spent in waiting.
*/ */
float waiting_time; float waiting_time;
/**
* Starting location.
*/
FVector starting_location;
}; };

View File

@ -3,6 +3,11 @@
#include "EnemyPawn.h" #include "EnemyPawn.h"
#include "Pickup.h" #include "Pickup.h"
#include "Util.h"
#include "EnemyAIController.h"
#define LogInfo(Msg) Util::log_info(TEXT("EnemyPawn"), Msg)
#define LogError(Msg) Util::log_error(TEXT("EnemyPawn"), Msg)
void AEnemyPawn::BeginPlay() void AEnemyPawn::BeginPlay()
{ {
@ -33,3 +38,25 @@ void AEnemyPawn::destroy_self()
ABasePawn::destroy_self(); ABasePawn::destroy_self();
} }
void AEnemyPawn::handle_hit_internal(AActor* other)
{
const AEnemyPawn* is_enemy = Cast<AEnemyPawn>(other);
if (is_enemy)
{
return;
}
const ABasePawn* other_pawn = Cast<ABasePawn>(other);
if (other_pawn)
{
AEnemyAIController* controller = Cast<AEnemyAIController>(GetController());
if (!controller)
{
LogError("EnemyAIController not used!");
return;
}
controller->redirect_target();
}
}

View File

@ -18,6 +18,7 @@ class PRESIDENTSBRIGADE_API AEnemyPawn : public ABasePawn
protected: protected:
virtual void BeginPlay() override; virtual void BeginPlay() override;
virtual void destroy_self() override; virtual void destroy_self() override;
virtual void handle_hit_internal(AActor* other_actor) override;
/** /**
* Pickup class to spawn on death. * Pickup class to spawn on death.

View File

@ -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. // 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; PrimaryActorTick.bCanEverTick = true;
level_location = TUniquePtr<LevelLocation>(new LevelLocation());
spawn_delay_s = 5.0f; spawn_delay_s = 5.0f;
max_spawn_count = 5;
} }
// Called when the game starts or when spawned // Called when the game starts or when spawned
@ -27,6 +30,10 @@ void AEnemySpawner::BeginPlay()
void AEnemySpawner::Tick(float delta_s) void AEnemySpawner::Tick(float delta_s)
{ {
Super::Tick(delta_s); Super::Tick(delta_s);
if (!level_location->is_initialized())
{
level_location->initialize(GetWorld(), marker_class);
}
spawn_wait_s += delta_s; spawn_wait_s += delta_s;
if (next_spawn_threshold_s == 0) 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) else if (spawn_wait_s > next_spawn_threshold_s)
{ {
FActorSpawnParameters spawnParameters; const int spawn_count = random.RandRange(0, max_spawn_count);
spawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
spawnParameters.Owner = this; for (int i = 0; i < spawn_count; i++)
AAIController* controller = GetWorld()->SpawnActor<AAIController>( {
controller_class, const FVector location = level_location->get_random_mark();
GetActorLocation(), FActorSpawnParameters spawnParameters;
FRotator(0,0,0), spawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
spawnParameters spawnParameters.Owner = this;
); AAIController* controller = GetWorld()->SpawnActor<AAIController>(
controller_class,
location,
FRotator(0,0,0),
spawnParameters
);
ABasePawn* enemy = GetWorld()->SpawnActor<ABasePawn>(
pawn_class,
location,
FRotator(0,0,0),
spawnParameters
);
controller->Possess(enemy);
}
ABasePawn* enemy = GetWorld()->SpawnActor<ABasePawn>(
pawn_class,
GetActorLocation(),
FRotator(0,0,0),
spawnParameters
);
controller->Possess(enemy);
spawn_wait_s = 0; spawn_wait_s = 0;
next_spawn_threshold_s = spawn_delay_s + random.GetFraction(); next_spawn_threshold_s = spawn_delay_s + random.GetFraction();
} }

View File

@ -6,6 +6,7 @@
#include "GameFramework/Actor.h" #include "GameFramework/Actor.h"
#include "BasePawn.h" #include "BasePawn.h"
#include "AIController.h" #include "AIController.h"
#include "LevelLocation.h"
#include "EnemySpawner.generated.h" #include "EnemySpawner.generated.h"
UCLASS() UCLASS()
@ -41,8 +42,25 @@ protected:
UPROPERTY(EditAnywhere, Category = "Enemy") UPROPERTY(EditAnywhere, Category = "Enemy")
TSubclassOf<AAIController> controller_class; TSubclassOf<AAIController> 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: private:
/**
* Level location.
*/
TUniquePtr<LevelLocation> level_location;
/** /**
* Random number stream. * Random number stream.
*/ */

View File

@ -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<AActor> 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];
}

View File

@ -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<FVector> locations;
};

View File

@ -8,6 +8,11 @@
#define LogInfo(Msg) Util::log_info(TEXT("APresidentAIController"), Msg) #define LogInfo(Msg) Util::log_info(TEXT("APresidentAIController"), Msg)
#define LogError(Msg) Util::log_error(TEXT("APresidentAIController"), Msg) #define LogError(Msg) Util::log_error(TEXT("APresidentAIController"), Msg)
APresidentAIController::APresidentAIController()
{
level_location = TUniquePtr<LevelLocation>(new LevelLocation());
}
void APresidentAIController::BeginPlay() void APresidentAIController::BeginPlay()
{ {
Super::BeginPlay(); Super::BeginPlay();
@ -31,7 +36,7 @@ void APresidentAIController::Tick(float delta)
const FVector offset(1.0, 1.0, 0); const FVector offset(1.0, 1.0, 0);
starting_location = GetPawn()->GetActorLocation() + offset; starting_location = GetPawn()->GetActorLocation() + offset;
target_location = starting_location; target_location = starting_location;
marker_locations = get_markers(); level_location->initialize(GetWorld(), marker_class);
// Update state. // Update state.
current_state = state_t::idle; current_state = state_t::idle;
@ -47,7 +52,7 @@ void APresidentAIController::Tick(float delta)
} }
else else
{ {
target_location = is_home ? get_random_mark() : starting_location; target_location = is_home ? level_location->get_random_mark() : starting_location;
MoveToLocation(target_location); MoveToLocation(target_location);
current_state = state_t::moving; current_state = state_t::moving;
} }
@ -80,50 +85,3 @@ void APresidentAIController::OnMoveCompleted(FAIRequestID request_id, const FPat
{ {
move_completed = true; move_completed = true;
} }
/**
* Helper function to load all the markers in the level.
*/
TArray<FVector> APresidentAIController::get_markers() const
{
TArray<FVector> locations;
for (TActorIterator<AActor> 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];
}

View File

@ -4,6 +4,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "AIController.h" #include "AIController.h"
#include "LevelLocation.h"
#include "PresidentAIController.generated.h" #include "PresidentAIController.generated.h"
/** /**
@ -15,6 +16,8 @@ class PRESIDENTSBRIGADE_API APresidentAIController : public AAIController
GENERATED_BODY() GENERATED_BODY()
public: public:
APresidentAIController();
virtual void Tick(float delta) override; virtual void Tick(float delta) override;
protected: protected:
@ -34,13 +37,10 @@ private:
waiting waiting
}; };
TArray<FVector> get_markers() const;
FVector get_random_mark() const;
/** /**
* List of marker locations. * Level location tracker.
*/ */
TArray<FVector> marker_locations; TUniquePtr<LevelLocation> level_location;
/** /**
* Random number stream. * Random number stream.

View File

@ -8,3 +8,7 @@ APresidentsBrigadeGameModeBase::APresidentsBrigadeGameModeBase()
{ {
} }
void APresidentsBrigadeGameModeBase::PreInitializeComponents()
{
Super::PreInitializeComponents();
}

View File

@ -16,4 +16,6 @@ class PRESIDENTSBRIGADE_API APresidentsBrigadeGameModeBase : public AGameModeBas
public: public:
APresidentsBrigadeGameModeBase(); APresidentsBrigadeGameModeBase();
virtual void PreInitializeComponents() override;
}; };

View File

@ -19,6 +19,8 @@ xAdd art
x Car models x Car models
x Street shader x Street shader
xAdd HUD UI for single player. xAdd HUD UI for single player.
Make boosting fun
- Enemy car ricochets after hitting player/president
Add sounds Add sounds
Add juice Add juice
Add muzzle on shoot Add muzzle on shoot