From 0663e3192ea48034179607dc473314af2340bc38 Mon Sep 17 00:00:00 2001 From: _Redstone_c_ <2824517378@qq.com> Date: Sun, 13 Dec 2020 11:20:17 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20AutoSave=20=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E7=AE=A1=E7=90=86=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复 AVoxelWorld::CenterAgent 为空时 RE 修复区块不能根据距离正常卸载的问题 --- Source/Voxel/Private/Chunk/VoxelChunk.cpp | 139 +++++++++------------- Source/Voxel/Private/VoxelHelper.cpp | 30 ++++- Source/Voxel/Private/VoxelWorld.cpp | 44 +++++-- Source/Voxel/Public/Chunk/VoxelChunk.h | 31 ++++- Source/Voxel/Public/VoxelHelper.h | 3 + Source/Voxel/Public/VoxelWorld.h | 11 ++ Source/Voxel/Voxel.Build.cs | 1 + Voxel.uplugin | 16 ++- 8 files changed, 168 insertions(+), 107 deletions(-) diff --git a/Source/Voxel/Private/Chunk/VoxelChunk.cpp b/Source/Voxel/Private/Chunk/VoxelChunk.cpp index 90278b5..5bec6bd 100644 --- a/Source/Voxel/Private/Chunk/VoxelChunk.cpp +++ b/Source/Voxel/Private/Chunk/VoxelChunk.cpp @@ -6,6 +6,24 @@ #include "ProceduralMeshComponent.h" #include "KismetProceduralMeshLibrary.h" +bool FVoxelChunkData::Serialize(FArchive & Slot) +{ + UScriptStruct* VoxelBlockStruct = FVoxelBlock::StaticStruct(); + + for (int32 X = 0; X < 16; ++X) + { + for (int32 Y = 0; Y < 16; ++Y) + { + for (int32 Z = 0; Z < 256; ++Z) + { + VoxelBlockStruct->SerializeItem(Slot, &Blocks[X][Y][Z], nullptr); + } + } + } + + return true; +} + AVoxelChunk::AVoxelChunk(const class FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { @@ -24,14 +42,35 @@ AVoxelChunk::AVoxelChunk(const class FObjectInitializer& ObjectInitializer) void AVoxelChunk::BeginPlay() { + Super::BeginPlay(); + UGameInstance* GameInstance = GetGameInstance(); VoxelSubsystem = GameInstance->GetSubsystem(); + + AttachToActor(GetOwner(), FAttachmentTransformRules::KeepRelativeTransform); + FlushMaterials(); + + ArchiveFile = VoxelWorld->GetWorldSetting().ArchiveFolder / FString::Printf(TEXT("%08X%08X"), ChunkLocation.X, ChunkLocation.Y); + + FSaveStructLoadDelegate OnLoaded; + OnLoaded.BindUFunction(this, TEXT("OnDataLoaded")); + Data = MakeShared>(GetGameInstance()->GetSubsystem(), ArchiveFile, OnLoaded); + + check(Data->IsValid()); +} + +void AVoxelChunk::EndPlay(const EEndPlayReason::Type EndPlayReason) +{ + Data = nullptr; + + Super::EndPlay(EndPlayReason); } const FVoxelBlock & AVoxelChunk::GetBlockByRelativeLocation(const FIntVector & Location) const { checkCode( - if (!FMath::IsWithin(Location.X, 0, 16) + if (!Data->IsLoaded() + || !FMath::IsWithin(Location.X, 0, 16) || !FMath::IsWithin(Location.Y, 0, 16) || !FMath::IsWithin(Location.Z, 0, 256)) { @@ -40,13 +79,14 @@ const FVoxelBlock & AVoxelChunk::GetBlockByRelativeLocation(const FIntVector & L } ); - return Blocks[Location.X][Location.Y][Location.Z]; + return (*Data)->Blocks[Location.X][Location.Y][Location.Z]; } void AVoxelChunk::SetBlockByRelativeLocation(const FIntVector& Location, const FVoxelBlock& NewBlock) { checkCode( - if (!FMath::IsWithin(Location.X, 0, 16) + if (!Data->IsLoaded() + || !FMath::IsWithin(Location.X, 0, 16) || !FMath::IsWithin(Location.Y, 0, 16) || !FMath::IsWithin(Location.Z, 0, 256)) { @@ -55,7 +95,7 @@ void AVoxelChunk::SetBlockByRelativeLocation(const FIntVector& Location, const F } ); - Blocks[Location.X][Location.Y][Location.Z] = NewBlock; + (*Data)->Blocks[Location.X][Location.Y][Location.Z] = NewBlock; FlushMeshFlags |= 1 << (Location.Z / 16); @@ -146,78 +186,10 @@ void AVoxelChunk::FlushMaterials() } } -void AVoxelChunk::Load() -{ - QUICK_SCOPE_CYCLE_COUNTER(STAT_VoxelChunk_Load); - - if (VoxelWorld->GetWorldSetting().ArchiveFolder.IsEmpty()) - { - ArchiveFile.Empty(); - return; - } - - ArchiveFile = VoxelWorld->GetWorldSetting().ArchiveFolder / FString::Printf(TEXT("%08X%08X"), ChunkLocation.X, ChunkLocation.Y) + ChunkFileExtension; - - if (FPaths::FileExists(ArchiveFile)) - { - TArray ChunkDataBuffer; - if (FFileHelper::LoadFileToArray(ChunkDataBuffer, *ArchiveFile)) - { - FMemoryReader MemoryReader(ChunkDataBuffer); - - UScriptStruct* VoxelBlockStruct = FVoxelBlock::StaticStruct(); - - for (int32 X = 0; X < 16; ++X) - { - for (int32 Y = 0; Y < 16; ++Y) - { - for (int32 Z = 0; Z < 256; ++Z) - { - VoxelBlockStruct->SerializeItem(MemoryReader, &Blocks[X][Y][Z], nullptr); - } - } - } - - UE_LOG(LogVoxel, Log, TEXT("Load Chunk %d, %d From File '%s'"), ChunkLocation.X, ChunkLocation.Y, *ArchiveFile); - } - } - -} - -void AVoxelChunk::Unload() -{ - QUICK_SCOPE_CYCLE_COUNTER(STAT_VoxelChunk_Unload); - - if (ArchiveFile.IsEmpty()) return; - - TArray ChunkDataBuffer; - FMemoryWriter MemoryWriter(ChunkDataBuffer); - - UScriptStruct* VoxelBlockStruct = FVoxelBlock::StaticStruct(); - - for (int32 X = 0; X < 16; ++X) - { - for (int32 Y = 0; Y < 16; ++Y) - { - for (int32 Z = 0; Z < 256; ++Z) - { - VoxelBlockStruct->SerializeItem(MemoryWriter, &Blocks[X][Y][Z], nullptr); - } - } - } - - if (FFileHelper::SaveArrayToFile(ChunkDataBuffer, *ArchiveFile)) - { - UE_LOG(LogVoxel, Log, TEXT("Save Chunk %d, %d To File '%s'"), ChunkLocation.X, ChunkLocation.Y, *ArchiveFile); - } - else - { - UE_LOG(LogVoxel, Error, TEXT("Failed To Save Chunk %d, %d To File '%s'"), ChunkLocation.X, ChunkLocation.Y, *ArchiveFile); - } -} - void AVoxelChunk::FlushMeshSection(int32 SectionIndex) { + if (!Data->IsLoaded()) return; + static FVoxelMeshData CubeTopFaceBuffer = FVoxelMeshData::CubeTopFace; static FVoxelMeshData CubeBottomFaceBuffer = FVoxelMeshData::CubeBottomFace; static FVoxelMeshData CubeFrontFaceBuffer = FVoxelMeshData::CubeFrontFace; @@ -244,16 +216,16 @@ void AVoxelChunk::FlushMeshSection(int32 SectionIndex) { for (int32 Z = 0 + SectionIndex * 16; Z < 16 + SectionIndex * 16; ++Z) { - const FVoxelBlockType& CurrentVoxelBlock = VoxelSubsystem->GetBlockType(Blocks[X][Y][Z].Type); + const FVoxelBlockType& CurrentVoxelBlock = VoxelSubsystem->GetBlockType((*Data)->Blocks[X][Y][Z].Type); if (CurrentVoxelBlock.Shape != EVoxelBlockShape::Cube) continue; - const FVoxelBlockType& TopVoxelBlock = VoxelSubsystem->GetBlockType(Z + 1 < 256 ? Blocks[X][Y][Z + 1].Type : TEXT("Air")); - const FVoxelBlockType& BottomVoxelBlock = VoxelSubsystem->GetBlockType(Z - 1 >= 0 ? Blocks[X][Y][Z - 1].Type : TEXT("Air")); - const FVoxelBlockType& FrontVoxelBlock = VoxelSubsystem->GetBlockType(X + 1 < 16 ? Blocks[X + 1][Y][Z].Type : (FrontChunk ? FrontChunk->GetBlockByRelativeLocation(FIntVector(0, Y, Z)).Type : TEXT("Air"))); - const FVoxelBlockType& BackVoxelBlock = VoxelSubsystem->GetBlockType(X - 1 >= 0 ? Blocks[X - 1][Y][Z].Type : (BackChunk ? BackChunk->GetBlockByRelativeLocation(FIntVector(15, Y, Z)).Type : TEXT("Air"))); - const FVoxelBlockType& LeftVoxelBlock = VoxelSubsystem->GetBlockType(Y - 1 >= 0 ? Blocks[X][Y - 1][Z].Type : (LeftChunk ? LeftChunk->GetBlockByRelativeLocation(FIntVector(X, 15, Z)).Type : TEXT("Air"))); - const FVoxelBlockType& RightVoxelBlock = VoxelSubsystem->GetBlockType(Y + 1 < 16 ? Blocks[X][Y + 1][Z].Type : (RightChunk ? RightChunk->GetBlockByRelativeLocation(FIntVector(X, 0, Z)).Type : TEXT("Air"))); + const FVoxelBlockType& TopVoxelBlock = VoxelSubsystem->GetBlockType(Z + 1 < 256 ? (*Data)->Blocks[X][Y][Z + 1].Type : TEXT("Air")); + const FVoxelBlockType& BottomVoxelBlock = VoxelSubsystem->GetBlockType(Z - 1 >= 0 ? (*Data)->Blocks[X][Y][Z - 1].Type : TEXT("Air")); + const FVoxelBlockType& FrontVoxelBlock = VoxelSubsystem->GetBlockType(X + 1 < 16 ? (*Data)->Blocks[X + 1][Y][Z].Type : (FrontChunk ? FrontChunk->GetBlockByRelativeLocation(FIntVector(0, Y, Z)).Type : TEXT("Air"))); + const FVoxelBlockType& BackVoxelBlock = VoxelSubsystem->GetBlockType(X - 1 >= 0 ? (*Data)->Blocks[X - 1][Y][Z].Type : (BackChunk ? BackChunk->GetBlockByRelativeLocation(FIntVector(15, Y, Z)).Type : TEXT("Air"))); + const FVoxelBlockType& LeftVoxelBlock = VoxelSubsystem->GetBlockType(Y - 1 >= 0 ? (*Data)->Blocks[X][Y - 1][Z].Type : (LeftChunk ? LeftChunk->GetBlockByRelativeLocation(FIntVector(X, 15, Z)).Type : TEXT("Air"))); + const FVoxelBlockType& RightVoxelBlock = VoxelSubsystem->GetBlockType(Y + 1 < 16 ? (*Data)->Blocks[X][Y + 1][Z].Type : (RightChunk ? RightChunk->GetBlockByRelativeLocation(FIntVector(X, 0, Z)).Type : TEXT("Air"))); if (TopVoxelBlock.Shape != EVoxelBlockShape::Cube) { @@ -321,3 +293,8 @@ void AVoxelChunk::FlushMeshSection(int32 SectionIndex) true ); } + +void AVoxelChunk::OnDataLoaded(const FString & Filename) +{ + VoxelWorld->AddChunkToMeshFlushBuffer(ChunkLocation); +} diff --git a/Source/Voxel/Private/VoxelHelper.cpp b/Source/Voxel/Private/VoxelHelper.cpp index 7365606..4fd3981 100644 --- a/Source/Voxel/Private/VoxelHelper.cpp +++ b/Source/Voxel/Private/VoxelHelper.cpp @@ -1,20 +1,46 @@ #include "VoxelHelper.h" #include "VoxelWorld.h" +#include "Kismet/GameplayStatics.h" AVoxelWorld * UVoxelHelper::CreateVoxelWorld(UObject* WorldContextObject, const FVoxelWorldSetting& WorldSetting) { + if (!IsWorldSettingValid(WorldContextObject, WorldSetting)) return nullptr; + UWorld* World = WorldContextObject->GetWorld(); if (!World) return nullptr; - - AVoxelWorld* VoxelWorld = World->SpawnActor(); + + AVoxelWorld* VoxelWorld = Cast( + UGameplayStatics::BeginDeferredActorSpawnFromClass( + World, + AVoxelWorld::StaticClass(), + FTransform(), + ESpawnActorCollisionHandlingMethod::AlwaysSpawn, + nullptr + ) + ); VoxelWorld->WorldSetting = WorldSetting; + UGameplayStatics::FinishSpawningActor(VoxelWorld, FTransform()); + return VoxelWorld; } +bool UVoxelHelper::IsWorldSettingValid(UObject * WorldContextObject, const FVoxelWorldSetting & WorldSetting) +{ + FString TestFilePath = WorldSetting.ArchiveFolder / TEXT("TestFile"); + + if (!FFileHelper::SaveStringToFile(TEXT(""), *TestFilePath)) + return false; + + IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); + PlatformFile.DeleteFile(*TestFilePath); + + return true; +} + void UVoxelHelper::WorldToRelativeLocation(const FIntVector & InWorldLocation, FIntPoint & OutChunkLocation, FIntVector & OutRelativeLocation) { OutChunkLocation.X = InWorldLocation.X / 16; diff --git a/Source/Voxel/Private/VoxelWorld.cpp b/Source/Voxel/Private/VoxelWorld.cpp index def01e1..724e57c 100644 --- a/Source/Voxel/Private/VoxelWorld.cpp +++ b/Source/Voxel/Private/VoxelWorld.cpp @@ -6,6 +6,7 @@ #include "Block/VoxelBlock.h" #include "Chunk/VoxelChunk.h" #include "VoxelAgentInterface.h" +#include "Kismet/GameplayStatics.h" const TArray ChunkLoadOrder = { @@ -160,6 +161,10 @@ void AVoxelWorld::BeginPlay() UGameInstance* GameInstance = GetGameInstance(); VoxelSubsystem = GameInstance->GetSubsystem(); + + Data = MakeShared>(GetGameInstance()->GetSubsystem(), WorldSetting.ArchiveFolder / TEXT("VoxelWorld")); + + check(Data->IsValid()); } void AVoxelWorld::Tick(float DeltaSeconds) @@ -169,7 +174,6 @@ void AVoxelWorld::Tick(float DeltaSeconds) ManageChunk(); FlushMeshs(); - } void AVoxelWorld::EndPlay(const EEndPlayReason::Type EndPlayReason) @@ -180,6 +184,8 @@ void AVoxelWorld::EndPlay(const EEndPlayReason::Type EndPlayReason) for (const FIntPoint& Chunk : ChunksToUnload) UnloadChunk(Chunk); + Data = nullptr; + Super::EndPlay(EndPlayReason); } @@ -223,14 +229,22 @@ void AVoxelWorld::LoadChunk(const FIntPoint & ChunkLocation) check(World); - AVoxelChunk* NewVoxelChunk = World->SpawnActor(); + FTransform ChunkTransform(FVector(ChunkLocation.X * 1600.0f, ChunkLocation.Y * 1600.0f, 0.0f)); + + AVoxelChunk* NewVoxelChunk = Cast( + UGameplayStatics::BeginDeferredActorSpawnFromClass( + this, + AVoxelChunk::StaticClass(), + ChunkTransform, + ESpawnActorCollisionHandlingMethod::AlwaysSpawn, + this + ) + ); + NewVoxelChunk->VoxelWorld = this; NewVoxelChunk->ChunkLocation = ChunkLocation; - NewVoxelChunk->SetActorLocation(FVector(ChunkLocation.X * 1600.0f, ChunkLocation.Y * 1600.0f, 0.0f)); - NewVoxelChunk->AttachToActor(this, FAttachmentTransformRules::KeepRelativeTransform); - NewVoxelChunk->FlushMaterials(); - NewVoxelChunk->Load(); - AddChunkToMeshFlushBuffer(ChunkLocation); + + UGameplayStatics::FinishSpawningActor(NewVoxelChunk, ChunkTransform); Chunks.Add(ChunkLocation, NewVoxelChunk); @@ -241,7 +255,6 @@ void AVoxelWorld::UnloadChunk(const FIntPoint & ChunkLocation) { if (!Chunks.Contains(ChunkLocation)) return; - Chunks[ChunkLocation]->Unload(); Chunks[ChunkLocation]->Destroy(); Chunks.Remove(ChunkLocation); @@ -259,9 +272,16 @@ void AVoxelWorld::FlushMaterials() void AVoxelWorld::ManageChunk() { - FIntPoint RootChunk; - FIntVector RLocation; - UVoxelHelper::WorldToRelativeLocation(IVoxelAgentInterface::Execute_GetAgentLocation(CenterAgent.GetObject()), RootChunk, RLocation); + if (GetGameInstance()->GetSubsystem()->GetIdleThreadNum() <= 0) return; + + FIntPoint RootChunk = FIntPoint(0, 0); + + if (CenterAgent.GetObject()) + { + FIntVector RLocation; + UVoxelHelper::WorldToRelativeLocation(IVoxelAgentInterface::Execute_GetAgentLocation(CenterAgent.GetObject()), RootChunk, RLocation); + } + // Load for (const FIntPoint& Chunk : ChunkLoadOrder) @@ -286,7 +306,7 @@ void AVoxelWorld::ManageChunk() const FIntPoint WorldLocation = Chunk.Key; const FIntPoint RelativeLocation = WorldLocation - RootChunk; - const bool bInUnloadDistance = FMath::Abs(RelativeLocation.X) > ChunkUnloadDistance && FMath::Abs(RelativeLocation.Y) > ChunkUnloadDistance; + const bool bInUnloadDistance = FMath::Abs(RelativeLocation.X) > ChunkUnloadDistance || FMath::Abs(RelativeLocation.Y) > ChunkUnloadDistance; if (bInUnloadDistance) { diff --git a/Source/Voxel/Public/Chunk/VoxelChunk.h b/Source/Voxel/Public/Chunk/VoxelChunk.h index a1f46b8..1059971 100644 --- a/Source/Voxel/Public/Chunk/VoxelChunk.h +++ b/Source/Voxel/Public/Chunk/VoxelChunk.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "SaveStruct.h" #include "Block/VoxelBlock.h" #include "GameFramework/Actor.h" #include "VoxelChunk.generated.h" @@ -9,7 +10,24 @@ class AVoxelWorld; class UVoxelSubsystem; class UProceduralMeshComponent; -const FString ChunkFileExtension = TEXT(".voxelchunk"); +USTRUCT() +struct FVoxelChunkData : public FSaveStruct +{ + GENERATED_BODY() + + FVoxelBlock Blocks[16][16][256]; + + bool Serialize(FArchive& Slot); +}; + +template<> +struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 +{ + enum + { + WithSerializer = true, + }; +}; UCLASS(BlueprintType) class VOXEL_API AVoxelChunk : public AActor @@ -24,6 +42,8 @@ public: virtual void BeginPlay() override; + virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; + UFUNCTION(BlueprintCallable, Category = "Voxel|Chunk") AVoxelWorld* GetVoxelWorld() const { return VoxelWorld; } @@ -59,16 +79,15 @@ private: uint16 FlushMeshFlags; - FVoxelBlock Blocks[16][16][256]; + TSharedPtr> Data; FVoxelMeshData MeshSectionBuffer; private: - void Load(); - - void Unload(); - void FlushMeshSection(int32 SectionIndex); + UFUNCTION() + void OnDataLoaded(const FString& Filename); + }; diff --git a/Source/Voxel/Public/VoxelHelper.h b/Source/Voxel/Public/VoxelHelper.h index bc5703d..3209daf 100644 --- a/Source/Voxel/Public/VoxelHelper.h +++ b/Source/Voxel/Public/VoxelHelper.h @@ -17,6 +17,9 @@ public: UFUNCTION(BlueprintCallable, Category = "Voxel|Helper", meta = (WorldContext = "WorldContextObject")) static AVoxelWorld* CreateVoxelWorld(UObject* WorldContextObject, const FVoxelWorldSetting& WorldSetting); + UFUNCTION(BlueprintCallable, Category = "Voxel|Helper", meta = (WorldContext = "WorldContextObject")) + static bool IsWorldSettingValid(UObject* WorldContextObject, const FVoxelWorldSetting& WorldSetting); + UFUNCTION(BlueprintPure, Category = "Voxel|Helper") static void WorldToRelativeLocation(const FIntVector& InWorldLocation, FIntPoint& OutChunkLocation, FIntVector& OutRelativeLocation); diff --git a/Source/Voxel/Public/VoxelWorld.h b/Source/Voxel/Public/VoxelWorld.h index 43ef78d..ea7292a 100644 --- a/Source/Voxel/Public/VoxelWorld.h +++ b/Source/Voxel/Public/VoxelWorld.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "SaveStruct.h" #include "GameFramework/Info.h" #include "VoxelWorld.generated.h" @@ -22,6 +23,12 @@ struct VOXEL_API FVoxelWorldSetting }; +USTRUCT() +struct FVoxelWorldData : public FSaveStruct +{ + GENERATED_BODY() +}; + UCLASS(BlueprintType) class VOXEL_API AVoxelWorld : public AInfo { @@ -79,10 +86,14 @@ private: UPROPERTY() TSet MeshFlushBuffer; + TSharedPtr> Data; + private: void ManageChunk(); void FlushMeshs(); + bool TestWorldSettingValid(); + }; diff --git a/Source/Voxel/Voxel.Build.cs b/Source/Voxel/Voxel.Build.cs index d1f207a..530df55 100644 --- a/Source/Voxel/Voxel.Build.cs +++ b/Source/Voxel/Voxel.Build.cs @@ -39,6 +39,7 @@ public class Voxel : ModuleRules "Slate", "SlateCore", "ProceduralMeshComponent", + "AutoSave", // ... add private dependencies that you statically link with here ... } ); diff --git a/Voxel.uplugin b/Voxel.uplugin index 4ebeb71..72da926 100644 --- a/Voxel.uplugin +++ b/Voxel.uplugin @@ -21,10 +21,14 @@ "LoadingPhase": "Default" } ], - "Plugins": [ - { - "Name": "ProceduralMeshComponent", - "Enabled": true - } - ] + "Plugins": [ + { + "Name": "ProceduralMeshComponent", + "Enabled": true + }, + { + "Name": "AutoSave", + "Enabled": true + } + ] } \ No newline at end of file