setup npc visuals
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
[/Script/EngineSettings.GameMapsSettings]
|
[/Script/EngineSettings.GameMapsSettings]
|
||||||
EditorStartupMap=/Game/Test/Maps/TestLevel.TestLevel
|
EditorStartupMap=/Game/Test/Maps/L_Test.L_Test
|
||||||
LocalMapOptions=
|
LocalMapOptions=
|
||||||
TransitionMap=None
|
TransitionMap=None
|
||||||
bUseSplitscreen=True
|
bUseSplitscreen=True
|
||||||
|
|||||||
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.
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -7,6 +7,7 @@
|
|||||||
#include "NPCTypeDefinition.h"
|
#include "NPCTypeDefinition.h"
|
||||||
#include "NPCAIController.h"
|
#include "NPCAIController.h"
|
||||||
#include "NPCTargetLocation.h"
|
#include "NPCTargetLocation.h"
|
||||||
|
#include "NPCVisualsConfig.h"
|
||||||
#include "BehaviorTree/BlackboardComponent.h"
|
#include "BehaviorTree/BlackboardComponent.h"
|
||||||
#include "Components/SkeletalMeshComponent.h"
|
#include "Components/SkeletalMeshComponent.h"
|
||||||
#include "GameFramework/CharacterMovementComponent.h"
|
#include "GameFramework/CharacterMovementComponent.h"
|
||||||
@@ -33,22 +34,22 @@ ANPC::ANPC()
|
|||||||
|
|
||||||
ENPCType ANPC::GetNPCType() const
|
ENPCType ANPC::GetNPCType() const
|
||||||
{
|
{
|
||||||
return NPCTypeDefinition ? NPCTypeDefinition->Type : ENPCType::Walker;
|
return TypeDefinition ? TypeDefinition->Type : ENPCType::Walker;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ANPC::GetObservationWeight() const
|
float ANPC::GetObservationWeight() const
|
||||||
{
|
{
|
||||||
return NPCTypeDefinition ? NPCTypeDefinition->ObservationWeight : 1.0f;
|
return TypeDefinition ? TypeDefinition->ObservationWeight : 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ANPC::ShouldStopToObserve() const
|
bool ANPC::ShouldStopToObserve() const
|
||||||
{
|
{
|
||||||
return NPCTypeDefinition ? NPCTypeDefinition->bStopsToObserve : false;
|
return TypeDefinition ? TypeDefinition->bStopsToObserve : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ANPC::GetObserveDuration() const
|
float ANPC::GetObserveDuration() const
|
||||||
{
|
{
|
||||||
return NPCTypeDefinition ? NPCTypeDefinition->ObserveDurationSeconds : 0.0f;
|
return TypeDefinition ? TypeDefinition->ObserveDurationSeconds : 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ANPC::ActivateFromPool(const FVector& Location, const FRotator& Rotation)
|
void ANPC::ActivateFromPool(const FVector& Location, const FRotator& Rotation)
|
||||||
@@ -97,4 +98,21 @@ void ANPC::DeactivateToPool()
|
|||||||
|
|
||||||
SetActorHiddenInGame(true);
|
SetActorHiddenInGame(true);
|
||||||
SetActorEnableCollision(false);
|
SetActorEnableCollision(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ANPC::Init(ENPCType InType)
|
||||||
|
{
|
||||||
|
if (InType == ENPCType::None)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Type = InType;
|
||||||
|
|
||||||
|
if (!NPCTypeDefinitions.Contains(Type))
|
||||||
|
return;
|
||||||
|
|
||||||
|
UNPCTypeDefinition* Definition = NPCTypeDefinitions[Type];
|
||||||
|
if (!Definition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TypeDefinition = Definition;
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "NPCType.h"
|
#include "NPCType.h"
|
||||||
#include "NPC.generated.h"
|
#include "NPC.generated.h"
|
||||||
|
|
||||||
|
class UNPCVisualsConfig;
|
||||||
class ANPCTargetLocation;
|
class ANPCTargetLocation;
|
||||||
class UNPCTypeDefinition;
|
class UNPCTypeDefinition;
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ public:
|
|||||||
// Behavioral template for this NPC (Walker / Stalker / …). Author one DA_* per type and assign
|
// Behavioral template for this NPC (Walker / Stalker / …). Author one DA_* per type and assign
|
||||||
// here (or on the NPC blueprint). Null falls back to Walker-ish defaults (GDD §10.2, §17.4).
|
// here (or on the NPC blueprint). Null falls back to Walker-ish defaults (GDD §10.2, §17.4).
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "NPC")
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "NPC")
|
||||||
TObjectPtr<UNPCTypeDefinition> NPCTypeDefinition;
|
TMap<ENPCType, TObjectPtr<UNPCTypeDefinition>> NPCTypeDefinitions;
|
||||||
|
|
||||||
UFUNCTION(BlueprintPure, Category = "NPC")
|
UFUNCTION(BlueprintPure, Category = "NPC")
|
||||||
ENPCType GetNPCType() const;
|
ENPCType GetNPCType() const;
|
||||||
@@ -43,8 +44,16 @@ public:
|
|||||||
// layer restart and pick a fresh destination (BT startup lives in BP, §17.5).
|
// layer restart and pick a fresh destination (BT startup lives in BP, §17.5).
|
||||||
void ActivateFromPool(const FVector& Location, const FRotator& Rotation);
|
void ActivateFromPool(const FVector& Location, const FRotator& Rotation);
|
||||||
void DeactivateToPool();
|
void DeactivateToPool();
|
||||||
|
void Init(ENPCType InType);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
UPROPERTY(BlueprintReadWrite, Category = "NPC")
|
UPROPERTY(BlueprintReadWrite, Category = "NPC")
|
||||||
TObjectPtr<ANPCTargetLocation> TargetLocationActor;
|
TObjectPtr<ANPCTargetLocation> TargetLocationActor;
|
||||||
|
|
||||||
|
private:
|
||||||
|
UPROPERTY()
|
||||||
|
ENPCType Type = ENPCType::None;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
TObjectPtr<UNPCTypeDefinition> TypeDefinition;
|
||||||
};
|
};
|
||||||
@@ -14,9 +14,9 @@ USTRUCT(BlueprintType)
|
|||||||
struct FNPCSpawnEntry
|
struct FNPCSpawnEntry
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
UPROPERTY(EditDefaultsOnly, Category = "NPC")
|
UPROPERTY(EditDefaultsOnly, Category = "NPC")
|
||||||
TSubclassOf<ANPC> NPCClass;
|
ENPCType Type = ENPCType::Walker;
|
||||||
|
|
||||||
UPROPERTY(EditDefaultsOnly, Category = "NPC", meta = (ClampMin = "0.0"))
|
UPROPERTY(EditDefaultsOnly, Category = "NPC", meta = (ClampMin = "0.0"))
|
||||||
float Weight = 1.0f;
|
float Weight = 1.0f;
|
||||||
@@ -59,4 +59,7 @@ public:
|
|||||||
// Weighted classes drawn into the pool at prewarm. Empty = director spawns nothing.
|
// Weighted classes drawn into the pool at prewarm. Empty = director spawns nothing.
|
||||||
UPROPERTY(EditDefaultsOnly, Category = "Population")
|
UPROPERTY(EditDefaultsOnly, Category = "Population")
|
||||||
TArray<FNPCSpawnEntry> SpawnTable;
|
TArray<FNPCSpawnEntry> SpawnTable;
|
||||||
|
|
||||||
|
UPROPERTY(EditDefaultsOnly, Category = "NPC")
|
||||||
|
TArray<TSubclassOf<ANPC>> NPCClasses;
|
||||||
};
|
};
|
||||||
@@ -67,11 +67,13 @@ void UNPCDirectorSubsystem::PrewarmPool()
|
|||||||
|
|
||||||
for (int32 i = 0; i < Config->MaxNPCs; ++i)
|
for (int32 i = 0; i < Config->MaxNPCs; ++i)
|
||||||
{
|
{
|
||||||
const TSubclassOf<ANPC> NPCClass = PickWeightedClass();
|
const ENPCType NPCType = PickWeightedClass();
|
||||||
if (!NPCClass)
|
if (NPCType == ENPCType::None)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ANPC* NPC = World->SpawnActor<ANPC>(NPCClass, FVector::ZeroVector, FRotator::ZeroRotator, Params);
|
TSubclassOf<ANPC> SelectedNPCClass = GetRandomNPCClass(Config->NPCClasses);
|
||||||
|
ANPC* NPC = World->SpawnActor<ANPC>(SelectedNPCClass, FVector::ZeroVector, FRotator::ZeroRotator, Params);
|
||||||
|
NPC->Init(NPCType);
|
||||||
if (!NPC)
|
if (!NPC)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -169,31 +171,31 @@ void UNPCDirectorSubsystem::ReturnToPool(ANPC* NPC)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TSubclassOf<ANPC> UNPCDirectorSubsystem::PickWeightedClass() const
|
ENPCType UNPCDirectorSubsystem::PickWeightedClass() const
|
||||||
{
|
{
|
||||||
const UNPCDirectorConfig* Config = GetConfig();
|
const UNPCDirectorConfig* Config = GetConfig();
|
||||||
if (!Config)
|
if (!Config)
|
||||||
return nullptr;
|
return ENPCType::None;
|
||||||
|
|
||||||
float TotalWeight = 0.0f;
|
float TotalWeight = 0.0f;
|
||||||
for (const FNPCSpawnEntry& Entry : Config->SpawnTable)
|
for (const FNPCSpawnEntry& Entry : Config->SpawnTable)
|
||||||
{
|
{
|
||||||
if (Entry.NPCClass && Entry.Weight > 0.0f)
|
if (Entry.Type != ENPCType::None && Entry.Weight > 0.0f)
|
||||||
TotalWeight += Entry.Weight;
|
TotalWeight += Entry.Weight;
|
||||||
}
|
}
|
||||||
if (TotalWeight <= 0.0f)
|
if (TotalWeight <= 0.0f)
|
||||||
return nullptr;
|
return ENPCType::None;
|
||||||
|
|
||||||
float Roll = FMath::FRandRange(0.0f, TotalWeight);
|
float Roll = FMath::FRandRange(0.0f, TotalWeight);
|
||||||
for (const FNPCSpawnEntry& Entry : Config->SpawnTable)
|
for (const FNPCSpawnEntry& Entry : Config->SpawnTable)
|
||||||
{
|
{
|
||||||
if (!Entry.NPCClass || Entry.Weight <= 0.0f)
|
if (Entry.Type == ENPCType::None || Entry.Weight <= 0.0f)
|
||||||
continue;
|
continue;
|
||||||
Roll -= Entry.Weight;
|
Roll -= Entry.Weight;
|
||||||
if (Roll <= 0.0f)
|
if (Roll <= 0.0f)
|
||||||
return Entry.NPCClass;
|
return Entry.Type;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return ENPCType::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UNPCDirectorSubsystem::FindSpawnPoint(const FVector& Around, FVector& OutLocation) const
|
bool UNPCDirectorSubsystem::FindSpawnPoint(const FVector& Around, FVector& OutLocation) const
|
||||||
@@ -219,6 +221,12 @@ bool UNPCDirectorSubsystem::FindSpawnPoint(const FVector& Around, FVector& OutLo
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TSubclassOf<ANPC> UNPCDirectorSubsystem::GetRandomNPCClass(TArray<TSubclassOf<ANPC>> InNPCClasses)
|
||||||
|
{
|
||||||
|
const int32 RandomIndex = FMath::RandRange(0, InNPCClasses.Num() - 1);
|
||||||
|
return InNPCClasses[RandomIndex];
|
||||||
|
}
|
||||||
|
|
||||||
APawn* UNPCDirectorSubsystem::GetPlayerPawn() const
|
APawn* UNPCDirectorSubsystem::GetPlayerPawn() const
|
||||||
{
|
{
|
||||||
return UGameplayStatics::GetPlayerPawn(GetWorld(), 0);
|
return UGameplayStatics::GetPlayerPawn(GetWorld(), 0);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
|
#include "NPCType.h"
|
||||||
#include "Subsystems/WorldSubsystem.h"
|
#include "Subsystems/WorldSubsystem.h"
|
||||||
#include "NakedDesire/Global/TimeOfDaySubsystem.h"
|
#include "NakedDesire/Global/TimeOfDaySubsystem.h"
|
||||||
#include "NPCDirectorSubsystem.generated.h"
|
#include "NPCDirectorSubsystem.generated.h"
|
||||||
@@ -40,8 +41,9 @@ private:
|
|||||||
|
|
||||||
ANPC* TakeFromPool();
|
ANPC* TakeFromPool();
|
||||||
void ReturnToPool(ANPC* NPC);
|
void ReturnToPool(ANPC* NPC);
|
||||||
TSubclassOf<ANPC> PickWeightedClass() const;
|
ENPCType PickWeightedClass() const;
|
||||||
bool FindSpawnPoint(const FVector& Around, FVector& OutLocation) const;
|
bool FindSpawnPoint(const FVector& Around, FVector& OutLocation) const;
|
||||||
|
TSubclassOf<ANPC> GetRandomNPCClass(TArray<TSubclassOf<ANPC>> InNPCClasses);
|
||||||
|
|
||||||
APawn* GetPlayerPawn() const;
|
APawn* GetPlayerPawn() const;
|
||||||
UNPCDirectorConfig* GetConfig() const;
|
UNPCDirectorConfig* GetConfig() const;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
UENUM(BlueprintType)
|
UENUM(BlueprintType)
|
||||||
enum class ENPCType : uint8
|
enum class ENPCType : uint8
|
||||||
{
|
{
|
||||||
|
None UMETA(DIsplayName = "None"),
|
||||||
Walker UMETA(DisplayName = "Walker"),
|
Walker UMETA(DisplayName = "Walker"),
|
||||||
Stalker UMETA(DisplayName = "Stalker"),
|
Stalker UMETA(DisplayName = "Stalker"),
|
||||||
Blogger UMETA(DisplayName = "Blogger"),
|
Blogger UMETA(DisplayName = "Blogger"),
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// © 2025 Naked People Team. All Rights Reserved.
|
||||||
|
|
||||||
|
|
||||||
|
#include "NPCVisualsConfig.h"
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// © 2025 Naked People Team. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Engine/DataAsset.h"
|
||||||
|
#include "NPCVisualsConfig.generated.h"
|
||||||
|
|
||||||
|
UCLASS()
|
||||||
|
class NAKEDDESIRE_API UNPCVisualsConfig : public UPrimaryDataAsset
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UPROPERTY(EditDefaultsOnly, Category = "Visuals")
|
||||||
|
TArray<TObjectPtr<USkeletalMesh>> BodyMeshes;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user