From 7a7f15881644328e91b0c3c2d57a97b8f9eb13e5 Mon Sep 17 00:00:00 2001 From: Louis Hobbs Date: Fri, 3 Feb 2023 01:29:38 +0000 Subject: [PATCH] Add EnemyAIController class --- Source/Nakatomi/EnemyAIController.cpp | 112 ++++++++++++++++++++++++++ Source/Nakatomi/EnemyAIController.h | 48 +++++++++++ 2 files changed, 160 insertions(+) create mode 100644 Source/Nakatomi/EnemyAIController.cpp create mode 100644 Source/Nakatomi/EnemyAIController.h diff --git a/Source/Nakatomi/EnemyAIController.cpp b/Source/Nakatomi/EnemyAIController.cpp new file mode 100644 index 0000000..411525b --- /dev/null +++ b/Source/Nakatomi/EnemyAIController.cpp @@ -0,0 +1,112 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "EnemyAIController.h" +#include "Engine/EngineTypes.h" +#include "Components/CapsuleComponent.h" + +AEnemyAIController::AEnemyAIController(const FObjectInitializer& object_initializer) +{ + Blackboard = CreateDefaultSubobject(TEXT("Blackboard")); + BehaviorTree = CreateDefaultSubobject(TEXT("Behavior Tree")); +} + +void AEnemyAIController::OnPossess(APawn* InPawn) +{ + Super::OnPossess(InPawn); + + auto enemy = Cast(InPawn); + check(enemy); + + SetPerceptionComponent(*enemy->GetPerceptionComponent()); + enemy->GetPerceptionComponent()->OnPerceptionUpdated.AddDynamic(this, &AEnemyAIController::OnPerceptionUpdated); + enemy->GetCharacterMovement()->MaxWalkSpeed = 500.f; + enemy->GetHealthComponent()->OnDamaged.BindUFunction(this, "OnDamaged"); + enemy->GetHealthComponent()->OnDeath.BindUFunction(this, "OnDeath"); + + if (auto behaviourTree = enemy->GetBehaviourTree()) + { + Blackboard->InitializeBlackboard(*behaviourTree->BlackboardAsset); + BehaviorTree->StartTree(*behaviourTree); + } + + //ensure(enemy->GetMovementComponent()->UseAccelerationForPathFollowing()); +} + +void AEnemyAIController::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); +} + +AEnemyCharacter* AEnemyAIController::GetEnemyCharacter() +{ + return Cast(GetCharacter()); +} + +void AEnemyAIController::OnDamaged(FDamageInfo info) +{ + if (GetEnemyCharacter()->GetHealthComponent()->GetCurrentHealth() > 0.0f) + { + // TODO: Do stuff here + } +} + +void AEnemyAIController::OnDeath(FDamageInfo info) +{ + // TODO: Do stuff here + + auto enemy = GetEnemyCharacter(); + + enemy->DetachFromControllerPendingDestroy(); + enemy->GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision); + enemy->GetCapsuleComponent()->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore); + + enemy->GetMesh()->SetCollisionProfileName("Ragdoll"); + enemy->SetActorEnableCollision(true); + + enemy->GetMesh()->SetAllBodiesSimulatePhysics(true); + enemy->GetMesh()->SetSimulatePhysics(true); + enemy->GetMesh()->WakeAllRigidBodies(); + enemy->GetMesh()->bBlendPhysics = true; + + if (auto characterMovementComponent = enemy->GetCharacterMovement()) + { + characterMovementComponent->StopMovementImmediately(); + characterMovementComponent->DisableMovement(); + characterMovementComponent->SetComponentTickEnabled(false); + } + + enemy->SetLifeSpan(10.0f); +} + +void AEnemyAIController::OnPerceptionUpdated(const TArray& actors) +{ + for (auto actor : actors) + { + if (!actor->GetClass()->IsChildOf(APlayerCharacter::StaticClass())) + { + continue; + } + + FActorPerceptionBlueprintInfo perceptionInfo; + GetPerceptionComponent()->GetActorsPerception(actor, perceptionInfo); + + for (auto& stimulus : perceptionInfo.LastSensedStimuli) + { + if (!stimulus.IsValid() || stimulus.IsExpired()) + { + continue; + } + + if (stimulus.IsActive()) + { + PlayerCharacter = Cast(actor); + SetFocus(PlayerCharacter, EAIFocusPriority::Gameplay); + } + else + { + PlayerCharacter = nullptr; + SetFocus(nullptr, EAIFocusPriority::Gameplay); + } + } + } +} diff --git a/Source/Nakatomi/EnemyAIController.h b/Source/Nakatomi/EnemyAIController.h new file mode 100644 index 0000000..ffda70c --- /dev/null +++ b/Source/Nakatomi/EnemyAIController.h @@ -0,0 +1,48 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "AIController.h" +#include "BehaviorTree/BehaviorTreeComponent.h" +#include "BehaviorTree/BehaviorTree.h" +#include "BehaviorTree/BlackboardComponent.h" +#include "EnemyCharacter.h" +#include "GameFramework/CharacterMovementComponent.h" +#include "PlayerCharacter.h" +#include "EnemyAIController.generated.h" + +/** + * + */ +UCLASS() +class NAKATOMI_API AEnemyAIController : public AAIController +{ + GENERATED_BODY() + +private: + + UBlackboardComponent* Blackboard; + + UBehaviorTreeComponent* BehaviorTree; + + APlayerCharacter* PlayerCharacter; + +public: + AEnemyAIController(const FObjectInitializer& object_initializer); + + virtual void OnPossess(APawn* InPawn) override; + + virtual void Tick(float DeltaTime) override; + + virtual AEnemyCharacter* GetEnemyCharacter(); + + UFUNCTION() + virtual void OnDamaged(FDamageInfo info); + + UFUNCTION() + virtual void OnDeath(FDamageInfo info); + + UFUNCTION() + void OnPerceptionUpdated(const TArray& actors); +};