Skip to content

Commit

Permalink
Supports sub-level merging
Browse files Browse the repository at this point in the history
  • Loading branch information
shun126 committed Apr 21, 2023
1 parent ca5b081 commit 0bcaf24
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 81 deletions.
4 changes: 2 additions & 2 deletions DungeonGenerator.uplugin
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"FileVersion": 3,
"Version": 6,
"VersionName": "1.4.1",
"Version": 7,
"VersionName": "1.4.2",
"FriendlyName": "DungeonGenerator",
"Description": "Procedural 3D dungeon generator plugin. Easy generation of levels, mini-maps and missions.",
"Category": "Procedural",
Expand Down
26 changes: 13 additions & 13 deletions Source/DungeonGenerator/Private/Core/Generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -807,19 +807,19 @@ namespace dungeon
// 部屋を生成
for (const auto& room : mRooms)
{
const FIntVector min(room->GetLeft(), room->GetTop(), room->GetBackground());
const FIntVector max(room->GetRight(), room->GetBottom(), room->GetForeground());
mVoxel->Rectangle(min, max
, Grid::CreateFloor(parameter.GetRandom(), room->GetIdentifier().Get())
, Grid::CreateDeck(parameter.GetRandom(), room->GetIdentifier().Get())
);

const FVector center = (FVector(min) + FVector(max)) / 2;
const FVector dataSize(room->GetDataWidth(), room->GetDataDepth(), room->GetDataHeight());
const FVector dataHalfSize = dataSize / 2;
const FIntVector dataMin = FIntVector(center - dataHalfSize);
const FIntVector dataMax = dataMin + FIntVector(dataSize);
mVoxel->NoMeshGeneration(dataMin, dataMax, room->IsNoRoofMeshGeneration(), room->IsNoFloorMeshGeneration());
{
const FIntVector min(room->GetLeft(), room->GetTop(), room->GetBackground());
const FIntVector max(room->GetRight(), room->GetBottom(), room->GetForeground());
mVoxel->Rectangle(min, max
, Grid::CreateFloor(parameter.GetRandom(), room->GetIdentifier().Get())
, Grid::CreateDeck(parameter.GetRandom(), room->GetIdentifier().Get())
);
}
{
FIntVector min, max;
room->GetDataBounds(min, max);
mVoxel->NoMeshGeneration(min, max, room->IsNoRoofMeshGeneration(), room->IsNoFloorMeshGeneration());
}
}

// 通路の距離が短い順に並べ替える
Expand Down
21 changes: 21 additions & 0 deletions Source/DungeonGenerator/Private/Core/Room.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,4 +299,25 @@ namespace dungeon
};
return names[static_cast<size_t>(mItem)];
}

FIntVector Room::GetDataSize() const noexcept
{
return FIntVector(mDataWidth, mDataDepth, mDataHeight);
}

void Room::SetDataSize(const uint32_t dataWidth, const uint32_t dataDepth, const uint32_t dataHeight)
{
mDataWidth = dataWidth;
mDataDepth = dataDepth;
mDataHeight = dataHeight;
}

void Room::GetDataBounds(FIntVector& min, FIntVector& max) const noexcept
{
const FIntVector center(GetGroundCenter());
const FIntVector size = GetDataSize();
const FIntVector offset(size.X / 2, size.Y / 2, 0);
min = center - offset;
max = min + size;
}
}
38 changes: 22 additions & 16 deletions Source/DungeonGenerator/Private/Core/Room.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,33 +294,39 @@ namespace dungeon



uint32_t GetDataWidth() const noexcept { return mDataWidth; }
uint32_t GetDataDepth() const noexcept { return mDataDepth; }
uint32_t GetDataHeight() const noexcept { return mDataHeight; }
/*
Get the size of the mesh generation prohibited area
*/
FIntVector GetDataSize() const noexcept;

void SetDataSize(const uint32_t dataWidth, const uint32_t dataDepth, const uint32_t dataHeight)
{
mDataWidth = dataWidth;
mDataDepth = dataDepth;
mDataHeight = dataHeight;
}
/*
Set the size of the mesh generation prohibited area
*/
void SetDataSize(const uint32_t dataWidth, const uint32_t dataDepth, const uint32_t dataHeight);

/*
Get mesh generation prohibited area
\param[out] min Minimum Position
\param[out] max Maximum position
*/
void GetDataBounds(FIntVector& min, FIntVector& max) const noexcept;

