Compare commits
3 Commits
c9b345ed7b
...
4c88c7da5c
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4c88c7da5c | ||
![]() |
7ba7379699 | ||
![]() |
770e33fce1 |
@ -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)
|
||||
|
BIN
Content/EnemyMarker.uasset
Executable file
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.
@ -71,6 +71,13 @@ void ABasePawn::handle_hit(
|
||||
// Update current health.
|
||||
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
|
||||
|
@ -75,6 +75,8 @@ protected:
|
||||
const FHitResult& hit
|
||||
);
|
||||
|
||||
virtual void handle_hit_internal(AActor* other_actor);
|
||||
|
||||
virtual UPawnMovementComponent* GetMovementComponent() const;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="Player")
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "EnemyAIController.h"
|
||||
#include "EngineUtils.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
#include "Kismet/KismetSystemLibrary.h"
|
||||
#include "Util.h"
|
||||
|
||||
#define LogInfo(Msg) Util::log_info(TEXT("AEnemyIController"), Msg)
|
||||
@ -17,7 +18,7 @@ void AEnemyAIController::Tick(float delta_time)
|
||||
{
|
||||
case state_t::init:
|
||||
president = get_president();
|
||||
if (president)
|
||||
if (UKismetSystemLibrary::IsValid(president))
|
||||
{
|
||||
current_state = state_t::wait;
|
||||
}
|
||||
@ -31,7 +32,7 @@ void AEnemyAIController::Tick(float delta_time)
|
||||
}
|
||||
break;
|
||||
case state_t::target:
|
||||
if (!president)
|
||||
if (!UKismetSystemLibrary::IsValid(president))
|
||||
{
|
||||
current_state = state_t::init;
|
||||
}
|
||||
@ -39,26 +40,28 @@ void AEnemyAIController::Tick(float delta_time)
|
||||
{
|
||||
FVector target_location = president->get_location();
|
||||
const ABasePawn* local_pawn = Cast<ABasePawn>(GetPawn());
|
||||
if (local_pawn)
|
||||
if (UKismetSystemLibrary::IsValid(local_pawn))
|
||||
{
|
||||
FVector current_location = local_pawn->get_location();
|
||||
starting_location = current_location;
|
||||
|
||||
FVector between = target_location - current_location;
|
||||
between = FVector(between.X, between.Y, 0);
|
||||
between.Normalize();
|
||||
FVector extended = (between * overshoot_multiplier) + target_location;
|
||||
|
||||
MoveToLocation(extended);
|
||||
current_state = state_t::moving;
|
||||
current_state = state_t::moving_attack;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This will be cleaned up shortly.
|
||||
current_state = state_t::dead;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case state_t::moving:
|
||||
case state_t::moving_attack:
|
||||
case state_t::moving_retreat:
|
||||
if (move_completed)
|
||||
{
|
||||
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)
|
||||
{
|
||||
move_completed = true;
|
||||
|
@ -18,6 +18,7 @@ class PRESIDENTSBRIGADE_API AEnemyAIController : public AAIController
|
||||
public:
|
||||
virtual void Tick(float delta_time) override;
|
||||
|
||||
void redirect_target();
|
||||
protected:
|
||||
virtual void OnMoveCompleted(FAIRequestID request_id, const FPathFollowingResult& result) override;
|
||||
|
||||
@ -42,7 +43,8 @@ private:
|
||||
init,
|
||||
wait,
|
||||
target,
|
||||
moving,
|
||||
moving_attack,
|
||||
moving_retreat,
|
||||
dead
|
||||
};
|
||||
|
||||
@ -67,4 +69,9 @@ private:
|
||||
* Time spent in waiting.
|
||||
*/
|
||||
float waiting_time;
|
||||
|
||||
/**
|
||||
* Starting location.
|
||||
*/
|
||||
FVector starting_location;
|
||||
};
|
||||
|
@ -3,6 +3,11 @@
|
||||
|
||||
#include "EnemyPawn.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()
|
||||
{
|
||||
@ -33,3 +38,25 @@ void AEnemyPawn::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();
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ class PRESIDENTSBRIGADE_API AEnemyPawn : public ABasePawn
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
virtual void destroy_self() override;
|
||||
virtual void handle_hit_internal(AActor* other_actor) override;
|
||||
|
||||
/**
|
||||
* Pickup class to spawn on death.
|
||||
|
@ -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<LevelLocation>(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<AAIController>(
|
||||
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<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;
|
||||
next_spawn_threshold_s = spawn_delay_s + random.GetFraction();
|
||||
}
|
||||
|
@ -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<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:
|
||||
|
||||
/**
|
||||
* Level location.
|
||||
*/
|
||||
TUniquePtr<LevelLocation> level_location;
|
||||
|
||||
/**
|
||||
* Random number stream.
|
||||
*/
|
||||
|
73
Source/PresidentsBrigade/LevelLocation.cpp
Executable file
73
Source/PresidentsBrigade/LevelLocation.cpp
Executable 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];
|
||||
}
|
35
Source/PresidentsBrigade/LevelLocation.h
Executable file
35
Source/PresidentsBrigade/LevelLocation.h
Executable 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;
|
||||
};
|
@ -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<LevelLocation>(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<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];
|
||||
}
|
||||
|
||||
|
@ -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<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.
|
||||
|
@ -8,3 +8,7 @@ APresidentsBrigadeGameModeBase::APresidentsBrigadeGameModeBase()
|
||||
{
|
||||
}
|
||||
|
||||
void APresidentsBrigadeGameModeBase::PreInitializeComponents()
|
||||
{
|
||||
Super::PreInitializeComponents();
|
||||
}
|
||||
|
@ -16,4 +16,6 @@ class PRESIDENTSBRIGADE_API APresidentsBrigadeGameModeBase : public AGameModeBas
|
||||
|
||||
public:
|
||||
APresidentsBrigadeGameModeBase();
|
||||
|
||||
virtual void PreInitializeComponents() override;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user