From d005e4543f38eb131e0094d393fc8dca14d58199 Mon Sep 17 00:00:00 2001 From: charnet3d Date: Wed, 6 Mar 2024 14:39:12 +0100 Subject: [PATCH] Section 15: AI and Behavior Trees - Lecture 284 Stunned Blackboard Decorator --- .../_Game/Enemies/Animations/EnemyHit.uasset | 4 +- Content/_Game/Enemies/EnemyBP.uasset | 4 +- Content/_Game/Enemies/GruxAnimBP.uasset | 4 +- .../EnemyController/EnemyBehaviorTree.uasset | 4 +- .../EnemyController/EnemyBlackboard.uasset | 4 +- Content/_Game/Maps/DefaultMap.umap | 4 +- Source/Shooter/Enemy.cpp | 45 +++++++++++++++++-- Source/Shooter/Enemy.h | 26 +++++++++++ Source/Shooter/GruxAnimInstance.cpp | 14 ++++++ Source/Shooter/GruxAnimInstance.h | 15 ++++++- 10 files changed, 108 insertions(+), 16 deletions(-) diff --git a/Content/_Game/Enemies/Animations/EnemyHit.uasset b/Content/_Game/Enemies/Animations/EnemyHit.uasset index 18231c2a..74195cd9 100644 --- a/Content/_Game/Enemies/Animations/EnemyHit.uasset +++ b/Content/_Game/Enemies/Animations/EnemyHit.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82edd6e694de0e0c0138e74fdfc0e939df9fc757e5a2889e5028289e622cbdd0 -size 12586 +oid sha256:9cd4fee853ead2ae252902c63c6977e30c9e3047954fd502ba8b27b086645f34 +size 14599 diff --git a/Content/_Game/Enemies/EnemyBP.uasset b/Content/_Game/Enemies/EnemyBP.uasset index 2f02113f..0045e1ce 100644 --- a/Content/_Game/Enemies/EnemyBP.uasset +++ b/Content/_Game/Enemies/EnemyBP.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60207243d023df1dcd3ef62c14c9f00d3dd4bbaeb02a353a4b93c57e855cbf99 -size 132630 +oid sha256:afc1ca0b48635cfef741333769d512d723ca3f7571916815d28c51c26afd31ac +size 132582 diff --git a/Content/_Game/Enemies/GruxAnimBP.uasset b/Content/_Game/Enemies/GruxAnimBP.uasset index 17b7089a..459a37b2 100644 --- a/Content/_Game/Enemies/GruxAnimBP.uasset +++ b/Content/_Game/Enemies/GruxAnimBP.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f0ddbf7b7698d913466186eb9c24f643cc590d0800dff2eaf60b04d0ea2909a -size 68548 +oid sha256:552c7eb2f715478a0e787e82c170338e086d2cf4af4ec131bcea0236f654a124 +size 87718 diff --git a/Content/_Game/EnemyController/EnemyBehaviorTree.uasset b/Content/_Game/EnemyController/EnemyBehaviorTree.uasset index beffe377..11fb98f3 100644 --- a/Content/_Game/EnemyController/EnemyBehaviorTree.uasset +++ b/Content/_Game/EnemyController/EnemyBehaviorTree.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c553be7d4751b78d3e0c54182f38c772dec797f553a1ed7f225ab6ebbd16a54 -size 5928 +oid sha256:11ff4b9238b0193a8f98f84b2a2bc67ba683bde7a6d0a8d417de0b2f9cf1e081 +size 13198 diff --git a/Content/_Game/EnemyController/EnemyBlackboard.uasset b/Content/_Game/EnemyController/EnemyBlackboard.uasset index ddd31980..5bb6c9d1 100644 --- a/Content/_Game/EnemyController/EnemyBlackboard.uasset +++ b/Content/_Game/EnemyController/EnemyBlackboard.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b69ffc0f53462966801e5013c80d64f97474cb04aca873051f7390007194053f -size 2603 +oid sha256:593304d55215bd4486d74c0aa1a76a3404473831416cca2680d3740640078517 +size 3565 diff --git a/Content/_Game/Maps/DefaultMap.umap b/Content/_Game/Maps/DefaultMap.umap index b8d2af8c..8134432b 100644 --- a/Content/_Game/Maps/DefaultMap.umap +++ b/Content/_Game/Maps/DefaultMap.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72c1d191dfd0235b9a2609b05238abd55452ebedc0181c4cfc9b9bc54dae5bc7 -size 3650510 +oid sha256:822554c9946ebf2222f444df9837ddb0735ac222840db2cd4861afdf30579c54 +size 3652087 diff --git a/Source/Shooter/Enemy.cpp b/Source/Shooter/Enemy.cpp index f6445fe1..3db64b69 100644 --- a/Source/Shooter/Enemy.cpp +++ b/Source/Shooter/Enemy.cpp @@ -11,6 +11,8 @@ #include "DrawDebugHelpers.h" #include "EnemyController.h" #include "BehaviorTree/BlackboardComponent.h" +#include "Components/SphereComponent.h" +#include "ShooterCharacter.h" // Sets default values AEnemy::AEnemy() : @@ -20,10 +22,16 @@ AEnemy::AEnemy() : HitReactTimeMin(.5f), HitReactTimeMax(1.f), bCanHitReact(true), - HitNumberDestroyTime(1.5f) + HitNumberDestroyTime(1.5f), + bStunned(false), + StunChance(.5f) { // Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; + + // Create the Agro Sphere + AgroSphere = CreateDefaultSubobject(TEXT("AgroSphere")); + AgroSphere->SetupAttachment(GetRootComponent()); } // Called when the game starts or when spawned @@ -31,6 +39,8 @@ void AEnemy::BeginPlay() { Super::BeginPlay(); + AgroSphere->OnComponentBeginOverlap.AddDynamic(this, &AEnemy::AgroSphereOverlap); + GetMesh()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Visibility, ECollisionResponse::ECR_Block); // Get the AI Controller @@ -39,12 +49,15 @@ void AEnemy::BeginPlay() const FVector WorldPatrolPoint = UKismetMathLibrary::TransformLocation(GetActorTransform(), PatrolPoint); DrawDebugSphere(GetWorld(), WorldPatrolPoint, 25.f, 12, FColor::Red, true); + const FVector WorldPatrolPoint2 = UKismetMathLibrary::TransformLocation(GetActorTransform(), PatrolPoint2); + DrawDebugSphere(GetWorld(), WorldPatrolPoint2, 25.f, 12, FColor::Blue, true); + if (EnemyController) { EnemyController->GetBlackboardComponent()->SetValueAsVector("PatrolPoint", WorldPatrolPoint); + EnemyController->GetBlackboardComponent()->SetValueAsVector("PatrolPoint2", WorldPatrolPoint2); EnemyController->RunBehaviorTree(BehaviorTree); } - } void AEnemy::ShowHealthBar_Implementation() @@ -108,6 +121,25 @@ void AEnemy::UpdateHitNumbers() } } +void AEnemy::AgroSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, + UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) +{ + if (!OtherActor) return; + auto Character = Cast(OtherActor); + if (!Character) return; + + // Set the value of the Target blackboard key + EnemyController->GetBlackboardComponent()->SetValueAsObject(TEXT("Target"), Character); +} + +void AEnemy::SetStunned(bool Stunned) +{ + bStunned = Stunned; + + if (!EnemyController) return; + EnemyController->GetBlackboardComponent()->SetValueAsBool(TEXT("Stunned"), Stunned); +} + // Called every frame void AEnemy::Tick(float DeltaTime) { @@ -137,7 +169,14 @@ void AEnemy::BulletHit_Implementation(FHitResult HitResult) ShowHealthBar(); - PlayHitMontage(FName("HitReactFront")); + // Determine whether bullet hit stuns + const float Stunned = FMath::FRandRange(0.f, 1.f); + if (Stunned <= StunChance) + { + // Stun the Enemy + PlayHitMontage(FName("HitReactFront")); + SetStunned(true); + } } float AEnemy::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, diff --git a/Source/Shooter/Enemy.h b/Source/Shooter/Enemy.h index 56b04a1d..dc918075 100644 --- a/Source/Shooter/Enemy.h +++ b/Source/Shooter/Enemy.h @@ -12,6 +12,7 @@ class USoundCue; class UAnimMontage; class UBehaviorTree; class AEnemyController; +class USphereComponent; UCLASS() class SHOOTER_API AEnemy : public ACharacter, public IBulletHitInterface @@ -46,6 +47,15 @@ protected: void DestroyHitNumber(UUserWidget* HitNumber); void UpdateHitNumbers(); + + /** Called when something overlaps with the agro sphere */ + UFUNCTION() + void AgroSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, + UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, + const FHitResult& SweepResult); + + UFUNCTION(BlueprintCallable) + void SetStunned(bool Stunned); private: /** Particles to spawn when hit by bullets */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) @@ -102,7 +112,23 @@ private: UPROPERTY(EditAnywhere, Category = "Behavior Tree", meta = (AllowPrivateAccess = true, MakeEditWidget = true)) FVector PatrolPoint; + /** 2nd Point for the enemy to move to */ + UPROPERTY(EditAnywhere, Category = "Behavior Tree", meta = (AllowPrivateAccess = true, MakeEditWidget = true)) + FVector PatrolPoint2; + AEnemyController* EnemyController; + + /** Overlap sphere for when the enemy becomes hostile */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Behavior Tree", meta = (AllowPrivateAccess = true)) + USphereComponent* AgroSphere; + + /** True when playing the get hit animation */ + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) + bool bStunned; + + /** Chance of being stunned. 0: no stun chance, 1: 100% stun chance */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) + float StunChance; public: // Called every frame virtual void Tick(float DeltaTime) override; diff --git a/Source/Shooter/GruxAnimInstance.cpp b/Source/Shooter/GruxAnimInstance.cpp index 4c72b016..fc711730 100644 --- a/Source/Shooter/GruxAnimInstance.cpp +++ b/Source/Shooter/GruxAnimInstance.cpp @@ -3,3 +3,17 @@ #include "GruxAnimInstance.h" +#include "Enemy.h" + +void UGruxAnimInstance::UpdateAnimationProperties(float DeltaTime) +{ + if (!Enemy) + { + Enemy = Cast(TryGetPawnOwner()); + if (!Enemy) return; + } + + FVector Velocity { Enemy->GetVelocity() }; + Velocity.Z = 0.f; + Speed = Velocity.Size(); +} diff --git a/Source/Shooter/GruxAnimInstance.h b/Source/Shooter/GruxAnimInstance.h index 38081686..53e269da 100644 --- a/Source/Shooter/GruxAnimInstance.h +++ b/Source/Shooter/GruxAnimInstance.h @@ -6,6 +6,8 @@ #include "Animation/AnimInstance.h" #include "GruxAnimInstance.generated.h" +class AEnemy; + /** * */ @@ -13,5 +15,16 @@ UCLASS() class SHOOTER_API UGruxAnimInstance : public UAnimInstance { GENERATED_BODY() - + +public: + + UFUNCTION(BlueprintCallable) + void UpdateAnimationProperties(float DeltaTime); +private: + /** Lateral Movement Speed */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = true)) + float Speed; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = true)) + AEnemy* Enemy; };