/**
メッシュ生成禁止に設定します
\param[in] noRoofMeshGeneration 天井のメッシュ生成禁止
\param[in] noFloorMeshGeneration 床のメッシュ生成禁止
Sets mesh generation prohibition
\param[in] noRoofMeshGeneration No roof mesh generation
\param[in] noFloorMeshGeneration No floor mesh generation
*/
void SetNoMeshGeneration(const bool noRoofMeshGeneration, const bool noFloorMeshGeneration);

/**
床のメッシュ生成禁止か取得します
\return trueならメッシュ生成禁止
Floor mesh generation is prohibited or acquired.
\return If true, mesh generation is prohibited
*/
bool IsNoFloorMeshGeneration() const noexcept;

/**
天井のメッシュ生成禁止か取得します
\return trueならメッシュ生成禁止
Mesh generation of the ceiling is prohibited or acquired.
\return If true, mesh generation is prohibited
*/
bool IsNoRoofMeshGeneration() const noexcept;

Expand Down
103 changes: 58 additions & 45 deletions Source/DungeonGenerator/Private/DungeonGeneratorCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ All Rights Reserved.
// 定義するとミッショングラフのデバッグファイル(PlantUML)を出力します
#define DEBUG_GENERATE_MISSION_GRAPH_FILE

static const FName DungeonGeneratorTag("DungeonGenerator");
static const FName DungeonGeneratorTag(TEXT("DungeonGenerator"));

namespace
{
Expand Down Expand Up @@ -191,19 +191,18 @@ bool CDungeonGeneratorCore::CreateImpl_AddRoomAsset(const UDungeonGenerateParame
break;
}

FVector centerPosition = room->GetGroundCenter() * parameter->GetGridSize();
if ((room->GetWidth() & 1) != (dungeonRoomLocator.GetWidth() & 1))
{
centerPosition.X += parameter->GetGridSize() / 2;
}
if ((room->GetDepth() & 1) != (dungeonRoomLocator.GetDepth() & 1))
{
centerPosition.Y += parameter->GetGridSize() / 2;
}
if (RequestStreamLevel(dungeonRoomLocator.GetLevelPath(), centerPosition))
if (!IsStreamLevelRequested(dungeonRoomLocator.GetLevelPath()))
{
room->SetDataSize(dungeonRoomLocator.GetWidth(), dungeonRoomLocator.GetDepth(), dungeonRoomLocator.GetHeight());

FIntVector min, max;
room->GetDataBounds(min, max);
room->SetNoMeshGeneration(!dungeonRoomLocator.IsGenerateRoofMesh(), !dungeonRoomLocator.IsGenerateFloorMesh());

const float halfGridSize = parameter->GetGridSize() * 0.5f;
const FVector halfOffset(halfGridSize, halfGridSize, 0);

RequestStreamLevel(dungeonRoomLocator.GetLevelPath(), FVector(min) * parameter->GetGridSize() + halfOffset);
}
});

Expand All @@ -230,7 +229,7 @@ void CDungeonGeneratorCore::AddTerrain()
const size_t gridIndex = mGenerator->GetVoxel()->Index(location);
const float gridSize = parameter->GetGridSize();
const float halfGridSize = gridSize * 0.5f;
const FVector halfOffset = FVector(halfGridSize, halfGridSize, 0);
const FVector halfOffset(halfGridSize, halfGridSize, 0);
const FVector position = parameter->ToWorld(location);
const FVector centerPosition = position + halfOffset;

Expand Down Expand Up @@ -1013,19 +1012,19 @@ std::shared_ptr<const dungeon::Generator> CDungeonGeneratorCore::GetGenerator()
}

////////////////////////////////////////////////////////////////////////////////////////////////////
bool CDungeonGeneratorCore::RequestStreamLevel(const FSoftObjectPath& levelPath, const FVector& levelLocation)
bool CDungeonGeneratorCore::IsStreamLevelRequested(const FSoftObjectPath& levelPath) const
{
const auto requestStreamLevel = std::find_if(mRequestLoadStreamLevels.begin(), mRequestLoadStreamLevels.end(), [&levelPath](const LoadStreamLevelParameter& requestStreamLevel)
{
return requestStreamLevel.mPath == levelPath;
}
);
if (requestStreamLevel != mRequestLoadStreamLevels.end())
return false;
return requestStreamLevel != mRequestLoadStreamLevels.end();
}

void CDungeonGeneratorCore::RequestStreamLevel(const FSoftObjectPath& levelPath, const FVector& levelLocation)
{
mRequestLoadStreamLevels.emplace_back(levelPath, levelLocation);

return true;
}

