setup npc visuals
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
|
||||
|
||||
[/Script/EngineSettings.GameMapsSettings]
|
||||
EditorStartupMap=/Game/Test/Maps/TestLevel.TestLevel
|
||||
EditorStartupMap=/Game/Test/Maps/L_Test.L_Test
|
||||
LocalMapOptions=
|
||||
TransitionMap=None
|
||||
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 "NPCAIController.h"
|
||||
#include "NPCTargetLocation.h"
|
||||
#include "NPCVisualsConfig.h"
|
||||
#include "BehaviorTree/BlackboardComponent.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
@@ -33,22 +34,22 @@ ANPC::ANPC()
|
||||
|
||||
ENPCType ANPC::GetNPCType() const
|
||||
{
|
||||
return NPCTypeDefinition ? NPCTypeDefinition->Type : ENPCType::Walker;
|
||||
return TypeDefinition ? TypeDefinition->Type : ENPCType::Walker;
|
||||
}
|
||||
|
||||
float ANPC::GetObservationWeight() const
|
||||
{
|
||||
return NPCTypeDefinition ? NPCTypeDefinition->ObservationWeight : 1.0f;
|
||||
return TypeDefinition ? TypeDefinition->ObservationWeight : 1.0f;
|
||||
}
|
||||
|
||||
bool ANPC::ShouldStopToObserve() const
|
||||
{
|
||||
return NPCTypeDefinition ? NPCTypeDefinition->bStopsToObserve : false;
|
||||
return TypeDefinition ? TypeDefinition->bStopsToObserve : false;
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -98,3 +99,20 @@ void ANPC::DeactivateToPool()
|
||||
SetActorHiddenInGame(true);
|
||||
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 "NPC.generated.h"
|
||||
|
||||
class UNPCVisualsConfig;
|
||||
class ANPCTargetLocation;
|
||||
class UNPCTypeDefinition;
|
||||
|
||||
@@ -21,7 +22,7 @@ public:
|
||||
// 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).
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "NPC")
|
||||
TObjectPtr<UNPCTypeDefinition> NPCTypeDefinition;
|
||||
TMap<ENPCType, TObjectPtr<UNPCTypeDefinition>> NPCTypeDefinitions;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "NPC")
|
||||
ENPCType GetNPCType() const;
|
||||
@@ -43,8 +44,16 @@ public:
|
||||
// layer restart and pick a fresh destination (BT startup lives in BP, §17.5).
|
||||
void ActivateFromPool(const FVector& Location, const FRotator& Rotation);
|
||||
void DeactivateToPool();
|
||||
void Init(ENPCType InType);
|
||||
|
||||
protected:
|
||||
UPROPERTY(BlueprintReadWrite, Category = "NPC")
|
||||
TObjectPtr<ANPCTargetLocation> TargetLocationActor;
|
||||
|
||||
private:
|
||||
UPROPERTY()
|
||||
ENPCType Type = ENPCType::None;
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<UNPCTypeDefinition> TypeDefinition;
|
||||
};
|
||||
@@ -16,7 +16,7 @@ struct FNPCSpawnEntry
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "NPC")
|
||||
TSubclassOf<ANPC> NPCClass;
|
||||
ENPCType Type = ENPCType::Walker;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "NPC", meta = (ClampMin = "0.0"))
|
||||
float Weight = 1.0f;
|
||||
@@ -59,4 +59,7 @@ public:
|
||||
// Weighted classes drawn into the pool at prewarm. Empty = director spawns nothing.
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Population")
|
||||
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)
|
||||
{
|
||||
const TSubclassOf<ANPC> NPCClass = PickWeightedClass();
|
||||
if (!NPCClass)
|
||||
const ENPCType NPCType = PickWeightedClass();
|
||||
if (NPCType == ENPCType::None)
|
||||
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)
|
||||
continue;
|
||||
|
||||
@@ -169,31 +171,31 @@ void UNPCDirectorSubsystem::ReturnToPool(ANPC* NPC)
|
||||
}
|
||||
}
|
||||
|
||||
TSubclassOf<ANPC> UNPCDirectorSubsystem::PickWeightedClass() const
|
||||
ENPCType UNPCDirectorSubsystem::PickWeightedClass() const
|
||||
{
|
||||
const UNPCDirectorConfig* Config = GetConfig();
|
||||
if (!Config)
|
||||
return nullptr;
|
||||
return ENPCType::None;
|
||||
|
||||
float TotalWeight = 0.0f;
|
||||
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;
|
||||
}
|
||||
if (TotalWeight <= 0.0f)
|
||||
return nullptr;
|
||||
return ENPCType::None;
|
||||
|
||||
float Roll = FMath::FRandRange(0.0f, TotalWeight);
|
||||
for (const FNPCSpawnEntry& Entry : Config->SpawnTable)
|
||||
{
|
||||
if (!Entry.NPCClass || Entry.Weight <= 0.0f)
|
||||
if (Entry.Type == ENPCType::None || Entry.Weight <= 0.0f)
|
||||
continue;
|
||||
Roll -= Entry.Weight;
|
||||
if (Roll <= 0.0f)
|
||||
return Entry.NPCClass;
|
||||
return Entry.Type;
|
||||
}
|
||||
return nullptr;
|
||||
return ENPCType::None;
|
||||
}
|
||||
|
||||
bool UNPCDirectorSubsystem::FindSpawnPoint(const FVector& Around, FVector& OutLocation) const
|
||||
@@ -219,6 +221,12 @@ bool UNPCDirectorSubsystem::FindSpawnPoint(const FVector& Around, FVector& OutLo
|
||||
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
|
||||
{
|
||||
return UGameplayStatics::GetPlayerPawn(GetWorld(), 0);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "NPCType.h"
|
||||
#include "Subsystems/WorldSubsystem.h"
|
||||
#include "NakedDesire/Global/TimeOfDaySubsystem.h"
|
||||
#include "NPCDirectorSubsystem.generated.h"
|
||||
@@ -40,8 +41,9 @@ private:
|
||||
|
||||
ANPC* TakeFromPool();
|
||||
void ReturnToPool(ANPC* NPC);
|
||||
TSubclassOf<ANPC> PickWeightedClass() const;
|
||||
ENPCType PickWeightedClass() const;
|
||||
bool FindSpawnPoint(const FVector& Around, FVector& OutLocation) const;
|
||||
TSubclassOf<ANPC> GetRandomNPCClass(TArray<TSubclassOf<ANPC>> InNPCClasses);
|
||||
|
||||
APawn* GetPlayerPawn() const;
|
||||
UNPCDirectorConfig* GetConfig() const;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
UENUM(BlueprintType)
|
||||
enum class ENPCType : uint8
|
||||
{
|
||||
None UMETA(DIsplayName = "None"),
|
||||
Walker UMETA(DisplayName = "Walker"),
|
||||
Stalker UMETA(DisplayName = "Stalker"),
|
||||
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