Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Change Log - Procedural 3D Dungeon Generator Plug-in

## 20251004-1.7.8 (61)
### Changes
* Added the ability to assist actors spawning into the aisle grid
* Changed DungeonRoomSensor to not spawn on the client side
* Fixed several bugs
### 変更点
* 通路グリッドへのアクターをスポーンを補助する機能を追加
* DungeonRoomSensorをクライアント側でスポーンしないように変更
* いくつかの不具合を修正

## 20250903-1.7.7 (60)
### Changes
* Add DungeonRoomSensorDatabase
Expand Down
Binary file modified Content/Actors/BP_SampleDungeonDoor.uasset
Binary file not shown.
Binary file modified Content/Actors/BP_SampleDungeonRoomSensor.uasset
Binary file not shown.
Binary file modified Content/Maps/Demonstration.umap
Binary file not shown.
Binary file modified Content/Parameters/DGP_Sample.uasset
Binary file not shown.
Binary file added Content/Parameters/DRSD_Sample.uasset
Binary file not shown.
19 changes: 6 additions & 13 deletions DungeonGenerator.uplugin
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"FileVersion": 3,
"Version": 60,
"VersionName": "1.7.7",
"Version": 61,
"VersionName": "1.7.8",
"FriendlyName": "Dungeon Generator",
"Description": "Procedural 3d dungeon generator plugin. Easy generation of levels, mini-maps and missions.",
"Category": "Procedural Systems",
"CreatedBy": "Narcis Software",
"CreatedByURL": "https://github.com/shun126/UE5-DungeonGeneratorDemo",
"CreatedByURL": "https://github.com/shun126/DungeonGenerator",
"DocsURL": "https://github.com/shun126/UE5-DungeonGeneratorDemo/wiki",
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/36a8b87d859f44439cfe1515975d7197",
"SupportURL": "https://github.com/shun126/UE5-DungeonGeneratorDemo/issues",
"SupportURL": "https://github.com/shun126/DungeonGenerator/discussions",
"CanContainContent": true,
"IsBetaVersion": true,
"IsExperimentalVersion": false,
Expand All @@ -18,19 +18,12 @@
{
"Name": "DungeonGenerator",
"Type": "Runtime",
"LoadingPhase": "Default",
"PlatformAllowList": [
"Win64",
"Android"
]
"LoadingPhase": "Default"
},
{
"Name": "DungeonGeneratorEditor",
"Type": "UncookedOnly",
"LoadingPhase": "Default",
"PlatformAllowList": [
"Win64"
]
"LoadingPhase": "Default"
}
]
}
1 change: 1 addition & 0 deletions Source/DungeonGenerator/Private/DungeonGenerateActor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ ADungeonGenerateActorは配置可能(Placeable)、ADungeonGeneratedActorは配
#include "Core/Math/Vector.h"
#include "Core/Voxelization/Grid.h"
#include "Core/Voxelization/Voxel.h"
#include "Helper/DungeonAisleGridMap.h"
#include "MainLevel/DungeonMainLevelScriptActor.h"
#include "Parameter/DungeonGenerateParameter.h"
#include <Components/HierarchicalInstancedStaticMeshComponent.h>
Expand Down
62 changes: 41 additions & 21 deletions Source/DungeonGenerator/Private/DungeonGenerateBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ ADungeonGenerateActorは配置可能(Placeable)、ADungeonGeneratedActorは配

