diff --git a/.gitignore b/.gitignore index c671293d..0e2ad45f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,8 +8,7 @@ Saved *.pdb *.exe *-DebugGame.dll -SurvivalGame/Binaries/Win64/SurvivalGame.exp -SurvivalGame/Binaries/Win64/SurvivalGame.lib +SurvivalGame/Binaries SurvivalGame/Releases *.log *.target.xml diff --git a/SurvivalGame/Binaries/Win64/UE4Editor-SurvivalGame.dll b/SurvivalGame/Binaries/Win64/UE4Editor-SurvivalGame.dll deleted file mode 100644 index 88875e61..00000000 Binary files a/SurvivalGame/Binaries/Win64/UE4Editor-SurvivalGame.dll and /dev/null differ diff --git a/SurvivalGame/Source/SurvivalGame/Private/AI/SZombieAIController.cpp b/SurvivalGame/Source/SurvivalGame/Private/AI/SZombieAIController.cpp index d7faae56..c2ae3836 100644 --- a/SurvivalGame/Source/SurvivalGame/Private/AI/SZombieAIController.cpp +++ b/SurvivalGame/Source/SurvivalGame/Private/AI/SZombieAIController.cpp @@ -5,6 +5,9 @@ #include "SZombieCharacter.h" /* AI Specific includes */ +#include "Perception/AISenseConfig_Sight.h" +#include "Perception/AISenseConfig_Hearing.h" +#include "Perception/AIPerceptionComponent.h" #include "BehaviorTree/BehaviorTree.h" #include "BehaviorTree/BehaviorTreeComponent.h" #include "BehaviorTree/BlackboardComponent.h" @@ -16,6 +19,30 @@ ASZombieAIController::ASZombieAIController(const class FObjectInitializer& Objec BehaviorComp = ObjectInitializer.CreateDefaultSubobject(this, TEXT("BehaviorComp")); BlackboardComp = ObjectInitializer.CreateDefaultSubobject(this, TEXT("BlackboardComp")); + /* Setup sight sense config */ + UAISenseConfig_Sight* SightConfig = ObjectInitializer.CreateDefaultSubobject(this, TEXT("Sight Config")); + SightConfig->SightRadius = 2000; + SightConfig->LoseSightRadius = 2500; + SightConfig->PeripheralVisionAngleDegrees = 60.0f; + SightConfig->DetectionByAffiliation.bDetectEnemies = true; + SightConfig->SetMaxAge(2.5f); + + /* Setup hearing sense config */ + UAISenseConfig_Hearing* HearingConfig = ObjectInitializer.CreateDefaultSubobject(this, TEXT("Hearing Config")); + HearingConfig->HearingRange = 600.0f; + HearingConfig->LoSHearingRange = 1200.0f; + HearingConfig->DetectionByAffiliation.bDetectEnemies = true; + HearingConfig->DetectionByAffiliation.bDetectFriendlies = true; + HearingConfig->SetMaxAge(2.5f); + + /* Configure AI perception */ + UAIPerceptionComponent* AIPerceptionComp = CreateDefaultSubobject(TEXT("AIPerceptionComp")); + AIPerceptionComp->OnTargetPerceptionUpdated.AddDynamic(this, &ASZombieAIController::OnTargetPerceptionUpdated); + AIPerceptionComp->ConfigureSense(*SightConfig); + AIPerceptionComp->SetDominantSense(SightConfig->GetSenseImplementation()); + AIPerceptionComp->ConfigureSense(*HearingConfig); + SetPerceptionComponent(*AIPerceptionComp); + /* Match with the AI/ZombieBlackboard */ PatrolLocationKeyName = "PatrolLocation"; CurrentWaypointKeyName = "CurrentWaypoint"; @@ -56,6 +83,24 @@ void ASZombieAIController::UnPossess() } +void ASZombieAIController::OnTargetPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus) +{ + ASBaseCharacter* Target = Cast(Actor); + + if (Target && (!Target->IsAlive() || !Stimulus.IsActive())) + { + Target = nullptr; + } + + SetTargetEnemy(Target); + ASZombieCharacter* ZombieBot = Cast(GetPawn()); + if (ZombieBot) + { + ZombieBot->SetSensedTarget(Target ? true : false); + } +} + + void ASZombieAIController::SetWaypoint(ASBotWaypoint* NewWaypoint) { if (BlackboardComp) @@ -103,3 +148,9 @@ void ASZombieAIController::SetBlackboardBotType(EBotBehaviorType NewType) BlackboardComp->SetValueAsEnum(BotTypeKeyName, (uint8)NewType); } } + + +ETeamAttitude::Type ASZombieAIController::GetTeamAttitudeTowards(const AActor & Other) const +{ + return Cast(&Other) ? ETeamAttitude::Type::Hostile : ETeamAttitude::Type::Friendly; +} diff --git a/SurvivalGame/Source/SurvivalGame/Private/AI/SZombieCharacter.cpp b/SurvivalGame/Source/SurvivalGame/Private/AI/SZombieCharacter.cpp index 7e207f14..ba8a7985 100644 --- a/SurvivalGame/Source/SurvivalGame/Private/AI/SZombieCharacter.cpp +++ b/SurvivalGame/Source/SurvivalGame/Private/AI/SZombieCharacter.cpp @@ -8,9 +8,6 @@ #include "SBotWaypoint.h" #include "SPlayerState.h" -/* AI Include */ -#include "Perception/PawnSensingComponent.h" - // Sets default values ASZombieCharacter::ASZombieCharacter(const class FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) @@ -19,12 +16,10 @@ ASZombieCharacter::ASZombieCharacter(const class FObjectInitializer& ObjectIniti Because the zombie AIController is a blueprint in content and it's better to avoid content references in code. */ /*AIControllerClass = ASZombieAIController::StaticClass();*/ - /* Our sensing component to detect players by visibility and noise checks. */ - PawnSensingComp = CreateDefaultSubobject(TEXT("PawnSensingComp")); - PawnSensingComp->SetPeripheralVisionAngle(60.0f); - PawnSensingComp->SightRadius = 2000; - PawnSensingComp->HearingThreshold = 600; - PawnSensingComp->LOSHearingThreshold = 1200; + /* Ignore this channel or it will absorb the trace impacts instead of the skeletal mesh */ + GetCapsuleComponent()->SetCollisionResponseToChannel(COLLISION_WEAPON, ECR_Ignore); + GetCapsuleComponent()->SetCapsuleHalfHeight(96.0f, false); + GetCapsuleComponent()->SetCapsuleRadius(42.0f); /* Ignore this channel or it will absorb the trace impacts instead of the skeletal mesh */ GetCapsuleComponent()->SetCollisionResponseToChannel(COLLISION_WEAPON, ECR_Ignore); @@ -55,7 +50,6 @@ ASZombieCharacter::ASZombieCharacter(const class FObjectInitializer& ObjectIniti /* By default we will not let the AI patrol, we can override this value per-instance. */ BotType = EBotBehaviorType::Passive; - SenseTimeOut = 2.5f; /* Note: Visual Setup is done in the AI/ZombieCharacter Blueprint file */ } @@ -65,12 +59,6 @@ void ASZombieCharacter::BeginPlay() { Super::BeginPlay(); - /* This is the earliest moment we can bind our delegates to the component */ - if (PawnSensingComp) - { - PawnSensingComp->OnSeePawn.AddDynamic(this, &ASZombieCharacter::OnSeePlayer); - PawnSensingComp->OnHearNoise.AddDynamic(this, &ASZombieCharacter::OnHearNoise); - } if (MeleeCollisionComp) { MeleeCollisionComp->OnComponentBeginOverlap.AddDynamic(this, &ASZombieCharacter::OnMeleeCompBeginOverlap); @@ -88,76 +76,6 @@ void ASZombieCharacter::BeginPlay() } -void ASZombieCharacter::Tick(float DeltaSeconds) -{ - Super::Tick(DeltaSeconds); - - /* Check if the last time we sensed a player is beyond the time out value to prevent bot from endlessly following a player. */ - if (bSensedTarget && (GetWorld()->TimeSeconds - LastSeenTime) > SenseTimeOut - && (GetWorld()->TimeSeconds - LastHeardTime) > SenseTimeOut) - { - ASZombieAIController* AIController = Cast(GetController()); - if (AIController) - { - bSensedTarget = false; - /* Reset */ - AIController->SetTargetEnemy(nullptr); - - /* Stop playing the hunting sound */ - BroadcastUpdateAudioLoop(false); - } - } -} - - -void ASZombieCharacter::OnSeePlayer(APawn* Pawn) -{ - if (!IsAlive()) - { - return; - } - - if (!bSensedTarget) - { - BroadcastUpdateAudioLoop(true); - } - - /* Keep track of the time the player was last sensed in order to clear the target */ - LastSeenTime = GetWorld()->GetTimeSeconds(); - bSensedTarget = true; - - ASZombieAIController* AIController = Cast(GetController()); - ASBaseCharacter* SensedPawn = Cast(Pawn); - if (AIController && SensedPawn->IsAlive()) - { - AIController->SetTargetEnemy(SensedPawn); - } -} - - -void ASZombieCharacter::OnHearNoise(APawn* PawnInstigator, const FVector& Location, float Volume) -{ - if (!IsAlive()) - { - return; - } - - if (!bSensedTarget) - { - BroadcastUpdateAudioLoop(true); - } - - bSensedTarget = true; - LastHeardTime = GetWorld()->GetTimeSeconds(); - - ASZombieAIController* AIController = Cast(GetController()); - if (AIController) - { - AIController->SetTargetEnemy(PawnInstigator); - } -} - - void ASZombieCharacter::PerformMeleeStrike(AActor* HitActor) { if (LastMeleeAttackTime > GetWorld()->GetTimeSeconds() - MeleeStrikeCooldown) @@ -290,6 +208,17 @@ bool ASZombieCharacter::IsSprinting() const } +void ASZombieCharacter::SetSensedTarget(bool bNewSensedTarget) +{ + if (bSensedTarget != bNewSensedTarget) + { + BroadcastUpdateAudioLoop(bSensedTarget); + } + + bSensedTarget = bNewSensedTarget; +} + + void ASZombieCharacter::BroadcastUpdateAudioLoop_Implementation(bool bNewSensedTarget) { /* Start playing the hunting sound and the "noticed player" sound if the state is about to change */ diff --git a/SurvivalGame/Source/SurvivalGame/Private/Player/SBaseCharacter.cpp b/SurvivalGame/Source/SurvivalGame/Private/Player/SBaseCharacter.cpp index cb5a4b21..30680e99 100644 --- a/SurvivalGame/Source/SurvivalGame/Private/Player/SBaseCharacter.cpp +++ b/SurvivalGame/Source/SurvivalGame/Private/Player/SBaseCharacter.cpp @@ -6,7 +6,6 @@ #include "SCharacterMovementComponent.h" #include "SDamageType.h" - ASBaseCharacter::ASBaseCharacter(const class FObjectInitializer& ObjectInitializer) /* Override the movement class from the base class to our own to support multiple speeds (eg. sprinting) */ : Super(ObjectInitializer.SetDefaultSubobjectClass(ACharacter::CharacterMovementComponentName)) @@ -16,9 +15,6 @@ ASBaseCharacter::ASBaseCharacter(const class FObjectInitializer& ObjectInitializ TargetingSpeedModifier = 0.5f; SprintingSpeedModifier = 2.0f; - /* Noise emitter for both players and enemies. This keeps track of MakeNoise data and is used by the pawnsensing component in our SZombieCharacter class */ - NoiseEmitterComp = CreateDefaultSubobject(TEXT("NoiseEmitterComp")); - /* Don't collide with camera checks to keep 3rd person camera at position when zombies or other players are standing behind us */ GetMesh()->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore); GetCapsuleComponent()->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore); diff --git a/SurvivalGame/Source/SurvivalGame/Public/AI/SZombieAIController.h b/SurvivalGame/Source/SurvivalGame/Public/AI/SZombieAIController.h index 47d9faa1..a0a954e0 100644 --- a/SurvivalGame/Source/SurvivalGame/Public/AI/SZombieAIController.h +++ b/SurvivalGame/Source/SurvivalGame/Public/AI/SZombieAIController.h @@ -5,6 +5,7 @@ #include "AIController.h" #include "SCharacter.h" #include "SBotWaypoint.h" +#include "Perception/AIPerceptionTypes.h" #include "SZombieAIController.generated.h" class UBehaviorTreeComponent; @@ -24,6 +25,13 @@ class SURVIVALGAME_API ASZombieAIController : public AAIController virtual void UnPossess() override; + /* Called whenever AI stimulus is updated */ + UFUNCTION() + void OnTargetPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus); + + /* Used to determine whether or not an actor is hostile or friendly */ + virtual ETeamAttitude::Type GetTeamAttitudeTowards(const AActor& Other) const override; + UBehaviorTreeComponent* BehaviorComp; UBlackboardComponent* BlackboardComp; @@ -41,7 +49,6 @@ class SURVIVALGAME_API ASZombieAIController : public AAIController FName BotTypeKeyName; public: - ASBotWaypoint* GetWaypoint(); ASBaseCharacter* GetTargetEnemy(); diff --git a/SurvivalGame/Source/SurvivalGame/Public/AI/SZombieCharacter.h b/SurvivalGame/Source/SurvivalGame/Public/AI/SZombieCharacter.h index 597fc759..b3b90b45 100644 --- a/SurvivalGame/Source/SurvivalGame/Public/AI/SZombieCharacter.h +++ b/SurvivalGame/Source/SurvivalGame/Public/AI/SZombieCharacter.h @@ -10,41 +10,18 @@ class SURVIVALGAME_API ASZombieCharacter : public ASBaseCharacter { GENERATED_BODY() - /* Last time the player was spotted */ - float LastSeenTime; - - /* Last time the player was heard */ - float LastHeardTime; - /* Last time we attacked something */ float LastMeleeAttackTime; - /* Time-out value to clear the sensed position of the player. Should be higher than Sense interval in the PawnSense component not never miss sense ticks. */ - UPROPERTY(EditDefaultsOnly, Category = "AI") - float SenseTimeOut; - /* Resets after sense time-out to avoid unnecessary clearing of target each tick */ bool bSensedTarget; - UPROPERTY(VisibleAnywhere, Category = "AI") - class UPawnSensingComponent* PawnSensingComp; - virtual void BeginPlay() override; - virtual void Tick(float DeltaSeconds) override; - protected: virtual bool IsSprinting() const override; - /* Triggered by pawn sensing component when a pawn is spotted */ - /* When using functions as delegates they need to be marked with UFUNCTION(). We assign this function to FSeePawnDelegate */ - UFUNCTION() - void OnSeePlayer(APawn* Pawn); - - UFUNCTION() - void OnHearNoise(APawn* PawnInstigator, const FVector& Location, float Volume); - UPROPERTY(VisibleAnywhere, Category = "Attacking") UCapsuleComponent* MeleeCollisionComp; @@ -125,4 +102,6 @@ class SURVIVALGAME_API ASZombieCharacter : public ASBaseCharacter /* Change default bot type during gameplay */ void SetBotType(EBotBehaviorType NewType); + + void SetSensedTarget(bool bNewSensedTarget); }; diff --git a/SurvivalGame/Source/SurvivalGame/Public/Player/SBaseCharacter.h b/SurvivalGame/Source/SurvivalGame/Public/Player/SBaseCharacter.h index fda49bde..97705c7f 100644 --- a/SurvivalGame/Source/SurvivalGame/Public/Player/SBaseCharacter.h +++ b/SurvivalGame/Source/SurvivalGame/Public/Player/SBaseCharacter.h @@ -11,9 +11,6 @@ class SURVIVALGAME_API ASBaseCharacter : public ACharacter { GENERATED_BODY() - /* Tracks noise data used by the pawn sensing component */ - UPawnNoiseEmitterComponent* NoiseEmitterComp; - public: // Sets default values for this character's properties ASBaseCharacter(const class FObjectInitializer& ObjectInitializer);