Add placed actor persistence foundation
This commit is contained in:
@@ -24,6 +24,9 @@
|
|||||||
- [x] Resource nodes can use item definition assets for harvest yields.
|
- [x] Resource nodes can use item definition assets for harvest yields.
|
||||||
- [x] Building placement component added.
|
- [x] Building placement component added.
|
||||||
- [x] Character now owns a building placement component.
|
- [x] Character now owns a building placement component.
|
||||||
|
- [x] Persistent actor component added.
|
||||||
|
- [x] Persistence subsystem can capture and restore saveable world actors.
|
||||||
|
- [x] Primitive shelter actor is marked as a persistent world actor.
|
||||||
|
|
||||||
## Next Unreal Editor Tasks
|
## Next Unreal Editor Tasks
|
||||||
|
|
||||||
@@ -45,6 +48,6 @@
|
|||||||
- [x] Add building placement component.
|
- [x] Add building placement component.
|
||||||
- [x] Add simple crafting recipe defaults or data asset pipeline.
|
- [x] Add simple crafting recipe defaults or data asset pipeline.
|
||||||
- [x] Add item definition data asset class.
|
- [x] Add item definition data asset class.
|
||||||
- [ ] Add save/load capture for placed actors.
|
- [x] Add save/load capture for placed actors.
|
||||||
- [ ] Add admin/dev console commands.
|
- [ ] Add admin/dev console commands.
|
||||||
- [ ] Add wildlife base actor.
|
- [ ] Add wildlife base actor.
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
// Copyright Pacificao. All Rights Reserved.
|
// Copyright Pacificao. All Rights Reserved.
|
||||||
|
|
||||||
#include "AgrarianPersistenceSubsystem.h"
|
#include "AgrarianPersistenceSubsystem.h"
|
||||||
|
#include "AgrarianPersistentActorComponent.h"
|
||||||
#include "AgrarianSaveGame.h"
|
#include "AgrarianSaveGame.h"
|
||||||
|
#include "EngineUtils.h"
|
||||||
|
#include "Engine/World.h"
|
||||||
#include "Kismet/GameplayStatics.h"
|
#include "Kismet/GameplayStatics.h"
|
||||||
|
|
||||||
UAgrarianSaveGame* UAgrarianPersistenceSubsystem::CreateEmptySave() const
|
UAgrarianSaveGame* UAgrarianPersistenceSubsystem::CreateEmptySave() const
|
||||||
@@ -31,3 +34,121 @@ bool UAgrarianPersistenceSubsystem::DoesSaveExist() const
|
|||||||
{
|
{
|
||||||
return UGameplayStatics::DoesSaveGameExist(DefaultSlotName, UserIndex);
|
return UGameplayStatics::DoesSaveGameExist(DefaultSlotName, UserIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UAgrarianPersistenceSubsystem::RegisterWorldActorClass(FName ActorTypeId, TSubclassOf<AActor> ActorClass)
|
||||||
|
{
|
||||||
|
if (ActorTypeId != NAME_None && ActorClass)
|
||||||
|
{
|
||||||
|
WorldActorClassRegistry.Add(ActorTypeId, ActorClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 UAgrarianPersistenceSubsystem::CaptureWorldActors(UAgrarianSaveGame* SaveGame) const
|
||||||
|
{
|
||||||
|
if (!SaveGame)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<UAgrarianPersistentActorComponent*> PersistentComponents;
|
||||||
|
FindPersistentComponents(PersistentComponents);
|
||||||
|
|
||||||
|
SaveGame->WorldActors.Reset();
|
||||||
|
for (const UAgrarianPersistentActorComponent* Component : PersistentComponents)
|
||||||
|
{
|
||||||
|
if (Component && Component->IsSaveable())
|
||||||
|
{
|
||||||
|
SaveGame->WorldActors.Add(Component->CaptureSaveState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SaveGame->WorldActors.Num();
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 UAgrarianPersistenceSubsystem::RestoreWorldActors(const UAgrarianSaveGame* SaveGame, bool bClearExistingActors) const
|
||||||
|
{
|
||||||
|
UWorld* World = GetWorld();
|
||||||
|
if (!World || !SaveGame)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bClearExistingActors)
|
||||||
|
{
|
||||||
|
TArray<UAgrarianPersistentActorComponent*> ExistingComponents;
|
||||||
|
FindPersistentComponents(ExistingComponents);
|
||||||
|
|
||||||
|
for (UAgrarianPersistentActorComponent* Component : ExistingComponents)
|
||||||
|
{
|
||||||
|
if (AActor* Owner = Component ? Component->GetOwner() : nullptr)
|
||||||
|
{
|
||||||
|
Owner->Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 RestoredCount = 0;
|
||||||
|
for (const FAgrarianSavedWorldActor& SavedActor : SaveGame->WorldActors)
|
||||||
|
{
|
||||||
|
const TSubclassOf<AActor>* ActorClass = WorldActorClassRegistry.Find(SavedActor.ActorTypeId);
|
||||||
|
if (!ActorClass || !(*ActorClass))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
FActorSpawnParameters SpawnParams;
|
||||||
|
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||||
|
|
||||||
|
AActor* SpawnedActor = World->SpawnActor<AActor>(*ActorClass, SavedActor.Transform, SpawnParams);
|
||||||
|
if (!SpawnedActor)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UAgrarianPersistentActorComponent* Component = SpawnedActor->FindComponentByClass<UAgrarianPersistentActorComponent>())
|
||||||
|
{
|
||||||
|
Component->ApplySaveState(SavedActor);
|
||||||
|
}
|
||||||
|
|
||||||
|
RestoredCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RestoredCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UAgrarianPersistenceSubsystem::SaveCurrentWorld() const
|
||||||
|
{
|
||||||
|
UAgrarianSaveGame* SaveGame = LoadOrCreateSave();
|
||||||
|
if (!SaveGame)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CaptureWorldActors(SaveGame);
|
||||||
|
return WriteSave(SaveGame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UAgrarianPersistenceSubsystem::FindPersistentComponents(TArray<UAgrarianPersistentActorComponent*>& OutComponents) const
|
||||||
|
{
|
||||||
|
OutComponents.Reset();
|
||||||
|
|
||||||
|
UWorld* World = GetWorld();
|
||||||
|
if (!World)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (TActorIterator<AActor> ActorIt(World); ActorIt; ++ActorIt)
|
||||||
|
{
|
||||||
|
AActor* Actor = *ActorIt;
|
||||||
|
if (!Actor || Actor->IsPendingKillPending())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UAgrarianPersistentActorComponent* Component = Actor->FindComponentByClass<UAgrarianPersistentActorComponent>())
|
||||||
|
{
|
||||||
|
OutComponents.Add(Component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "AgrarianPersistenceSubsystem.generated.h"
|
#include "AgrarianPersistenceSubsystem.generated.h"
|
||||||
|
|
||||||
class UAgrarianSaveGame;
|
class UAgrarianSaveGame;
|
||||||
|
class UAgrarianPersistentActorComponent;
|
||||||
|
|
||||||
UCLASS()
|
UCLASS()
|
||||||
class UAgrarianPersistenceSubsystem : public UGameInstanceSubsystem
|
class UAgrarianPersistenceSubsystem : public UGameInstanceSubsystem
|
||||||
@@ -20,6 +21,9 @@ public:
|
|||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Persistence")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Persistence")
|
||||||
int32 UserIndex = 0;
|
int32 UserIndex = 0;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Persistence")
|
||||||
|
TMap<FName, TSubclassOf<AActor>> WorldActorClassRegistry;
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "Agrarian|Persistence")
|
UFUNCTION(BlueprintCallable, Category = "Agrarian|Persistence")
|
||||||
UAgrarianSaveGame* CreateEmptySave() const;
|
UAgrarianSaveGame* CreateEmptySave() const;
|
||||||
|
|
||||||
@@ -31,4 +35,19 @@ public:
|
|||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "Agrarian|Persistence")
|
UFUNCTION(BlueprintCallable, Category = "Agrarian|Persistence")
|
||||||
bool DoesSaveExist() const;
|
bool DoesSaveExist() const;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Agrarian|Persistence")
|
||||||
|
void RegisterWorldActorClass(FName ActorTypeId, TSubclassOf<AActor> ActorClass);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Agrarian|Persistence")
|
||||||
|
int32 CaptureWorldActors(UAgrarianSaveGame* SaveGame) const;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Agrarian|Persistence")
|
||||||
|
int32 RestoreWorldActors(const UAgrarianSaveGame* SaveGame, bool bClearExistingActors = true) const;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Agrarian|Persistence")
|
||||||
|
bool SaveCurrentWorld() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void FindPersistentComponents(TArray<UAgrarianPersistentActorComponent*>& OutComponents) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright Pacificao. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "AgrarianPersistentActorComponent.h"
|
||||||
|
|
||||||
|
UAgrarianPersistentActorComponent::UAgrarianPersistentActorComponent()
|
||||||
|
{
|
||||||
|
PrimaryComponentTick.bCanEverTick = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UAgrarianPersistentActorComponent::IsSaveable() const
|
||||||
|
{
|
||||||
|
return ActorTypeId != NAME_None && GetOwner() != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
FAgrarianSavedWorldActor UAgrarianPersistentActorComponent::CaptureSaveState() const
|
||||||
|
{
|
||||||
|
FAgrarianSavedWorldActor SavedActor;
|
||||||
|
SavedActor.ActorTypeId = ActorTypeId;
|
||||||
|
SavedActor.StringState = StringState;
|
||||||
|
SavedActor.NumberState = NumberState;
|
||||||
|
|
||||||
|
if (bSaveTransform && GetOwner())
|
||||||
|
{
|
||||||
|
SavedActor.Transform = GetOwner()->GetActorTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
return SavedActor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UAgrarianPersistentActorComponent::ApplySaveState(const FAgrarianSavedWorldActor& SavedActor)
|
||||||
|
{
|
||||||
|
StringState = SavedActor.StringState;
|
||||||
|
NumberState = SavedActor.NumberState;
|
||||||
|
|
||||||
|
if (ActorTypeId == NAME_None)
|
||||||
|
{
|
||||||
|
ActorTypeId = SavedActor.ActorTypeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bSaveTransform && GetOwner())
|
||||||
|
{
|
||||||
|
GetOwner()->SetActorTransform(SavedActor.Transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright Pacificao. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Components/ActorComponent.h"
|
||||||
|
#include "AgrarianSaveGame.h"
|
||||||
|
#include "AgrarianPersistentActorComponent.generated.h"
|
||||||
|
|
||||||
|
UCLASS(ClassGroup = (Agrarian), BlueprintType, Blueprintable, meta = (BlueprintSpawnableComponent))
|
||||||
|
class UAgrarianPersistentActorComponent : public UActorComponent
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UAgrarianPersistentActorComponent();
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Persistence")
|
||||||
|
FName ActorTypeId = NAME_None;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Persistence")
|
||||||
|
bool bSaveTransform = true;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Persistence")
|
||||||
|
TMap<FName, FString> StringState;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Persistence")
|
||||||
|
TMap<FName, float> NumberState;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Agrarian|Persistence")
|
||||||
|
bool IsSaveable() const;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Agrarian|Persistence")
|
||||||
|
FAgrarianSavedWorldActor CaptureSaveState() const;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Agrarian|Persistence")
|
||||||
|
void ApplySaveState(const FAgrarianSavedWorldActor& SavedActor);
|
||||||
|
};
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright Pacificao. All Rights Reserved.
|
// Copyright Pacificao. All Rights Reserved.
|
||||||
|
|
||||||
#include "AgrarianShelterActor.h"
|
#include "AgrarianShelterActor.h"
|
||||||
|
#include "AgrarianPersistentActorComponent.h"
|
||||||
#include "Components/BoxComponent.h"
|
#include "Components/BoxComponent.h"
|
||||||
#include "Components/StaticMeshComponent.h"
|
#include "Components/StaticMeshComponent.h"
|
||||||
|
|
||||||
@@ -15,4 +16,7 @@ AAgrarianShelterActor::AAgrarianShelterActor()
|
|||||||
ProtectionVolume->SetupAttachment(RootComponent);
|
ProtectionVolume->SetupAttachment(RootComponent);
|
||||||
ProtectionVolume->SetBoxExtent(FVector(250.0f, 250.0f, 180.0f));
|
ProtectionVolume->SetBoxExtent(FVector(250.0f, 250.0f, 180.0f));
|
||||||
ProtectionVolume->SetCollisionProfileName(TEXT("OverlapAllDynamic"));
|
ProtectionVolume->SetCollisionProfileName(TEXT("OverlapAllDynamic"));
|
||||||
|
|
||||||
|
PersistentActorComponent = CreateDefaultSubobject<UAgrarianPersistentActorComponent>(TEXT("PersistentActorComponent"));
|
||||||
|
PersistentActorComponent->ActorTypeId = TEXT("primitive_shelter");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "AgrarianShelterActor.generated.h"
|
#include "AgrarianShelterActor.generated.h"
|
||||||
|
|
||||||
class UBoxComponent;
|
class UBoxComponent;
|
||||||
|
class UAgrarianPersistentActorComponent;
|
||||||
class UStaticMeshComponent;
|
class UStaticMeshComponent;
|
||||||
|
|
||||||
UCLASS(Blueprintable)
|
UCLASS(Blueprintable)
|
||||||
@@ -23,6 +24,9 @@ public:
|
|||||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Shelter")
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Shelter")
|
||||||
TObjectPtr<UBoxComponent> ProtectionVolume;
|
TObjectPtr<UBoxComponent> ProtectionVolume;
|
||||||
|
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Shelter")
|
||||||
|
TObjectPtr<UAgrarianPersistentActorComponent> PersistentActorComponent;
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Shelter", meta = (ClampMin = "0", ClampMax = "1"))
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Shelter", meta = (ClampMin = "0", ClampMax = "1"))
|
||||||
float WeatherProtection = 0.65f;
|
float WeatherProtection = 0.65f;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user