parent
f798fd22b1
commit
3e2f409f43
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.
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.
Binary file not shown.
@ -0,0 +1,149 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#include "EnemyAIController.h"
|
||||||
|
#include "SinglePlayerGS.h"
|
||||||
|
#include "Kismet/GameplayStatics.h"
|
||||||
|
#include "GenericPlatform/GenericPlatformMath.h"
|
||||||
|
|
||||||
|
AEnemyAIController::AEnemyAIController()
|
||||||
|
: Super() {
|
||||||
|
PrimaryActorTick.bCanEverTick = true;
|
||||||
|
SetActorTickInterval(0.3f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AEnemyAIController::Tick(float DeltaSeconds) {
|
||||||
|
Super::Tick(DeltaSeconds);
|
||||||
|
if (bIsAITurn && !bIsActing) {
|
||||||
|
if (TroopersCursor >= PossessedTroopers.Num()) {
|
||||||
|
EndTurn();
|
||||||
|
} else {
|
||||||
|
MakeMove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AEnemyAIController::StartTurn() {
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("Enemy AI StartTurn"));
|
||||||
|
bIsAITurn = true;
|
||||||
|
bIsActing = false;
|
||||||
|
// MakeMove();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AEnemyAIController::EndTurn() {
|
||||||
|
bIsAITurn = false;
|
||||||
|
TroopersCursor = 0;
|
||||||
|
bIsEnded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AEnemyAIController::ActionDone() {
|
||||||
|
bIsActing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AEnemyAIController::IsAITurn() const {
|
||||||
|
return bIsAITurn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AEnemyAIController::MakeMove() {
|
||||||
|
while (TroopersCursor < PossessedTroopers.Num()) {
|
||||||
|
while (TroopersCursor < PossessedTroopers.Num() && (
|
||||||
|
!PossessedTroopers[TroopersCursor] ||
|
||||||
|
!PossessedTroopers[TroopersCursor]->IsValidLowLevel()
|
||||||
|
|| PossessedTroopers[TroopersCursor]->IsDead())) {
|
||||||
|
++TroopersCursor;
|
||||||
|
}
|
||||||
|
if (TroopersCursor >= PossessedTroopers.Num()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int Index = GetClosestTrooper();
|
||||||
|
bool failed;
|
||||||
|
if (!IsCloseEnough(Index)) {
|
||||||
|
failed = MoveTo(Index);
|
||||||
|
} else {
|
||||||
|
failed = TryAttack(Index);
|
||||||
|
}
|
||||||
|
if (failed) {
|
||||||
|
TroopersCursor++;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AEnemyAIController::IsCloseEnough(int TrooperIndex) const {
|
||||||
|
const ATrooper *CurrentTrooper = PossessedTroopers[TroopersCursor];
|
||||||
|
const ATrooper *OtherTrooper = PlayerTroopers[TrooperIndex];
|
||||||
|
return (CurrentTrooper->GetLocation() - OtherTrooper->GetLocation()).Size()
|
||||||
|
<= CurrentTrooper->GetRealActionRadius(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AEnemyAIController::TryAttack(int TrooperIndex) {
|
||||||
|
ATrooper *Attacker = PossessedTroopers[TroopersCursor];
|
||||||
|
const auto Location = PlayerTroopers[TrooperIndex]->GetLocation();
|
||||||
|
constexpr int ActionIndex = 1;
|
||||||
|
if (Attacker->CheckAttackCorrectness(Location, ActionIndex)) {
|
||||||
|
bIsActing = true;
|
||||||
|
Attacker->Attack(ActionIndex, Location);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AEnemyAIController::MoveTo(int TrooperIndex) {
|
||||||
|
ATrooper *Trooper = PossessedTroopers[TroopersCursor];
|
||||||
|
const FVector CurrentLocation = Trooper->GetLocation();
|
||||||
|
const FVector Destination = PlayerTroopers[TrooperIndex]->GetLocation();
|
||||||
|
constexpr int ActionIndex = 1;
|
||||||
|
constexpr int MoveActionIndex = 0;
|
||||||
|
const float AttackRadius = Trooper->GetRealActionRadius(ActionIndex);
|
||||||
|
const FVector Vector = Destination - CurrentLocation;
|
||||||
|
const float MaxLength = Trooper->GetRealActionRadius(MoveActionIndex);
|
||||||
|
float PathLength = Vector.Size() - AttackRadius + 10.f;
|
||||||
|
PathLength = FMath::Min(PathLength, MaxLength);
|
||||||
|
FVector Location = CurrentLocation + Vector.GetSafeNormal2D() *
|
||||||
|
PathLength;
|
||||||
|
Location.Z = 0.0f;
|
||||||
|
if (PathLength > 1.0f && Trooper->CheckMoveCorrectness(Location)) {
|
||||||
|
bIsActing = true;
|
||||||
|
Trooper->MoveTrooper(Location);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AEnemyAIController::GetClosestTrooper() const {
|
||||||
|
float minDistance = 1000000.0f;
|
||||||
|
int minIndex = 0;
|
||||||
|
const ATrooper *CurrentTrooper = PossessedTroopers[TroopersCursor];
|
||||||
|
for (int index = 0; index < PlayerTroopers.Num(); ++index) {
|
||||||
|
const ATrooper *OtherTrooper = PlayerTroopers[index];
|
||||||
|
if (OtherTrooper->IsValidLowLevel() && !OtherTrooper->IsDead()) {
|
||||||
|
const float distance = (
|
||||||
|
CurrentTrooper->GetLocation() - OtherTrooper->
|
||||||
|
GetLocation()).Size();
|
||||||
|
if (distance < minDistance) {
|
||||||
|
minDistance = distance;
|
||||||
|
minIndex = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return minIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AEnemyAIController::InitializeTroopers(
|
||||||
|
const TArray<ATrooper *> &Troopers) {
|
||||||
|
for (const auto Trooper : Troopers) {
|
||||||
|
if (Trooper != nullptr) {
|
||||||
|
if (Trooper->GetPlayerIndex() == AI_INDEX) {
|
||||||
|
Trooper->SetAIPossession(this);
|
||||||
|
PossessedTroopers.Add(Trooper);
|
||||||
|
} else {
|
||||||
|
PlayerTroopers.Add(Trooper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AEnemyAIController::BeginPlay() {
|
||||||
|
Super::BeginPlay();
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "EnemyAIController.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class TURNBASEDTUTORIAL_API AEnemyAIController : public AActor {
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr int AI_INDEX = 1;
|
||||||
|
|
||||||
|
AEnemyAIController();
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void StartTurn();
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void EndTurn();
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void ActionDone();
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void InitializeTroopers(const TArray<ATrooper *> &Troopers);
|
||||||
|
|
||||||
|
virtual void BeginPlay() override;
|
||||||
|
|
||||||
|
virtual void Tick(float DeltaSeconds) override;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
bool bIsEnded = false;
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
bool IsAITurn() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
UFUNCTION()
|
||||||
|
void MakeMove();
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
int TroopersCursor = 0;
|
||||||
|
|
||||||
|
int GetClosestTrooper() const;
|
||||||
|
|
||||||
|
bool IsCloseEnough(int TrooperIndex) const;
|
||||||
|
|
||||||
|
bool TryAttack(int TrooperIndex);
|
||||||
|
|
||||||
|
bool MoveTo(int TrooperIndex);
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
bool bIsAITurn = false;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
bool bIsActing = false;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
TArray<ATrooper *> PossessedTroopers;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
TArray<ATrooper *> PlayerTroopers;
|
||||||
|
};
|
@ -0,0 +1,19 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#include "SinglePlayerGM.h"
|
||||||
|
#include "SinglePlayerGS.h"
|
||||||
|
|
||||||
|
ASinglePlayerGM::ASinglePlayerGM() : Super() {
|
||||||
|
GameStateClass = ASinglePlayerGS::StaticClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ASinglePlayerGM::BeginPlay() {
|
||||||
|
Super::BeginPlay();
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("SinglePlayer GameMode BeginPlay"));
|
||||||
|
GameStateClass = ASinglePlayerGS::StaticClass();
|
||||||
|
StartGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ASinglePlayerGM::PostLogin(APlayerController *NewPlayer) {
|
||||||
|
AGameMode::PostLogin(NewPlayer);
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "MyGameMode.h"
|
||||||
|
#include "SinglePlayerGM.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class TURNBASEDTUTORIAL_API ASinglePlayerGM : public AMyGameMode {
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
ASinglePlayerGM();
|
||||||
|
|
||||||
|
virtual void BeginPlay() override;
|
||||||
|
|
||||||
|
virtual void PostLogin(APlayerController *NewPlayer) override;
|
||||||
|
|
||||||
|
};
|
@ -0,0 +1,56 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#include "SinglePlayerGS.h"
|
||||||
|
#include "Net/UnrealNetwork.h"
|
||||||
|
|
||||||
|
ASinglePlayerGS::ASinglePlayerGS()
|
||||||
|
: Super() {
|
||||||
|
IsMultiplayer = false;
|
||||||
|
PrimaryActorTick.bCanEverTick = true;
|
||||||
|
SetActorTickInterval(0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ASinglePlayerGS::BeginPlay() {
|
||||||
|
Super::BeginPlay();
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("SinglePlayer GameState BeginPlay"));
|
||||||
|
if (LivingTroopersCount.Num() < 2) {
|
||||||
|
LivingTroopersCount.SetNum(2);
|
||||||
|
}
|
||||||
|
EnemyAiManager = GetWorld()->SpawnActor<AEnemyAIController>(
|
||||||
|
AEnemyAIController::StaticClass(), FVector(0.0f, 0.0f, 1000.0f),
|
||||||
|
FRotator(0.0f, 0.0f, 0.0f), FActorSpawnParameters());
|
||||||
|
EnemyAiManager->InitializeTroopers(Troopers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ASinglePlayerGS::CycleTurns() {
|
||||||
|
if (CurrentPlayerTurn == 0) {
|
||||||
|
PlayerInTurn()->EndTurn();
|
||||||
|
}
|
||||||
|
for (const auto Trooper : Troopers) {
|
||||||
|
if (Trooper != nullptr) {
|
||||||
|
Trooper->ResetActionPoints();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CurrentPlayerTurn = !CurrentPlayerTurn;
|
||||||
|
if (CurrentPlayerTurn == 0) {
|
||||||
|
PlayerInTurn()->StartTurn();
|
||||||
|
} else {
|
||||||
|
EnemyAiManager->StartTurn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ASinglePlayerGS::Tick(float DeltaSeconds) {
|
||||||
|
Super::Tick(DeltaSeconds);
|
||||||
|
if (EnemyAiManager->bIsEnded) {
|
||||||
|
EnemyAiManager->bIsEnded = false;
|
||||||
|
CycleTurns();
|
||||||
|
} else if (CurrentPlayerTurn == 1 && !EnemyAiManager->IsAITurn()) {
|
||||||
|
CycleTurns();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ASinglePlayerGS::GetLifetimeReplicatedProps(
|
||||||
|
TArray<FLifetimeProperty> &OutLifetimeProps) const {
|
||||||
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||||
|
DOREPLIFETIME(ASinglePlayerGS, EnemyAiManager);
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "EnemyAIController.h"
|
||||||
|
#include "MyGameState.h"
|
||||||
|
#include "SinglePlayerGS.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class TURNBASEDTUTORIAL_API ASinglePlayerGS : public AMyGameState {
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
ASinglePlayerGS();
|
||||||
|
|
||||||
|
virtual void BeginPlay() override;
|
||||||
|
|
||||||
|
virtual void CycleTurns() override;
|
||||||
|
|
||||||
|
virtual void Tick(float DeltaSeconds) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
UPROPERTY(Replicated)
|
||||||
|
AEnemyAIController *EnemyAiManager = nullptr;
|
||||||
|
};
|
Loading…
Reference in new issue