void CDungeonGeneratorCore::AsyncLoadStreamLevels()
Expand Down Expand Up @@ -1060,6 +1059,7 @@ void CDungeonGeneratorCore::AsyncLoadStreamLevels()
}
}

#if WITH_EDITOR
void CDungeonGeneratorCore::SyncLoadStreamLevels()
{
UWorld* world = mWorld.Get();
Expand All @@ -1086,17 +1086,19 @@ void CDungeonGeneratorCore::SyncLoadStreamLevels()
ULevel* loadedLevel = levelStreaming->GetLoadedLevel();
if (IsValid(loadedLevel))
{
#if WITH_EDITOR
FString path, filename, extension;
FPaths::Split(levelStreaming->PackageNameToLoad.ToString(), path, filename, extension);
#endif
FString folder = levelStreaming->PackageNameToLoad.ToString();
folder.RemoveFromStart("/Game/", ESearchCase::IgnoreCase);
folder.RemoveFromStart("Map/", ESearchCase::IgnoreCase);
folder.RemoveFromStart("Maps/", ESearchCase::IgnoreCase);
folder.RemoveFromStart("Level/", ESearchCase::IgnoreCase);
folder.RemoveFromStart("Levels/", ESearchCase::IgnoreCase);

for (AActor* actor : loadedLevel->Actors)
{
actor->Tags.Add(GetDungeonGeneratorTag());
#if WITH_EDITOR
const FName folderPath(FString(TEXT("Dungeon/Levels/")) + filename);

const FName folderPath(FString(TEXT("Dungeon/Levels/")) + folder);
actor->SetFolderPath(folderPath);
#endif
}

moveActors.Append(loadedLevel->Actors);
Expand All @@ -1120,10 +1122,11 @@ void CDungeonGeneratorCore::SyncLoadStreamLevels()
mRequestLoadStreamLevels.clear();
}
}
#endif

void CDungeonGeneratorCore::UnloadStreamLevels()
{
SyncLoadStreamLevels();
mRequestLoadStreamLevels.clear();

UWorld* world = mWorld.Get();
if (IsValid(world))
Expand All @@ -1136,38 +1139,48 @@ void CDungeonGeneratorCore::UnloadStreamLevels()
}
}

void CDungeonGeneratorCore::UnloadStreamLevel(const FSoftObjectPath& levelPath)
TSoftObjectPtr<const ULevelStreamingDynamic> CDungeonGeneratorCore::FindLoadedStreamLevel(const FSoftObjectPath& levelPath) const
{
SyncLoadStreamLevels();

UWorld* world = mWorld.Get();
if (IsValid(world))
for (const TSoftObjectPtr<const ULevelStreamingDynamic>& loadedStreamLevel : mLoadedStreamLevels)
{
for (int32 i = 0; i < mLoadedStreamLevels.Num(); ++i)
if (loadedStreamLevel.IsValid())
{
const TSoftObjectPtr<ULevelStreamingDynamic>& loadedStreamLevel = mLoadedStreamLevels[i];
if (loadedStreamLevel->PackageNameToLoad == levelPath.GetAssetPathName())
//if (loadedStreamLevel->PackageNameToLoad == levelPath.GetAssetPathName())
if (loadedStreamLevel->PackageNameToLoad == levelPath.GetAssetPath().GetPackageName())
{
world->RemoveStreamingLevel(loadedStreamLevel.Get());

mLoadedStreamLevels.RemoveAt(i);
break;
return loadedStreamLevel;
}
}
}
return nullptr;
}

TSoftObjectPtr<const ULevelStreamingDynamic> CDungeonGeneratorCore::FindLoadedStreamLevel(const FSoftObjectPath& levelPath) const
void CDungeonGeneratorCore::LoadStreamLevelImplement(UWorld* world, const FSoftObjectPath& path, const FTransform& transform)
{
for (const TSoftObjectPtr<const ULevelStreamingDynamic>& loadedStreamLevel : mLoadedStreamLevels)
const FName& longPackageName = path.GetLongPackageFName();
ULevelStreaming* levelStreaming;

levelStreaming = UGameplayStatics::GetStreamingLevel(world, longPackageName);
if (IsValid(levelStreaming))
{
if (loadedStreamLevel.IsValid())
{
if (loadedStreamLevel->PackageNameToLoad == levelPath.GetAssetPathName())
return loadedStreamLevel;
}
UnloadStreamLevelImplement(world, path, true);
}
return nullptr;

FLatentActionInfo LatentInfo;
UGameplayStatics::LoadStreamLevel(world, longPackageName, false, false, LatentInfo);

levelStreaming = UGameplayStatics::GetStreamingLevel(world, longPackageName);
if (IsValid(levelStreaming))
{
levelStreaming->LevelTransform = transform;
levelStreaming->SetShouldBeVisible(true);
}
}