namespace
{
const FString ActorsFolderPath = TEXT("Actors");
const FString DoorsFolderPath = TEXT("Actors/Doors");
const FString TorchesFolderPath = TEXT("Actors/Torches");
const FString SensorsFolderPath = TEXT("Actors/Sensors");
Expand Down Expand Up @@ -422,11 +423,12 @@ bool ADungeonGenerateBase::BeginDungeonGeneration(const UDungeonGenerateParamete
dungeon::CreateDebugDirectory();
#endif

DUNGEON_GENERATOR_LOG(TEXT("version '%s', license '%s', uuid '%s', commit '%s'"),
DUNGEON_GENERATOR_LOG(TEXT("version '%s', license '%s', uuid '%s', commit '%s', HasAuthority '%s'"),
TEXT(DUNGENERATOR_PLUGIN_VERSION_NAME),
TEXT(JENKINS_LICENSE),
TEXT(JENKINS_UUID),
TEXT(JENKINS_GIT_COMMIT)
TEXT(JENKINS_GIT_COMMIT),
hasAuthority ? TEXT("Yes") : TEXT("No")
);

// CRC32の値を初期化
Expand Down Expand Up @@ -528,7 +530,7 @@ bool ADungeonGenerateBase::BeginDungeonGeneration(const UDungeonGenerateParamete
mAisleGridMap = NewObject<UDungeonAisleGridMap>(this);

// 生成終了イベントの登録
dungeon::Finalizer finalizer([this]()
dungeon::Finalizer finalizer([this, hasAuthority]()->void
{
#if defined(DEBUG_ENABLE_MEASURE_GENERATION_TIME)
dungeon::Stopwatch stopwatch;
Expand All @@ -540,6 +542,21 @@ bool ADungeonGenerateBase::BeginDungeonGeneration(const UDungeonGenerateParamete
EndGeneration(random, mAisleGridMap);
OnEndGeneration.Broadcast(random, mAisleGridMap);

mParameter->OnEndGeneration(random, mAisleGridMap, [this, hasAuthority](const FSoftObjectPath& spawnPath, const FTransform& transform)
{
if (hasAuthority)
{
const FSoftObjectPath path(spawnPath.ToString() + "_C");
const TSoftClassPtr<AActor> softClassPointer(path);
auto* actorClass = softClassPointer.LoadSynchronous();

FActorSpawnParameters actorSpawnParameters;
actorSpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding;
SpawnActorImpl(actorClass, ActorsFolderPath, transform, actorSpawnParameters);
}
}
);

#if defined(DEBUG_ENABLE_MEASURE_GENERATION_TIME)
DUNGEON_GENERATOR_LOG(TEXT("On end generation event: %lf seconds"), stopwatch.Lap());
#endif
Expand Down Expand Up @@ -595,7 +612,7 @@ bool ADungeonGenerateBase::BeginDungeonGeneration(const UDungeonGenerateParamete
// メッシュの生成
{
RoomAndRoomSensorMap roomSensorCache;
CreateImplement_PrepareSpawnRoomSensor(roomSensorCache);
CreateImplement_PrepareSpawnRoomSensor(roomSensorCache, hasAuthority);
CreateImplement_QueryAisleGeneration(hasAuthority);
CreateImplement_AddTerrain(roomSensorCache, hasAuthority);
/*
Expand Down Expand Up @@ -1303,16 +1320,15 @@ void ADungeonGenerateBase::CreateImplement_AddPillarAndTorch(const CreateImpleme
ADungeonRoomSensorBaseはリプリケートされない前提のアクターなので
必ず同期乱数(GetSynchronizedRandom)を使ってください。
*/
void ADungeonGenerateBase::CreateImplement_PrepareSpawnRoomSensor(RoomAndRoomSensorMap& roomSensorCache) const
void ADungeonGenerateBase::CreateImplement_PrepareSpawnRoomSensor(RoomAndRoomSensorMap& roomSensorCache, const bool hasAuthority) const
{
check(IsValid(mParameter));

#if defined(DEBUG_ENABLE_MEASURE_GENERATION_TIME)
dungeon::Stopwatch stopwatch;
#endif

// RoomSensorActorを生成
mGenerator->ForEach([this, &roomSensorCache](const std::shared_ptr<const dungeon::Room>& room)
mGenerator->ForEach([this, &roomSensorCache, hasAuthority](const std::shared_ptr<const dungeon::Room>& room)
{
auto* roomSensorClass = mParameter->GetRoomSensorClass();
if (const auto* roomSensorDatabase = mParameter->GetRoomSensorDatabase())
Expand All @@ -1328,20 +1344,24 @@ void ADungeonGenerateBase::CreateImplement_PrepareSpawnRoomSensor(RoomAndRoomSen
GetSynchronizedRandom()
);
}
if (roomSensorClass)
// サーバーならRoomSensorActorを生成
if (hasAuthority)
{
auto* roomSensorActor = SpawnRoomSensorActorDeferred(
roomSensorClass,
room->GetIdentifier(),
room->GetCenter() * mParameter->GetGridSize().To3D() + GetActorLocation(),
room->GetExtent() * mParameter->GetGridSize().To3D(),
static_cast<EDungeonRoomParts>(room->GetParts()),
static_cast<EDungeonRoomItem>(room->GetItem()),
room->GetBranchId(),
room->GetDepthFromStart(),
mGenerator->GetDeepestDepthFromStart()
);
roomSensorCache[room.get()] = roomSensorActor;
if (roomSensorClass)
{
auto* roomSensorActor = SpawnRoomSensorActorDeferred(
roomSensorClass,
room->GetIdentifier(),
room->GetCenter() * mParameter->GetGridSize().To3D() + GetActorLocation(),
room->GetExtent() * mParameter->GetGridSize().To3D(),
static_cast<EDungeonRoomParts>(room->GetParts()),
static_cast<EDungeonRoomItem>(room->GetItem()),
room->GetBranchId(),
room->GetDepthFromStart(),
mGenerator->GetDeepestDepthFromStart()
);
roomSensorCache[room.get()] = roomSensorActor;
}
}
}
);
Expand Down Expand Up @@ -1636,7 +1656,7 @@ AStaticMeshActor* ADungeonGenerateBase::SpawnStaticMeshActor(UStaticMesh* static

if (auto* staticMeshComponent = GetValid(actor->GetStaticMeshComponent()))
{
if (const UWorld* world = actor->GetWorld())
if (const auto* world = actor->GetWorld())
{
if (world->HasBegunPlay() == true)
actor->SetMobility(EComponentMobility::Movable);
Expand Down
17 changes: 10 additions & 7 deletions Source/DungeonGenerator/Private/Helper/DungeonAisleGridMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,17 @@ void UDungeonAisleGridMap::Register(const int32 identifier, const EDungeonDirect
}
}

void UDungeonAisleGridMapBlueprintFunctionLibrary::ForEach(const UDungeonAisleGridMap* aisleGridArray, const FDungeonAisleGridMapLoopSignature& OnLoop)
void UDungeonAisleGridMap::ForEach(const FDungeonAisleGridMapLoopSignature& OnLoop) const
{
if (aisleGridArray)
for (const auto& aisleGrid : mAisleGridMap)
{
for (const auto& aisleGrid : aisleGridArray->mAisleGridMap)
{
if (OnLoop.ExecuteIfBound(aisleGrid.second) == false)
break;
}
if (OnLoop.ExecuteIfBound(aisleGrid.second) == false)
break;
}
}

void UDungeonAisleGridMapBlueprintFunctionLibrary::ForEach(const UDungeonAisleGridMap* aisleGridArray, const FDungeonAisleGridMapLoopSignature& OnLoop)
{
if (aisleGridArray)
aisleGridArray->ForEach(OnLoop);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ All Rights Reserved.
#include "Core/Debug/Debug.h"
#include "Core/Math/Random.h"
#include "Core/Voxelization/Grid.h"
#include "SubActor/DungeonRoomSensorDatabase.h"

#include <Net/UnrealNetwork.h>
#include <Net/Core/PushModel/PushModel.h>

#if WITH_EDITOR
#include <Misc/FileHelper.h>
#endif
#include <unordered_set>

UDungeonGenerateParameter::UDungeonGenerateParameter(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
Expand Down Expand Up @@ -485,4 +487,10 @@ void UDungeonGenerateParameter::EachAisleCatwalkParts(const std::function<void(c
void UDungeonGenerateParameter::EachPillarParts(const std::function<void(const FDungeonMeshParts&)>& function) const
{
FDungeonMeshSet::EachParts(PillarParts, function);
}
}

void UDungeonGenerateParameter::OnEndGeneration(UDungeonRandom* synchronizedRandom, const UDungeonAisleGridMap* aisleGridMap, const std::function<void(const FSoftObjectPath&, const FTransform&)>& spawnActor) const
{
if (DungeonRoomSensorDatabase)
DungeonRoomSensorDatabase->OnEndGeneration(synchronizedRandom, aisleGridMap, VerticalGridSize, spawnActor);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ All Rights Reserved.
#include "SubActor/DungeonRoomSensorDatabase.h"
#include "Core/Math/Random.h"
#include "Core/Debug/Debug.h"
#include "Helper/DungeonAisleGridMap.h"
#include "Helper/DungeonRandom.h"
#include "Parameter/DungeonMeshSetDatabase.h"
#include <cmath>
#include <unordered_set>

UDungeonRoomSensorDatabase::UDungeonRoomSensorDatabase(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
Expand Down Expand Up @@ -51,3 +54,40 @@ UClass* UDungeonRoomSensorDatabase::Select(const uint16_t identifier, const uint
return nullptr;
}
}

void UDungeonRoomSensorDatabase::OnEndGeneration(UDungeonRandom* synchronizedRandom, const UDungeonAisleGridMap* aisleGridMap, const float verticalGridSize, const std::function<void(const FSoftObjectPath&, const FTransform&)>& spawnActor) const
{
if (synchronizedRandom == nullptr)
return;
if (aisleGridMap == nullptr)
return;
if (SpawnActorInAisle.IsEmpty())
return;
aisleGridMap->Each([this, synchronizedRandom, verticalGridSize, spawnActor](const TArray<FDungeonAisleGrid>& aisleGridArray)
{
// スポーン先グリッドを抽選します
std::unordered_set<int32> gridIndexes;
const int32 totalGrids = aisleGridArray.Num();
const int32 count = totalGrids / 3;
for (int32 i = 0; i < count; ++i)
{
gridIndexes.emplace(synchronizedRandom->GetIntegerFrom(totalGrids));
}

const int32 totalActors = SpawnActorInAisle.Num();
for (const auto gridIndex : gridIndexes)
{
// スポーンする姿勢を求める
const auto& aisleGrid = aisleGridArray[gridIndex];
const FTransform transform(
dungeon::detail::ToRotator(aisleGrid.Direction),
aisleGrid.Location + FVector(0, 0, verticalGridSize / 2.f)
);

// アクターをスポーンします
const int32 actorType = synchronizedRandom->GetIntegerFrom(totalActors);
spawnActor(SpawnActorInAisle[actorType], transform);
}
}
);
}
2 changes: 1 addition & 1 deletion Source/DungeonGenerator/Public/DungeonBlueprint.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ADungeonRoomSensorBase;
BluePrint function library
ダンジョン生成ブループリント関数
*/
UCLASS()
UCLASS(ClassGroup = "DungeonGenerator")
class UDungeonBlueprint : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
Expand Down
22 changes: 16 additions & 6 deletions Source/DungeonGenerator/Public/DungeonGenerateBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ All Rights Reserved.
*/

#pragma once
#include "helper/DungeonRandom.h"
#include "Helper/DungeonRandom.h"
#include "Mission/DungeonRoomItem.h"
#include "Mission/DungeonRoomParts.h"
#include "Mission/DungeonRoomProps.h"
Expand Down Expand Up @@ -159,10 +159,10 @@ class DUNGEONGENERATOR_API ADungeonGenerateBase : public AActor
// アクターのスポーンと破棄
public:
/**
アクターをスポーンします。
DungeonGeneratorというタグを追加します。
スポーンしたアクターはDestroySpawnedActorsで破棄されます。
*/
* アクターをスポーンします。
* DungeonGeneratorというタグを追加します。
* スポーンしたアクターはDestroySpawnedActorsで破棄されます。
*/
static AActor* SpawnActorImpl(UWorld* world, UClass* actorClass, const FString& folderPath, const FTransform& transform, const FActorSpawnParameters& actorSpawnParameters);

/**
Expand Down Expand Up @@ -237,14 +237,24 @@ class DUNGEONGENERATOR_API ADungeonGenerateBase : public AActor

/**
* Event called at the end of the Create function
* synchronizedRandom is a random number that is synchronized between clients. It must always be called the same number of times on server and client.
* aisleGridMap is a container for the generated aisle grid
*
* Create関数終了時に呼び出されるイベントです
* synchronizedRandomは、クライアント間で同期する乱数です。かならずサーバーとクライアントで同じ回数を呼び出す必要があります
* aisleGridMapは、生成された通路グリッドのコンテナ
*/
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "DungeonGenerator")
void EndGeneration(UDungeonRandom* synchronizedRandom, const UDungeonAisleGridMap* aisleGridMap);

/**
* Event called at the end of the Create function
* synchronizedRandom is a random number that is synchronized between clients. It must always be called the same number of times on server and client.
* aisleGridMap is a container for the generated aisle grid
*
* Create関数終了時に呼び出されるイベントです
* synchronizedRandomは、クライアント間で同期する乱数です。かならずサーバーとクライアントで同じ回数を呼び出す必要があります
* aisleGridMapは、生成された通路グリッドのコンテナ
*/
UPROPERTY(BlueprintAssignable, Category = "DungeonGenerator|Event")
FDungeonGenerateBaseOnEndGenerateSignature OnEndGeneration;
Expand Down Expand Up @@ -302,7 +312,7 @@ class DUNGEONGENERATOR_API ADungeonGenerateBase : public AActor
void CreateImplement_AddPillarAndTorch(const CreateImplementParameter& cp, ADungeonRoomSensorBase* dungeonRoomSensorBase, const bool hasAuthority) const;

// Room sensor
void CreateImplement_PrepareSpawnRoomSensor(RoomAndRoomSensorMap& roomSensorCache) const;
void CreateImplement_PrepareSpawnRoomSensor(RoomAndRoomSensorMap& roomSensorCache, const bool hasAuthority) const;
static void CreateImplement_FinishSpawnRoomSensor(const RoomAndRoomSensorMap& roomSensorCache);

// Navigation
Expand Down
Loading