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 22, 2023
1 parent ca5b081 commit 307ebbd
Show file tree
Hide file tree
Showing 11 changed files with 302 additions and 82 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
156 changes: 110 additions & 46 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 All @@ -1040,11 +1039,30 @@ void CDungeonGeneratorCore::AsyncLoadStreamLevels()
UWorld* world = mWorld.Get();
if (IsValid(world))
{
bool bSuccess = false;
#if UE_VERSION_NEWER_THAN(5, 1, 0)
const FTransform transform(FRotator::ZeroRotator, requestStreamLevel.mLocation);
ULevelStreamingDynamic::FLoadLevelInstanceParams parameter(world, requestStreamLevel.mPath.GetLongPackageName(), transform);
parameter.OptionalLevelStreamingClass = UDungeonLevelStreamingDynamic::StaticClass();
bool bSuccess = false;
ULevelStreamingDynamic* levelStreaming = ULevelStreamingDynamic::LoadLevelInstance(parameter, bSuccess);
#elif UE_VERSION_NEWER_THAN(5, 0, 0)
ULevelStreamingDynamic* levelStreaming = ULevelStreamingDynamic::LoadLevelInstance(
world,
requestStreamLevel.mPath.GetLongPackageName(),
requestStreamLevel.mLocation,
FRotator::ZeroRotator,
bSuccess,
TEXT(""),
UDungeonLevelStreamingDynamic::StaticClass(),
false);
#else
ULevelStreamingDynamic* levelStreaming = ULevelStreamingDynamic::LoadLevelInstance(
world,
requestStreamLevel.mPath.GetLongPackageName(),
requestStreamLevel.mLocation,
FRotator::ZeroRotator,
bSuccess);
#endif
if (bSuccess && IsValid(levelStreaming))
{
mLoadedStreamLevels.Add(levelStreaming);
Expand All @@ -1060,6 +1078,7 @@ void CDungeonGeneratorCore::AsyncLoadStreamLevels()
}
}

#if WITH_EDITOR
void CDungeonGeneratorCore::SyncLoadStreamLevels()
{
UWorld* world = mWorld.Get();
Expand All @@ -1072,11 +1091,31 @@ void CDungeonGeneratorCore::SyncLoadStreamLevels()
if (FindLoadedStreamLevel(requestStreamLevel.mPath))
continue;

bool bSuccess = false;

#if UE_VERSION_NEWER_THAN(5, 1, 0)
const FTransform transform(FRotator::ZeroRotator, requestStreamLevel.mLocation);
ULevelStreamingDynamic::FLoadLevelInstanceParams parameter(world, requestStreamLevel.mPath.GetLongPackageName(), transform);
parameter.OptionalLevelStreamingClass = UDungeonLevelStreamingDynamic::StaticClass();
bool bSuccess = false;
ULevelStreamingDynamic* levelStreaming = ULevelStreamingDynamic::LoadLevelInstance(parameter, bSuccess);
#elif UE_VERSION_NEWER_THAN(5, 0, 0)
ULevelStreamingDynamic* levelStreaming = ULevelStreamingDynamic::LoadLevelInstance(
world,
requestStreamLevel.mPath.GetLongPackageName(),
requestStreamLevel.mLocation,
FRotator::ZeroRotator,
bSuccess,
TEXT(""),
UDungeonLevelStreamingDynamic::StaticClass(),
false);
#else
ULevelStreamingDynamic* levelStreaming = ULevelStreamingDynamic::LoadLevelInstance(
world,
requestStreamLevel.mPath.GetLongPackageName(),
requestStreamLevel.mLocation,
FRotator::ZeroRotator,
bSuccess);
#endif
if (bSuccess && IsValid(levelStreaming))
{
levelStreaming->bShouldBlockOnLoad = true;
Expand All @@ -1086,17 +1125,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 +1161,11 @@ void CDungeonGeneratorCore::SyncLoadStreamLevels()
mRequestLoadStreamLevels.clear();
}
}
#endif

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

UWorld* world = mWorld.Get();
if (IsValid(world))
Expand All @@ -1136,38 +1178,60 @@ 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 UE_VERSION_OLDER_THAN(5, 1, 0)
if (loadedStreamLevel->PackageNameToLoad == levelPath.GetAssetPathName())
#else
if (loadedStreamLevel->PackageNameToLoad == levelPath.GetAssetPath().GetPackageName())
#endif
{
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)
#if UE_VERSION_NEWER_THAN(5, 0, 0)
const FName& longPackageName = path.GetLongPackageFName();
#else
const FName& longPackageName = FName(path.GetLongPackageName());
#endif
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)
{
#if UE_VERSION_NEWER_THAN(5, 0, 0)
const FName& longPackageName = path.GetLongPackageFName();
#else
const FName& longPackageName = FName(path.GetLongPackageName());
#endif
FLatentActionInfo LatentInfo;
UGameplayStatics::UnloadStreamLevel(world, longPackageName, LatentInfo, shouldBlockOnUnload);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
Loading

0 comments on commit 307ebbd

Please sign in to comment.