void CDungeonGeneratorCore::UnloadStreamLevelImplement(UWorld* world, const FSoftObjectPath& path, const bool shouldBlockOnUnload)
{
FLatentActionInfo LatentInfo;
UGameplayStatics::UnloadStreamLevel(world, path.GetLongPackageFName(), LatentInfo, shouldBlockOnUnload);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
52 changes: 52 additions & 0 deletions Source/DungeonGenerator/Private/DungeonLevelStreamingActor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
\author Shun Moriya
\copyright 2023- Shun Moriya
All Rights Reserved.
*/

#include "DungeonLevelStreamingActor.h"
#include <Components/BoxComponent.h>
#include <GameFramework/Character.h>
#include <Kismet/GameplayStatics.h>

ADungeonLevelStreamingActor::ADungeonLevelStreamingActor(const FObjectInitializer& initializer)
: Super(initializer)
{
PrimaryActorTick.bCanEverTick = true;

OverlapVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("OverlapVolume"));
OverlapVolume->OnComponentBeginOverlap.AddUniqueDynamic(this, &ADungeonLevelStreamingActor::OverlapBegins);
OverlapVolume->OnComponentEndOverlap.AddUniqueDynamic(this, &ADungeonLevelStreamingActor::OverlapEnds);
RootComponent = OverlapVolume;
}

void ADungeonLevelStreamingActor::OverlapBegins(UPrimitiveComponent* overlappedComponent, AActor* otherActor, UPrimitiveComponent* otherComp, int32 otherBodyIndex, bool fromSweep, const FHitResult& sweepResult)
{
if (Path.IsValid())
{
ACharacter* MyCharacter = UGameplayStatics::GetPlayerCharacter(this, 0);
if (otherActor == Cast<AActor>(MyCharacter))
{
FLatentActionInfo LatentInfo;
UGameplayStatics::LoadStreamLevel(this, Path.GetLongPackageFName(), true, true, LatentInfo);
#if 0
ULevelStreaming* level = UGameplayStatics::GetStreamingLevel(GetWorld(), LevelToLoad);
level->LevelTransform = GetTransform();
level->SetShouldBeVisible(true);
#endif
}
}
}

void ADungeonLevelStreamingActor::OverlapEnds(UPrimitiveComponent* overlappedComponent, AActor* otherActor, UPrimitiveComponent* otherComp, int32 otherBodyIndex)
{
if (Path.IsValid())
{
ACharacter* MyCharacter = UGameplayStatics::GetPlayerCharacter(this, 0);
if (otherActor == Cast<AActor>(MyCharacter))
{
FLatentActionInfo LatentInfo;
UGameplayStatics::UnloadStreamLevel(this, Path.GetLongPackageFName(), LatentInfo, false);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/**
\author Shun Moriya
\copyright 2023- Shun Moriya
All Rights Reserved.
*/

#include "DungeonLevelStreamingDynamic.h"

UDungeonLevelStreamingDynamic::UDungeonLevelStreamingDynamic(const FObjectInitializer& initializer)
Expand Down
6 changes: 3 additions & 3 deletions Source/DungeonGenerator/Public/DungeonGenerateParameter.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,15 +374,15 @@ class DUNGEONGENERATOR_API UDungeonGenerateParameter : public UObject
TArray<FDungeonDoorActorParts> DoorParts;

// Move PlayerStart to the starting point.
UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Door", BlueprintReadWrite)
UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Start", BlueprintReadWrite)
bool MovePlayerStartToStartingPoint = true;

// starting point
UPROPERTY(EditAnywhere, Category = "DungeonGenerator", BlueprintReadWrite)
UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Start", BlueprintReadWrite)
FDungeonActorPartsWithDirection StartParts;

// goal position
UPROPERTY(EditAnywhere, Category = "DungeonGenerator", BlueprintReadWrite)
UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Goal", BlueprintReadWrite)
FDungeonActorPartsWithDirection GoalParts;

// Room Sensor Class
Expand Down
Loading

0 comments on commit 0bcaf24

Please sign in to comment.