init
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
|
||||
#include "NPC.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
|
||||
ANPC::ANPC()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = false;
|
||||
|
||||
GetCharacterMovement()->MaxWalkSpeed = 200.0f;
|
||||
GetCharacterMovement()->bUseControllerDesiredRotation = true;
|
||||
GetCharacterMovement()->bOrientRotationToMovement = false;
|
||||
bUseControllerRotationYaw = false;
|
||||
AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;
|
||||
}
|
||||
|
||||
void ANPC::Destroyed()
|
||||
{
|
||||
Super::Destroyed();
|
||||
|
||||
OnDestroyed.Broadcast(this);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "NPC.generated.h"
|
||||
|
||||
class ANPC;
|
||||
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FOnNPCDestroyed, ANPC* NPC);
|
||||
|
||||
UCLASS()
|
||||
class NAKEDDESIRE_API ANPC : public ACharacter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ANPC();
|
||||
|
||||
FOnNPCDestroyed OnDestroyed;
|
||||
|
||||
protected:
|
||||
virtual void Destroyed() override;
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
|
||||
#include "NPCAIController.h"
|
||||
|
||||
#include "NavigationSystem.h"
|
||||
#include "NPCTargetLocation.h"
|
||||
#include "AI/NavigationSystemBase.h"
|
||||
#include "BehaviorTree/BlackboardComponent.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "NakedDesire/Global/NakedDesireGameMode.h"
|
||||
#include "NakedDesire/Player/NakedDesireCharacter.h"
|
||||
|
||||
void ANPCAIController::SetShouldReactToPlayer(const bool Value)
|
||||
{
|
||||
Blackboard->SetValueAsBool(FName(TEXT("ShouldReactToPlayer")), Value);
|
||||
}
|
||||
|
||||
void ANPCAIController::OnPossess(APawn* InPawn)
|
||||
{
|
||||
Super::OnPossess(InPawn);
|
||||
|
||||
NavigationSystem = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
|
||||
GameMode = Cast<ANakedDesireGameMode>(UGameplayStatics::GetGameMode(GetWorld()));
|
||||
|
||||
RunBehaviorTree(BehaviorTreeAsset);
|
||||
|
||||
const FVector SpawnLocation = InPawn->GetActorLocation();
|
||||
|
||||
TArray<AActor*> TargetActors;
|
||||
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ANPCTargetLocation::StaticClass(), TargetActors);
|
||||
|
||||
const int RandomIndex = FMath::RandRange(0, TargetActors.Num() - 1);
|
||||
Blackboard->SetValueAsVector("TargetLocation", TargetActors[RandomIndex]->GetActorLocation());
|
||||
Blackboard->SetValueAsVector("SpawnLocation", SpawnLocation);
|
||||
|
||||
PlayerCharacter = Cast<ANakedDesireCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
|
||||
Blackboard->SetValueAsObject("Player", PlayerCharacter);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "DetourCrowdAIController.h"
|
||||
#include "NPCAIController.generated.h"
|
||||
|
||||
class ANakedDesireGameMode;
|
||||
class UNavigationSystemV1;
|
||||
class ANakedDesireCharacter;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class NAKEDDESIRE_API ANPCAIController : public ADetourCrowdAIController
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY()
|
||||
ANakedDesireCharacter* PlayerCharacter = nullptr;
|
||||
|
||||
UPROPERTY()
|
||||
const UNavigationSystemV1* NavigationSystem = nullptr;
|
||||
|
||||
UPROPERTY()
|
||||
ANakedDesireGameMode* GameMode = nullptr;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
UBehaviorTree* BehaviorTreeAsset = nullptr;
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void SetShouldReactToPlayer(bool Value);
|
||||
|
||||
protected:
|
||||
virtual void OnPossess(APawn* InPawn) override;
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
|
||||
#include "NPCSpawner.h"
|
||||
#include "NPC.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "NakedDesire/Global/NakedDesireGameMode.h"
|
||||
|
||||
ANPCSpawner::ANPCSpawner()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = false;
|
||||
}
|
||||
|
||||
void ANPCSpawner::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
PlayerCharacter = UGameplayStatics::GetPlayerCharacter(GetWorld(), 0);
|
||||
|
||||
FTimerHandle TimerHandle;
|
||||
const int32 RandomDelay = FMath::RandRange(5, 30);
|
||||
GetWorldTimerManager().SetTimer(TimerHandle, this, &ANPCSpawner::OnTimerTick, 30, true, RandomDelay);
|
||||
OnTimerTick();
|
||||
|
||||
if (AGameModeBase* CurrentGameMode = UGameplayStatics::GetGameMode(GetWorld()))
|
||||
{
|
||||
GameMode = Cast<ANakedDesireGameMode>(CurrentGameMode);
|
||||
}
|
||||
}
|
||||
|
||||
void ANPCSpawner::OnTimerTick()
|
||||
{
|
||||
if (!PlayerCharacter || !GameMode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool IsPlayerClose = FVector::Dist(PlayerCharacter->GetActorLocation(), GetActorLocation()) < 5000;
|
||||
const FTimecode CurrentTime = GameMode->GetCurrentTime();
|
||||
const bool IsDay = CurrentTime.Hours >= 9.0f && CurrentTime.Hours < 21.00f;
|
||||
const bool CanSpawnMore = IsDay ? NPCs.Num() < MaxCountDay : NPCs.Num() < MaxCountNight;
|
||||
|
||||
if (CanSpawnMore && PlayerCharacter && IsPlayerClose && (FMath::RandBool() && !AlwaysSpawn))
|
||||
{
|
||||
FActorSpawnParameters SpawnParameters;
|
||||
SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
|
||||
const int RandomIndex = FMath::RandRange(0, NPCClasses.Num() - 1);
|
||||
ANPC* NewNPC = GetWorld()->SpawnActor<ANPC>(NPCClasses[RandomIndex], GetActorLocation(), GetActorRotation(), SpawnParameters);
|
||||
NewNPC->OnDestroyed.AddUObject(this, &ANPCSpawner::OnNPCDestroyed);
|
||||
NPCs.Add(NewNPC);
|
||||
}
|
||||
}
|
||||
|
||||
void ANPCSpawner::OnNPCDestroyed(ANPC* NPC)
|
||||
{
|
||||
NPCs.Remove(NPC);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "NPCSpawner.generated.h"
|
||||
|
||||
class ANakedDesireGameMode;
|
||||
class ANPC;
|
||||
|
||||
UCLASS()
|
||||
class NAKEDDESIRE_API ANPCSpawner : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
TArray<TSubclassOf<ANPC>> NPCClasses;
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
bool AlwaysSpawn = false;
|
||||
|
||||
UPROPERTY()
|
||||
ACharacter* PlayerCharacter = nullptr;
|
||||
|
||||
bool IsPlayerInRange = false;
|
||||
|
||||
UPROPERTY()
|
||||
ANakedDesireGameMode* GameMode = nullptr;
|
||||
|
||||
UPROPERTY()
|
||||
TArray<ANPC*> NPCs;
|
||||
|
||||
UPROPERTY(EditAnywhere, meta = (ClampMin = 1, ClampMax = 30, UIMin = 1, UIMax = 30))
|
||||
int MaxCountDay = 10;
|
||||
|
||||
UPROPERTY(EditAnywhere, meta = (ClampMin = 1, ClampMax = 30, UIMin = 1, UIMax = 30))
|
||||
int MaxCountNight = 5;
|
||||
|
||||
public:
|
||||
ANPCSpawner();
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
private:
|
||||
void OnTimerTick();
|
||||
void OnNPCDestroyed(ANPC* NPC);
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
|
||||
#include "NPCTargetLocation.h"
|
||||
|
||||
// Sets default values
|
||||
ANPCTargetLocation::ANPCTargetLocation()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "NPCTargetLocation.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class NAKEDDESIRE_API ANPCTargetLocation : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ANPCTargetLocation();
|
||||
};
|
||||
Reference in New Issue
Block a user