Add reworked Worker Enemy AI #3
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.
BIN
Content/Enemy/FireWeaponTasks.uasset (Stored with Git LFS)
BIN
Content/Enemy/FireWeaponTasks.uasset (Stored with Git LFS)
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/Enemy/Worker/BB_Worker.uasset (Stored with Git LFS)
BIN
Content/Enemy/Worker/BB_Worker.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/Enemy/Worker/BT_Worker.uasset (Stored with Git LFS)
BIN
Content/Enemy/Worker/BT_Worker.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/Enemy/Worker/C_Worker.uasset (Stored with Git LFS)
BIN
Content/Enemy/Worker/C_Worker.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/Levels/TestLevel.umap (Stored with Git LFS)
BIN
Content/Levels/TestLevel.umap (Stored with Git LFS)
Binary file not shown.
Binary file not shown.
BIN
Content/Weapons/Pistol/Pistol.uasset (Stored with Git LFS)
BIN
Content/Weapons/Pistol/Pistol.uasset (Stored with Git LFS)
Binary file not shown.
|
@ -0,0 +1,27 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "../Decorators/BTDCanSeeTarget.h"
|
||||
|
||||
#include "AIController.h"
|
||||
#include "BehaviorTree/BlackboardComponent.h"
|
||||
|
||||
bool UBTDCanSeeTarget::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const
|
||||
{
|
||||
FHitResult HitResult;
|
||||
|
||||
FVector Start = OwnerComp.GetAIOwner()->GetPawn()->GetActorLocation();
|
||||
|
||||
UBlackboardComponent* BlackboardComponent = OwnerComp.GetBlackboardComponent();
|
||||
AActor* TargetActor = Cast<AActor>(BlackboardComponent->GetValueAsObject(TargetActorKey.SelectedKeyName));
|
||||
FVector End = TargetActor->GetActorLocation();
|
||||
|
||||
GetWorld()->LineTraceSingleByChannel(HitResult, Start, End, ECC_Pawn);
|
||||
|
||||
if (HitResult.GetActor())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "BehaviorTree/BTDecorator.h"
|
||||
#include "BTDCanSeeTarget.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class NAKATOMI_API UBTDCanSeeTarget : public UBTDecorator
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, Category = "Options",
|
||||
Meta = (AllowPrivateAccess = "true", DisplayName = "Target Actor Key"))
|
||||
FBlackboardKeySelector TargetActorKey;
|
||||
|
||||
protected:
|
||||
virtual bool CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const override;
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "../Decorators/BTDHasPatrolRoute.h"
|
||||
|
||||
#include "Nakatomi/EnemyAIController.h"
|
||||
|
||||
bool UBTDHasPatrolRoute::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const
|
||||
{
|
||||
AEnemyAIController* enemyController = Cast<AEnemyAIController>(OwnerComp.GetAIOwner());
|
||||
AEnemyCharacter* enemyPawn = Cast<AEnemyCharacter>(enemyController->GetPawn());
|
||||
|
||||
return enemyPawn->CurrentPatrolRoute ? true : false;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "BehaviorTree/BTDecorator.h"
|
||||
#include "BTDHasPatrolRoute.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(Blueprintable)
|
||||
class NAKATOMI_API UBTDHasPatrolRoute : public UBTDecorator
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual bool CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const override;
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "../Decorators/BTDIsHealthBelowThreshold.h"
|
||||
|
||||
#include "BehaviorTree/BlackboardComponent.h"
|
||||
|
||||
bool UBTDIsHealthBelowThreshold::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const
|
||||
{
|
||||
UBlackboardComponent* BlackboardComponent = OwnerComp.GetBlackboardComponent();
|
||||
float currentHealth = BlackboardComponent->GetValueAsFloat(CurrentHealthKey.SelectedKeyName);
|
||||
|
||||
return currentHealth <= HealthThreshold ? true : false;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "BehaviorTree/BTDecorator.h"
|
||||
#include "BTDIsHealthBelowThreshold.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class NAKATOMI_API UBTDIsHealthBelowThreshold : public UBTDecorator
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Options",
|
||||
Meta = (AllowPrivateAccess = "true", DisplayName = "Current Health Key"))
|
||||
FBlackboardKeySelector CurrentHealthKey;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Options",
|
||||
Meta = (AllowPrivateAccess = "true", DisplayName = "Health Threshold"))
|
||||
float HealthThreshold = 25.0f;
|
||||
|
||||
protected:
|
||||
virtual bool CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const override;
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "../Decorators/BTDIsWithinRange.h"
|
||||
|
||||
#include "AIController.h"
|
||||
#include "navigationSystem.h"
|
||||
#include "BehaviorTree/BlackboardComponent.h"
|
||||
#include "Nakatomi/NakatomiCharacter.h"
|
||||
|
||||
bool UBTDIsWithinRange::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const
|
||||
{
|
||||
UBlackboardComponent* BlackboardComponent = OwnerComp.GetBlackboardComponent();
|
||||
float Dist = BlackboardComponent->GetValueAsFloat(DistanceKey.SelectedKeyName);
|
||||
UObject* Target = BlackboardComponent->GetValueAsObject(TargetActorKey.SelectedKeyName);
|
||||
auto TargetLocation = Cast<AActor>(Target)->GetActorLocation();
|
||||
UNavigationSystemV1* NavigationSystem = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
|
||||
|
||||
APawn* pawn = OwnerComp.GetAIOwner()->GetPawn();
|
||||
|
||||
if (pawn && NavigationSystem && TargetLocation != FVector::ZeroVector)
|
||||
{
|
||||
double Distance = -1.0;
|
||||
NavigationSystem->GetPathLength(TargetLocation, pawn->GetTransform().GetLocation(), Distance);
|
||||
return Distance <= Dist ? true : false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "BehaviorTree/BTDecorator.h"
|
||||
#include "BTDIsWithinRange.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class NAKATOMI_API UBTDIsWithinRange : public UBTDecorator
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Options",
|
||||
Meta = (AllowPrivateAccess = "true", DisplayName = "Target Actor Key"))
|
||||
FBlackboardKeySelector TargetActorKey;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Options",
|
||||
Meta = (AllowPrivateAccess = "true", DisplayName = "Distance Key"))
|
||||
FBlackboardKeySelector DistanceKey;
|
||||
|
||||
public:
|
||||
|
||||
virtual bool CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const override;
|
||||
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "../Decorators/BTDWeaponCooldown.h"
|
||||
|
||||
#include "AIController.h"
|
||||
#include "Nakatomi/EnemyCharacter.h"
|
||||
|
||||
void UBTDWeaponCooldown::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
|
||||
{
|
||||
AEnemyCharacter* EnemyCharacter = Cast<AEnemyCharacter>(OwnerComp.GetAIOwner()->GetPawn());
|
||||
|
||||
if (EnemyCharacter && EnemyCharacter->GetCurrentWeapon())
|
||||
{
|
||||
const float Cooldown = EnemyCharacter->GetCurrentWeapon()->GetWeaponProperties()->WeaponCooldown;
|
||||
if (CoolDownTime != Cooldown)
|
||||
{
|
||||
CoolDownTime = Cooldown;
|
||||
}
|
||||
}
|
||||
|
||||
Super::TickNode(OwnerComp, NodeMemory, DeltaSeconds);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "BehaviorTree/Decorators/BTDecorator_Cooldown.h"
|
||||
#include "BTDWeaponCooldown.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class NAKATOMI_API UBTDWeaponCooldown : public UBTDecorator_Cooldown
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
virtual void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "EAIState.h"
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EAIState : uint8
|
||||
{
|
||||
PASSIVE UMETA(DisplayName = "Passive"),
|
||||
ATTACKING UMETA(DisplayName = "Attacking"),
|
||||
FROZEN UMETA(DisplayName = "Frozen"),
|
||||
INVESTIGATING UMETA(DisplayName = "Investigating"),
|
||||
DEAD UMETA(DisplayName = "Dead"),
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "EPatrolMovementEnum.h"
|
|
@ -0,0 +1,16 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EPatrolMovementEnum : uint8
|
||||
{
|
||||
SLOWWALK UMETA(DisplayName = "Slow Walk"),
|
||||
WALK UMETA(DisplayName = "Walk"),
|
||||
SPRINT UMETA(DisplayName = "Sprint"),
|
||||
};
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "EnemyAIController.h"
|
||||
#include <Kismet/GameplayStatics.h>
|
||||
|
||||
#include "EAIState.h"
|
||||
#include "NakatomiGameInstance.h"
|
||||
#include "BehaviorTree/BehaviorTree.h"
|
||||
#include "BehaviorTree/BlackboardComponent.h"
|
||||
|
@ -25,8 +27,19 @@ AEnemyAIController::AEnemyAIController(const FObjectInitializer& object_initiali
|
|||
SightConfig->DetectionByAffiliation.bDetectEnemies = true;
|
||||
SightConfig->DetectionByAffiliation.bDetectNeutrals = true;
|
||||
|
||||
HearingConfig = CreateDefaultSubobject<UAISenseConfig_Hearing>(TEXT("Hearing Sense Config"));
|
||||
HearingConfig->HearingRange = 800.0f;
|
||||
HearingConfig->SetMaxAge(3.0f);
|
||||
HearingConfig->DetectionByAffiliation.bDetectEnemies = true;
|
||||
HearingConfig->DetectionByAffiliation.bDetectNeutrals = true;
|
||||
|
||||
DamageConfig = CreateDefaultSubobject<UAISenseConfig_Damage>(TEXT("Damage Sense Config"));
|
||||
DamageConfig->SetMaxAge(5.0f);
|
||||
|
||||
AIPerception->SetDominantSense(SightConfig->GetSenseImplementation());
|
||||
AIPerception->ConfigureSense(*SightConfig);
|
||||
AIPerception->ConfigureSense(*HearingConfig);
|
||||
AIPerception->ConfigureSense(*DamageConfig);
|
||||
}
|
||||
|
||||
void AEnemyAIController::OnPossess(APawn* InPawn)
|
||||
|
@ -48,6 +61,11 @@ void AEnemyAIController::OnPossess(APawn* InPawn)
|
|||
BehaviorTree->StartTree(*behaviourTree);
|
||||
Blackboard->SetValueAsObject("SelfActor", enemy);
|
||||
}
|
||||
|
||||
Blackboard->SetValueAsEnum("State", static_cast<uint8>(EAIState::PASSIVE));
|
||||
|
||||
Blackboard->SetValueAsFloat("AttackRadius", enemy->AttackRadius);
|
||||
Blackboard->SetValueAsFloat("DefendRadius", enemy->DefendRadius);
|
||||
|
||||
//ensure(enemy->GetMovementComponent()->UseAccelerationForPathFollowing());
|
||||
}
|
||||
|
@ -116,7 +134,7 @@ void AEnemyAIController::OnDeath(FDamageInfo info)
|
|||
|
||||
void AEnemyAIController::OnPerceptionUpdated(const TArray<AActor*>& actors)
|
||||
{
|
||||
for (auto actor : actors)
|
||||
for (AActor* actor : actors)
|
||||
{
|
||||
if (!actor->GetClass()->IsChildOf(APlayerCharacter::StaticClass()))
|
||||
{
|
||||
|
@ -126,13 +144,46 @@ void AEnemyAIController::OnPerceptionUpdated(const TArray<AActor*>& actors)
|
|||
FActorPerceptionBlueprintInfo perceptionInfo;
|
||||
PerceptionComponent->GetActorsPerception(actor, perceptionInfo);
|
||||
|
||||
for (auto& stimulus : perceptionInfo.LastSensedStimuli)
|
||||
for (FAIStimulus& stimulus : perceptionInfo.LastSensedStimuli)
|
||||
{
|
||||
if (!stimulus.IsValid() || stimulus.IsExpired())
|
||||
if (!stimulus.IsValid() || stimulus.IsExpired() ||
|
||||
static_cast<EAIState>(Blackboard->GetValueAsEnum("State")) == EAIState::DEAD)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FAISenseID SightID;
|
||||
FAISenseID HearingID;
|
||||
FAISenseID DamageID;
|
||||
|
||||
if (SightConfig)
|
||||
{
|
||||
SightID = SightConfig->GetSenseID();
|
||||
}
|
||||
|
||||
if (HearingConfig)
|
||||
{
|
||||
HearingID = HearingConfig->GetSenseID();
|
||||
}
|
||||
|
||||
if (DamageConfig)
|
||||
{
|
||||
DamageID = DamageConfig->GetSenseID();
|
||||
}
|
||||
|
||||
if (stimulus.Type == SightID)
|
||||
{
|
||||
SensedSight(actor, stimulus);
|
||||
}
|
||||
else if (stimulus.Type == HearingID)
|
||||
{
|
||||
SensedHearing(actor, stimulus);
|
||||
}
|
||||
else if (stimulus.Type == DamageID)
|
||||
{
|
||||
SensedDamaged(actor, stimulus);
|
||||
}
|
||||
|
||||
Blackboard->SetValueAsObject("TargetActor", actor);
|
||||
|
||||
if (stimulus.IsActive())
|
||||
|
@ -179,3 +230,51 @@ bool AEnemyAIController::GetHasAttackToken()
|
|||
{
|
||||
return HasAttackToken;
|
||||
}
|
||||
|
||||
void AEnemyAIController::SetState(EAIState state)
|
||||
{
|
||||
Blackboard->SetValueAsEnum("State", static_cast<uint8>(state));
|
||||
}
|
||||
|
||||
void AEnemyAIController::SetStateAsPassive()
|
||||
{
|
||||
SetState(EAIState::PASSIVE);
|
||||
}
|
||||
|
||||
void AEnemyAIController::SetStateAsAttacking(AActor* target)
|
||||
{
|
||||
Blackboard->SetValueAsObject("TargetActor", target);
|
||||
SetState(EAIState::ATTACKING);
|
||||
}
|
||||
|
||||
AActor* AEnemyAIController::GetTargetActor()
|
||||
{
|
||||
return Cast<AActor>(Blackboard->GetValueAsObject("TargetActor"));
|
||||
}
|
||||
|
||||
void AEnemyAIController::SensedSight(AActor* actor, FAIStimulus& stimulus)
|
||||
{
|
||||
EAIState CurrentState = static_cast<EAIState>(Blackboard->GetValueAsEnum("State"));
|
||||
|
||||
|
||||
if (CurrentState == EAIState::PASSIVE || CurrentState == EAIState::INVESTIGATING)
|
||||
{
|
||||
SetStateAsAttacking(actor);
|
||||
}
|
||||
}
|
||||
|
||||
void AEnemyAIController::SensedHearing(AActor* actor, FAIStimulus& stimulus)
|
||||
{
|
||||
EAIState CurrentState = static_cast<EAIState>(Blackboard->GetValueAsEnum("State"));
|
||||
|
||||
if (CurrentState == EAIState::PASSIVE || CurrentState == EAIState::INVESTIGATING)
|
||||
{
|
||||
SetState(EAIState::INVESTIGATING);
|
||||
Blackboard->SetValueAsVector("InvestigationLocation", stimulus.StimulusLocation);
|
||||
}
|
||||
}
|
||||
|
||||
void AEnemyAIController::SensedDamaged(AActor* actor, FAIStimulus& stimulus)
|
||||
{
|
||||
SetStateAsAttacking(actor);
|
||||
}
|
||||
|
|
|
@ -4,8 +4,11 @@
|
|||
|
||||
#include "CoreMinimal.h"
|
||||
#include "AIController.h"
|
||||
#include "EAIState.h"
|
||||
#include "EnemyCharacter.h"
|
||||
#include "PlayerCharacter.h"
|
||||
#include "Perception/AISenseConfig_Damage.h"
|
||||
#include "Perception/AISenseConfig_Hearing.h"
|
||||
#include "Perception/AISenseConfig_Sight.h"
|
||||
#include "EnemyAIController.generated.h"
|
||||
|
||||
|
@ -29,6 +32,10 @@ private:
|
|||
|
||||
UAISenseConfig_Sight* SightConfig;
|
||||
|
||||
UAISenseConfig_Hearing* HearingConfig;
|
||||
|
||||
UAISenseConfig_Damage* DamageConfig;
|
||||
|
||||
bool HasAttackToken = false;
|
||||
|
||||
public:
|
||||
|
@ -59,4 +66,24 @@ public:
|
|||
|
||||
UFUNCTION()
|
||||
bool GetHasAttackToken();
|
||||
|
||||
UFUNCTION()
|
||||
void SetState(EAIState state);
|
||||
|
||||
UFUNCTION()
|
||||
void SetStateAsPassive();
|
||||
|
||||
UFUNCTION()
|
||||
void SetStateAsAttacking(AActor* target);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
AActor* GetTargetActor();
|
||||
|
||||
private:
|
||||
|
||||
void SensedSight(AActor* actor, FAIStimulus& stimulus);
|
||||
|
||||
void SensedHearing(AActor* actor, FAIStimulus& stimulus);
|
||||
|
||||
void SensedDamaged(AActor* actor, FAIStimulus& stimulus);
|
||||
};
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
|
||||
#include "EnemyCharacter.h"
|
||||
#include "EnemyAIController.h"
|
||||
#include "EnemyHealthComponent.h"
|
||||
#include "InteractableComponent.h"
|
||||
#include "BehaviorTree/BehaviorTree.h"
|
||||
#include "BehaviorTree/BlackboardComponent.h"
|
||||
#include "BehaviorTree/BlackboardData.h"
|
||||
|
||||
#define COLLISION_WEAPON ECC_GameTraceChannel1
|
||||
|
||||
|
@ -10,8 +14,12 @@ AEnemyCharacter::AEnemyCharacter(const FObjectInitializer& ObjectInitializer) :
|
|||
{
|
||||
RandomWeaponParameters = CreateDefaultSubobject<URandomWeaponParameters>(TEXT("Random Weapon Parameters"));
|
||||
|
||||
auto healthComponent = CreateDefaultSubobject<UEnemyHealthComponent>(TEXT("Health Component"));
|
||||
SetHealthComponent(healthComponent);
|
||||
GetHealthComponent()->OnDamaged.BindUFunction(this, "OnDamaged");
|
||||
GetHealthComponent()->OnDeath.BindUFunction(this, "OnDeath");
|
||||
GetHealthComponent()->SetMaxHealth(100.0f);
|
||||
|
||||
|
||||
this->Tags.Add(FName("Enemy"));
|
||||
}
|
||||
|
||||
|
@ -49,6 +57,10 @@ void AEnemyCharacter::WeaponCooldownHandler()
|
|||
void AEnemyCharacter::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
AEnemyAIController* controller = Cast<AEnemyAIController>(GetController());
|
||||
controller->GetBlackboardComponent()->SetValueAsFloat("CurrentHealth", GetHealthComponent()->GetCurrentHealth());
|
||||
GetHealthComponent()->OnDamaged.BindUFunction(this, "OnDamaged");
|
||||
}
|
||||
|
||||
void AEnemyCharacter::PlayOnFireAnimations()
|
||||
|
@ -125,3 +137,12 @@ void AEnemyCharacter::ProcessHits(TArray<FHitResult> hits)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AEnemyCharacter::OnDamaged()
|
||||
{
|
||||
Super::OnDamaged();
|
||||
|
||||
AEnemyAIController* controller = Cast<AEnemyAIController>(GetController());
|
||||
controller->GetBlackboardComponent()->SetValueAsFloat("CurrentHealth", GetHealthComponent()->GetCurrentHealth());
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "CoreMinimal.h"
|
||||
#include "NakatomiCharacter.h"
|
||||
#include "PatrolRoute.h"
|
||||
#include "BehaviorTree/BehaviorTreeComponent.h"
|
||||
#include "RandomWeaponParameters.h"
|
||||
#include "EnemyCharacter.generated.h"
|
||||
|
@ -17,6 +18,16 @@ class NAKATOMI_API AEnemyCharacter : public ANakatomiCharacter
|
|||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, Meta = (AllowPrivateAccess = "true"))
|
||||
APatrolRoute* CurrentPatrolRoute;
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
float AttackRadius = 300.0;
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
float DefendRadius = 500.0f;
|
||||
|
||||
private:
|
||||
UPROPERTY(EditDefaultsOnly, Meta = (AllowPrivateAccess = "true"))
|
||||
UBehaviorTree* BehaviourTree;
|
||||
|
@ -44,4 +55,7 @@ private:
|
|||
virtual void CalculateHits(TArray<FHitResult>* hits) override;
|
||||
|
||||
virtual void ProcessHits(TArray<FHitResult> hits) override;
|
||||
|
||||
protected:
|
||||
virtual void OnDamaged() override;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "EnemyHealthComponent.h"
|
||||
|
||||
#include "EnemyAIController.h"
|
||||
#include "EnemyCharacter.h"
|
||||
|
||||
void UEnemyHealthComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
}
|
||||
|
||||
void UEnemyHealthComponent::TakeDamage(AActor* damagedActor, float damage, const UDamageType* damageType,
|
||||
AController* instigatedBy, AActor* damageCauser)
|
||||
{
|
||||
if (damagedActor == nullptr || IsDead || !CanDamage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentHealth -= damage;
|
||||
|
||||
AEnemyCharacter* enemyCharacter = Cast<AEnemyCharacter>(damagedActor);
|
||||
UAISense_Damage::ReportDamageEvent(GetWorld(), damagedActor, damageCauser, 1,
|
||||
damageCauser->GetTransform().GetLocation(),
|
||||
damageCauser->GetTransform().GetLocation());
|
||||
|
||||
OnDamaged.ExecuteIfBound({damagedActor, damage, damageType, instigatedBy, damageCauser});
|
||||
|
||||
if (CurrentHealth <= 0.0f)
|
||||
{
|
||||
IsDead = true;
|
||||
OnDeath.ExecuteIfBound({damagedActor, damage, damageType, instigatedBy, damageCauser});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "HealthComponent.h"
|
||||
#include "EnemyHealthComponent.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class NAKATOMI_API UEnemyHealthComponent : public UHealthComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
virtual void TakeDamage(AActor* damagedActor, float damage, const UDamageType* damageType, AController* instigatedBy, AActor* damageCauser) override;
|
||||
|
||||
};
|
|
@ -39,7 +39,7 @@ public:
|
|||
FOnDamageDelegate OnDamaged;
|
||||
FOnDeathDelegate OnDeath;
|
||||
|
||||
private:
|
||||
protected:
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
float MaxHealth = 100.f;
|
||||
|
||||
|
@ -55,7 +55,7 @@ public:
|
|||
UHealthComponent();
|
||||
|
||||
UFUNCTION()
|
||||
void TakeDamage(AActor* damagedActor, float damage, const UDamageType* damageType, AController* instigatedBy,
|
||||
virtual void TakeDamage(AActor* damagedActor, float damage, const UDamageType* damageType, AController* instigatedBy,
|
||||
AActor* damageCauser);
|
||||
|
||||
UFUNCTION()
|
||||
|
|
|
@ -19,6 +19,12 @@ void UNakatomiCMC::InitializeComponent()
|
|||
NakatomiCharacterOwner = Cast<ANakatomiCharacter>(GetOwner());
|
||||
}
|
||||
|
||||
void UNakatomiCMC::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
DefaultMaxWalkSpeed = Walk_MaxWalkSpeed;
|
||||
}
|
||||
|
||||
// Checks if we can combine the NewMove with the current move to save on data.
|
||||
// If all the data in a saved move is identical, if true, we cam tell the server to run the existing move instead.
|
||||
bool UNakatomiCMC::FSavedMove_Nakatomi::CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* InCharacter,
|
||||
|
@ -271,6 +277,16 @@ bool UNakatomiCMC::IsCustomMovementMode(ECustomMovementMove InCustomMovementMode
|
|||
return MovementMode == MOVE_Custom && CustomMovementMode == InCustomMovementMode;
|
||||
}
|
||||
|
||||
void UNakatomiCMC::SetMaxWalkSpeed(float newMaxSpeed)
|
||||
{
|
||||
Walk_MaxWalkSpeed = newMaxSpeed;
|
||||
}
|
||||
|
||||
void UNakatomiCMC::SetMaxWalkSpeedToDefault()
|
||||
{
|
||||
Walk_MaxWalkSpeed = DefaultMaxWalkSpeed;
|
||||
}
|
||||
|
||||
void UNakatomiCMC::EnterSlide()
|
||||
{
|
||||
Safe_bWantsToSlide = true;
|
||||
|
|
|
@ -108,6 +108,8 @@ class NAKATOMI_API UNakatomiCMC : public UCharacterMovementComponent
|
|||
UPROPERTY(Transient)
|
||||
ANakatomiCharacter* NakatomiCharacterOwner;
|
||||
|
||||
float DefaultMaxWalkSpeed;
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FDashStartDelegate DashStartDelegate;
|
||||
|
@ -118,6 +120,8 @@ public:
|
|||
protected:
|
||||
virtual void InitializeComponent() override;
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
virtual FNetworkPredictionData_Client* GetPredictionData_Client() const override;
|
||||
|
||||
protected:
|
||||
|
@ -167,6 +171,12 @@ public:
|
|||
UFUNCTION()
|
||||
bool IsCustomMovementMode(ECustomMovementMove InCustomMovementMode) const;
|
||||
|
||||
UFUNCTION()
|
||||
void SetMaxWalkSpeed(float newMaxSpeed);
|
||||
|
||||
UFUNCTION()
|
||||
void SetMaxWalkSpeedToDefault();
|
||||
|
||||
private:
|
||||
void EnterSlide();
|
||||
void ExitSlide();
|
||||
|
|
|
@ -14,9 +14,12 @@ ANakatomiCharacter::ANakatomiCharacter(const FObjectInitializer& ObjectInitializ
|
|||
|
||||
NakatomiCMC = Cast<UNakatomiCMC>(GetCharacterMovement());
|
||||
|
||||
HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("Health Component"));
|
||||
HealthComponent->OnDamaged.BindUFunction(this, "OnDamaged");
|
||||
HealthComponent->OnDeath.BindUFunction(this, "OnDeath");
|
||||
// if (!HealthComponent)
|
||||
// {
|
||||
// HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("Health Component"));
|
||||
// HealthComponent->OnDamaged.BindUFunction(this, "OnDamaged");
|
||||
// HealthComponent->OnDeath.BindUFunction(this, "OnDeath");
|
||||
// }
|
||||
}
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "PatrolRoute.h"
|
||||
|
||||
// Sets default values
|
||||
APatrolRoute::APatrolRoute()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = false;
|
||||
|
||||
Spline = CreateDefaultSubobject<USplineComponent>(TEXT("Spline"));
|
||||
Spline->SetupAttachment(RootComponent);
|
||||
}
|
||||
|
||||
void APatrolRoute::IncrementPatrolRoute()
|
||||
{
|
||||
if (PatrolIndex == Spline->GetNumberOfSplinePoints() - 1)
|
||||
{
|
||||
Direction = -1;
|
||||
}
|
||||
else if (PatrolIndex == 0)
|
||||
{
|
||||
Direction = 1;
|
||||
}
|
||||
|
||||
PatrolIndex += Direction;
|
||||
}
|
||||
|
||||
FVector APatrolRoute::GetSplinePointAtWorld()
|
||||
{
|
||||
return GetSplinePointAtWorld(PatrolIndex);
|
||||
}
|
||||
|
||||
FVector APatrolRoute::GetSplinePointAtWorld(int pointIndex)
|
||||
{
|
||||
return Spline->GetLocationAtSplinePoint(pointIndex, ESplineCoordinateSpace::World);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/SplineComponent.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "PatrolRoute.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class NAKATOMI_API APatrolRoute : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
USplineComponent* Spline;
|
||||
|
||||
private:
|
||||
int PatrolIndex = 0;
|
||||
|
||||
int Direction;
|
||||
|
||||
public:
|
||||
// Sets default values for this actor's properties
|
||||
APatrolRoute();
|
||||
|
||||
void IncrementPatrolRoute();
|
||||
|
||||
FVector GetSplinePointAtWorld();
|
||||
|
||||
FVector GetSplinePointAtWorld(int pointIndex);
|
||||
};
|
|
@ -30,6 +30,10 @@ APlayerCharacter::APlayerCharacter(const FObjectInitializer& ObjectInitializer)
|
|||
//bUseControllerRotationYaw = true;
|
||||
//bUseControllerRotationRoll = false;
|
||||
|
||||
SetHealthComponent(CreateDefaultSubobject<UHealthComponent>(TEXT("Health Component")));
|
||||
GetHealthComponent()->OnDamaged.BindUFunction(this, "OnDamaged");
|
||||
GetHealthComponent()->OnDeath.BindUFunction(this, "OnDeath");
|
||||
|
||||
// Setup the camera boom
|
||||
CameraSpringArmComponent = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraSpringArmComponent"));
|
||||
CameraSpringArmComponent->SetupAttachment(RootComponent);
|
||||
|
@ -398,6 +402,7 @@ void APlayerCharacter::ProcessHits(TArray<FHitResult> hits)
|
|||
{
|
||||
healthComponent->TakeDamage(Hit.GetActor(), CurrentWeapon->GetWeaponProperties()->WeaponDamage, nullptr,
|
||||
GetController(), this);
|
||||
|
||||
if (!healthComponent->GetIsDead())
|
||||
{
|
||||
OnEnemyHit.ExecuteIfBound();
|
||||
|
@ -437,6 +442,8 @@ void APlayerCharacter::ProcessHits(TArray<FHitResult> hits)
|
|||
true);
|
||||
}
|
||||
}
|
||||
|
||||
MakeNoise(1, this, Hit.Location);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -639,6 +646,8 @@ void APlayerCharacter::OnFire()
|
|||
|
||||
CurrentWeapon->PlayFireSoundAtLocation(this->GetTransform().GetLocation());
|
||||
|
||||
MakeNoise(1,this, this->GetTransform().GetLocation());
|
||||
|
||||
PlayOnFireAnimations();
|
||||
|
||||
CurrentWeapon->SetCurrentWeaponStatus(Cooldown);
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Tasks/BTTClearFocus.h"
|
||||
#include "../Tasks/BTTClearFocus.h"
|
||||
|
||||
#include "Nakatomi/EnemyAIController.h"
|
||||
|
||||
EBTNodeResult::Type UBTTClearFocus::ExecuteTask(UBehaviorTreeComponent& owner, uint8* memory)
|
||||
{
|
||||
auto enemyController = Cast<AEnemyAIController>(owner.GetAIOwner());
|
||||
enemyController->ClearFocus(EAIFocusPriority::Default);
|
||||
return EBTNodeResult::Succeeded;
|
||||
}
|
||||
|
|
|
@ -13,5 +13,8 @@ UCLASS()
|
|||
class NAKATOMI_API UBTTClearFocus : public UBTTaskNode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& owner, uint8* memory) override;
|
||||
|
||||
};
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "../Tasks/BTTMoveAlongPatrolRoute.h"
|
||||
|
||||
#include "navigationSystem.h"
|
||||
#include "BehaviorTree/BlackboardComponent.h"
|
||||
#include "Nakatomi/EnemyAIController.h"
|
||||
#include "Nakatomi/NakatomiCMC.h"
|
||||
|
||||
EBTNodeResult::Type UBTTMoveAlongPatrolRoute::ExecuteTask(UBehaviorTreeComponent& owner, uint8* memory)
|
||||
{
|
||||
AEnemyAIController* enemyController = Cast<AEnemyAIController>(owner.GetAIOwner());
|
||||
AEnemyCharacter* enemyPawn = Cast<AEnemyCharacter>(enemyController->GetPawn());
|
||||
|
||||
if (enemyPawn->CurrentPatrolRoute)
|
||||
{
|
||||
FVector location = enemyPawn->CurrentPatrolRoute->GetSplinePointAtWorld();
|
||||
UBlackboardComponent* blackboardComponent = owner.GetBlackboardComponent();
|
||||
blackboardComponent->SetValueAsVector(PatrolLocationKey.SelectedKeyName, location);
|
||||
|
||||
enemyPawn->CurrentPatrolRoute->IncrementPatrolRoute();
|
||||
|
||||
return EBTNodeResult::Succeeded;
|
||||
}
|
||||
|
||||
return EBTNodeResult::Failed;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "BehaviorTree/BTTaskNode.h"
|
||||
#include "BTTMoveAlongPatrolRoute.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class NAKATOMI_API UBTTMoveAlongPatrolRoute : public UBTTaskNode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, Category = "Options",
|
||||
Meta = (AllowPrivateAccess = "true", DisplayName = "Patrol Location Key"))
|
||||
FBlackboardKeySelector PatrolLocationKey;
|
||||
|
||||
public:
|
||||
virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& owner, uint8* memory) override;
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "../Tasks/BTTSetMovementSpeed.h"
|
||||
|
||||
#include "Nakatomi/EnemyAIController.h"
|
||||
#include "Nakatomi/NakatomiCMC.h"
|
||||
|
||||
enum class EPatrolMovementEnum : uint8;
|
||||
|
||||
EBTNodeResult::Type UBTTSetMovementSpeed::ExecuteTask(UBehaviorTreeComponent& owner, uint8* memory)
|
||||
{
|
||||
AEnemyAIController* enemyController = Cast<AEnemyAIController>(owner.GetAIOwner());
|
||||
AEnemyCharacter* enemyPawn = Cast<AEnemyCharacter>(enemyController->GetPawn());
|
||||
|
||||
switch (MaxWalkSpeedKey)
|
||||
{
|
||||
case EPatrolMovementEnum::SLOWWALK:
|
||||
enemyPawn->GetCharacterMovementComponent()->SetMaxWalkSpeed(slowWalkSpeed);
|
||||
break;
|
||||
case EPatrolMovementEnum::WALK:
|
||||
enemyPawn->GetCharacterMovementComponent()->SetMaxWalkSpeed(walkSpeed);
|
||||
break;
|
||||
case EPatrolMovementEnum::SPRINT:
|
||||
enemyPawn->GetCharacterMovementComponent()->SetMaxWalkSpeed(sprintSpeed);
|
||||
break;
|
||||
default:
|
||||
enemyPawn->GetCharacterMovementComponent()->SetMaxWalkSpeedToDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
return EBTNodeResult::Succeeded;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "BehaviorTree/BTTaskNode.h"
|
||||
#include "Nakatomi/EPatrolMovementEnum.h"
|
||||
#include "BTTSetMovementSpeed.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class NAKATOMI_API UBTTSetMovementSpeed : public UBTTaskNode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Options",
|
||||
Meta = (AllowPrivateAccess = "true", DisplayName = "Max Walk Speed Key"))
|
||||
EPatrolMovementEnum MaxWalkSpeedKey;
|
||||
|
||||
private:
|
||||
float slowWalkSpeed = 250.0f;
|
||||
|
||||
float walkSpeed = 500.0f;
|
||||
|
||||
float sprintSpeed = 750.0f;
|
||||
|
||||
public:
|
||||
virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& owner, uint8* memory) override;
|
||||
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "../Tasks/BTTSetState.h"
|
||||
|
||||
#include "Nakatomi/EnemyAIController.h"
|
||||
|
||||
EBTNodeResult::Type UBTTSetState::ExecuteTask(UBehaviorTreeComponent& owner, uint8* memory)
|
||||
{
|
||||
if (AEnemyAIController* EnemyController = Cast<AEnemyAIController>(owner.GetAIOwner()))
|
||||
{
|
||||
EnemyController->SetState(NewState);
|
||||
return EBTNodeResult::Succeeded;
|
||||
}
|
||||
|
||||
return EBTNodeResult::Failed;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "BehaviorTree/BTTaskNode.h"
|
||||
#include "Nakatomi/EAIState.h"
|
||||
#include "BTTSetState.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class NAKATOMI_API UBTTSetState : public UBTTaskNode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Options",
|
||||
Meta = (AllowPrivateAccess = "true", DisplayName = "New State"))
|
||||
EAIState NewState;
|
||||
|
||||
public:
|
||||
virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& owner, uint8* memory) override;
|
||||
|
||||
};
|
Loading…
Reference in New Issue