From 637e9d6a94760680403001a6d843a0639f8aae39 Mon Sep 17 00:00:00 2001 From: Shun Moriya <23472415+shun126@users.noreply.github.com> Date: Sun, 15 Oct 2023 18:42:39 +0900 Subject: [PATCH] version 1.5.0 --- CHANGELOG.md | 146 ++ Document/DungeonGenerator.doxyfile | 216 ++- DungeonGenerator.uplugin | 22 +- README.md | 8 +- Samples/.gitignore | 1 - Samples/Base.mtl | 4 - Samples/SM_BaseFloor.obj | 30 - Samples/SM_BasePillar.obj | 26 - Samples/SM_BaseRoof.obj | 24 - Samples/SM_BaseSlope.obj | 18 - Samples/SM_BaseWall.obj | 26 - Source/DungeonGenerator/Private/Core/Aisle.h | 1 + .../Private/Core/Debug/BuildInfomation.h | 6 +- .../Private/Core/Debug/Debug.cpp | 19 +- .../Private/Core/Debug/Debug.h | 56 +- .../Private/Core/Debug/Stopwatch.h | 38 +- .../Private/Core/DelaunayTriangulation3D.h | 7 + .../DungeonGenerator/Private/Core/Direction.h | 13 +- .../Private/Core/Direction.inl | 5 +- .../DungeonGenerator/Private/Core/DrawLots.h | 23 +- .../Private/Core/GateFinder.h | 7 + .../Private/Core/GenerateParameter.h | 83 +- .../Private/Core/GenerateParameter.inl | 92 ++ .../Private/Core/Generator.cpp | 312 +++-- .../DungeonGenerator/Private/Core/Generator.h | 115 +- .../Private/Core/Generator.inl | 95 ++ Source/DungeonGenerator/Private/Core/Grid.cpp | 80 +- Source/DungeonGenerator/Private/Core/Grid.h | 26 +- Source/DungeonGenerator/Private/Core/Grid.inl | 19 +- .../Private/Core/Helper/Crc.h | 58 + .../Private/Core/Identifier.h | 12 + .../Private/Core/Identifier.inl | 5 + .../DungeonGenerator/Private/Core/Math/Math.h | 72 +- .../Private/Core/Math/PerlinNoise.cpp | 7 +- .../Private/Core/Math/PerlinNoise.h | 5 +- .../Private/Core/Math/Point.cpp | 11 + .../Private/Core/Math/Point.h | 5 + .../Private/Core/Math/Random.h | 58 +- .../Private/Core/Math/Random.inl | 28 +- .../Private/Core/Math/Tetrahedron.h | 1 + .../Private/Core/MinimumSpanningTree.cpp | 2 +- .../Private/Core/MinimumSpanningTree.h | 9 +- .../Core/MissionGraph/MissionGraph.cpp | 53 +- .../Private/Core/PathFinder.cpp | 22 +- .../Private/Core/PathFinder.h | 8 + .../Private/Core/PathGoalCondition.h | 7 + .../Private/Core/PathNodeSwitcher.h | 7 + .../Private/Core/PathNodeSwitcher.inl | 12 +- Source/DungeonGenerator/Private/Core/Room.cpp | 25 +- Source/DungeonGenerator/Private/Core/Room.h | 17 +- Source/DungeonGenerator/Private/Core/Room.inl | 10 + .../DungeonGenerator/Private/Core/Voxel.cpp | 36 +- Source/DungeonGenerator/Private/Core/Voxel.h | 29 + .../Decorator/DungeonInteriorDatabase.cpp | 39 + .../Decorator/DungeonInteriorDecorator.cpp | 300 ++++ .../{ => Decorator}/DungeonInteriorParts.cpp | 5 +- .../Private/DungeonAisleMeshSet.cpp | 17 + .../Private/DungeonAisleMeshSetDatabase.cpp | 23 + .../Private/DungeonBlueprint.cpp | 49 + .../Private/DungeonGenerateActor.cpp | 391 ++++-- .../Private/DungeonGenerateParameter.cpp | 398 +++--- .../Private/DungeonGeneratorCore.cpp | 1226 ++++++++++------- .../Private/DungeonInteriorAsset.cpp | 55 - .../Private/DungeonInteriorDecorator.cpp | 254 ---- .../Private/DungeonLevelStreamingDynamic.cpp | 12 - .../Private/DungeonMeshSet.cpp | 59 + ...mponent.cpp => DungeonMeshSetDatabase.cpp} | 2 +- .../Private/DungeonRoomMeshSet.cpp | 7 + .../Private/DungeonRoomMeshSetDatabase.cpp | 23 + .../Private/MiniMap/DungeonMiniMap.cpp | 18 + .../{ => MiniMap}/DungeonMiniMapTexture.cpp | 3 +- .../DungeonMiniMapTextureLayer.cpp | 34 +- .../Private/{ => Mission}/DungeonRoomItem.cpp | 2 +- .../{ => Mission}/DungeonRoomParts.cpp | 7 +- .../{ => Mission}/DungeonRoomProps.cpp | 2 +- .../Private/PluginInfomation.h | 18 +- .../Private/{ => SubActor}/DungeonDoor.cpp | 2 +- .../{ => SubActor}/DungeonRoomSensor.cpp | 26 +- .../DungeonLevelStreamingActor.cpp | 2 +- .../SubLevel/DungeonLevelStreamingDynamic.cpp | 7 + .../DungeonInteriorDatabase.h} | 19 +- .../Decorator/DungeonInteriorDecorator.h | 213 +++ .../DungeonInteriorLocationComponent.h | 15 +- .../{ => Decorator}/DungeonInteriorParts.h | 35 +- .../Decorator/DungeonInteriorSystemTags.h | 132 ++ .../DungeonInteriorUnplaceableBounds.h | 83 ++ .../Public/DungeonAisleMeshSet.h | 28 + .../Public/DungeonAisleMeshSetDatabase.h | 43 + .../Public/DungeonBlueprint.h | 24 + .../Public/DungeonGenerateActor.h | 76 +- .../Public/DungeonGenerateParameter.h | 476 +++---- .../Public/DungeonGeneratorCore.h | 88 +- .../Public/DungeonInteriorDecorator.h | 115 -- .../DungeonGenerator/Public/DungeonMeshSet.h | 200 +++ .../Public/DungeonMeshSetDatabase.h | 33 + .../Public/DungeonRoomLocator.h | 168 --- .../Public/DungeonRoomMeshSet.h | 80 ++ .../Public/DungeonRoomMeshSetDatabase.h | 43 + .../Public/MiniMap/DungeonMiniMap.h | 90 ++ .../{ => MiniMap}/DungeonMiniMapTexture.h | 5 +- .../DungeonMiniMapTextureLayer.h | 32 +- .../Public/{ => Mission}/DungeonRoomItem.h | 0 .../Public/{ => Mission}/DungeonRoomParts.h | 30 +- .../Public/{ => Mission}/DungeonRoomProps.h | 0 .../Public/{ => SubActor}/DungeonDoor.h | 2 +- .../Public/{ => SubActor}/DungeonRoomSensor.h | 24 +- .../DungeonLevelStreamingActor.h | 2 +- .../DungeonLevelStreamingDynamic.h | 6 +- .../DungeonRoomDatabase.h} | 18 +- .../Public/SubLevel/DungeonRoomLocator.h | 81 ++ .../Public/SubLevel/DungeonRoomRegister.h | 112 ++ .../Private/BuildInfomation.h | 6 +- .../DungeonAisleMeshSetDatabaseFactory.cpp | 20 + ...DungeonAisleMeshSetDatabaseTypeActions.cpp | 13 + .../DungeonGenerateParameterTypeActions.cpp | 25 - .../Private/DungeonGeneratorEditorModule.cpp | 654 +++++---- .../Private/DungeonInteriorAssetFactory.cpp | 20 - .../DungeonInteriorAssetTypeActions.cpp | 38 - .../DungeonInteriorDatabaseFactory.cpp | 20 + .../DungeonInteriorDatabaseTypeActions.cpp | 13 + .../Private/DungeonMiniMapFactory.cpp | 20 + .../Private/DungeonMiniMapTypeActions.cpp | 13 + .../Private/DungeonRoomAssetFactory.cpp | 20 - .../Private/DungeonRoomAssetTypeActions.cpp | 38 - .../Private/DungeonRoomDatabaseFactory.cpp | 20 + .../DungeonRoomDatabaseTypeActions.cpp | 13 + .../DungeonRoomMeshSetDatabaseFactory.cpp | 20 + .../DungeonRoomMeshSetDatabaseTypeActions.cpp | 13 + .../Private/PluginInfomation.h | 18 +- ...h => DungeonAisleMeshSetDatabaseFactory.h} | 15 +- .../DungeonAisleMeshSetDatabaseTypeActions.h | 53 + .../Public/DungeonGenerateParameterFactory.h | 7 - .../DungeonGenerateParameterTypeActions.h | 25 + .../Public/DungeonGeneratorEditorModule.h | 8 +- .../Public/DungeonInteriorAssetTypeActions.h | 28 - .../Public/DungeonInteriorDatabaseFactory.h | 29 + .../DungeonInteriorDatabaseTypeActions.h | 53 + ...AssetFactory.h => DungeonMiniMapFactory.h} | 8 +- .../Public/DungeonMiniMapTypeActions.h | 53 + .../Public/DungeonRoomAssetTypeActions.h | 28 - .../Public/DungeonRoomDatabaseFactory.h | 29 + .../Public/DungeonRoomDatabaseTypeActions.h | 53 + .../DungeonRoomMeshSetDatabaseFactory.h | 23 + .../DungeonRoomMeshSetDatabaseTypeActions.h | 53 + 144 files changed, 5675 insertions(+), 3045 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 Samples/.gitignore delete mode 100644 Samples/Base.mtl delete mode 100644 Samples/SM_BaseFloor.obj delete mode 100644 Samples/SM_BasePillar.obj delete mode 100644 Samples/SM_BaseRoof.obj delete mode 100644 Samples/SM_BaseSlope.obj delete mode 100644 Samples/SM_BaseWall.obj create mode 100644 Source/DungeonGenerator/Private/Core/Helper/Crc.h create mode 100644 Source/DungeonGenerator/Private/Decorator/DungeonInteriorDatabase.cpp create mode 100644 Source/DungeonGenerator/Private/Decorator/DungeonInteriorDecorator.cpp rename Source/DungeonGenerator/Private/{ => Decorator}/DungeonInteriorParts.cpp (50%) create mode 100644 Source/DungeonGenerator/Private/DungeonAisleMeshSet.cpp create mode 100644 Source/DungeonGenerator/Private/DungeonAisleMeshSetDatabase.cpp delete mode 100644 Source/DungeonGenerator/Private/DungeonInteriorAsset.cpp delete mode 100644 Source/DungeonGenerator/Private/DungeonInteriorDecorator.cpp delete mode 100644 Source/DungeonGenerator/Private/DungeonLevelStreamingDynamic.cpp create mode 100644 Source/DungeonGenerator/Private/DungeonMeshSet.cpp rename Source/DungeonGenerator/Private/{DungeonInteriorLocationComponent.cpp => DungeonMeshSetDatabase.cpp} (63%) create mode 100644 Source/DungeonGenerator/Private/DungeonRoomMeshSet.cpp create mode 100644 Source/DungeonGenerator/Private/DungeonRoomMeshSetDatabase.cpp create mode 100644 Source/DungeonGenerator/Private/MiniMap/DungeonMiniMap.cpp rename Source/DungeonGenerator/Private/{ => MiniMap}/DungeonMiniMapTexture.cpp (95%) rename Source/DungeonGenerator/Private/{ => MiniMap}/DungeonMiniMapTextureLayer.cpp (83%) rename Source/DungeonGenerator/Private/{ => Mission}/DungeonRoomItem.cpp (90%) rename Source/DungeonGenerator/Private/{ => Mission}/DungeonRoomParts.cpp (90%) rename Source/DungeonGenerator/Private/{ => Mission}/DungeonRoomProps.cpp (90%) rename Source/DungeonGenerator/Private/{ => SubActor}/DungeonDoor.cpp (91%) rename Source/DungeonGenerator/Private/{ => SubActor}/DungeonRoomSensor.cpp (89%) rename Source/DungeonGenerator/Private/{ => SubLevel}/DungeonLevelStreamingActor.cpp (97%) create mode 100644 Source/DungeonGenerator/Private/SubLevel/DungeonLevelStreamingDynamic.cpp rename Source/DungeonGenerator/Public/{DungeonInteriorAsset.h => Decorator/DungeonInteriorDatabase.h} (55%) create mode 100644 Source/DungeonGenerator/Public/Decorator/DungeonInteriorDecorator.h rename Source/DungeonGenerator/Public/{ => Decorator}/DungeonInteriorLocationComponent.h (70%) rename Source/DungeonGenerator/Public/{ => Decorator}/DungeonInteriorParts.h (79%) create mode 100644 Source/DungeonGenerator/Public/Decorator/DungeonInteriorSystemTags.h create mode 100644 Source/DungeonGenerator/Public/Decorator/DungeonInteriorUnplaceableBounds.h create mode 100644 Source/DungeonGenerator/Public/DungeonAisleMeshSet.h create mode 100644 Source/DungeonGenerator/Public/DungeonAisleMeshSetDatabase.h delete mode 100644 Source/DungeonGenerator/Public/DungeonInteriorDecorator.h create mode 100644 Source/DungeonGenerator/Public/DungeonMeshSet.h create mode 100644 Source/DungeonGenerator/Public/DungeonMeshSetDatabase.h delete mode 100644 Source/DungeonGenerator/Public/DungeonRoomLocator.h create mode 100644 Source/DungeonGenerator/Public/DungeonRoomMeshSet.h create mode 100644 Source/DungeonGenerator/Public/DungeonRoomMeshSetDatabase.h create mode 100644 Source/DungeonGenerator/Public/MiniMap/DungeonMiniMap.h rename Source/DungeonGenerator/Public/{ => MiniMap}/DungeonMiniMapTexture.h (96%) rename Source/DungeonGenerator/Public/{ => MiniMap}/DungeonMiniMapTextureLayer.h (84%) rename Source/DungeonGenerator/Public/{ => Mission}/DungeonRoomItem.h (100%) rename Source/DungeonGenerator/Public/{ => Mission}/DungeonRoomParts.h (50%) rename Source/DungeonGenerator/Public/{ => Mission}/DungeonRoomProps.h (100%) rename Source/DungeonGenerator/Public/{ => SubActor}/DungeonDoor.h (98%) rename Source/DungeonGenerator/Public/{ => SubActor}/DungeonRoomSensor.h (91%) rename Source/DungeonGenerator/Public/{ => SubLevel}/DungeonLevelStreamingActor.h (96%) rename Source/DungeonGenerator/Public/{ => SubLevel}/DungeonLevelStreamingDynamic.h (82%) rename Source/DungeonGenerator/Public/{DungeonRoomAsset.h => SubLevel/DungeonRoomDatabase.h} (50%) create mode 100644 Source/DungeonGenerator/Public/SubLevel/DungeonRoomLocator.h create mode 100644 Source/DungeonGenerator/Public/SubLevel/DungeonRoomRegister.h create mode 100644 Source/DungeonGeneratorEditor/Private/DungeonAisleMeshSetDatabaseFactory.cpp create mode 100644 Source/DungeonGeneratorEditor/Private/DungeonAisleMeshSetDatabaseTypeActions.cpp delete mode 100644 Source/DungeonGeneratorEditor/Private/DungeonInteriorAssetFactory.cpp delete mode 100644 Source/DungeonGeneratorEditor/Private/DungeonInteriorAssetTypeActions.cpp create mode 100644 Source/DungeonGeneratorEditor/Private/DungeonInteriorDatabaseFactory.cpp create mode 100644 Source/DungeonGeneratorEditor/Private/DungeonInteriorDatabaseTypeActions.cpp create mode 100644 Source/DungeonGeneratorEditor/Private/DungeonMiniMapFactory.cpp create mode 100644 Source/DungeonGeneratorEditor/Private/DungeonMiniMapTypeActions.cpp delete mode 100644 Source/DungeonGeneratorEditor/Private/DungeonRoomAssetFactory.cpp delete mode 100644 Source/DungeonGeneratorEditor/Private/DungeonRoomAssetTypeActions.cpp create mode 100644 Source/DungeonGeneratorEditor/Private/DungeonRoomDatabaseFactory.cpp create mode 100644 Source/DungeonGeneratorEditor/Private/DungeonRoomDatabaseTypeActions.cpp create mode 100644 Source/DungeonGeneratorEditor/Private/DungeonRoomMeshSetDatabaseFactory.cpp create mode 100644 Source/DungeonGeneratorEditor/Private/DungeonRoomMeshSetDatabaseTypeActions.cpp rename Source/DungeonGeneratorEditor/Public/{DungeonInteriorAssetFactory.h => DungeonAisleMeshSetDatabaseFactory.h} (58%) create mode 100644 Source/DungeonGeneratorEditor/Public/DungeonAisleMeshSetDatabaseTypeActions.h delete mode 100644 Source/DungeonGeneratorEditor/Public/DungeonInteriorAssetTypeActions.h create mode 100644 Source/DungeonGeneratorEditor/Public/DungeonInteriorDatabaseFactory.h create mode 100644 Source/DungeonGeneratorEditor/Public/DungeonInteriorDatabaseTypeActions.h rename Source/DungeonGeneratorEditor/Public/{DungeonRoomAssetFactory.h => DungeonMiniMapFactory.h} (64%) create mode 100644 Source/DungeonGeneratorEditor/Public/DungeonMiniMapTypeActions.h delete mode 100644 Source/DungeonGeneratorEditor/Public/DungeonRoomAssetTypeActions.h create mode 100644 Source/DungeonGeneratorEditor/Public/DungeonRoomDatabaseFactory.h create mode 100644 Source/DungeonGeneratorEditor/Public/DungeonRoomDatabaseTypeActions.h create mode 100644 Source/DungeonGeneratorEditor/Public/DungeonRoomMeshSetDatabaseFactory.h create mode 100644 Source/DungeonGeneratorEditor/Public/DungeonRoomMeshSetDatabaseTypeActions.h diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..56e4a58 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,146 @@ +# Change Log + +## [Unreleased] -1.5.1 (15) +### Changes +### 変更点 + +## 20231010-1.5.0 (14) +### Changes +### 変更点 + +## 20230930-1.4.8 (13) +### Changes +* Fixed a bug when the start room was not set +### 変更点 +* スタート部屋を未設定の時の不具合を修正 + +## 20230920-1.4.7 (12) +### Changes +* Register dungeon mesh in DungeonPartsDatabase asset +* Fixed some bugs +### 変更点 +* DungeonPartsDatabaseアセットにダンジョンのメッシュを登録 +* いくつかの不具合を修正 + +## 20230908-1.4.6 (11) +### Changes +* Support for Unreal Engine 5.3 +* Unreal Engine 4 no longer supported +* Support for loading sublevels in the start and finish rooms +* Added plug-in content +### 変更点 +* Unreal Engine 5.3に対応 +* Unreal Engine 4のサポートを終了 +* スタート部屋、ゴール部屋のサブレベル読み込みに対応 +* プラグインコンテンツの追加 + +## 20230901-1.4.5 (10) +### Changes +* Modified to generate dungeons on a flat surface if room merging or room margins are less than 1 +* Added minimap information asset +* Added world space to texture space conversion class +* modified paths for plugin content +* Added sample data to plugin, eliminated dungeon hierarchy specification +* Fixed cache misalignment in system tags +* Started network functionality verification +* Moved PlayerStart off center of start room if more than one PlayerStart was installed +### 変更点 +* 部屋の結合または部屋の余白が1以下ならば平面にダンジョンを生成するように修正 +* ミニマップ情報アセットを追加 +* ワールド空間からテクスチャ空間への変換クラスを追加 +* プラグインコンテンツのパスを修正 +* プラグインにサンプルデータを追加、ダンジョンの階層指定を廃止 +* システムタグのキャッシュずれを修正 +* ネットワーク機能検証開始 +* PlayerStartが複数設置されていた場合はスタート部屋の中心からずらして配置 + + +## 20230801-1.4.4 (9) +### Changes +* Interior Decorator beta version released +* Added interior assets +* Fixed some bugs +### 変更点 +* インテリアデコレーターベータ版リリース +* インテリアアセットを追加 +* いくつかの不具合を修正 + + +## 20230514-1.4.3 (8) +### Changes +* Interior Decorator Verification +* Removed sample models as plug-in assets were added +### 変更点 +* インテリアデコレーターの検証 +* プラグインアセットを追加に伴ってサンプルモデルを削除 + + +## 20230514-1.4.2 (7) +### Changes +* UE5.2 support +* Supports sub-level merging +* Copy LevelStreaming actors in editor mode to level +### 変更点 +* Unreal Engine 5.2に対応 +* サブレベルのマージに対応 +* エディタモードの LevelStreamingアクターをレベルにコピー + + +## 20230409-v1.4.1 (6) +### Changes +* Confirmed that the package can be created. +* Add a test that generates pre-created sublevels in the room. +* Add a test for vertical margins. +* Add a test generation of minimap texture assets. +### 変更点 +* パッケージが作成できることを確認 +* 部屋にあらかじめ作成されたサブレベルを生成するテスト +* 垂直マージンのテスト +* ミニマップのテクスチャアセットの生成テスト + + +## 20230403-v1.4.0 (5) +### Changes +* Generate mini-maps in two types of pixel size and resolution (dots/meters) +### 変更点 +* ミニマップをピクセルサイズと解像度(ドット/メートル)の二種類から生成 + + +## 20230321-v1.3.1 (4) +### Changes +* Fixed mini-map generationo fail. +### 変更点 +* ミニマップ生成時にクラッシュする問題を修正 + + +## 20230319-v1.3.0 (3) +### Changes +* Supports mini-maps +### 変更点 +* ミニマップに対応 + + +## 20230316-v1.2.0 (2) +### Changes +* Compatible with Unreal Engine 4.27.2 +### 変更点 +* Unreal Engine 4.27.2に対応 + + +## 20230308-v1.0.1 (1) +### Changes +* Fixed hang-up when referencing minimap textures when dungeon creation fails. +* Changed random room placement method to be more randomly distributed. +* Improved dungeon creation speed. +### 変更点 +* ダンジョン生成に失敗した時にミニマップのテクスチャを参照するとハングアップする問題を修正 +* 部屋のランダム配置方法をよりランダムに分散するよう変更 +* ダンジョン生成速度を改善 + + +## 20230303-v1.0.0 (0) +### Changes +* Initial release version +### 変更点 +* 初回リリース版 + diff --git a/Document/DungeonGenerator.doxyfile b/Document/DungeonGenerator.doxyfile index 9184afe..fa662d6 100644 --- a/Document/DungeonGenerator.doxyfile +++ b/Document/DungeonGenerator.doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.9.3 +# Doxyfile 1.9.6 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -12,6 +12,16 @@ # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options @@ -32,19 +42,19 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = DungeonGenerator +PROJECT_NAME = "Dungeon Generator" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 0.01 +PROJECT_NUMBER = 1.4.2 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = +PROJECT_BRIEF = "Procedural 3D dungeon generator plugin for Unreal Engine 4 & 5. Easy generation of levels, mini-maps and missions." # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 @@ -60,16 +70,28 @@ PROJECT_LOGO = OUTPUT_DIRECTORY = ./doxygen -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes -# performance problems for the file system. +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode @@ -81,14 +103,14 @@ ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English @@ -357,7 +379,7 @@ AUTOLINK_SUPPORT = YES # diagrams that involve STL classes more complete and accurate. # The default value is: NO. -BUILTIN_STL_SUPPORT = NO +BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. @@ -452,7 +474,7 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 -# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing @@ -546,7 +568,8 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO @@ -577,14 +600,15 @@ INTERNAL_DOCS = NO # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that -# are not case sensitive the option should be be set to NO to properly deal with +# are not case sensitive the option should be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On # Windows (including Cygwin) and MacOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. -# The default value is: system dependent. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. CASE_SENSE_NAMES = NO @@ -658,14 +682,14 @@ SORT_BRIEF_DOCS = NO # detailed member documentation. # The default value is: NO. -SORT_MEMBERS_CTORS_1ST = NO +SORT_MEMBERS_CTORS_1ST = YES # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. -SORT_GROUP_NAMES = NO +SORT_GROUP_NAMES = YES # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will @@ -836,6 +860,14 @@ WARN_IF_INCOMPLETE_DOC = YES WARN_NO_PARAMDOC = NO +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but @@ -851,10 +883,21 @@ WARN_AS_ERROR = NO # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). In case the file specified cannot be opened for writing the @@ -881,10 +924,21 @@ INPUT = ../Source # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding +# "INPUT_ENCODING" for further information on supported encodings. + +INPUT_FILE_ENCODING = + # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. @@ -1033,6 +1087,11 @@ IMAGE_PATH = # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. @@ -1074,6 +1133,15 @@ FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- @@ -1211,10 +1279,11 @@ CLANG_DATABASE_PATH = ALPHABETICAL_INDEX = YES -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = @@ -1293,7 +1362,12 @@ HTML_STYLESHEET = # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = @@ -1308,6 +1382,19 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a color-wheel, see @@ -1606,7 +1693,7 @@ DISABLE_INDEX = NO # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -GENERATE_TREEVIEW = NO +GENERATE_TREEVIEW = YES # When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the # FULL_SIDEBAR option determines if the side bar is limited to only the treeview @@ -1618,7 +1705,7 @@ GENERATE_TREEVIEW = NO # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -FULL_SIDEBAR = NO +FULL_SIDEBAR = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. @@ -1671,17 +1758,6 @@ HTML_FORMULA_FORMAT = png FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. @@ -2276,7 +2352,8 @@ SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the -# preprocessor. +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = @@ -2400,26 +2477,38 @@ HAVE_DOT = NO DOT_NUM_THREADS = 0 -# When you want a differently looking font in the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. +# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of +# subgraphs. When you want a differently looking font in the dot files that +# doxygen generates you can specify fontname, fontcolor and fontsize attributes. +# For details please see Node, +# Edge and Graph Attributes specification You need to make sure dot is able +# to find the font, which can be done by putting it in a standard location or by +# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. Default graphviz fontsize is 14. +# The default value is: fontname=Helvetica,fontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTNAME = Helvetica +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. +# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can +# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about +# arrows shapes. +# The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTSIZE = 10 +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" -# By default doxygen will tell dot to use the default font as specified with -# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set -# the path where dot can find it using this tag. +# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes +# around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification +# The default value is: shape=box,height=0.2,width=0.4. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" + +# You can set the path where dot can find font specified with fontname in +# DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = @@ -2445,7 +2534,8 @@ CLASS_GRAPH = YES COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. +# groups, showing the direct groups dependencies. See also the chapter Grouping +# in the manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2661,18 +2751,6 @@ DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = NO - # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support diff --git a/DungeonGenerator.uplugin b/DungeonGenerator.uplugin index ab7f8f5..bc2257c 100644 --- a/DungeonGenerator.uplugin +++ b/DungeonGenerator.uplugin @@ -1,17 +1,17 @@ { "FileVersion": 3, - "Version": 7, - "VersionName": "1.4.2", - "FriendlyName": "DungeonGenerator", + "Version": 14, + "VersionName": "1.5.0", + "FriendlyName": "Procedural 3D Dungeon Generator", "Description": "Procedural 3D dungeon generator plugin. Easy generation of levels, mini-maps and missions.", "Category": "Procedural", "CreatedBy": "Narcis Software", - "CreatedByURL": "https://happy-game-dev.undo.jp/", - "DocsURL": "https://github.com/shun126/DungeonGenerator/wiki/", + "CreatedByURL": "https://github.com/shun126/UE5-DungeonGeneratorDemo", + "DocsURL": "https://github.com/shun126/UE5-DungeonGeneratorDemo/wiki", "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/36a8b87d859f44439cfe1515975d7197", - "SupportURL": "https://github.com/shun126/DungeonGenerator/discussions/", + "SupportURL": "https://github.com/shun126/UE5-DungeonGeneratorDemo/discussions/", "CanContainContent": false, - "IsBetaVersion": false, + "IsBetaVersion": true, "IsExperimentalVersion": false, "Installed": false, "Modules": [ @@ -19,9 +19,6 @@ "Name": "DungeonGenerator", "Type": "Runtime", "LoadingPhase": "Default", - "WhitelistPlatforms": [ - "Win64" - ], "PlatformAllowList": [ "Win64" ] @@ -30,12 +27,9 @@ "Name": "DungeonGeneratorEditor", "Type": "UncookedOnly", "LoadingPhase": "Default", - "WhitelistPlatforms": [ - "Win64" - ], "PlatformAllowList": [ "Win64" ] } ] -} +} \ No newline at end of file diff --git a/README.md b/README.md index 709426f..067a931 100644 --- a/README.md +++ b/README.md @@ -46,13 +46,15 @@ The dungeon generator was based on Vazgriz's algorithm. You can read more about This is an easy to use. Simply drop the DungeonGenerateActor into your level, set the grid scale and number of rooms and start generating out your structures. Please read the [Wiki](https://github.com/shun126/DungeonGenerator/wiki) for more information. # 🔧 Requirements -* [Unreal Engine 5.1.1 or Unreal Engine 4.27.2](https://www.unrealengine.com/) +* [Unreal Engine 5.3.1 or Unreal Engine 4.27.2](https://www.unrealengine.com/) * [Visual Studio 2022](https://visualstudio.microsoft.com/) # 📜 License -* GPL-3.0 +you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. -[UnrealEngine marketplace](https://www.unrealengine.com/marketplace/slug/36a8b87d859f44439cfe1515975d7197) is releasing it under Epic license. If you need a license other than the GPL, please consider it. Proceeds will be used to fund the development of our game. +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied arranty of MERCHANTABILITY or FITNESS FOR A ARTICULAR PURPOSE. See the GNU General Public License for more details. + +Or, [UnrealEngine marketplace](https://www.unrealengine.com/marketplace/slug/36a8b87d859f44439cfe1515975d7197) is releasing it under Epic license. If you need a license other than the GPL, please consider it. Proceeds will be used to fund the development of our game. ## This is the screenshot of our game diff --git a/Samples/.gitignore b/Samples/.gitignore deleted file mode 100644 index fd399d3..0000000 --- a/Samples/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!*.obj diff --git a/Samples/Base.mtl b/Samples/Base.mtl deleted file mode 100644 index ff7a5f6..0000000 --- a/Samples/Base.mtl +++ /dev/null @@ -1,4 +0,0 @@ -# Exported from 3D Builder - -newmtl Gray_0 -Kd 0.5 0.5 0.5 diff --git a/Samples/SM_BaseFloor.obj b/Samples/SM_BaseFloor.obj deleted file mode 100644 index e1d8706..0000000 --- a/Samples/SM_BaseFloor.obj +++ /dev/null @@ -1,30 +0,0 @@ -# Exported from 3D Builder -mtllib Base.mtl - -o Object.1 -v -2.000000 0.25000000 -2.000000 -v -2.000000 0.25000000 2.000000 -v 2.000000 0.25000000 -2.000000 -v 2.000000 0.25000000 2.000000 -v -2.000000 0.000000 -2.000000 -v -2.000000 0.000000 2.000000 -v 2.000000 0.000000 -2.000000 -v 2.000000 0.000000 2.000000 - -usemtl Green_0 -# Top -f 1 2 3 -f 3 2 4 -# Front -f 2 6 4 -f 4 6 8 -# Right -f 4 8 3 -f 3 8 7 -# Left -f 1 5 2 -f 2 5 6 -# Back -f 1 3 5 -f 5 3 7 - diff --git a/Samples/SM_BasePillar.obj b/Samples/SM_BasePillar.obj deleted file mode 100644 index 767104c..0000000 --- a/Samples/SM_BasePillar.obj +++ /dev/null @@ -1,26 +0,0 @@ -# Exported from 3D Builder -mtllib Base.mtl - -o Object.1 -v -0.25 4.000000 -0.25 -v -0.25 4.000000 0.25 -v 0.25 4.000000 -0.25 -v 0.25 4.000000 0.25 -v -0.25 0.000000 -0.25 -v -0.25 0.000000 0.25 -v 0.25 0.000000 -0.25 -v 0.25 0.000000 0.25 - -usemtl Green_0 -f 1 2 3 -f 3 2 4 -f 2 6 4 -f 4 6 8 -f 4 8 3 -f 3 8 7 -f 2 5 6 -f 2 1 5 -f 1 3 5 -f 5 3 7 -f 5 7 8 -f 5 8 6 \ No newline at end of file diff --git a/Samples/SM_BaseRoof.obj b/Samples/SM_BaseRoof.obj deleted file mode 100644 index 92468fb..0000000 --- a/Samples/SM_BaseRoof.obj +++ /dev/null @@ -1,24 +0,0 @@ -# Exported from 3D Builder -mtllib Base.mtl - -o Object.1 -v -2.000000 4.000000 -2.000000 -v -2.000000 4.000000 2.000000 -v 2.000000 4.000000 -2.000000 -v 2.000000 4.000000 2.000000 -v -2.000000 3.900000 -2.000000 -v -2.000000 3.900000 2.000000 -v 2.000000 3.900000 -2.000000 -v 2.000000 3.900000 2.000000 - -usemtl Green_0 -f 2 6 4 -f 4 6 8 -f 4 8 3 -f 3 8 7 -f 2 5 6 -f 2 1 5 -f 1 3 5 -f 5 3 7 -f 5 7 8 -f 5 8 6 diff --git a/Samples/SM_BaseSlope.obj b/Samples/SM_BaseSlope.obj deleted file mode 100644 index 3f03b62..0000000 --- a/Samples/SM_BaseSlope.obj +++ /dev/null @@ -1,18 +0,0 @@ -# Exported from 3D Builder -mtllib Base.mtl - -o Object.1 -v -2.000000 4.000000 -6.000000 -v 2.000000 4.000000 -6.000000 -v -2.000000 0.000000 -6.000000 -v -2.000000 0.000000 2.000000 -v 2.000000 0.000000 -6.000000 -v 2.000000 0.000000 2.000000 - -usemtl Green_0 -f 1 4 6 -f 1 6 2 -f 2 6 5 -f 1 3 4 -f 1 2 3 -f 2 5 3 diff --git a/Samples/SM_BaseWall.obj b/Samples/SM_BaseWall.obj deleted file mode 100644 index e7599d6..0000000 --- a/Samples/SM_BaseWall.obj +++ /dev/null @@ -1,26 +0,0 @@ -# Exported from 3D Builder -mtllib Base.mtl - -o Object.1 -v -2.000000 4.000000 -0.15000 -v -2.000000 4.000000 0.15000 -v 2.000000 4.000000 -0.15000 -v 2.000000 4.000000 0.15000 -v -2.000000 0.000000 -0.15000 -v -2.000000 0.000000 0.15000 -v 2.000000 0.000000 -0.15000 -v 2.000000 0.000000 0.15000 - -usemtl Green_0 -f 1 2 3 -f 3 2 4 -f 2 6 4 -f 4 6 8 -f 4 8 3 -f 3 8 7 -f 2 5 6 -f 2 1 5 -f 1 3 5 -f 5 3 7 -f 5 7 8 -f 5 8 6 \ No newline at end of file diff --git a/Source/DungeonGenerator/Private/Core/Aisle.h b/Source/DungeonGenerator/Private/Core/Aisle.h index 1bc7ce9..cb04ef7 100644 --- a/Source/DungeonGenerator/Private/Core/Aisle.h +++ b/Source/DungeonGenerator/Private/Core/Aisle.h @@ -9,6 +9,7 @@ All Rights Reserved. #pragma once #include "Identifier.h" #include +#include namespace dungeon { diff --git a/Source/DungeonGenerator/Private/Core/Debug/BuildInfomation.h b/Source/DungeonGenerator/Private/Core/Debug/BuildInfomation.h index c374658..4e82212 100644 --- a/Source/DungeonGenerator/Private/Core/Debug/BuildInfomation.h +++ b/Source/DungeonGenerator/Private/Core/Debug/BuildInfomation.h @@ -3,6 +3,7 @@ This is automatically generated file. DO NOT EDIT THIS FILE. \author Shun Moriya \copyright 2023- Shun Moriya +All Rights Reserved. */ #pragma once #if !defined(JENKINS_BUILD_DETECTED) @@ -17,5 +18,6 @@ DO NOT EDIT THIS FILE. #define JENKINS_GIT_COMMIT "UNDEFINED COMMIT" #define JENKINS_UUID "3da61b40-1fba-7757-b07f-110de2a901e3" #define JENKINS_LICENSE "GPL v3" -#define JENKINS_FOR_DEVELOP 1 -#endif +#define JENKINS_FOR_DEVELOP 1 +#define JENKINS_FOR_RELEASE 0 +#endif diff --git a/Source/DungeonGenerator/Private/Core/Debug/Debug.cpp b/Source/DungeonGenerator/Private/Core/Debug/Debug.cpp index cc4f530..911532c 100644 --- a/Source/DungeonGenerator/Private/Core/Debug/Debug.cpp +++ b/Source/DungeonGenerator/Private/Core/Debug/Debug.cpp @@ -1,5 +1,8 @@ /** -デバッグに関するソースファイル +Debug function source files + +To prevent conflicts with other Windows macros, +do not include this file from the header. \author Shun Moriya \copyright 2023- Shun Moriya @@ -9,7 +12,7 @@ All Rights Reserved. #include "Debug.h" #include -// ログマクロ +// log macro #if UE_BUILD_DEBUG + UE_BUILD_DEVELOPMENT + UE_BUILD_TEST + UE_BUILD_SHIPPING > 0 DEFINE_LOG_CATEGORY(DungeonGeneratorLogger); #else @@ -22,8 +25,9 @@ namespace dungeon #if UE_BUILD_DEBUG + UE_BUILD_DEVELOPMENT + UE_BUILD_TEST + UE_BUILD_SHIPPING == 0 #if defined(_WINDOWS) && (defined(_DEBUG) || defined(DEBUG)) /** - VisualStudioの出力ウィンドウに出力します - ソースファイルからのみincludeされる前提なのでstatic関数で良い + Output to VisualStudio output window + Assumed to be included only from source files, + so static functions are fine. */ extern void OutputDebugStringWithArgument(const char* pszFormat, ...) { @@ -41,9 +45,6 @@ namespace dungeon #endif #endif - /** - BMPファイル生成 - */ namespace bmp { Canvas::Canvas() noexcept @@ -104,11 +105,11 @@ namespace dungeon return 0; } - // ヘッダ部分を生成、書き込み + // Generate and write header section fwrite(&mBmpHeader, sizeof(BMPFILEHEADER), 1, fp); fwrite(&mBmpInfo, sizeof(BMPINFOHEADER), 1, fp); - // データ部分を一行ずつ書き込み + // Write the data part line by line for (int64_t i = static_cast(mHeight) - 1; i >= 0; --i) { fwrite(&mRgbImage.get()[i * mWidth], sizeof(RGBCOLOR), mWidth, fp); diff --git a/Source/DungeonGenerator/Private/Core/Debug/Debug.h b/Source/DungeonGenerator/Private/Core/Debug/Debug.h index cb09067..fc72696 100644 --- a/Source/DungeonGenerator/Private/Core/Debug/Debug.h +++ b/Source/DungeonGenerator/Private/Core/Debug/Debug.h @@ -1,7 +1,8 @@ /** -デバッグに関するヘッダーファイル +Debug function header files -他のWindowsマクロと衝突を防ぐため、このファイルをヘッダーからincludeしないで下さい。 +To prevent conflicts with other Windows macros, +do not include this file from the header. \author Shun Moriya \copyright 2023- Shun Moriya @@ -27,7 +28,7 @@ All Rights Reserved. #define UE_BUILD_SHIPPING 0 #endif -// ログマクロ +// log macro #if UE_BUILD_DEBUG + UE_BUILD_DEVELOPMENT + UE_BUILD_TEST + UE_BUILD_SHIPPING > 0 #include DECLARE_LOG_CATEGORY_EXTERN(DungeonGeneratorLogger, Log, All); @@ -53,11 +54,17 @@ DECLARE_LOG_CATEGORY_EXTERN(DungeonGeneratorLogger, Log, All); namespace dungeon { /** - VisualStudioの出力ウィンドウに出力します - ソースファイルからのみincludeされる前提なのでstatic関数で良い + @addtogroup Debug + @{ + */ + + /*! + Output to VisualStudio output window + Assumed to be included only from source files, so static functions are fine. */ extern void OutputDebugStringWithArgument(const char* pszFormat, ...); + //! Microsoft Windows Bitmap Image Implementation namespace bmp { #pragma pack(1) @@ -94,55 +101,42 @@ namespace dungeon #pragma pack() /** - キャンバスクラス + Microsoft Windows Bitmap canvas class */ class Canvas { public: - // コントラクタ Canvas() noexcept; - - /** - コンストラクタ - */ Canvas(const uint32_t width, const uint32_t height) noexcept; - - // デストラクタ virtual ~Canvas() = default; - /** - 画像データの生成 - */ + //! Generate image data void Create(const uint32_t width, const uint32_t height) noexcept; - /* - 画像データの吐き出し - */ + //! Saves image data to a file int Write(const std::string& filename) noexcept; - /** - 点の描画 - */ + //! Draw point void Put(int32_t x, int32_t y, const RGBCOLOR color) noexcept; - /** - 矩形の描画 - */ + //! Draw rectangle void Rectangle(int32_t left, int32_t top, int32_t right, int32_t bottom, const RGBCOLOR color) noexcept; - /** - フレームの描画 - */ + //! Draw frame void Frame(int32_t left, int32_t top, int32_t right, int32_t bottom, const RGBCOLOR color) noexcept; private: BMPFILEHEADER mBmpHeader; BMPINFOHEADER mBmpInfo; - uint32_t mWidth; // 横幅 - uint32_t mHeight; // 縦幅 + uint32_t mWidth; + uint32_t mHeight; - std::unique_ptr mRgbImage; // 画像データの本体 + std::unique_ptr mRgbImage; }; } + + /** + @} + */ } diff --git a/Source/DungeonGenerator/Private/Core/Debug/Stopwatch.h b/Source/DungeonGenerator/Private/Core/Debug/Stopwatch.h index ddd677c..9b8ec6c 100644 --- a/Source/DungeonGenerator/Private/Core/Debug/Stopwatch.h +++ b/Source/DungeonGenerator/Private/Core/Debug/Stopwatch.h @@ -1,20 +1,41 @@ #pragma once #include -class Stopwatch +namespace dungeon { -public: - Stopwatch() + /** + @addtogroup Debug + @{ + */ + + //! Measure time + class Stopwatch + { + public: + //! Start time measurement + Stopwatch(); + + //! Start time measurement + void Start(); + + //! Get elapsed time + double Lap(); + + private: + std::chrono::system_clock::time_point mStartTime; + }; + + inline Stopwatch::Stopwatch() { Start(); } - void Start() + inline void Stopwatch::Start() { mStartTime = std::chrono::system_clock::now(); } - double Lap() + inline double Stopwatch::Lap() { const auto now = std::chrono::system_clock::now(); const auto delta = now - mStartTime; @@ -23,6 +44,7 @@ class Stopwatch return static_cast(nano) / 1000000000.0; } -private: - std::chrono::system_clock::time_point mStartTime; -}; + /** + @} + */ +} diff --git a/Source/DungeonGenerator/Private/Core/DelaunayTriangulation3D.h b/Source/DungeonGenerator/Private/Core/DelaunayTriangulation3D.h index 8505f03..d45d37b 100644 --- a/Source/DungeonGenerator/Private/Core/DelaunayTriangulation3D.h +++ b/Source/DungeonGenerator/Private/Core/DelaunayTriangulation3D.h @@ -17,6 +17,10 @@ All Rights Reserved. namespace dungeon { + /** + @addtogroup PathSelection + @{ + */ /** 三次元ドロネー三角形分割クラス @@ -64,6 +68,9 @@ namespace dungeon private: std::vector mTriangles; }; + /** + @} + */ } #include "DelaunayTriangulation3D.inl" diff --git a/Source/DungeonGenerator/Private/Core/Direction.h b/Source/DungeonGenerator/Private/Core/Direction.h index 6511a34..bf1d9b9 100644 --- a/Source/DungeonGenerator/Private/Core/Direction.h +++ b/Source/DungeonGenerator/Private/Core/Direction.h @@ -7,11 +7,17 @@ All Rights Reserved. */ #pragma once -#include "Core/Math/Random.h" #include +#include namespace dungeon { + class Random; + + /** + @addtogroup PathGeneration + @{ + */ /** 方向クラス */ @@ -128,11 +134,14 @@ namespace dungeon ランダムな方向を取得します \return ランダムな方向 */ - static Direction CreateFromRandom(Random& random) noexcept; + static Direction CreateFromRandom(const std::shared_ptr& random) noexcept; private: Index mIndex; }; + /** + @} + */ } #include "Direction.inl" diff --git a/Source/DungeonGenerator/Private/Core/Direction.inl b/Source/DungeonGenerator/Private/Core/Direction.inl index 20f2dc5..1bc870d 100644 --- a/Source/DungeonGenerator/Private/Core/Direction.inl +++ b/Source/DungeonGenerator/Private/Core/Direction.inl @@ -7,6 +7,7 @@ All Rights Reserved. */ #pragma once +#include "Core/Math/Random.h" namespace dungeon { @@ -82,8 +83,8 @@ namespace dungeon return GetVector(mIndex); } - inline Direction Direction::CreateFromRandom(Random& random) noexcept + inline Direction Direction::CreateFromRandom(const std::shared_ptr& random) noexcept { - return Direction(static_cast(random.Get() % 4)); + return Direction(static_cast(random->Get(4))); } } diff --git a/Source/DungeonGenerator/Private/Core/DrawLots.h b/Source/DungeonGenerator/Private/Core/DrawLots.h index b96f8f5..522e693 100644 --- a/Source/DungeonGenerator/Private/Core/DrawLots.h +++ b/Source/DungeonGenerator/Private/Core/DrawLots.h @@ -6,7 +6,8 @@ All Rights Reserved. */ -#include +#include "Math/Random.h" +#include #include namespace dungeon @@ -15,9 +16,8 @@ namespace dungeon 重み付き抽選 */ template - InputIterator DrawLots(InputIterator first, InputIterator last, Predicate pred) noexcept + InputIterator DrawLots(const std::shared_ptr& random, InputIterator first, InputIterator last, Predicate pred) noexcept { - std::random_device random; std::size_t totalWeight = 0; struct Entry final @@ -42,15 +42,12 @@ namespace dungeon } // TODO: specify random numbers externally. - const std::size_t rnd = random() % totalWeight; - - for (const auto& weight : weights) - { - // cppcheck-suppress [useStlAlgorithm] - if (rnd < weight.mWeight) - return weight.mBody; - } - - return last; + const std::size_t rnd = random->Get(totalWeight); + const auto i = std::find_if(weights.begin(), weights.end(), [rnd](const Entry& weight) + { + return rnd < weight.mWeight; + } + ); + return i != weights.end() ? i->mBody : last; } } diff --git a/Source/DungeonGenerator/Private/Core/GateFinder.h b/Source/DungeonGenerator/Private/Core/GateFinder.h index 99f56c6..f85aa3b 100644 --- a/Source/DungeonGenerator/Private/Core/GateFinder.h +++ b/Source/DungeonGenerator/Private/Core/GateFinder.h @@ -13,6 +13,10 @@ All Rights Reserved. namespace dungeon { + /** + @addtogroup PathGeneration + @{ + */ class GateFinder { public: @@ -36,4 +40,7 @@ namespace dungeon std::vector> mOpenGates; std::vector> mCloseGates; }; + /** + @} + */ } diff --git a/Source/DungeonGenerator/Private/Core/GenerateParameter.h b/Source/DungeonGenerator/Private/Core/GenerateParameter.h index 00c6ab2..533d1c8 100644 --- a/Source/DungeonGenerator/Private/Core/GenerateParameter.h +++ b/Source/DungeonGenerator/Private/Core/GenerateParameter.h @@ -7,10 +7,12 @@ All Rights Reserved. */ #pragma once -#include "Core/Math/Random.h" +#include namespace dungeon { + class Random; + /** デフォルトダンジョン生成パラメータクラス */ @@ -19,7 +21,7 @@ namespace dungeon /** コンストラクタ */ - GenerateParameter() = default; + GenerateParameter(); /** デストラクタ @@ -27,61 +29,66 @@ namespace dungeon ~GenerateParameter() = default; /** - ダンジョンの階層 + ダンジョンの階層の候補 + 部屋の初期生成数であり、最終的に生成される階層の数ではありません。 */ - uint8_t GetNumberOfCandidateFloors() const noexcept { return mNumberOfCandidateFloors; } + uint8_t GetNumberOfCandidateFloors() const noexcept; /** 生成する部屋の数の候補 部屋の初期生成数であり、最終的に生成される部屋の数ではありません。 */ - uint8_t GetNumberOfCandidateRooms() const noexcept { return mNumberOfCandidateRooms; } + uint8_t GetNumberOfCandidateRooms() const noexcept; /** 部屋の最小の幅 */ - uint32_t GetMinRoomWidth() const noexcept { return mMinRoomWidth; } + uint32_t GetMinRoomWidth() const noexcept; /** 部屋の最大の幅 */ - uint32_t GetMaxRoomWidth() const noexcept { return mMaxRoomWidth; } + uint32_t GetMaxRoomWidth() const noexcept; /** 部屋の最小の奥行き */ - uint32_t GetMinRoomDepth() const noexcept { return mMinRoomDepth; } + uint32_t GetMinRoomDepth() const noexcept; /** 部屋の最大の奥行き */ - uint32_t GetMaxRoomDepth() const noexcept { return mMaxRoomDepth; } + uint32_t GetMaxRoomDepth() const noexcept; /** 部屋の最小の高さ */ - uint32_t GetMinRoomHeight() const noexcept { return mMinRoomHeight; } + uint32_t GetMinRoomHeight() const noexcept; /** 部屋の最大の高さ */ - uint32_t GetMaxRoomHeight() const noexcept { return mMaxRoomHeight; } + uint32_t GetMaxRoomHeight() const noexcept; /** 部屋と部屋の水平方向の余白 */ - uint32_t GetHorizontalRoomMargin() const noexcept { return mHorizontalRoomMargin; }; + uint32_t GetHorizontalRoomMargin() const noexcept; /** 部屋と部屋の垂直方向の余白 */ - uint32_t GetVerticalRoomMargin() const noexcept { return mVerticalRoomMargin; }; + uint32_t GetVerticalRoomMargin() const noexcept; /** 乱数発生 */ - Random& GetRandom() const noexcept { return const_cast(this)->mRandom; } + std::shared_ptr GetRandom() noexcept; + /** + 乱数発生 + */ + std::shared_ptr GetRandom() const noexcept; @@ -89,91 +96,101 @@ namespace dungeon /** ダンジョンの幅 */ - uint32_t GetWidth() const noexcept { return mWidth; } + uint32_t GetWidth() const noexcept; /** ダンジョンの奥行き */ - uint32_t GetDepth() const noexcept { return mDepth; } + uint32_t GetDepth() const noexcept; /** ダンジョンの高さ */ - uint32_t GetHeight() const noexcept { return mHeight; } + uint32_t GetHeight() const noexcept; + + + bool IsGenerateStartRoomReserved() const noexcept; + bool IsGenerateGoalRoomReserved() const noexcept; + + + /** ダンジョンの幅 */ - uint32_t mWidth = 0; + uint32_t mWidth; /** ダンジョンの奥行き */ - uint32_t mDepth = 0; + uint32_t mDepth; /** ダンジョンの高さ */ - uint32_t mHeight = 3; - + uint32_t mHeight; /** - 生成する階層の候補 + 生成する階層の数の候補 + 最終的に生成される階層の数ではありません。 */ - uint8_t mNumberOfCandidateFloors = 3; + uint8_t mNumberOfCandidateFloors; /** 生成する部屋の数の候補 部屋の初期生成数であり、最終的に生成される部屋の数ではありません。 */ - uint8_t mNumberOfCandidateRooms = 5; + uint8_t mNumberOfCandidateRooms; /** 部屋の最小の幅 */ - uint32_t mMinRoomWidth = 2; + uint32_t mMinRoomWidth; /** 部屋の最大の幅 */ - uint32_t mMaxRoomWidth = 3; + uint32_t mMaxRoomWidth; /** 部屋の最小の奥行き */ - uint32_t mMinRoomDepth = 2; + uint32_t mMinRoomDepth; /** 部屋の最大の奥行き */ - uint32_t mMaxRoomDepth = 3; + uint32_t mMaxRoomDepth; /** 部屋の最小の高さ */ - uint32_t mMinRoomHeight = 1; + uint32_t mMinRoomHeight; /** 部屋の最大の高さ */ - uint32_t mMaxRoomHeight = 2; + uint32_t mMaxRoomHeight; /** 部屋と部屋の水平方向の余白 */ - uint32_t mHorizontalRoomMargin = 0; + uint32_t mHorizontalRoomMargin; /** 部屋と部屋の垂直方向の余白 */ - uint32_t mVerticalRoomMargin = 0; + uint32_t mVerticalRoomMargin; /** 乱数生成器 */ - Random mRandom; + std::shared_ptr mRandom; + + FIntVector mStartRoomSize = { 0, 0, 0 }; + FIntVector mGoalRoomSize = { 0, 0, 0 }; }; } diff --git a/Source/DungeonGenerator/Private/Core/GenerateParameter.inl b/Source/DungeonGenerator/Private/Core/GenerateParameter.inl index 98f8eb8..7b50a6f 100644 --- a/Source/DungeonGenerator/Private/Core/GenerateParameter.inl +++ b/Source/DungeonGenerator/Private/Core/GenerateParameter.inl @@ -7,7 +7,99 @@ All Rights Reserved. */ #pragma once +#include "Core/Math/Random.h" +#include +#include namespace dungeon { + inline GenerateParameter::GenerateParameter() + : mRandom(std::make_shared()) + { + } + + inline uint8_t GenerateParameter::GetNumberOfCandidateFloors() const noexcept + { + return mNumberOfCandidateFloors; + } + + inline uint8_t GenerateParameter::GetNumberOfCandidateRooms() const noexcept + { + return mNumberOfCandidateRooms; + } + + inline uint32_t GenerateParameter::GetMinRoomWidth() const noexcept + { + return mMinRoomWidth; + } + + inline uint32_t GenerateParameter::GetMaxRoomWidth() const noexcept + { + return mMaxRoomWidth; + } + + inline uint32_t GenerateParameter::GetMinRoomDepth() const noexcept + { + return mMinRoomDepth; + } + + inline uint32_t GenerateParameter::GetMaxRoomDepth() const noexcept + { + return mMaxRoomDepth; + } + + inline uint32_t GenerateParameter::GetMinRoomHeight() const noexcept + { + return mMinRoomHeight; + } + + inline uint32_t GenerateParameter::GetMaxRoomHeight() const noexcept + { + return mMaxRoomHeight; + } + + inline uint32_t GenerateParameter::GetHorizontalRoomMargin() const noexcept + { + return mHorizontalRoomMargin; + }; + + inline uint32_t GenerateParameter::GetVerticalRoomMargin() const noexcept + { + return mVerticalRoomMargin; + }; + + inline std::shared_ptr GenerateParameter::GetRandom() noexcept + { + return mRandom; + } + + inline std::shared_ptr GenerateParameter::GetRandom() const noexcept + { + return const_cast(this)->mRandom; + } + + inline uint32_t GenerateParameter::GetWidth() const noexcept + { + return mWidth; + } + + inline uint32_t GenerateParameter::GetDepth() const noexcept + { + return mDepth; + } + + inline uint32_t GenerateParameter::GetHeight() const noexcept + { + return mHeight; + } + + inline bool GenerateParameter::IsGenerateStartRoomReserved() const noexcept + { + return mStartRoomSize.IsZero() == false; + } + + inline bool GenerateParameter::IsGenerateGoalRoomReserved() const noexcept + { + return mGoalRoomSize.IsZero() == false; + } } diff --git a/Source/DungeonGenerator/Private/Core/Generator.cpp b/Source/DungeonGenerator/Private/Core/Generator.cpp index 4c897f5..1789689 100644 --- a/Source/DungeonGenerator/Private/Core/Generator.cpp +++ b/Source/DungeonGenerator/Private/Core/Generator.cpp @@ -15,6 +15,7 @@ All Rights Reserved. #include "Debug/BuildInfomation.h" #include "Debug/Debug.h" #include "Debug/Stopwatch.h" +#include "Helper/Crc.h" #include "Math/Math.h" #include "Math/PerlinNoise.h" #include "Math/Plane.h" @@ -22,7 +23,7 @@ All Rights Reserved. #include "MissionGraph/MissionGraph.h" -#if WITH_EDITOR && JENKINS_FOR_DEVELOP +#if WITH_EDITOR & JENKINS_FOR_DEVELOP #include // 定義するとデバッグに便利なログを出力します @@ -51,14 +52,6 @@ namespace dungeon } #endif - Generator::Generator() noexcept - { - } - - Generator::~Generator() noexcept - { - } - void Generator::Reset() { mVoxel.reset(); @@ -73,6 +66,8 @@ namespace dungeon void Generator::Generate(const GenerateParameter& parameter) noexcept { + Identifier::ResetCounter(); + mLastError = Error::Success; mGenerateParameter = parameter; @@ -133,56 +128,61 @@ namespace dungeon // 部屋の生成 stopwatch.Start(); - if (!GenerateRooms(parameter)) + const bool resultGenerateRooms = GenerateRooms(parameter); + DUNGEON_GENERATOR_LOG(TEXT("GenerateRooms: %lf seconds"), stopwatch.Lap()); + if (!resultGenerateRooms) return false; - DUNGEON_GENERATOR_LOG(TEXT("GenerateRooms: %lf sec"), stopwatch.Lap()); // 部屋の分離 stopwatch.Start(); - if (!SeparateRooms(parameter)) + const bool resultSeparateRooms = SeparateRooms(parameter); + DUNGEON_GENERATOR_LOG(TEXT("SeparateRooms: %lf seconds"), stopwatch.Lap()); + if (!resultSeparateRooms) return false; - DUNGEON_GENERATOR_LOG(TEXT("SeparateRooms: %lf sec"), stopwatch.Lap()); // 全ての部屋が収まるように空間を拡張します stopwatch.Start(); - if (!ExpandSpace(parameter)) + const bool resultExpandSpace = ExpandSpace(parameter); + DUNGEON_GENERATOR_LOG(TEXT("ExpandSpace: %lf seconds"), stopwatch.Lap()); + if (!resultExpandSpace) return false; - DUNGEON_GENERATOR_LOG(TEXT("ExpandSpace: %lf sec"), stopwatch.Lap()); - // 重複した部屋や範囲外の部屋を除去 + // 通路の生成 stopwatch.Start(); - if (!RemoveInvalidRooms(parameter)) + const bool resultExtractionAisles = ExtractionAisles(parameter); + DUNGEON_GENERATOR_LOG(TEXT("ExtractionAisles: %lf seconds"), stopwatch.Lap()); + if (!resultExtractionAisles) return false; - DUNGEON_GENERATOR_LOG(TEXT("RemoveInvalidRooms: %lf sec"), stopwatch.Lap()); - // 通路の生成 - stopwatch.Start(); - if (!ExtractionAisles(parameter)) + // 開始部屋と終了部屋のサブレベルを配置する隙間を調整 + if (!AdjustedStartAndGoalSublevel(parameter)) return false; - DUNGEON_GENERATOR_LOG(TEXT("ExtractionAisles: %lf sec"), stopwatch.Lap()); // ブランチIDの生成 stopwatch.Start(); - if (!Branch()) + const bool resultBranch = Branch(); + DUNGEON_GENERATOR_LOG(TEXT("Branch: %lf seconds"), stopwatch.Lap()); + if (!resultBranch) return false; - DUNGEON_GENERATOR_LOG(TEXT("Branch: %lf sec"), stopwatch.Lap()); // 階層情報の生成 stopwatch.Start(); - if (!DetectFloorHeight()) + const bool resultDetectFloorHeight = DetectFloorHeight(); + DUNGEON_GENERATOR_LOG(TEXT("DetectFloorHeight: %lf seconds"), stopwatch.Lap()); + if (!resultDetectFloorHeight) return false; - DUNGEON_GENERATOR_LOG(TEXT("DetectFloorHeight: %lf sec"), stopwatch.Lap()); // 部屋と通路に意味付けする stopwatch.Start(); MissionGraph missionGraph(shared_from_this(), mGoalPoint); - DUNGEON_GENERATOR_LOG(TEXT("MissionGraph: %lf sec"), stopwatch.Lap()); + DUNGEON_GENERATOR_LOG(TEXT("MissionGraph: %lf seconds"), stopwatch.Lap()); // ボクセル情報を生成します stopwatch.Start(); - if (!GenerateVoxel(parameter)) + const bool resultGenerateVoxel = GenerateVoxel(parameter); + DUNGEON_GENERATOR_LOG(TEXT("GenerateVoxel: %lf seconds"), stopwatch.Lap()); + if (!resultGenerateVoxel) return false; - DUNGEON_GENERATOR_LOG(TEXT("GenerateVoxel: %lf sec"), stopwatch.Lap()); return true; } @@ -204,13 +204,34 @@ namespace dungeon maxRoomWidth += parameter.GetHorizontalRoomMargin(); range *= maxRoomWidth; + // Register Start Room + if (!parameter.mStartRoomSize.IsZero()) + { + int32_t height = static_cast(parameter.GetNumberOfCandidateFloors()) - 1; + height -= parameter.mStartRoomSize.Z; + if (height < 0) + height = 0; + mStartRoom = mRooms.emplace_back( + std::make_shared(FIntVector(0, range, height), parameter.mStartRoomSize, true) + ); + } + + // Register Goal Room + if (!parameter.mGoalRoomSize.IsZero()) + { + mGoalRoom = mRooms.emplace_back( + std::make_shared(FIntVector(0, -range, 0), parameter.mGoalRoomSize, true) + ); + } + + // Register Rooms for (size_t i = 0; i < parameter.GetNumberOfCandidateRooms(); ++i) { - const float radian = parameter.GetRandom().Get() * (3.14159265359f * 2.f); + const float radian = parameter.GetRandom()->Get() * (3.14159265359f * 2.f); const float width = std::sin(radian); const float depth = std::cos(radian); const float height = std::max(0.f, static_cast(parameter.GetNumberOfCandidateFloors()) - 1); - const float distance = parameter.GetRandom().Get(range); + const float distance = parameter.GetRandom()->Get(range); float noise = perlinNoise.Noise(width, depth); noise = noise * 0.5f + 0.5f; noise *= noiseBoostRatio; @@ -231,7 +252,7 @@ namespace dungeon location.Z = 0; } - auto room = std::make_shared(parameter, location); + std::shared_ptr room = std::make_shared(parameter, location); #if defined(DEBUG_SHOW_DEVELOP_LOG) DUNGEON_GENERATOR_LOG(TEXT("Room: X=%d,Y=%d,Z=%d W=%d,D=%d,H=%d center(%f, %f, %f)") , room->GetX(), room->GetY(), room->GetZ() @@ -242,11 +263,6 @@ namespace dungeon mRooms.emplace_back(std::move(room)); } -#if defined(DEBUG_SHOW_DEVELOP_LOG) - // サブレベル配置テスト用の部屋 - //mRooms.emplace_back(std::make_shared(16, 15, 5, 3, 3, 1)); -#endif - GenerateRoomImageForDebug("generator_1.bmp"); #if defined(DEBUG_GENERATE_BITMAP_FILE) @@ -347,7 +363,7 @@ namespace dungeon // 中心が一致してしまったので適当な方向に押し出す if (direction.SizeSquared() == 0.) { - const double ratio = parameter.GetRandom().Get(); + const double ratio = parameter.GetRandom()->Get(); const double radian = ratio * (3.14159265359 * 2.); direction.X = std::cos(radian); direction.Y = std::sin(radian); @@ -517,68 +533,6 @@ namespace dungeon return true; } - /** - 重複した部屋や範囲外の部屋を除去をします - */ - bool Generator::RemoveInvalidRooms(const GenerateParameter& parameter) noexcept - { -#if defined(DEBUG_SHOW_DEVELOP_LOG) - DUNGEON_GENERATOR_LOG(TEXT("Remove duplicate rooms and out-of-range rooms")); -#endif - - auto result = std::remove_if(mRooms.begin(), mRooms.end(), [¶meter, this](const std::shared_ptr& room) -> bool - { - // 範囲外の部屋なら削除 - if ( - room->GetLeft() < 0 || static_cast(parameter.GetWidth()) <= room->GetRight() || - room->GetTop() < 0 || static_cast(parameter.GetDepth()) <= room->GetBottom() || - room->GetBackground() < 0 || static_cast(parameter.GetHeight()) <= room->GetForeground()) - { -#if defined(DEBUG_SHOW_DEVELOP_LOG) - DUNGEON_GENERATOR_LOG(TEXT("Room: X=%d,Y=%d W=%d,H=%d center(%f, %f) 範囲外") - , room->GetX(), room->GetY(), room->GetWidth(), room->GetDepth() - , room->GetCenter().X, room->GetCenter().Y - ); -#endif - return true; - } - - // 他の部屋に含まれている? - for (const auto& otherRoom : mRooms) - { - // 無効な部屋はスキップ - if (otherRoom == nullptr) - continue; - // 自分自身はスキップ - if (otherRoom == room) - continue; - // 内包? - if (room->Intersect(*otherRoom, parameter.GetHorizontalRoomMargin(), parameter.GetVerticalRoomMargin()) == true) - return true; - } - - return false; - } - ); - mRooms.erase(result, mRooms.end()); - -#if defined(DEBUG_GENERATE_BITMAP_FILE) - bmp::Canvas canvas(Scale(parameter.GetWidth()), Scale(parameter.GetDepth())); - for (const auto& room : mRooms) - { - canvas.Rectangle(Scale(room->GetLeft()), Scale(room->GetTop()), Scale(room->GetRight()), Scale(room->GetBottom()), baseDarkColor); - } - for (const auto& room : mRooms) - { - canvas.Frame(Scale(room->GetLeft()), Scale(room->GetTop()), Scale(room->GetRight()), Scale(room->GetBottom()), frameColor); - } - const auto filename = TCHAR_TO_ANSI(*(FPaths::ProjectSavedDir() + "/DungeonGenerator/generator_3.bmp")); - canvas.Write(filename); -#endif - - return true; - } - bool Generator::DetectFloorHeight() noexcept { mFloorHeight.clear(); @@ -699,10 +653,14 @@ namespace dungeon if (room->Contain(*mStartPoint)) { room->SetParts(Room::Parts::Start); + if (mOnStartParts) + mOnStartParts(room); } else if (room->Contain(*mGoalPoint)) { room->SetParts(Room::Parts::Goal); + if (mOnGoalParts) + mOnGoalParts(room); } else { @@ -721,13 +679,74 @@ namespace dungeon } } - if (mQueryParts) + if (mOnQueryParts) + mOnQueryParts(room); + } + + return mLastError == Error::Success; + } + + bool Generator::AdjustedStartAndGoalSublevel(GenerateParameter& parameter) noexcept + { + Stopwatch stopwatch; + + const bool isGenerateStartRoomReserved = (parameter.IsGenerateStartRoomReserved() && mStartPoint && mStartPoint->GetOwnerRoom()); + const bool isGenerateGoalRoomReserved = (parameter.IsGenerateGoalRoomReserved() && mGoalPoint && mGoalPoint->GetOwnerRoom()); + if (isGenerateStartRoomReserved || isGenerateGoalRoomReserved) + { + if (isGenerateStartRoomReserved) { - mQueryParts(room); + const std::shared_ptr& room = mStartPoint->GetOwnerRoom(); + room->SetWidth(parameter.mStartRoomSize.X); + room->SetDepth(parameter.mStartRoomSize.Y); + room->SetHeight(parameter.mStartRoomSize.Z); + } + if (isGenerateGoalRoomReserved) + { + const std::shared_ptr& room = mGoalPoint->GetOwnerRoom(); + room->SetWidth(parameter.mGoalRoomSize.X); + room->SetDepth(parameter.mGoalRoomSize.Y); + room->SetHeight(parameter.mGoalRoomSize.Z); + } + + { + // 部屋の分離 + stopwatch.Start(); + const bool resultSeparateRooms2 = SeparateRooms(parameter); + DUNGEON_GENERATOR_LOG(TEXT("SeparateRooms: %lf seconds"), stopwatch.Lap()); + if (!resultSeparateRooms2) + return false; + + // 全ての部屋が収まるように空間を拡張します + stopwatch.Start(); + const bool resultExpandSpace2 = ExpandSpace(parameter); + DUNGEON_GENERATOR_LOG(TEXT("ExpandSpace: %lf seconds"), stopwatch.Lap()); + if (!resultExpandSpace2) + return false; + } + + if (isGenerateStartRoomReserved) + { + std::const_pointer_cast(mStartPoint)->ResetByRoomGroundCenter(); + } + if (isGenerateGoalRoomReserved) + { + std::const_pointer_cast(mGoalPoint)->ResetByRoomGroundCenter(); + } + + for (Aisle& aisle : mAisles) + { + for (uint_fast8_t i = 0; i < 2; ++i) + { + if (const std::shared_ptr& point = std::const_pointer_cast(aisle.GetPoint(i))) + { + std::const_pointer_cast(point)->ResetByRoomGroundCenter(); + } + } } } - return mLastError == Error::Success; + return true; } float Generator::GetDistanceCenterToContact(const float width, const float depth, const FVector& direction, const float margin) const noexcept @@ -789,8 +808,16 @@ namespace dungeon DUNGEON_GENERATOR_LOG(TEXT("%d minimum spanning tree edges detected"), minimumSpanningTree.Size()); #endif - mStartPoint = minimumSpanningTree.GetStartPoint(); - mGoalPoint = minimumSpanningTree.GetGoalPoint(); + if (mStartRoom) + mStartPoint = std::make_shared(mStartRoom); + else + mStartPoint = minimumSpanningTree.GetStartPoint(); + + if (mGoalRoom) + mGoalPoint = std::make_shared(mGoalRoom); + else + mGoalPoint = minimumSpanningTree.GetGoalPoint(); + minimumSpanningTree.EachLeafPoint([this](const std::shared_ptr& point) { mLeafPoints.emplace_back(point); @@ -804,7 +831,7 @@ namespace dungeon { mVoxel = std::make_shared(parameter); - // 部屋を生成 + // Generate room for (const auto& room : mRooms) { { @@ -822,44 +849,43 @@ namespace dungeon } } - // 通路の距離が短い順に並べ替える + // Sort by shortest aisle distance std::sort(mAisles.begin(), mAisles.end(), [](const Aisle& l, const Aisle& r) { return l.GetLength() < r.GetLength(); } ); - // 通路を生成 + // Generate pathways for (const Aisle& aisle : mAisles) { - std::shared_ptr s = aisle.GetPoint(0); - std::shared_ptr e = aisle.GetPoint(1); + std::shared_ptr startPoint = aisle.GetPoint(0); + std::shared_ptr goalPoint = aisle.GetPoint(1); - check(s->GetOwnerRoom()->GetRect().Contains(ToIntPoint(*s))); - check(e->GetOwnerRoom()->GetRect().Contains(ToIntPoint(*e))); - - // Use the back room as a starting point. - if (s->GetOwnerRoom()->GetDepthFromStart() < e->GetOwnerRoom()->GetDepthFromStart()) + // Use the back room as a starting point + if (startPoint->GetOwnerRoom()->GetDepthFromStart() < goalPoint->GetOwnerRoom()->GetDepthFromStart()) { - const std::shared_ptr t = e; - e = s; - s = t; + const std::shared_ptr t = goalPoint; + goalPoint = startPoint; + startPoint = t; } - check(s->GetOwnerRoom()->GetRect().Contains(ToIntPoint(*s))); - check(e->GetOwnerRoom()->GetRect().Contains(ToIntPoint(*e))); + // Check if the start and end points are included in the room + check(startPoint->GetOwnerRoom()->GetRect().Contains(ToIntPoint(*startPoint))); + check(goalPoint->GetOwnerRoom()->GetRect().Contains(ToIntPoint(*goalPoint))); + + // Change to voxel coordinates + FIntVector start = ToIntVector(*startPoint); + FIntVector goal = ToIntVector(*goalPoint); - const std::shared_ptr& startRoom = s->GetOwnerRoom(); - const std::shared_ptr& goalRoom = e->GetOwnerRoom(); - check(startRoom); + // Conditions for reaching the passage. The endpoint can be anywhere in the goal room. + const std::shared_ptr& goalRoom = goalPoint->GetOwnerRoom(); check(goalRoom); + const PathGoalCondition pathGoalCondition(goalRoom->GetRect()); - FIntVector start = ToIntVector(*s); - FIntVector goal = ToIntVector(*e); - - // start周囲に侵入可能なグリッドを探す + // Find a voxel that can generate gates around the start FIntVector result; - if (mVoxel->SearchGateLocation(result, start, goal, PathGoalCondition(goalRoom->GetRect()), aisle.GetIdentifier())) + if (mVoxel->SearchGateLocation(result, start, goal, pathGoalCondition, aisle.GetIdentifier())) { start = result; } @@ -871,8 +897,9 @@ namespace dungeon } // Aisle generation by A*. - if (mVoxel->Aisle(start, goal, PathGoalCondition(goalRoom->GetRect()), aisle.GetIdentifier())) + if (mVoxel->Aisle(start, goal, pathGoalCondition, aisle.GetIdentifier())) { + // Put a lock on the gate of the back room if necessary. Grid grid = mVoxel->Get(start.X, start.Y, start.Z); check(grid.GetProps() == Grid::Props::None); if (grid.GetProps() == Grid::Props::None) @@ -971,27 +998,23 @@ namespace dungeon std::shared_ptr Generator::Find(const Point& point) const noexcept { - for (const auto& room : mRooms) - { - // cppcheck-suppress [useStlAlgorithm] - if (room->Contain(point)) - return room; - } - return nullptr; + const auto i = std::find_if(mRooms.begin(), mRooms.end(), [&point](const std::shared_ptr& room) + { + return room->Contain(point); + } + ); + return i != mRooms.end() ? *i : nullptr; } std::vector> Generator::FindAll(const Point& point) const noexcept { std::vector> result; result.reserve(mRooms.size()); - for (const auto& room : mRooms) - { - if (room->Contain(point)) + std::copy_if(mRooms.begin(), mRooms.end(), result.begin(), [&point](const std::shared_ptr& room) { - // cppcheck-suppress [useStlAlgorithm] - result.emplace_back(room); + return room->Contain(point); } - } + ); return result; } @@ -1233,4 +1256,9 @@ namespace dungeon return false; } + + uint32_t Generator::CalculateCRC32() const noexcept + { + return mVoxel ? mVoxel->CalculateCRC32() : 0; + } } diff --git a/Source/DungeonGenerator/Private/Core/Generator.h b/Source/DungeonGenerator/Private/Core/Generator.h index 182dfb8..3f2b77d 100644 --- a/Source/DungeonGenerator/Private/Core/Generator.h +++ b/Source/DungeonGenerator/Private/Core/Generator.h @@ -50,14 +50,14 @@ namespace dungeon /** コンストラクタ */ - Generator() noexcept; + Generator() = default; Generator(const Generator&) = delete; Generator& operator=(const Generator&) = delete; /** デストラクタ */ - virtual ~Generator() noexcept; + virtual ~Generator() = default; /** 生成 @@ -89,24 +89,12 @@ namespace dungeon /** 生成された部屋を更新します */ - void ForEach(std::function&)> func) noexcept - { - for (const auto& room : mRooms) - { - func(room); - } - } + void ForEach(std::function&)> func) noexcept; /** 生成された部屋を参照します */ - void ForEach(std::function&)> func) const noexcept - { - for (const auto& room : mRooms) - { - func(room); - } - } + void ForEach(std::function&)> func) const noexcept; // 深度による検索 std::vector> FindByDepth(const uint8_t depth) const noexcept; @@ -133,48 +121,16 @@ namespace dungeon public: //////////////////////////////////////////////////////////////////////////////////////////// // Aisle - void EachAisle(std::function func) const noexcept - { - for (const auto& aisle : mAisles) - { - func(aisle); - } - } + void EachAisle(std::function func) const noexcept; // 部屋に接続している通路を検索 - void FindAisle(const std::shared_ptr& room, std::function func) noexcept - { - for (auto& aisle : mAisles) - { - const auto& room0 = aisle.GetPoint(0)->GetOwnerRoom(); - const auto& room1 = aisle.GetPoint(1)->GetOwnerRoom(); - if (room == room0 || room == room1) - { - if (func(aisle)) - break; - } - } - } - - void FindAisle(const std::shared_ptr& room, std::function func) const noexcept - { - for (const auto& aisle : mAisles) - { - const auto& room0 = aisle.GetPoint(0)->GetOwnerRoom(); - const auto& room1 = aisle.GetPoint(1)->GetOwnerRoom(); - if (room == room0 || room == room1) - { - if (func(aisle)) - break; - } - } - } - - void OnQueryParts(std::function&)> func) noexcept - { - mQueryParts = func; - } + void FindAisle(const std::shared_ptr& room, std::function func) noexcept; + void FindAisle(const std::shared_ptr& room, std::function func) const noexcept; + + void OnQueryParts(std::function&)> func) noexcept; + void OnStartParts(std::function&)> func) noexcept; + void OnGoalParts(std::function&)> func) noexcept; //////////////////////////////////////////////////////////////////////////////////////////// // Point @@ -198,50 +154,44 @@ namespace dungeon 開始地点にふさわしい点を取得します \return 開始地点にふさわしい点 */ - const std::shared_ptr& GetStartPoint() const noexcept - { - return mStartPoint; - } + const std::shared_ptr& GetStartPoint() const noexcept; /** ゴール地点にふさわしい点を取得します \return ゴール地点にふさわしい点 */ - const std::shared_ptr& GetGoalPoint() const noexcept - { - return mGoalPoint; - } + const std::shared_ptr& GetGoalPoint() const noexcept; /** 行き止まりの点を更新します \param[in] func 点を元に更新する関数 */ - void EachLeafPoint(std::function& point)> func) const noexcept - { - for (auto& point : mLeafPoints) - { - func(point); - } - } + void EachLeafPoint(std::function& point)> func) const noexcept; + //////////////////////////////////////////////////////////////////////////////////////////// void DumpRoomDiagram(const std::string& path) const noexcept; void DumpRoomDiagram(std::ofstream& stream, std::unordered_set& generatedEdges, const std::shared_ptr& room) const noexcept; void DumpAisle(const std::string& path) const noexcept; + //////////////////////////////////////////////////////////////////////////////////////////// bool Branch() noexcept; bool Branch(std::unordered_set& generatedEdges, const std::shared_ptr& room, uint8_t& branchId) noexcept; - uint8_t GetDeepestDepthFromStart() const noexcept - { - return mDistance; - } + uint8_t GetDeepestDepthFromStart() const noexcept; + + //////////////////////////////////////////////////////////////////////////////////////////// + /** + Calculate CRC32 + \return CRC32 + */ + uint32_t CalculateCRC32() const noexcept; private: /** @@ -264,11 +214,6 @@ namespace dungeon */ bool ExpandSpace(GenerateParameter& parameter) noexcept; - /** - 重複した部屋や範囲外の部屋を除去をします - */ - bool RemoveInvalidRooms(const GenerateParameter& parameter) noexcept; - /* 階層の高さを検出 */ @@ -279,6 +224,11 @@ namespace dungeon */ bool ExtractionAisles(const GenerateParameter& parameter) noexcept; + /** + 開始部屋と終了部屋のサブレベルを配置する隙間を調整 + */ + bool AdjustedStartAndGoalSublevel(GenerateParameter& parameter) noexcept; + /** ボクセル情報を生成 */ @@ -312,7 +262,10 @@ namespace dungeon GenerateParameter mGenerateParameter; std::shared_ptr mVoxel; + std::list> mRooms; + std::shared_ptr mStartRoom; + std::shared_ptr mGoalRoom; std::vector mFloorHeight; @@ -322,7 +275,9 @@ namespace dungeon std::vector mAisles; - std::function&)> mQueryParts; + std::function&)> mOnQueryParts; + std::function&)> mOnStartParts; + std::function&)> mOnGoalParts; uint8_t mDistance = 0; diff --git a/Source/DungeonGenerator/Private/Core/Generator.inl b/Source/DungeonGenerator/Private/Core/Generator.inl index 78595ce..c219309 100644 --- a/Source/DungeonGenerator/Private/Core/Generator.inl +++ b/Source/DungeonGenerator/Private/Core/Generator.inl @@ -14,4 +14,99 @@ namespace dungeon { return mLastError; } + + inline void Generator::ForEach(std::function&)> func) noexcept + { + for (const auto& room : mRooms) + { + func(room); + } + } + + /** + 生成された部屋を参照します + */ + inline void Generator::ForEach(std::function&)> func) const noexcept + { + for (const auto& room : mRooms) + { + func(room); + } + } + + + inline void Generator::EachAisle(std::function func) const noexcept + { + for (const auto& aisle : mAisles) + { + func(aisle); + } + } + + inline void Generator::FindAisle(const std::shared_ptr& room, std::function func) noexcept + { + for (auto& aisle : mAisles) + { + const auto& room0 = aisle.GetPoint(0)->GetOwnerRoom(); + const auto& room1 = aisle.GetPoint(1)->GetOwnerRoom(); + if (room == room0 || room == room1) + { + if (func(aisle)) + break; + } + } + } + + inline void Generator::FindAisle(const std::shared_ptr& room, std::function func) const noexcept + { + for (const auto& aisle : mAisles) + { + const auto& room0 = aisle.GetPoint(0)->GetOwnerRoom(); + const auto& room1 = aisle.GetPoint(1)->GetOwnerRoom(); + if (room == room0 || room == room1) + { + if (func(aisle)) + break; + } + } + } + + inline void Generator::OnQueryParts(std::function&)> func) noexcept + { + mOnQueryParts = func; + } + + inline void Generator::OnStartParts(std::function&)> func) noexcept + { + mOnStartParts = func; + } + + inline void Generator::OnGoalParts(std::function&)> func) noexcept + { + mOnGoalParts = func; + } + + + inline const std::shared_ptr& Generator::GetStartPoint() const noexcept + { + return mStartPoint; + } + + inline const std::shared_ptr& Generator::GetGoalPoint() const noexcept + { + return mGoalPoint; + } + + inline void Generator::EachLeafPoint(std::function& point)> func) const noexcept + { + for (auto& point : mLeafPoints) + { + func(point); + } + } + + inline uint8_t Generator::GetDeepestDepthFromStart() const noexcept + { + return mDistance; + } } diff --git a/Source/DungeonGenerator/Private/Core/Grid.cpp b/Source/DungeonGenerator/Private/Core/Grid.cpp index 551dace..c0b7d10 100644 --- a/Source/DungeonGenerator/Private/Core/Grid.cpp +++ b/Source/DungeonGenerator/Private/Core/Grid.cpp @@ -160,7 +160,7 @@ namespace dungeon /* 自身からtoGridを見た時に壁が生成されるか判定します */ - bool Grid::CanBuildWall(const Grid& toGrid, const Direction::Index direction, const bool mergeRooms) const noexcept + bool Grid::CanBuildWall(const Grid& toGrid, const Grid& underGrid, const Direction::Index direction, const bool mergeRooms) const noexcept { // 部屋と部屋の間に壁を生成する? if (!mergeRooms) @@ -171,7 +171,7 @@ namespace dungeon */ if (IsKindOfRoomTypeWithoutGate() && toGrid.IsKindOfRoomTypeWithoutGate()) { - return mIdentifier != toGrid.mIdentifier; + return GetIdentifier() != toGrid.GetIdentifier(); } } @@ -204,9 +204,7 @@ namespace dungeon return toGrid.GetIdentifier() != GetIdentifier(); } - return - toGrid.IsKindOfRoomTypeWithoutGate() || - toGrid.IsKindOfSpatialType(); + return toGrid.IsKindOfSpatialType() || toGrid.IsKindOfRoomTypeWithoutGate(); } else if (mType == Type::Slope) { @@ -216,10 +214,10 @@ namespace dungeon // グリッドの識別番号が不一致なら壁がある return (toGrid.GetDirection().IsNorthSouth() != Direction::IsNorthSouth(direction)) || - (toGrid.mIdentifier != mIdentifier); + (toGrid.GetIdentifier() != GetIdentifier()); } - return toGrid.IsKindOfSpatialType(); + return toGrid.IsKindOfSpatialType() || toGrid.IsKindOfRoomTypeWithoutGate(); } else if (mType == Type::Atrium) { @@ -227,11 +225,20 @@ namespace dungeon { // 方向が交差していたら壁 // グリッドの識別番号が不一致なら壁がある - return toGrid.GetDirection().IsNorthSouth() != Direction::IsNorthSouth(direction) || - (toGrid.mIdentifier != mIdentifier); + return + (toGrid.GetDirection().IsNorthSouth() != Direction::IsNorthSouth(direction)) || + (toGrid.GetIdentifier() != GetIdentifier()); } - return toGrid.IsKindOfSpatialType(); + if (toGrid.IsKindOfSpatialType()) + { + if (GetDirection().IsNorthSouth() != Direction::IsNorthSouth(direction)) + return true; + + return underGrid.mType == Type::Slope; + } + + return toGrid.IsKindOfRoomTypeWithoutGate(); } return false; @@ -322,17 +329,38 @@ namespace dungeon */ bool Grid::CanBuildPillar(const Grid& toGrid) const noexcept { - /* - 判定が怪しいので見直してください - */ +#if 0 + // 部屋と部屋の間に壁を生成する? + //if (!mergeRooms) + { + /* + 部屋と部屋が隣接している場合、 + グリッドの識別番号(=部屋の識別番号)が不一致なら壁がある + */ + if (IsKindOfRoomTypeWithoutGate() && toGrid.IsKindOfRoomTypeWithoutGate()) + { + return GetIdentifier() != toGrid.GetIdentifier(); + } + } + + if (!toGrid.IsHorizontallyPassable()) + return false; + if (toGrid.GetType() == dungeon::Grid::Type::Empty) + return false; + if (toGrid.GetType() == dungeon::Grid::Type::Atrium || toGrid.GetType() == dungeon::Grid::Type::Slope) + { + // 通路の識別子が違うなら壁 + return toGrid.GetIdentifier() != GetIdentifier(); + } +#else + // 判定が怪しいので見直してください return - toGrid.IsHorizontallyPassable() && + toGrid.IsHorizontallyPassable() /* && ( - toGrid.GetType() != dungeon::Grid::Type::Empty && - /*result.GetType() != dungeon::Grid::Type::Gate &&*/ toGrid.GetType() != dungeon::Grid::Type::Atrium && toGrid.GetType() != dungeon::Grid::Type::Slope - ); + )*/; +#endif } /* @@ -416,4 +444,22 @@ namespace dungeon const size_t index = static_cast(mProps); return names[index]; } + + FString Grid::GetNoMeshGenerationName() const noexcept + { + FString noMeshGenerationName(TEXT("NoMeshGeneration: ")); + + if (mNoMeshGeneration & NoMeshGenerationRoofMask) + { + noMeshGenerationName += TEXT("Roof"); + } + if (mNoMeshGeneration & NoMeshGenerationFloorMask) + { + if (!noMeshGenerationName.IsEmpty()) + noMeshGenerationName += TEXT(","); + noMeshGenerationName += TEXT("Floor"); + } + + return noMeshGenerationName; + } } diff --git a/Source/DungeonGenerator/Private/Core/Grid.h b/Source/DungeonGenerator/Private/Core/Grid.h index f9241cc..7d4a5d7 100644 --- a/Source/DungeonGenerator/Private/Core/Grid.h +++ b/Source/DungeonGenerator/Private/Core/Grid.h @@ -7,12 +7,14 @@ All Rights Reserved. */ #pragma once -#include "Core/Math/Random.h" #include "Direction.h" -#include namespace dungeon { + /** + @addtogroup PathGeneration + @{ + */ /** グリッドクラス */ @@ -185,12 +187,12 @@ namespace dungeon /** 床(部屋)グリッドを生成します */ - static Grid CreateFloor(Random& random, const uint16_t identifier) noexcept; + static Grid CreateFloor(const std::shared_ptr& random, const uint16_t identifier) noexcept; /** デッキ(部屋の周辺)グリッドを生成します */ - static Grid CreateDeck(Random& random, const uint16_t identifier) noexcept; + static Grid CreateDeck(const std::shared_ptr& random, const uint16_t identifier) noexcept; // 判定補助関数 /** @@ -222,7 +224,7 @@ namespace dungeon \param[in] mergeRooms 部屋と部屋を結合する \return trueならば壁の生成が可能 */ - bool CanBuildWall(const Grid& toGrid, const Direction::Index direction, const bool mergeRooms) const noexcept; + bool CanBuildWall(const Grid& toGrid, const Grid& underGrid, const Direction::Index direction, const bool mergeRooms) const noexcept; /** 自身からtoGridを見た時に壁が生成されるか判定します @@ -278,22 +280,28 @@ namespace dungeon const FString& GetTypeName() const noexcept; const FString& GetPropsName() const noexcept; + FString GetNoMeshGenerationName() const noexcept; private: - static constexpr uint16_t InvalidIdentifier = static_cast(~0); - Type mType; Props mProps; Direction mDirection; - uint16_t mIdentifier = InvalidIdentifier; enum class NoMeshGeneration : uint8_t { Floor, Roof }; - std::bitset<2> mNoMeshGeneration = 0; + static constexpr uint8_t NoMeshGenerationRoofMask = 1 << static_cast(NoMeshGeneration::Roof); + static constexpr uint8_t NoMeshGenerationFloorMask = 1 << static_cast(NoMeshGeneration::Floor); + uint8_t mNoMeshGeneration = 0; + + static constexpr uint16_t InvalidIdentifier = static_cast(~0); + uint16_t mIdentifier = InvalidIdentifier; }; + /** + @} + */ } #include "Grid.inl" diff --git a/Source/DungeonGenerator/Private/Core/Grid.inl b/Source/DungeonGenerator/Private/Core/Grid.inl index 066a669..e379993 100644 --- a/Source/DungeonGenerator/Private/Core/Grid.inl +++ b/Source/DungeonGenerator/Private/Core/Grid.inl @@ -41,12 +41,12 @@ namespace dungeon { } - inline Grid Grid::CreateFloor(Random& random, const uint16_t identifier) noexcept + inline Grid Grid::CreateFloor(const std::shared_ptr& random, const uint16_t identifier) noexcept { return Grid(Type::Floor, Direction::CreateFromRandom(random), identifier); } - inline Grid Grid::CreateDeck(Random& random, const uint16_t identifier) noexcept + inline Grid Grid::CreateDeck(const std::shared_ptr& random, const uint16_t identifier) noexcept { return Grid(Type::Deck, Direction::CreateFromRandom(random), identifier); } @@ -98,17 +98,24 @@ namespace dungeon inline void Grid::SetNoMeshGeneration(const bool noRoofMeshGeneration, const bool noFloorMeshGeneration) { - mNoMeshGeneration.set(static_cast(NoMeshGeneration::Roof), noRoofMeshGeneration); - mNoMeshGeneration.set(static_cast(NoMeshGeneration::Floor), noFloorMeshGeneration); + if(noRoofMeshGeneration) + mNoMeshGeneration |= NoMeshGenerationRoofMask; + else + mNoMeshGeneration &= ~NoMeshGenerationRoofMask; + + if (noFloorMeshGeneration) + mNoMeshGeneration |= NoMeshGenerationFloorMask; + else + mNoMeshGeneration &= ~NoMeshGenerationFloorMask; } inline bool Grid::IsNoFloorMeshGeneration() const noexcept { - return mNoMeshGeneration.test(static_cast(NoMeshGeneration::Floor)); + return (mNoMeshGeneration & NoMeshGenerationFloorMask) != 0; } inline bool Grid::IsNoRoofMeshGeneration() const noexcept { - return mNoMeshGeneration.test(static_cast(NoMeshGeneration::Roof)); + return (mNoMeshGeneration & NoMeshGenerationRoofMask) != 0; } } diff --git a/Source/DungeonGenerator/Private/Core/Helper/Crc.h b/Source/DungeonGenerator/Private/Core/Helper/Crc.h new file mode 100644 index 0000000..cf4a269 --- /dev/null +++ b/Source/DungeonGenerator/Private/Core/Helper/Crc.h @@ -0,0 +1,58 @@ +#pragma once + +namespace dungeon +{ + namespace detail + { + // CRC-32 (zlib): 0xEDB88320, Reverse + static constexpr uint32_t Crc32Table[256] = { + 0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U, 0x706af48fU, 0xe963a535U, 0x9e6495a3U, + 0x0edb8832U, 0x79dcb8a4U, 0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U, 0x90bf1d91U, + 0x1db71064U, 0x6ab020f2U, 0xf3b97148U, 0x84be41deU, 0x1adad47dU, 0x6ddde4ebU, 0xf4d4b551U, 0x83d385c7U, + 0x136c9856U, 0x646ba8c0U, 0xfd62f97aU, 0x8a65c9ecU, 0x14015c4fU, 0x63066cd9U, 0xfa0f3d63U, 0x8d080df5U, + 0x3b6e20c8U, 0x4c69105eU, 0xd56041e4U, 0xa2677172U, 0x3c03e4d1U, 0x4b04d447U, 0xd20d85fdU, 0xa50ab56bU, + 0x35b5a8faU, 0x42b2986cU, 0xdbbbc9d6U, 0xacbcf940U, 0x32d86ce3U, 0x45df5c75U, 0xdcd60dcfU, 0xabd13d59U, + 0x26d930acU, 0x51de003aU, 0xc8d75180U, 0xbfd06116U, 0x21b4f4b5U, 0x56b3c423U, 0xcfba9599U, 0xb8bda50fU, + 0x2802b89eU, 0x5f058808U, 0xc60cd9b2U, 0xb10be924U, 0x2f6f7c87U, 0x58684c11U, 0xc1611dabU, 0xb6662d3dU, + 0x76dc4190U, 0x01db7106U, 0x98d220bcU, 0xefd5102aU, 0x71b18589U, 0x06b6b51fU, 0x9fbfe4a5U, 0xe8b8d433U, + 0x7807c9a2U, 0x0f00f934U, 0x9609a88eU, 0xe10e9818U, 0x7f6a0dbbU, 0x086d3d2dU, 0x91646c97U, 0xe6635c01U, + 0x6b6b51f4U, 0x1c6c6162U, 0x856530d8U, 0xf262004eU, 0x6c0695edU, 0x1b01a57bU, 0x8208f4c1U, 0xf50fc457U, + 0x65b0d9c6U, 0x12b7e950U, 0x8bbeb8eaU, 0xfcb9887cU, 0x62dd1ddfU, 0x15da2d49U, 0x8cd37cf3U, 0xfbd44c65U, + 0x4db26158U, 0x3ab551ceU, 0xa3bc0074U, 0xd4bb30e2U, 0x4adfa541U, 0x3dd895d7U, 0xa4d1c46dU, 0xd3d6f4fbU, + 0x4369e96aU, 0x346ed9fcU, 0xad678846U, 0xda60b8d0U, 0x44042d73U, 0x33031de5U, 0xaa0a4c5fU, 0xdd0d7cc9U, + 0x5005713cU, 0x270241aaU, 0xbe0b1010U, 0xc90c2086U, 0x5768b525U, 0x206f85b3U, 0xb966d409U, 0xce61e49fU, + 0x5edef90eU, 0x29d9c998U, 0xb0d09822U, 0xc7d7a8b4U, 0x59b33d17U, 0x2eb40d81U, 0xb7bd5c3bU, 0xc0ba6cadU, + 0xedb88320U, 0x9abfb3b6U, 0x03b6e20cU, 0x74b1d29aU, 0xead54739U, 0x9dd277afU, 0x04db2615U, 0x73dc1683U, + 0xe3630b12U, 0x94643b84U, 0x0d6d6a3eU, 0x7a6a5aa8U, 0xe40ecf0bU, 0x9309ff9dU, 0x0a00ae27U, 0x7d079eb1U, + 0xf00f9344U, 0x8708a3d2U, 0x1e01f268U, 0x6906c2feU, 0xf762575dU, 0x806567cbU, 0x196c3671U, 0x6e6b06e7U, + 0xfed41b76U, 0x89d32be0U, 0x10da7a5aU, 0x67dd4accU, 0xf9b9df6fU, 0x8ebeeff9U, 0x17b7be43U, 0x60b08ed5U, + 0xd6d6a3e8U, 0xa1d1937eU, 0x38d8c2c4U, 0x4fdff252U, 0xd1bb67f1U, 0xa6bc5767U, 0x3fb506ddU, 0x48b2364bU, + 0xd80d2bdaU, 0xaf0a1b4cU, 0x36034af6U, 0x41047a60U, 0xdf60efc3U, 0xa867df55U, 0x316e8eefU, 0x4669be79U, + 0xcb61b38cU, 0xbc66831aU, 0x256fd2a0U, 0x5268e236U, 0xcc0c7795U, 0xbb0b4703U, 0x220216b9U, 0x5505262fU, + 0xc5ba3bbeU, 0xb2bd0b28U, 0x2bb45a92U, 0x5cb36a04U, 0xc2d7ffa7U, 0xb5d0cf31U, 0x2cd99e8bU, 0x5bdeae1dU, + 0x9b64c2b0U, 0xec63f226U, 0x756aa39cU, 0x026d930aU, 0x9c0906a9U, 0xeb0e363fU, 0x72076785U, 0x05005713U, + 0x95bf4a82U, 0xe2b87a14U, 0x7bb12baeU, 0x0cb61b38U, 0x92d28e9bU, 0xe5d5be0dU, 0x7cdcefb7U, 0x0bdbdf21U, + 0x86d3d2d4U, 0xf1d4e242U, 0x68ddb3f8U, 0x1fda836eU, 0x81be16cdU, 0xf6b9265bU, 0x6fb077e1U, 0x18b74777U, + 0x88085ae6U, 0xff0f6a70U, 0x66063bcaU, 0x11010b5cU, 0x8f659effU, 0xf862ae69U, 0x616bffd3U, 0x166ccf45U, + 0xa00ae278U, 0xd70dd2eeU, 0x4e048354U, 0x3903b3c2U, 0xa7672661U, 0xd06016f7U, 0x4969474dU, 0x3e6e77dbU, + 0xaed16a4aU, 0xd9d65adcU, 0x40df0b66U, 0x37d83bf0U, 0xa9bcae53U, 0xdebb9ec5U, 0x47b2cf7fU, 0x30b5ffe9U, + 0xbdbdf21cU, 0xcabac28aU, 0x53b39330U, 0x24b4a3a6U, 0xbad03605U, 0xcdd70693U, 0x54de5729U, 0x23d967bfU, + 0xb3667a2eU, 0xc4614ab8U, 0x5d681b02U, 0x2a6f2b94U, 0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU, 0x2d02ef8dU + }; + } + + static constexpr uint32_t GenerateCrc32(const char* text, const size_t size, uint32_t hash = 0xffffffffU) noexcept + { + for (size_t i = 0; i < size; ++i) + { + const uint8_t index = static_cast((hash & 0xff) ^ text[i]); + hash = detail::Crc32Table[index] ^ (hash >> 8); + } + return hash ^ 0xffffffffU; + } + + static constexpr uint32_t GenerateCrc32(const void* data, const size_t size, uint32_t hash = 0xffffffffU) noexcept + { + return GenerateCrc32(static_cast(data), size, hash); + } +} diff --git a/Source/DungeonGenerator/Private/Core/Identifier.h b/Source/DungeonGenerator/Private/Core/Identifier.h index bb393c0..f07ecdf 100644 --- a/Source/DungeonGenerator/Private/Core/Identifier.h +++ b/Source/DungeonGenerator/Private/Core/Identifier.h @@ -10,6 +10,10 @@ All Rights Reserved. namespace dungeon { + /** + @addtogroup PathGeneration + @{ + */ /** 識別子クラス */ @@ -47,6 +51,11 @@ namespace dungeon Type GetType() const noexcept; bool IsType(const Type type) const noexcept; + /* + Reset counter to identify + */ + static void ResetCounter(); + // TODO:移行が完了したら削除して下さい uint16_t Get() const noexcept { return mIdentifier; } @@ -58,6 +67,9 @@ namespace dungeon static constexpr IdentifierType maskCounter = static_cast(~0) >> bitCount; static IdentifierType mCounter; }; + /** + @} + */ } #include "Identifier.inl" diff --git a/Source/DungeonGenerator/Private/Core/Identifier.inl b/Source/DungeonGenerator/Private/Core/Identifier.inl index c43e1e7..9d8b0fc 100644 --- a/Source/DungeonGenerator/Private/Core/Identifier.inl +++ b/Source/DungeonGenerator/Private/Core/Identifier.inl @@ -57,4 +57,9 @@ namespace dungeon { return type == GetType(); } + + inline void Identifier::ResetCounter() + { + mCounter = 0; + } } diff --git a/Source/DungeonGenerator/Private/Core/Math/Math.h b/Source/DungeonGenerator/Private/Core/Math/Math.h index a2be03f..1e88740 100644 --- a/Source/DungeonGenerator/Private/Core/Math/Math.h +++ b/Source/DungeonGenerator/Private/Core/Math/Math.h @@ -6,21 +6,89 @@ All Rights Reserved. #pragma once #include +#include namespace dungeon { + //! commonly used calculations Implementation namespace math { template - inline T Square(const T value) noexcept + static constexpr T Pi() noexcept + { + return static_cast(3.14159265358979323846264338327950288); + } + + template + static constexpr T PiHalf() noexcept + { + return Pi() * static_cast(0.5); + } + + template + static constexpr T Pi2() noexcept + { + return Pi() * static_cast(2.); + } + + template + static constexpr T Negative(const T t) noexcept + { + return -t; + } + + template + static constexpr T Inverse(const T t) noexcept + { + return static_cast(1.) / t; + } + + template + static constexpr T Sign(const T t) noexcept + { + return t >= static_cast(0.) ? static_cast(1.) : static_cast(-1.); + } + + template + static constexpr T Square(const T value) noexcept { return value * value; } template - T Clamp(const T value, const T min, const T max) noexcept + static constexpr T Clamp(const T value, const T min, const T max) noexcept { return std::max(min, std::min(value, max)); } + + template + static constexpr bool Equal(const T a, const T b, const T eps = std::numeric_limits::epsilon()) noexcept + { + return IsZero(a - b, eps); + } + + template + static constexpr bool IsZero(const T x, const T eps = std::numeric_limits::epsilon()) noexcept + { + return (-eps <= x && x <= eps); + } + + template + static constexpr float Lerp(const T a, const T b, const T ratio) + { + return a + (b - a) * ratio; + } + + template + static constexpr T ToRadian(const T degree) + { + return degree * (Pi() / static_cast(180.)); + } + + template + static constexpr T ToDegree(const T radian) + { + return radian * (static_cast(180.0) / Pi()); + } } } diff --git a/Source/DungeonGenerator/Private/Core/Math/PerlinNoise.cpp b/Source/DungeonGenerator/Private/Core/Math/PerlinNoise.cpp index 6262b24..dc62fbd 100644 --- a/Source/DungeonGenerator/Private/Core/Math/PerlinNoise.cpp +++ b/Source/DungeonGenerator/Private/Core/Math/PerlinNoise.cpp @@ -8,15 +8,16 @@ All Rights Reserved. #include "PerlinNoise.h" #include "Random.h" +#include namespace dungeon { - PerlinNoise::PerlinNoise(Random& random) + PerlinNoise::PerlinNoise(const std::shared_ptr& random) { SetSeed(random); } - void PerlinNoise::SetSeed(Random& random) + void PerlinNoise::SetSeed(const std::shared_ptr& random) { for (std::size_t i = 0; i < 256; ++i) { @@ -24,7 +25,7 @@ namespace dungeon } for (std::size_t i = 256 - 2; i >= 1; --i) { - const std::size_t j = random.Get(256); + const std::size_t j = random->Get(256); const uint8_t t = mHash[i]; mHash[i] = mHash[j]; mHash[j] = t; diff --git a/Source/DungeonGenerator/Private/Core/Math/PerlinNoise.h b/Source/DungeonGenerator/Private/Core/Math/PerlinNoise.h index 507b484..4d35b73 100644 --- a/Source/DungeonGenerator/Private/Core/Math/PerlinNoise.h +++ b/Source/DungeonGenerator/Private/Core/Math/PerlinNoise.h @@ -9,6 +9,7 @@ All Rights Reserved. #pragma once #include #include +#include namespace dungeon { @@ -20,11 +21,11 @@ namespace dungeon class PerlinNoise final { public: - explicit PerlinNoise(Random& random); + explicit PerlinNoise(const std::shared_ptr& random); ~PerlinNoise() = default; // SEED値を設定する - void SetSeed(Random& random); + void SetSeed(const std::shared_ptr& random); // オクターブ無しノイズを取得する float Noise(float x, float y) const noexcept; diff --git a/Source/DungeonGenerator/Private/Core/Math/Point.cpp b/Source/DungeonGenerator/Private/Core/Math/Point.cpp index 5c4d412..52df557 100644 --- a/Source/DungeonGenerator/Private/Core/Math/Point.cpp +++ b/Source/DungeonGenerator/Private/Core/Math/Point.cpp @@ -66,6 +66,17 @@ namespace dungeon return super::operator==(other); } + void Point::ResetByRoomGroundCenter() noexcept + { + if (mRoom) + { + const Point& groundCenter = mRoom->GetGroundCenter(); + X = groundCenter.X; + Y = groundCenter.Y; + Z = groundCenter.Z; + } + } + double Point::Dist(const Point& v0, const Point& v1) noexcept { return std::sqrt(Point::DistSquared(v0, v1)); diff --git a/Source/DungeonGenerator/Private/Core/Math/Point.h b/Source/DungeonGenerator/Private/Core/Math/Point.h index 2b2716c..e7d66ad 100644 --- a/Source/DungeonGenerator/Private/Core/Math/Point.h +++ b/Source/DungeonGenerator/Private/Core/Math/Point.h @@ -87,6 +87,11 @@ namespace dungeon */ bool operator==(const Point& other) const noexcept; + /** + オーナーの部屋の中心にリセットします + */ + void ResetByRoomGroundCenter() noexcept; + /** 2点間の距離を求めます \param[in] v0 点0 diff --git a/Source/DungeonGenerator/Private/Core/Math/Random.h b/Source/DungeonGenerator/Private/Core/Math/Random.h index 915a784..928c312 100644 --- a/Source/DungeonGenerator/Private/Core/Math/Random.h +++ b/Source/DungeonGenerator/Private/Core/Math/Random.h @@ -8,91 +8,83 @@ All Rights Reserved. namespace dungeon { - /** - 乱数クラス - */ + //! Generates random numbers by xorshift32 class Random { public: - //! コンストラクタ + //! Default constructor Random(); /** - コンストラクタ + Constructor \param[in] seed 乱数の種 */ explicit Random(const uint32_t seed); /** - コピーコンストラクタ + copy constructor */ explicit Random(const Random& other); /** - ムーブコンストラクタ + move constructor */ explicit Random(Random&& other) noexcept; /** - コピー代入 + copy assignment */ Random& operator = (const Random& other); /** - ムーブ代入 + move assignment */ Random& operator = (Random&& other) noexcept; /** - 乱数の種を設定します - \param[in] seed 乱数の種 + Sets the random number seed + \param[in] seed Random number seeds */ void SetSeed(const uint32_t seed); /** - 符号の乱数を取得します - \return -1または1 + \return -1 or 1 + T must be a signed type. */ template T GetSign(); /** - 乱数を取得します - \return 整数では[type_min,type_max]、実数では[0,1]の範囲を等確率で返す + Get a random number + \return Returns the range [type_min,type_max) if T is an integer, + or [0,1] with equal probability if T is a real number. */ template T Get(); /** - 乱数を取得します - \param[in] to 範囲 - \return 整数では[0,to)、実数では[0,to]の範囲を等確率で返す + Get a random number + \param[in] to Upper value + \return Returns the range [0,to) if T is an integer, + or [0,to] with equal probability if T is a real number. */ template T Get(const T to); /** - 乱数を取得します - \param[in] from 開始範囲 - \param[in] to 終了範囲 - \return 整数では[from,to)、実数では[from,to]の範囲を等確率で返す + Get a random number + \param[in] from Lower value + \param[in] to Upper value + \return Returns the range [from,to) if T is an integer, + or [from,to] with equal probability if T is a real number. */ template T Get(const T from, const T to); - /** - インスタンスを取得します - */ - static Random& Instance() - { - static Random instance; - return instance; - } - private: /** - uint32_t型の乱数を取得します - \return [0,std::numeric_limits::max]の範囲を返す + Get a random number of type uint32_t + \return Returns a range of [0,std::numeric_limits::max */ uint32_t GetU32(); diff --git a/Source/DungeonGenerator/Private/Core/Math/Random.inl b/Source/DungeonGenerator/Private/Core/Math/Random.inl index adbaaec..f5121a2 100644 --- a/Source/DungeonGenerator/Private/Core/Math/Random.inl +++ b/Source/DungeonGenerator/Private/Core/Math/Random.inl @@ -1,5 +1,4 @@ /** -乱数クラス \author Shun Moriya \copyright 2023- Shun Moriya All Rights Reserved. @@ -14,7 +13,7 @@ All Rights Reserved. namespace dungeon { /* - constexpr if内のブロックの評価を遅延させるためのテンプレート + constexpr Template for delaying evaluation of blocks in if */ template constexpr bool RandamFalse_v = false; @@ -70,7 +69,7 @@ namespace dungeon mZ = 521288629; mW = 88675123; - // 全て0にならないようにする + // Make sure it doesn't all go to zero. do { mX ^= seed; mX ^= mX >> 21; mX ^= mX >> 4; mX *= 1332534557; @@ -92,8 +91,11 @@ namespace dungeon { if constexpr (std::is_same()) { - // 線形合同法と異なり、XorShiftは最下位ビットの短周期の規則性はない。 - // 0または1を直接得ることができるため、最下位ビットを取る実装としている。 + /* + Unlike linear congruence, XorShift has no short - period regularity + in the least significant bit. Implementation that takes the least + significant bit because it is possible to obtain 0 or 1 directly. + */ return static_cast(GetU32() & 0x1); } else if constexpr (std::is_same()) @@ -108,15 +110,15 @@ namespace dungeon } else if constexpr (std::is_same()) { - const uint64_t hight = static_cast(GetU32()) << 32ULL; + const uint64_t high = static_cast(GetU32()) << 32ULL; const uint64_t low = static_cast(GetU32()); - return hight | low; + return high | low; } else if constexpr (std::is_same()) { - const uint64_t hight = static_cast(GetU32()) << 32ULL; + const uint64_t high = static_cast(GetU32()) << 32ULL; const uint64_t low = static_cast(GetU32()); - return static_cast(hight | low); + return static_cast(high | low); } else if constexpr (std::is_integral::value) { @@ -133,14 +135,14 @@ namespace dungeon { if constexpr (std::is_floating_point::value) { - const auto value = Get(); + const T value = Get(); return value * to; } else { if (to != 0) { - const size_t value = Get(); + const std::make_unsigned_t value = Get(); return value % to; } else @@ -156,14 +158,14 @@ namespace dungeon check(from <= to); if constexpr (std::is_floating_point::value) { - const auto value = Get(); + const T value = Get(); return from + value * (to - from); } else { if (from != to) { - const size_t value = Get(); + const std::make_unsigned_t value = Get(); return from + value % (to - from); } else diff --git a/Source/DungeonGenerator/Private/Core/Math/Tetrahedron.h b/Source/DungeonGenerator/Private/Core/Math/Tetrahedron.h index 4b9c741..2c59428 100644 --- a/Source/DungeonGenerator/Private/Core/Math/Tetrahedron.h +++ b/Source/DungeonGenerator/Private/Core/Math/Tetrahedron.h @@ -10,6 +10,7 @@ All Rights Reserved. #pragma once #include "Circle.h" #include +#include namespace dungeon { diff --git a/Source/DungeonGenerator/Private/Core/MinimumSpanningTree.cpp b/Source/DungeonGenerator/Private/Core/MinimumSpanningTree.cpp index 38d85ee..fd6fa8c 100644 --- a/Source/DungeonGenerator/Private/Core/MinimumSpanningTree.cpp +++ b/Source/DungeonGenerator/Private/Core/MinimumSpanningTree.cpp @@ -230,7 +230,7 @@ namespace dungeon mStartPoint = FindStartPoint(); size_t startPointIndex = verteces.Find(mStartPoint); - // cppcheck-suppress [knownConditionTrueFalse, unmatchedSuppression] + // cppcheck-suppress [knownConditionTrueFalse] if (startPointIndex != static_cast(~0)) { // スタートから各部屋の深さ(部屋の数)を設定 diff --git a/Source/DungeonGenerator/Private/Core/MinimumSpanningTree.h b/Source/DungeonGenerator/Private/Core/MinimumSpanningTree.h index 2ee8f40..a534cc4 100644 --- a/Source/DungeonGenerator/Private/Core/MinimumSpanningTree.h +++ b/Source/DungeonGenerator/Private/Core/MinimumSpanningTree.h @@ -14,7 +14,11 @@ All Rights Reserved. namespace dungeon { /** - 最小スパニングツリークラス + @addtogroup PathSelection + @{ + */ + /** + Minimum Spanning Tree Class */ class MinimumSpanningTree final { @@ -221,6 +225,9 @@ namespace dungeon uint8_t mDistance = 0; }; + /** + @} + */ } #include "MinimumSpanningTree.inl" diff --git a/Source/DungeonGenerator/Private/Core/MissionGraph/MissionGraph.cpp b/Source/DungeonGenerator/Private/Core/MissionGraph/MissionGraph.cpp index f10a590..e566f67 100644 --- a/Source/DungeonGenerator/Private/Core/MissionGraph/MissionGraph.cpp +++ b/Source/DungeonGenerator/Private/Core/MissionGraph/MissionGraph.cpp @@ -53,9 +53,10 @@ namespace dungeon keyRooms = mGenerator->FindByRoute(connectingRoom); if (keyRooms.size() > 0) { - // TODO:DrawLots内で乱数を使っています + const std::shared_ptr& random = mGenerator->GetGenerateParameter().GetRandom(); + const uint8_t roomBranch = room->GetBranchId(); - const auto keyRoom = DrawLots(keyRooms.begin(), keyRooms.end(), [roomBranch](const std::shared_ptr& room) + const auto keyRoom = DrawLots(random, keyRooms.begin(), keyRooms.end(), [roomBranch](const std::shared_ptr& room) { uint32_t weight = room->GetDepthFromStart(); if (room->GetBranchId() - roomBranch) @@ -86,9 +87,6 @@ namespace dungeon } } - /* - TODO:乱数を共通化して下さい - */ void MissionGraph::Generate(const std::shared_ptr& room) noexcept { #if 0 @@ -111,52 +109,18 @@ namespace dungeon keyRooms = mGenerator->FindByRoute(room == room0 ? room1 : room0); if (keyRooms.size() > 0) { -#if 0 - // TODO:DrawLots内で乱数を使っています - const uint8_t roomBranch = room->GetBranchId(); - const auto keyRoom = DrawLots(keyRooms.begin(), keyRooms.end(), [roomBranch](const std::shared_ptr& room) - { - const uint32_t deltaBranch = std::abs(roomBranch - room->GetBranchId()); - const uint32_t depthFromStart = room->GetDepthFromStart(); - uint32_t weight = deltaBranch + depthFromStart; - //if (room->GetParts() == Room::Parts::Hanare) - // weight *= 2; - return weight; - } - ); - if (keyRoom != keyRooms.end()) - { - // That's the room where I'm supposed to put the key. - check((*keyRoom)->GetItem() == Room::Item::Empty); - (*keyRoom)->SetItem(Room::Item::Key); -#if 0 - Generate(*keyRoom); -#else - //const auto lockRoom = keyRooms[std::rand() % keyRooms.size()]; - const auto lockRoom = DrawLots(keyRooms.begin(), keyRooms.end(), [](const std::shared_ptr& room) - { - return room->GetDepthFromStart(); - } - ); - Generate(*lockRoom); -#endif - } - else - { - // It did not decide on a room to put the key, so it will be unlocked - aisle->SetLock(false); - } -#else + const std::shared_ptr& random = mGenerator->GetGenerateParameter().GetRandom(); + // That's the room where I'm supposed to put the key. { - // TODO:共通の乱数を使用してください - const auto keyRoom = keyRooms[std::rand() % keyRooms.size()]; + const size_t index = random->Get(keyRooms.size()); + const auto keyRoom = keyRooms[index]; check(keyRoom->GetItem() == Room::Item::Empty); keyRoom->SetItem(Room::Item::Key); } // TODO:DrawLots内で乱数を使っています - const auto lockRoom = DrawLots(keyRooms.begin(), keyRooms.end(), [](const std::shared_ptr& room) + const auto lockRoom = DrawLots(random, keyRooms.begin(), keyRooms.end(), [](const std::shared_ptr& room) { const uint32_t depthFromStart = room->GetDepthFromStart(); return depthFromStart * 10; @@ -166,7 +130,6 @@ namespace dungeon { Generate(*lockRoom); } -#endif } else { diff --git a/Source/DungeonGenerator/Private/Core/PathFinder.cpp b/Source/DungeonGenerator/Private/Core/PathFinder.cpp index 0398b17..f80aa42 100644 --- a/Source/DungeonGenerator/Private/Core/PathFinder.cpp +++ b/Source/DungeonGenerator/Private/Core/PathFinder.cpp @@ -13,7 +13,7 @@ All Rights Reserved. // 定義するとマンハッタン距離で計算する。未定義ならユークリッド距離で計算する #define CALCULATE_IN_MANHATTAN_DISTANCE -#if WITH_EDITOR && JENKINS_FOR_DEVELOP +#if WITH_EDITOR & JENKINS_FOR_DEVELOP // 定義すると経路を調べるため、中間データを開放しない //#define CHECK_ROUTE #endif @@ -103,13 +103,13 @@ namespace dungeon // コスト計算 const uint32_t newCost = TotalCost(cost, location, goal); - // Openリスト内を検索 + // オープンリスト内を検索 const auto openNode = mOpen.find(key); - // Closeリスト内を検索 + // クローズリスト内を検索 const auto closeNode = mClose.find(key); - // OpenとClose両方に存在する事はありえない + // オープンとクローズ両方に存在する事はありえない check((openNode != mOpen.end() && closeNode != mClose.end()) == false); // オープンリストに追加するノードがある。かつ、新しいノードの方がトータルコストが低い @@ -117,7 +117,7 @@ namespace dungeon { if (openNode->second.mCost > newCost) { - // Openリストを更新 + // Replace node with open list openNode->second.mNodeType = nodeType; openNode->second.mDirection = direction; openNode->second.mParentKey = parentKey; @@ -130,18 +130,18 @@ namespace dungeon { if (closeNode->second.mCost > newCost) { - // Closeリストから消す + // Delete from close list mClose.erase(closeNode); - // Openリストに再登録 + // Re-register on open list mOpen.emplace(key, OpenNode(parentKey, nodeType, location, direction, searchDirection, newCost)); RevertOpenNode(key); } } - // オープンとクローズリストに追加するノードがない + // No nodes on open and closed list else { - // Openリストに登録 + // Register open List mOpen.emplace(key, OpenNode(parentKey, nodeType, location, direction, searchDirection, newCost)); } @@ -158,7 +158,7 @@ namespace dungeon if (mOpen.empty()) return false; - // 最もコストの低いノードを検索 + // Find the node with the lowest cost std::unordered_map::iterator result = mOpen.begin(); uint32_t minimumCost = std::numeric_limits::max(); for (std::unordered_map::iterator i = mOpen.begin(); i != mOpen.end(); ++i) @@ -168,7 +168,7 @@ namespace dungeon minimumCost = (*i).second.mCost; result = i; } - // タイブレーク(コストが同じならば上下移動を優先) + // Tiebreaker (if costs are the same, up/down movement is preferred) else if (minimumCost == (*i).second.mCost) { if ((*i).second.mNodeType == NodeType::Downstairs || (*i).second.mNodeType == NodeType::Upstairs) diff --git a/Source/DungeonGenerator/Private/Core/PathFinder.h b/Source/DungeonGenerator/Private/Core/PathFinder.h index 58411b2..8da2cbc 100644 --- a/Source/DungeonGenerator/Private/Core/PathFinder.h +++ b/Source/DungeonGenerator/Private/Core/PathFinder.h @@ -16,6 +16,11 @@ All Rights Reserved. namespace dungeon { + /** + @addtogroup PathGeneration + @{ + */ + /** A*によるパス検索クラス */ @@ -307,6 +312,9 @@ namespace dungeon std::unordered_map mClose; std::vector mRoute; }; + /** + @} + */ } #include "PathFinder.inl" diff --git a/Source/DungeonGenerator/Private/Core/PathGoalCondition.h b/Source/DungeonGenerator/Private/Core/PathGoalCondition.h index 287d85f..db85009 100644 --- a/Source/DungeonGenerator/Private/Core/PathGoalCondition.h +++ b/Source/DungeonGenerator/Private/Core/PathGoalCondition.h @@ -12,6 +12,10 @@ All Rights Reserved. namespace dungeon { + /** + @addtogroup PathGeneration + @{ + */ /** ゴール条件クラス */ @@ -73,6 +77,9 @@ namespace dungeon private: FIntRect mRect; }; + /** + @} + */ inline PathGoalCondition::PathGoalCondition(const FIntRect& rect) noexcept : mRect(rect) diff --git a/Source/DungeonGenerator/Private/Core/PathNodeSwitcher.h b/Source/DungeonGenerator/Private/Core/PathNodeSwitcher.h index df25f7a..f2f99c6 100644 --- a/Source/DungeonGenerator/Private/Core/PathNodeSwitcher.h +++ b/Source/DungeonGenerator/Private/Core/PathNodeSwitcher.h @@ -14,6 +14,10 @@ All Rights Reserved. namespace dungeon { + /** + @addtogroup PathGeneration + @{ + */ /* パス検索ノードの予約・使用中切り替えクラス */ @@ -49,6 +53,9 @@ namespace dungeon std::unordered_map> mReserved; std::unordered_map> mUsed; }; + /** + @} + */ } #include "PathNodeSwitcher.inl" diff --git a/Source/DungeonGenerator/Private/Core/PathNodeSwitcher.inl b/Source/DungeonGenerator/Private/Core/PathNodeSwitcher.inl index 9d96d81..a2e9b5e 100644 --- a/Source/DungeonGenerator/Private/Core/PathNodeSwitcher.inl +++ b/Source/DungeonGenerator/Private/Core/PathNodeSwitcher.inl @@ -9,6 +9,7 @@ All Rights Reserved. #pragma once #include "PathFinder.h" #include "PathNodeSwitcher.h" +#include namespace dungeon { @@ -51,12 +52,11 @@ namespace dungeon inline bool PathNodeSwitcher::IsUsing(const uint64_t key) const { - for (const auto& node : mUsed) - { - if (node.second->Contain(key)) - return true; - } - return false; + return std::any_of(mUsed.begin(), mUsed.end(), [key](const std::pair>& node) + { + return node.second->Contain(key); + } + ); } inline void PathNodeSwitcher::Clear() diff --git a/Source/DungeonGenerator/Private/Core/Room.cpp b/Source/DungeonGenerator/Private/Core/Room.cpp index 227821d..7a0d07f 100644 --- a/Source/DungeonGenerator/Private/Core/Room.cpp +++ b/Source/DungeonGenerator/Private/Core/Room.cpp @@ -22,24 +22,35 @@ namespace \param[in] maximum 最大値 \return 乱数で決めたサイズ */ - static uint32_t randSize(dungeon::Random& random, const uint32_t minimum, const uint32_t maximum) + static uint32_t randSize(const std::shared_ptr& random, const uint32_t minimum, const uint32_t maximum) { - return random.Get(minimum, maximum); + return random->Get(minimum, maximum + 1); } } namespace dungeon { Room::Room(const GenerateParameter& parameter, const FIntVector& location) noexcept + : mX(location.X) + , mY(location.Y) + , mZ(location.Z) { - mX = location.X; - mY = location.Y; - mZ = location.Z; mWidth = randSize(parameter.GetRandom(), parameter.GetMinRoomWidth(), parameter.GetMaxRoomWidth()); mDepth = randSize(parameter.GetRandom(), parameter.GetMinRoomDepth(), parameter.GetMaxRoomDepth()); mHeight = randSize(parameter.GetRandom(), parameter.GetMinRoomHeight(), parameter.GetMaxRoomHeight()); } + Room::Room(const FIntVector& location, const FIntVector& size, const bool undeletable) noexcept + : mX(location.X) + , mY(location.Y) + , mZ(location.Z) + , mWidth(size.X) + , mDepth(size.Y) + , mHeight(size.Z) + , mUndeletable(undeletable) + { + } + Room::Room(const Room& other) noexcept : mX(other.mX) , mY(other.mY) @@ -282,10 +293,10 @@ namespace dungeon { static const std::array names = { "unidentified", - "start", - "goal", "hall", "hanare", + "start", + "goal", }; return names[static_cast(mParts)]; } diff --git a/Source/DungeonGenerator/Private/Core/Room.h b/Source/DungeonGenerator/Private/Core/Room.h index 75331df..4d8c952 100644 --- a/Source/DungeonGenerator/Private/Core/Room.h +++ b/Source/DungeonGenerator/Private/Core/Room.h @@ -32,12 +32,12 @@ namespace dungeon enum class Parts : uint8_t { Unidentified, //!< 未識別 - Start, //!< スタート地点 - Goal, //!< ゴール地点 Hall, //!< 広間(通路が複数つながっている) Hanare, //!< 離れ(通路が一つだけつながっている) + Start, //!< スタート地点 + Goal, //!< ゴール地点 }; - static constexpr uint8_t PartsSize = static_cast(Parts::Hanare) + 1; + static constexpr uint8_t PartsSize = static_cast(Parts::Goal) + 1; /** 変更した場合はGetItemNameもあわせて修正して下さい @@ -56,6 +56,11 @@ namespace dungeon */ Room(const GenerateParameter& parameter, const FIntVector& location) noexcept; + /** + コンストラクタ + */ + Room(const FIntVector& location, const FIntVector& size, const bool undeletable) noexcept; + /** コピーコンストラクタ */ @@ -311,6 +316,10 @@ namespace dungeon */ void GetDataBounds(FIntVector& min, FIntVector& max) const noexcept; + bool IsUndeletable() const noexcept; + + void SetUndeletables(const bool undeletable) noexcept; + /** Sets mesh generation prohibition \param[in] noRoofMeshGeneration No roof mesh generation @@ -349,6 +358,8 @@ namespace dungeon uint8_t mDepthFromStart = std::numeric_limits::max(); uint8_t mBranchId = std::numeric_limits::max(); + bool mUndeletable = false; + enum class NoMeshGeneration : uint8_t { Floor, diff --git a/Source/DungeonGenerator/Private/Core/Room.inl b/Source/DungeonGenerator/Private/Core/Room.inl index a19d671..81ad5e2 100644 --- a/Source/DungeonGenerator/Private/Core/Room.inl +++ b/Source/DungeonGenerator/Private/Core/Room.inl @@ -10,6 +10,16 @@ All Rights Reserved. namespace dungeon { + inline bool Room::IsUndeletable() const noexcept + { + return mUndeletable; + } + + inline void Room::SetUndeletables(const bool undeletable) noexcept + { + mUndeletable = undeletable; + } + inline void Room::SetNoMeshGeneration(const bool noRoofMeshGeneration, const bool noFloorMeshGeneration) { mNoMeshGeneration.set(static_cast(NoMeshGeneration::Roof), noRoofMeshGeneration); diff --git a/Source/DungeonGenerator/Private/Core/Voxel.cpp b/Source/DungeonGenerator/Private/Core/Voxel.cpp index 64b352c..123f44b 100644 --- a/Source/DungeonGenerator/Private/Core/Voxel.cpp +++ b/Source/DungeonGenerator/Private/Core/Voxel.cpp @@ -13,10 +13,11 @@ All Rights Reserved. #include "PathGoalCondition.h" #include "Debug/BuildInfomation.h" #include "Debug/Debug.h" +#include "Helper/Crc.h" #include "Math/Math.h" #include -#if WITH_EDITOR && JENKINS_FOR_DEVELOP +#if WITH_EDITOR & JENKINS_FOR_DEVELOP // 定義するとデバッグに便利なログを出力します //#define DEBUG_SHOW_DEVELOP_LOG #endif @@ -130,7 +131,6 @@ namespace dungeon gateFinder.Entry(openLocation, goal); } } - } } @@ -146,11 +146,11 @@ namespace dungeon return false; } - // パス検索開始 + // Start of path search PathFinder pathFinder; pathFinder.Start(start, idealGoal, PathFinder::SearchDirection::Any); - // 最も有望な位置を取得します + // Get the most promising positions uint64_t nextKey; PathFinder::NodeType nextNodeType; uint32_t nextCost; @@ -159,7 +159,7 @@ namespace dungeon PathFinder::SearchDirection nextSearchDirection; while (pathFinder.Pop(nextKey, nextNodeType, nextCost, nextLocation, nextDirection, nextSearchDirection)) { - // ゴールに到達? + // Reaching the goal? if (IsReachedGoal(nextLocation, idealGoal.Z, goalCondition)) { if (nextNodeType == PathFinder::NodeType::Aisle && nextSearchDirection == PathFinder::SearchDirection::Any) @@ -168,7 +168,7 @@ namespace dungeon continue; } - // 水平方向へ探索 + // Search horizontally for (auto i = Direction::Begin(); i != Direction::End(); ++i) { if ( @@ -187,10 +187,10 @@ namespace dungeon } } - // 垂直方向へ探索 + // Vertical search if (nextNodeType == PathFinder::NodeType::Aisle) { - // 上 + // up const FIntVector upstairsOpenLocationU = nextLocation + FIntVector(0, 0, 1); const FIntVector upstairsOpenLocationF = nextLocation + nextDirection.GetVector(); const FIntVector upstairsOpenLocationUF = upstairsOpenLocationF + FIntVector(0, 0, 1); @@ -208,7 +208,7 @@ namespace dungeon pathFinder.ReserveOpenNode(upstairsOpenLocationUF, useNode); } - // 下 + // down const FIntVector downstairsOpenLocationD = nextLocation + FIntVector(0, 0, -1); const FIntVector downstairsOpenLocationF = nextLocation + nextDirection.GetVector(); const FIntVector downstairsOpenLocationDF = downstairsOpenLocationF + FIntVector(0, 0, -1); @@ -335,6 +335,17 @@ namespace dungeon return mGrids.get()[index]; } + const Grid& Voxel::Get(const FIntVector& location) const noexcept + { + return Get(location.X, location.Y, location.Z); + } + + const Grid& Voxel::Get(const size_t index) const noexcept + { + check(index < static_cast(mWidth) * mDepth * mHeight); + return mGrids.get()[index]; + } + void Voxel::Set(const uint32_t x, const uint32_t y, const uint32_t z, const Grid& grid) noexcept { if (x < mWidth && y < mDepth && z < mHeight) @@ -459,4 +470,11 @@ namespace dungeon { return mHeight; } + + uint32_t Voxel::CalculateCRC32() const noexcept + { + const size_t size = static_cast(mWidth) * mDepth * mHeight; + const uint32_t crc32 = GenerateCrc32(mGrids.get(), size); + return crc32; + } } diff --git a/Source/DungeonGenerator/Private/Core/Voxel.h b/Source/DungeonGenerator/Private/Core/Voxel.h index 639bcd4..6fd36be 100644 --- a/Source/DungeonGenerator/Private/Core/Voxel.h +++ b/Source/DungeonGenerator/Private/Core/Voxel.h @@ -18,6 +18,10 @@ namespace dungeon class PathGoalCondition; struct GenerateParameter; + /** + @addtogroup PathGeneration + @{ + */ /** グリッドクラス */ @@ -66,6 +70,22 @@ namespace dungeon */ const Grid& Get(const uint32_t x, const uint32_t y, const uint32_t z) const noexcept; + /** + グリッド内のグリッドを取得します + グリッド内のグリッドを取得します + \param[in] index 配列番号 + \return グリッド + */ + const Grid& Get(const FIntVector& location) const noexcept; + + /** + グリッド内のグリッドを取得します + グリッド内のグリッドを取得します + \param[in] index 配列番号 + \return グリッド + */ + const Grid& Get(const size_t index) const noexcept; + /** グリッド内のグリッドを設定します \param[in] x X座標 @@ -169,6 +189,12 @@ namespace dungeon */ Error GetLastError() const noexcept; + /** + Calculate CRC32 + \return CRC32 + */ + uint32_t CalculateCRC32() const noexcept; + private: /** 空きグリッドか調べます @@ -209,6 +235,9 @@ namespace dungeon Error mLastError = Error::Success; }; + /** + @} + */ } #include "Voxel.inl" diff --git a/Source/DungeonGenerator/Private/Decorator/DungeonInteriorDatabase.cpp b/Source/DungeonGenerator/Private/Decorator/DungeonInteriorDatabase.cpp new file mode 100644 index 0000000..f8ab7c3 --- /dev/null +++ b/Source/DungeonGenerator/Private/Decorator/DungeonInteriorDatabase.cpp @@ -0,0 +1,39 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "Decorator/DungeonInteriorDatabase.h" +#include "Core/Math/Random.h" +#include + +TArray UDungeonInteriorDatabase::Select(const TArray& interiorTags, const std::shared_ptr& random) const +{ + TArray selects; + + if (interiorTags.Num() > 0) + { + for (const FDungeonInteriorParts& parts : Parts) + { + if (parts.Frequency >= random->Get(100 + 1) && parts.Match(interiorTags)) + { + selects.Emplace(parts); + } + } + } + + return selects; +} + +void UDungeonInteriorDatabase::EachInteriorLocation(const AActor* actor, std::function function) const +{ + actor->ForEachComponent(false, [this, &function](UDungeonInteriorLocationComponent* interiorLocationComponent) + { + if (IsValid(interiorLocationComponent)) + { + function(interiorLocationComponent); + } + } + ); +} diff --git a/Source/DungeonGenerator/Private/Decorator/DungeonInteriorDecorator.cpp b/Source/DungeonGenerator/Private/Decorator/DungeonInteriorDecorator.cpp new file mode 100644 index 0000000..a889551 --- /dev/null +++ b/Source/DungeonGenerator/Private/Decorator/DungeonInteriorDecorator.cpp @@ -0,0 +1,300 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "Decorator/DungeonInteriorDecorator.h" +#include "DungeonGeneratorCore.h" +#include "Core/Math/Random.h" +#include "Core/Debug/Debug.h" +#include "Decorator/DungeonInteriorDatabase.h" +#include "Decorator/DungeonInteriorLocationComponent.h" +#include "Decorator/DungeonInteriorParts.h" +#include + +void FDungeonInteriorDecorator::ShuffleDecorationLocation(const std::shared_ptr& random) +{ + size_t count = mDecorationLocations.size() / 2; + while (count > 0) + { + const size_t a = random->Get(mDecorationLocations.size()); + const size_t b = random->Get(mDecorationLocations.size()); + if (a != b) + { + std::swap(mDecorationLocations[a], mDecorationLocations[b]); + } + --count; + } +} + +void FDungeonInteriorDecorator::TrySpawnActors(UWorld* world, const TArray& tags, const std::shared_ptr& random) +{ + auto i = mDecorationLocations.begin(); + while (i != mDecorationLocations.end()) + { + const std::shared_ptr& decorationLocation = *i; + const FTransform transform(FRotator(0, decorationLocation->Yaw, 0), decorationLocation->Position); + if (TrySpawnActorsImplement(world, tags, transform, random, 0)) + i = mDecorationLocations.erase(i); + else + ++i; + } +} + +bool FDungeonInteriorDecorator::TrySpawnActorsImplement(UWorld* world, const TArray& tags, const FTransform& parentTransform, const std::shared_ptr& random, const size_t depth) +{ + if (depth >= 5) + return true; + + if (!::IsValid(world)) + return false; + + // Select the parts to be placed + const TArray parts = Select(tags, random); + if (parts.Num() <= 0) + return false; + + const int32 partsIndex = random->Get(parts.Num()); + const FDungeonInteriorParts& selectedParts = parts[partsIndex]; + if (selectedParts.Class == nullptr) + { + DUNGEON_GENERATOR_LOG(TEXT("Set the class of interior parts")); + return false; + } + + // Placement angle error + double spawnYaw = 0; + switch (selectedParts.DungeonAngleTypeOfPlacement) + { + case EDungeonAngleTypeOfPlacement::None: + break; + case EDungeonAngleTypeOfPlacement::Every180Degrees: + if (random->Get() & 1) + spawnYaw = 180.; + break; + case EDungeonAngleTypeOfPlacement::Every90Degrees: + spawnYaw = (static_cast(random->Get() & 3) * 90.f); + break; + case EDungeonAngleTypeOfPlacement::Free: + spawnYaw = random->Get(-180.f, 180.f); + break; + } + const FQuat spawnRotation = FRotator(0, spawnYaw, 0).Quaternion(); + + // Calculate placement transforms + FTransform spawnTransform(spawnRotation); + spawnTransform *= parentTransform; + + // in a unplaceable bounds, interrupt. + if (IsInsideOrOnUnplacatableBounds(spawnTransform.GetLocation())) + return false; + + // Spawn actor + FActorSpawnParameters actorSpawnParameters; + //actorSpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; + actorSpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; + //actorSpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding; + //actorSpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::DontSpawnIfColliding; + actorSpawnParameters.bDeferConstruction = true; + AActor* actor = world->SpawnActor(selectedParts.Class, &spawnTransform, actorSpawnParameters); + if (!::IsValid(actor)) + { + DUNGEON_GENERATOR_LOG(TEXT("Failed to spawn interior actor %s"), *selectedParts.Class->GetName()); + return false; + } + + actor->Tags.Add(CDungeonGeneratorCore::GetDungeonGeneratorTag()); +#if WITH_EDITOR + actor->SetFolderPath(TEXT("Dungeon/Interiors")); +#endif + + actor->FinishSpawning(spawnTransform, true); + + /* + NOTE:I would like to know how to get the bounding box of the mesh before spawning + */ + FBox bounds = actor->GetComponentsBoundingBox(); + if (!bounds.IsValid) + { + DUNGEON_GENERATOR_LOG(TEXT("GetComponentsBoundingBox failed, %s"), *actor->GetName()); + bounds.Min = actor->GetActorLocation() - FVector(100, 100, 100); + bounds.Max = actor->GetActorLocation() + FVector(100, 100, 100); + } + + // in a unplaceable bounds, interrupt. + if (IntersectUnplacatableBounds(bounds)) + return false; + + { + // Bounding box size away from the generation position + FVector boundsCenter = bounds.GetCenter(); + const FVector& boundsExtent = bounds.GetExtent(); + const FVector& wallPosition = parentTransform.GetLocation(); + + // Placement position error + if (selectedParts.PercentageOfErrorInPlacement > 0) + { + const float percentageOfErrorInPlacement = static_cast(selectedParts.PercentageOfErrorInPlacement) / 100.f; + boundsCenter.X += boundsExtent.X * (random->Get(-1.f, 1.f) * percentageOfErrorInPlacement); + boundsCenter.Y += boundsExtent.Y * (random->Get(-1.f, 1.f) * percentageOfErrorInPlacement); + } + + // Move to avoid getting stuck in the around terrain + FVector movementDirection; + if (depth == 0) + { + movementDirection = boundsExtent * parentTransform.GetRotation().GetRightVector(); + + // Collision check + FCollisionQueryParams queryParams(TEXT("FDungeonInteriorDecorator:SpawnActorsImplement"), false, actor); + FHitResult hitResult; + + const FVector end = wallPosition + movementDirection; + const FVector start = end + FVector::UpVector * (bounds.Max.Z - bounds.Min.Z); + if (world->LineTraceSingleByChannel(hitResult, start, end, ECC_Visibility, queryParams)) + { + const auto delta = hitResult.ImpactPoint.Z - end.Z; + movementDirection.Z += delta; + } + + { + const FVector endX = start + FVector::ForwardVector * (bounds.Max.X - bounds.Min.X); + if (world->LineTraceSingleByChannel(hitResult, start, endX, ECC_Visibility, queryParams)) + { + const auto delta = hitResult.ImpactPoint.X - endX.X; + movementDirection.X = delta; + } + } + { + const FVector endX = start - FVector::ForwardVector * (bounds.Max.X - bounds.Min.X); + if (world->LineTraceSingleByChannel(hitResult, start, endX, ECC_Visibility, queryParams)) + { + const auto delta = hitResult.ImpactPoint.X - endX.X; + movementDirection.X = delta; + } + } + + { + const FVector endY = start + FVector::RightVector * (bounds.Max.Y - bounds.Min.Y); + if (world->LineTraceSingleByChannel(hitResult, start, endY, ECC_Visibility, queryParams)) + { + const auto delta = hitResult.ImpactPoint.Y - endY.Y; + movementDirection.Y = delta; + } + } + { + const FVector endY = start - FVector::RightVector * (bounds.Max.Y - bounds.Min.Y); + if (world->LineTraceSingleByChannel(hitResult, start, endY, ECC_Visibility, queryParams)) + { + const auto delta = hitResult.ImpactPoint.Y - endY.Y; + movementDirection.Y = delta; + } + } + + const auto pivotZ = boundsExtent.Z - (boundsCenter.Z - wallPosition.Z); + movementDirection.Z += pivotZ; + } + else + { + const auto pivotZ = boundsExtent.Z - (boundsCenter.Z - wallPosition.Z); + movementDirection.Set(0, 0, pivotZ); + } + + // Calculate new transform + const FVector targetPosition = wallPosition + movementDirection; + const FVector spawnOffset = targetPosition - boundsCenter; + bounds = bounds.ShiftBy(spawnOffset); + } + + bool result = false; + { + // Collision check + FCollisionQueryParams queryParams(TEXT("FDungeonInteriorDecorator:SpawnActorsImplement"), false, actor); + queryParams.bFindInitialOverlaps = true; + if (selectedParts.OverlapCheck && world->OverlapBlockingTestByChannel(bounds.GetCenter(), spawnRotation, ECC_Visibility, FCollisionShape::MakeBox(bounds.GetExtent() - FVector(10)), queryParams)) + { + DUNGEON_GENERATOR_LOG(TEXT("Overlap with other collisions, destroy %s"), *actor->GetName()); + actor->Destroy(); + result = false; + } + else + { + // Decorating Decorations (Fillables) + // http://www.archmagerises.com/news/2021/6/12/how-to-procedurally-generate-and-decorate-3d-dungeon-rooms-in-unity-c + + const FVector boundsCenter = bounds.GetCenter(); + + if (selectedParts.AdditionalExtentToProhibitPlacement > 0) + { + bounds.Min.X -= selectedParts.AdditionalExtentToProhibitPlacement; + bounds.Min.Y -= selectedParts.AdditionalExtentToProhibitPlacement; + bounds.Max.X += selectedParts.AdditionalExtentToProhibitPlacement; + bounds.Max.Y += selectedParts.AdditionalExtentToProhibitPlacement; + } + + if (Intersects(bounds)) + { + DUNGEON_GENERATOR_LOG(TEXT("Location of prohibited placement of surrounding interior actors, destroy %s"), *actor->GetName()); + actor->Destroy(); + result = false; + } + else + { + USceneComponent* rootComponent = actor->GetRootComponent(); + if (::IsValid(rootComponent)) + { + const EComponentMobility::Type mobility = rootComponent->Mobility; + rootComponent->SetMobility(EComponentMobility::Movable); + actor->SetActorLocation(boundsCenter); + rootComponent->SetMobility(mobility); + } + else + { + actor->SetActorLocation(boundsCenter); + } + + Asset->EachInteriorLocation(actor, [this, world, random, depth](UDungeonInteriorLocationComponent* component) + { + TrySpawnActorsImplement(world, component->GetInquireInteriorTags(), component->GetComponentTransform(), random, depth + 1); + } + ); + + mPlacedBounds.Emplace(bounds); + result = true; + } + } + } + return result; +} + +TArray FDungeonInteriorDecorator::Select(const TArray& interiorTags, const std::shared_ptr& random) const +{ + return ::IsValid(Asset) ? Asset->Select(interiorTags, random) : TArray(); +} + +bool FDungeonInteriorDecorator::Intersects(const FBox& bounds) const +{ + return std::any_of(mPlacedBounds.begin(), mPlacedBounds.end(), [&bounds](const FBox& sensor) + { + return sensor.Intersect(bounds); + } + ); +} + +std::shared_ptr FDungeonInteriorDecorator::CreateInteriorDecorator(const FBox& placeableBounds) const +{ + std::shared_ptr result = std::make_shared( + Asset, + mUnplaceableBounds + ); + for (const auto& i : mDecorationLocations) + { + if (placeableBounds.IsInsideOrOn(i->Position)) + { + result->mDecorationLocations.push_back(i); + } + } + + return result; +} diff --git a/Source/DungeonGenerator/Private/DungeonInteriorParts.cpp b/Source/DungeonGenerator/Private/Decorator/DungeonInteriorParts.cpp similarity index 50% rename from Source/DungeonGenerator/Private/DungeonInteriorParts.cpp rename to Source/DungeonGenerator/Private/Decorator/DungeonInteriorParts.cpp index f26331d..5f46d68 100644 --- a/Source/DungeonGenerator/Private/DungeonInteriorParts.cpp +++ b/Source/DungeonGenerator/Private/Decorator/DungeonInteriorParts.cpp @@ -4,13 +4,14 @@ All Rights Reserved. */ -#include "DungeonInteriorParts.h" +#include "Decorator/DungeonInteriorParts.h" bool FDungeonInteriorParts::Match(const TArray& tags) const noexcept { + const TSet& interiorSystemTags = InteriorSystemTags.InteriorSystemTags(); for (const FString& tag : tags) { - if (InteriorTags.Contains(tag) == false) + if (interiorSystemTags.Contains(tag) == false && InteriorAdditionalTags.Contains(tag) == false) return false; } return true; diff --git a/Source/DungeonGenerator/Private/DungeonAisleMeshSet.cpp b/Source/DungeonGenerator/Private/DungeonAisleMeshSet.cpp new file mode 100644 index 0000000..1e211c2 --- /dev/null +++ b/Source/DungeonGenerator/Private/DungeonAisleMeshSet.cpp @@ -0,0 +1,17 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "DungeonAisleMeshSet.h" + +const FDungeonMeshParts* FDungeonAisleMeshSet::SelectSlopeParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const +{ + return FDungeonRoomMeshSet::SelectParts(gridIndex, grid, random, SlopeParts, SloopPartsSelectionMethod); +} + +void FDungeonAisleMeshSet::EachSlopeParts(std::function func) const +{ + FDungeonRoomMeshSet::EachParts(SlopeParts, func); +} diff --git a/Source/DungeonGenerator/Private/DungeonAisleMeshSetDatabase.cpp b/Source/DungeonGenerator/Private/DungeonAisleMeshSetDatabase.cpp new file mode 100644 index 0000000..9843eca --- /dev/null +++ b/Source/DungeonGenerator/Private/DungeonAisleMeshSetDatabase.cpp @@ -0,0 +1,23 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "DungeonAisleMeshSetDatabase.h" +#include "Core/Math/Random.h" + +const FDungeonAisleMeshSet* UDungeonAisleMeshSetDatabase::Select(const std::shared_ptr& random) const +{ + const int32 size = Parts.Num(); + if (size <= 0) + return nullptr; + + const uint32_t index = random->Get(size); + return &Parts[index]; +} + +const FDungeonRoomMeshSet* UDungeonAisleMeshSetDatabase::SelectImplement(const std::shared_ptr& random) const +{ + return Select(random); +} diff --git a/Source/DungeonGenerator/Private/DungeonBlueprint.cpp b/Source/DungeonGenerator/Private/DungeonBlueprint.cpp index 5bd3b1d..d0cf631 100644 --- a/Source/DungeonGenerator/Private/DungeonBlueprint.cpp +++ b/Source/DungeonGenerator/Private/DungeonBlueprint.cpp @@ -5,4 +5,53 @@ All Rights Reserved. */ #include "DungeonBlueprint.h" +#include "PluginInfomation.h" +#include "Core/Debug/BuildInfomation.h" #include + +const FString& UDungeonBlueprint::GetPluginVersion() noexcept +{ + static const FString text(TEXT(DUNGENERATOR_PLUGIN_VERSION_NAME)); + return text; +} + +const FString& UDungeonBlueprint::GetDocumentURL() noexcept +{ + static const FString text(TEXT(DUNGENERATOR_PLUGIN_DOCS_URL)); + return text; +} + +const FString& UDungeonBlueprint::GetSupportURL() noexcept +{ + static const FString text(TEXT(DUNGENERATOR_PLUGIN_SUPPORT_URL)); + return text; +} + +const FString& UDungeonBlueprint::GetBuildTag() noexcept +{ + static const FString text(TEXT(JENKINS_BUILD_TAG)); + return text; +} + +const FString& UDungeonBlueprint::GetCommitID() noexcept +{ + static const FString text(TEXT(JENKINS_GIT_COMMIT)); + return text; +} + +const FString& UDungeonBlueprint::GetIdentifier() noexcept +{ + static const FString text(TEXT(JENKINS_UUID)); + return text; +} + +const FString& UDungeonBlueprint::GetLicenseType() noexcept +{ + static const FString text(TEXT(JENKINS_LICENSE)); + return text; +} + +FVector2D UDungeonBlueprint::TransformWorldToTexture(const FVector worldLocation, const float worldToTextureScale) noexcept +{ + return FVector2D(worldLocation.X, worldLocation.Y) * worldToTextureScale; +} diff --git a/Source/DungeonGenerator/Private/DungeonGenerateActor.cpp b/Source/DungeonGenerator/Private/DungeonGenerateActor.cpp index 5e3e645..4df2037 100644 --- a/Source/DungeonGenerator/Private/DungeonGenerateActor.cpp +++ b/Source/DungeonGenerator/Private/DungeonGenerateActor.cpp @@ -7,7 +7,6 @@ All Rights Reserved. #include "DungeonGenerateActor.h" #include "DungeonGenerateParameter.h" #include "DungeonGeneratorCore.h" -#include "DungeonMiniMapTextureLayer.h" #include "DungeonTransactionalHierarchicalInstancedStaticMeshComponent.h" #include "Core/Grid.h" #include "Core/Generator.h" @@ -16,17 +15,18 @@ All Rights Reserved. #include "Core/Debug/BuildInfomation.h" #include "Core/Debug/Debug.h" #include "Core/Math/Vector.h" - -//#include +#include "MiniMap/DungeonMiniMapTextureLayer.h" #include #include -//#include #include #include #include #include #include +#include +#include + #if WITH_EDITOR #include #include @@ -38,13 +38,17 @@ ADungeonGenerateActor::ADungeonGenerateActor(const FObjectInitializer& initializ , LicenseTag(TEXT(JENKINS_LICENSE)) , LicenseId(TEXT(JENKINS_UUID)) { - // ティック有効化 + // Tick Enable PrimaryActorTick.bCanEverTick = PrimaryActorTick.bStartWithTickEnabled = true; - // ルートシーンコンポーネントを生成 + // Replicates + bReplicates = ReplicatedServerDungeon; + + // Create root scene component RootComponent = initializer.CreateDefaultSubobject(this, TEXT("Scene"), true); + check(RootComponent); - // デフォルトパラメータを生成 + // Create default parameters DungeonGenerateParameter = NewObject(); check(DungeonGenerateParameter); } @@ -69,13 +73,10 @@ void ADungeonGenerateActor::AddInstance(TArrayGetStaticMesh() == staticMesh) { - if (mesh->GetStaticMesh() == staticMesh) - { - mesh->AddInstance(transform); - break; - } + mesh->AddInstance(transform); + break; } } } @@ -91,7 +92,7 @@ void ADungeonGenerateActor::EndAddInstance(TArrayOnResetDoor([this](AActor* actor, EDungeonRoomProps props) + { + OnResetDoor.Broadcast(actor, props); + } + ); + if (InstancedStaticMesh) { - DestroyImplementation(); - DungeonGenerateParameter->EachFloorParts([this](const FDungeonMeshParts& meshParts) { - auto component = NewObject(this); - if (IsValid(component)) + for (const UDungeonTransactionalHierarchicalInstancedStaticMeshComponent* mesh : FloorMeshs) { - AddInstanceComponent(component); - component->RegisterComponent(); - component->SetStaticMesh(meshParts.StaticMesh); + if (IsValid(mesh) && mesh->GetStaticMesh() == meshParts.StaticMesh) + return; } - FloorMeshs.Add(component); - } - ); - DungeonGenerateParameter->EachSlopeParts([this](const FDungeonMeshParts& meshParts) - { - auto component = NewObject(this); + UDungeonTransactionalHierarchicalInstancedStaticMeshComponent* component + = NewObject(this); if (IsValid(component)) { AddInstanceComponent(component); component->RegisterComponent(); component->SetStaticMesh(meshParts.StaticMesh); + FloorMeshs.Add(component); } - SlopeMeshs.Add(component); } ); DungeonGenerateParameter->EachWallParts([this](const FDungeonMeshParts& meshParts) { - auto component = NewObject(this); + for (const UDungeonTransactionalHierarchicalInstancedStaticMeshComponent* mesh : WallMeshs) + { + if (IsValid(mesh) && mesh->GetStaticMesh() == meshParts.StaticMesh) + return; + } + UDungeonTransactionalHierarchicalInstancedStaticMeshComponent* component + = NewObject(this); if (IsValid(component)) { AddInstanceComponent(component); component->RegisterComponent(); component->SetStaticMesh(meshParts.StaticMesh); + WallMeshs.Add(component); } - WallMeshs.Add(component); } ); - DungeonGenerateParameter->EachRoomRoofParts([this](const FDungeonMeshParts& meshParts) + DungeonGenerateParameter->EachRoofParts([this](const FDungeonMeshParts& meshParts) { - auto component = NewObject(this); + for (const UDungeonTransactionalHierarchicalInstancedStaticMeshComponent* mesh : RoofMeshs) + { + if (IsValid(mesh) && mesh->GetStaticMesh() == meshParts.StaticMesh) + return; + } + UDungeonTransactionalHierarchicalInstancedStaticMeshComponent* component + = NewObject(this); if (IsValid(component)) { AddInstanceComponent(component); component->RegisterComponent(); component->SetStaticMesh(meshParts.StaticMesh); + RoofMeshs.Add(component); } - RoomRoofMeshs.Add(component); } ); - DungeonGenerateParameter->EachAisleRoofParts([this](const FDungeonMeshParts& meshParts) + DungeonGenerateParameter->EachSlopeParts([this](const FDungeonMeshParts& meshParts) { - auto component = NewObject(this); + for (const UDungeonTransactionalHierarchicalInstancedStaticMeshComponent* mesh : SlopeMeshs) + { + if (IsValid(mesh) && mesh->GetStaticMesh() == meshParts.StaticMesh) + return; + } + UDungeonTransactionalHierarchicalInstancedStaticMeshComponent* component + = NewObject(this); if (IsValid(component)) { AddInstanceComponent(component); component->RegisterComponent(); component->SetStaticMesh(meshParts.StaticMesh); + SlopeMeshs.Add(component); } - AisleRoofMeshs.Add(component); } ); DungeonGenerateParameter->EachPillarParts([this](const FDungeonMeshParts& meshParts) { - auto component = NewObject(this); + for (const UDungeonTransactionalHierarchicalInstancedStaticMeshComponent* mesh : PillarMeshs) + { + if (IsValid(mesh) && mesh->GetStaticMesh() == meshParts.StaticMesh) + return; + } + UDungeonTransactionalHierarchicalInstancedStaticMeshComponent* component + = NewObject(this); if (IsValid(component)) { AddInstanceComponent(component); component->RegisterComponent(); component->SetStaticMesh(meshParts.StaticMesh); + PillarMeshs.Add(component); } - PillarMeshs.Add(component); } ); BeginAddInstance(FloorMeshs); - BeginAddInstance(SlopeMeshs); BeginAddInstance(WallMeshs); - BeginAddInstance(RoomRoofMeshs); - BeginAddInstance(AisleRoofMeshs); + BeginAddInstance(RoofMeshs); + BeginAddInstance(SlopeMeshs); BeginAddInstance(PillarMeshs); - // Add - mDungeonGeneratorCore->OnAddFloor([this](UStaticMesh* staticMesh, const FTransform& transform) + mDungeonGeneratorCore->OnAddFloor([this](const UStaticMesh* staticMesh, const FTransform& transform) { AddInstance(FloorMeshs, staticMesh, transform); OnCreateFloor.Broadcast(transform); } ); - mDungeonGeneratorCore->OnAddSlope([this](UStaticMesh* staticMesh, const FTransform& transform) + mDungeonGeneratorCore->OnAddSlope([this](const UStaticMesh* staticMesh, const FTransform& transform) { AddInstance(SlopeMeshs, staticMesh, transform); OnCreateSlope.Broadcast(transform); } ); - mDungeonGeneratorCore->OnAddWall([this](UStaticMesh* staticMesh, const FTransform& transform) + mDungeonGeneratorCore->OnAddWall([this](const UStaticMesh* staticMesh, const FTransform& transform) { AddInstance(WallMeshs, staticMesh, transform); OnCreateWall.Broadcast(transform); } ); - mDungeonGeneratorCore->OnAddRoomRoof([this](UStaticMesh* staticMesh, const FTransform& transform) - { - AddInstance(RoomRoofMeshs, staticMesh, transform); - OnCreateAisleRoof.Broadcast(transform); - } - ); - mDungeonGeneratorCore->OnAddAisleRoof([this](UStaticMesh* staticMesh, const FTransform& transform) + mDungeonGeneratorCore->OnAddRoof([this](const UStaticMesh* staticMesh, const FTransform& transform) { - AddInstance(AisleRoofMeshs, staticMesh, transform); - OnCreateAisleRoof.Broadcast(transform); + AddInstance(RoofMeshs, staticMesh, transform); + OnCreateRoomRoof.Broadcast(transform); } ); - mDungeonGeneratorCore->OnAddPillar([this](uint32_t gridHeight, UStaticMesh* staticMesh, const FTransform& transform) + mDungeonGeneratorCore->OnAddPillar([this](uint32_t gridHeight, const UStaticMesh* staticMesh, const FTransform& transform) { AddInstance(PillarMeshs, staticMesh, transform); OnCreatePillar.Broadcast(transform); } ); - // Reset - mDungeonGeneratorCore->OnResetDoor([this](AActor* actor, EDungeonRoomProps props) - { - OnResetDoor.Broadcast(actor, props); - } - ); - if (mDungeonGeneratorCore->Create(DungeonGenerateParameter)) { EndAddInstance(FloorMeshs); - EndAddInstance(SlopeMeshs); EndAddInstance(WallMeshs); - EndAddInstance(RoomRoofMeshs); - EndAddInstance(AisleRoofMeshs); + EndAddInstance(RoofMeshs); + EndAddInstance(SlopeMeshs); EndAddInstance(PillarMeshs); MovePlayerStart(); } @@ -278,8 +286,9 @@ void ADungeonGenerateActor::PreGenerateImplementation() } #if WITH_EDITOR - // ダンジョン生成した乱数を記録 + // Record dungeon-generated random numbers GeneratedRandomSeed = DungeonGenerateParameter->GetGeneratedRandomSeed(); + GeneratedDungeonCRC32 = CalculateCRC32(); #endif } @@ -320,71 +329,119 @@ void ADungeonGenerateActor::PostGenerateImplementation() void ADungeonGenerateActor::DestroyImplementation() { if (mDungeonGeneratorCore != nullptr) - { - mDungeonGeneratorCore->DestroySpawnedActors(); - mDungeonGeneratorCore->UnloadStreamLevels(); mDungeonGeneratorCore.reset(); - } FloorMeshs.Empty(); SlopeMeshs.Empty(); WallMeshs.Empty(); - RoomRoofMeshs.Empty(); - AisleRoofMeshs.Empty(); + RoofMeshs.Empty(); PillarMeshs.Empty(); DungeonMiniMapTextureLayer = nullptr; } /* -開始位置PlayerStart,終了位置ADungeonPlayerGoalの位置および -プレイヤー操作のキャラクターを移動します +The starting position PlayerStart, the ending position ADungeonPlayerGoal, +and the position of the Moves the player-controlled character. */ void ADungeonGenerateActor::MovePlayerStart() { - if (DungeonGenerateParameter && DungeonGenerateParameter->IsMovePlayerStartToStartingPoint()) + mDungeonGeneratorCore->MovePlayerStart(); +} + +void ADungeonGenerateActor::PreInitializeComponents() +{ + // Calling the parent class + Super::PreInitializeComponents(); + + if (AutoGenerateAtStart) { - if (mDungeonGeneratorCore) + if (ReplicatedServerDungeon) { - if (OnMovePlayerStart.IsBound()) + if (HasAuthority() == false) { - OnMovePlayerStart.Broadcast(mDungeonGeneratorCore->GetStartLocation()); + if (IsValid(DungeonGenerateParameter)) + { +#if WITH_EDITOR + DUNGEON_GENERATOR_LOG(TEXT("PreInitializeComponents: RandomSeed=%d, GeneratedRandomSeed=%d"), + DungeonGenerateParameter->GetRandomSeed(), + DungeonGenerateParameter->GetGeneratedRandomSeed() + ); +#endif + DungeonGenerateParameter->SetRandomSeed(DungeonGenerateParameter->GetGeneratedRandomSeed()); + } + else + { + DUNGEON_GENERATOR_ERROR(TEXT("DungeonGenerateParameter is not set")); + } } else { - mDungeonGeneratorCore->MovePlayerStart(); + DUNGEON_GENERATOR_LOG(TEXT("Has Authority")); } } + + PreGenerateImplementation(); + +#if WITH_EDITOR + if (IsValid(DungeonGenerateParameter)) + { + DUNGEON_GENERATOR_LOG(TEXT("PreInitializeComponents: RandomSeed=%d, GeneratedRandomSeed=%d"), + DungeonGenerateParameter->GetRandomSeed(), + DungeonGenerateParameter->GetGeneratedRandomSeed() + ); + } +#endif } } -void ADungeonGenerateActor::PreInitializeComponents() +void ADungeonGenerateActor::PostInitializeComponents() { - // 親クラスの呼び出し - Super::PreInitializeComponents(); + // Calling the parent class + Super::PostInitializeComponents(); - PreGenerateImplementation(); + if (mDungeonGeneratorCore) + { + mDungeonGeneratorCore->FlushLoadStreamLevels(); + } } void ADungeonGenerateActor::BeginPlay() { - // 親クラスの呼び出し + // Calling the parent class Super::BeginPlay(); - mPostGenerated = false; + if (AutoGenerateAtStart) + { + mPostGenerated = false; + } } void ADungeonGenerateActor::Tick(float DeltaSeconds) { + // Calling the parent class Super::Tick(DeltaSeconds); if (!mPostGenerated) { mPostGenerated = true; + PostGenerateImplementation(); + +#if WITH_EDITOR + if (IsValid(DungeonGenerateParameter)) + { + DUNGEON_GENERATOR_LOG(TEXT("PostGenerateImplementation: RandomSeed=%d, GeneratedRandomSeed=%d, CRC32=%d"), + DungeonGenerateParameter->GetRandomSeed(), + DungeonGenerateParameter->GetGeneratedRandomSeed(), + GeneratedRandomSeed, + CalculateCRC32() + ); + } +#endif } - // 部屋の生成通知 + // Room generation notification if (mDungeonGeneratorCore != nullptr) { mDungeonGeneratorCore->AsyncLoadStreamLevels(); @@ -399,10 +456,19 @@ void ADungeonGenerateActor::EndPlay(const EEndPlayReason::Type EndPlayReason) { DestroyImplementation(); - // 親クラスの呼び出し + // Calling the parent class Super::EndPlay(EndPlayReason); } +void ADungeonGenerateActor::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const +{ + // Calling the parent class + Super::GetLifetimeReplicatedProps(OutLifetimeProps); + + // Set variables to be replicated + //DOREPLIFETIME(ADungeonGenerateActor, GeneratedRandomSeed); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // BluePrint Useful Functions void ADungeonGenerateActor::GenerateDungeon() @@ -411,6 +477,19 @@ void ADungeonGenerateActor::GenerateDungeon() PostGenerateImplementation(); } +void ADungeonGenerateActor::GenerateDungeonWithSeed(const int32 generatedRandomSeed) +{ + if (IsValid(DungeonGenerateParameter)) + { + //DungeonGenerateParameter->SetGeneratedRandomSeed(generatedRandomSeed); + GenerateDungeon(); + } + else + { + DUNGEON_GENERATOR_ERROR(TEXT("DungeonGenerateParameter is not set")); + } +} + void ADungeonGenerateActor::DestroyDungeon() { DestroyImplementation(); @@ -479,6 +558,11 @@ UDungeonMiniMapTextureLayer* ADungeonGenerateActor::GetGeneratedMiniMapTextureLa return DungeonMiniMapTextureLayer; } +int32 ADungeonGenerateActor::CalculateCRC32() const noexcept +{ + return mDungeonGeneratorCore ? static_cast(mDungeonGeneratorCore->CalculateCRC32()) : 0; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // for debug #if WITH_EDITOR @@ -503,9 +587,10 @@ void ADungeonGenerateActor::DrawDebugInformation() const float gridSize = DungeonGenerateParameter->GetGridSize(); - // 部屋と接続情報のデバッグ情報を表示します + // Displays debugging information on room and connection information if (ShowRoomAisleInformation) { + // Debugging information by room generator->ForEach([this, gridSize](const std::shared_ptr& room) { UKismetSystemLibrary::DrawDebugBox( @@ -530,6 +615,7 @@ void ADungeonGenerateActor::DrawDebugInformation() } ); + // Debugging information per grid generator->EachAisle([this, gridSize](const dungeon::Aisle& edge) { UKismetSystemLibrary::DrawDebugLine( @@ -565,60 +651,80 @@ void ADungeonGenerateActor::DrawDebugInformation() ); } - // ボクセルグリッドのデバッグ情報を表示します + // Displays voxel grid debugging information if (ShowVoxelGridType) { - generator->GetVoxel()->Each([this, gridSize](const FIntVector& location, const dungeon::Grid& grid) - { - static const FColor colors[] = { - FColor::Blue, // Floor - FColor::Yellow, // Deck - FColor::Red, // Gate - FColor::Green, // Aisle - FColor::Magenta, // Slope - FColor::Cyan, // Atrium - FColor::Black, // Empty - FColor::Black, // OutOfBounds - }; - static constexpr size_t colorSize = sizeof(colors) / sizeof(colors[0]); - static_assert(colorSize == dungeon::Grid::TypeSize); - - //if (grid.mType == dungeon::Grid::Type::Aisle || grid.mType == dungeon::Grid::Type::Slope) - if (grid.GetType() != dungeon::Grid::Type::Empty && grid.GetType() != dungeon::Grid::Type::OutOfBounds) + const APawn* playerPawn = UGameplayStatics::GetPlayerPawn(this, 0); + if (IsValid(playerPawn)) + { + const FVector playerLocation = playerPawn->GetActorLocation(); + generator->GetVoxel()->Each([this, gridSize, &playerLocation](const FIntVector& location, const dungeon::Grid& grid) { - const double halfGridSize = gridSize / 2.; - const FVector halfGrid(halfGridSize, halfGridSize, halfGridSize); - const FVector center = dungeon::ToVector(location) * gridSize + halfGrid; - - const size_t index = static_cast(grid.GetType()); - UKismetSystemLibrary::DrawDebugBox( - GetWorld(), - center, - halfGrid * 0.95, - colors[index], - FRotator::ZeroRotator, - 0.f, - 10.f - ); + static const FColor colors[] = { + FColor::Blue, // Floor + FColor::Yellow, // Deck + FColor::Red, // Gate + FColor::Green, // Aisle + FColor::Magenta, // Slope + FColor::Cyan, // Atrium + FColor::Black, // Empty + FColor::Black, // OutOfBounds + }; + static constexpr size_t colorSize = sizeof(colors) / sizeof(colors[0]); + static_assert(colorSize == dungeon::Grid::TypeSize); + + //if (grid.mType == dungeon::Grid::Type::Aisle || grid.mType == dungeon::Grid::Type::Slope) + if (grid.GetType() != dungeon::Grid::Type::Empty && grid.GetType() != dungeon::Grid::Type::OutOfBounds) + { + const double halfGridSize = gridSize / 2.; + const FVector halfGrid(halfGridSize, halfGridSize, halfGridSize); + const FVector center = dungeon::ToVector(location) * gridSize + halfGrid; + + const size_t index = static_cast(grid.GetType()); + UKismetSystemLibrary::DrawDebugBox( + GetWorld(), + center, + halfGrid * 0.95, + colors[index], + FRotator::ZeroRotator, + 0.f, + 10.f + ); + + const FVector direction = dungeon::ToVector(grid.GetDirection().GetVector()); + UKismetSystemLibrary::DrawDebugArrow( + GetWorld(), + center, + center + direction * halfGridSize, + gridSize, + colors[index], + 0.f, + 5.f + ); + + if (FVector::Distance(center, playerLocation) < 10 * 100) + { + DrawDebugString( + GetWorld(), + center, + grid.GetNoMeshGenerationName(), + nullptr, + FColor::White, + 0.f, + true, + 1.f + ); + } + } - const FVector direction = dungeon::ToVector(grid.GetDirection().GetVector()); - UKismetSystemLibrary::DrawDebugArrow( - GetWorld(), - center, - center + direction * halfGridSize, - gridSize, - colors[index], - 0.f, - 5.f); + return true; } - - return true; - } - ); + ); + } } #if 0 - // ダンジョン全体の領域を可視化 + // Visualization of the entire dungeon area { const FBox& bounding = BoundingBox(); UKismetSystemLibrary::DrawDebugBox( @@ -633,7 +739,7 @@ void ADungeonGenerateActor::DrawDebugInformation() } #endif - // プレイヤーの位置のボクセルグリッドのデバッグ情報を表示します + // Displays debugging information for the voxel grid at the player's position if (ShowVoxelGridTypeAtPlayerLocation) { APawn* playerPawn = UGameplayStatics::GetPlayerPawn(this, 0); @@ -650,14 +756,25 @@ void ADungeonGenerateActor::DrawDebugInformation() //Direction GetDirection() const noexcept; output.Add(TEXT(" Props : ") + grid.GetPropsName()); - // 文字列を展開 FString message; for (const FString& line : output) { message.Append(line); message.Append(TEXT("\n")); } - DrawDebugString(playerPawn->GetWorld(), playerPawn->GetPawnViewLocation(), message, nullptr, FColor::White, 0, true, 1.f); + + message.Append(grid.GetNoMeshGenerationName() + TEXT("\n")); + + DrawDebugString( + playerPawn->GetWorld(), + playerPawn->GetPawnViewLocation(), + message, + nullptr, + FColor::White, + 0, + true, + 1.f + ); } } } diff --git a/Source/DungeonGenerator/Private/DungeonGenerateParameter.cpp b/Source/DungeonGenerator/Private/DungeonGenerateParameter.cpp index 94baf91..d91f0ab 100644 --- a/Source/DungeonGenerator/Private/DungeonGenerateParameter.cpp +++ b/Source/DungeonGenerator/Private/DungeonGenerateParameter.cpp @@ -5,16 +5,17 @@ All Rights Reserved. */ #include "DungeonGenerateParameter.h" -#include "DungeonRoomAsset.h" +#include "DungeonAisleMeshSetDatabase.h" +#include "DungeonRoomMeshSetDatabase.h" #include "PluginInfomation.h" +#include "SubLevel/DungeonRoomDatabase.h" #include "Core/Debug/BuildInfomation.h" #include "../Private/Core/Direction.h" #include "../Private/Core/Grid.h" #include "../Private/Core/Math/Random.h" #if WITH_EDITOR -#include -#include +#include #endif namespace @@ -48,7 +49,7 @@ FTransform FDungeonPartsTransform::CalculateWorldTransform(const FVector& positi return CalculateWorldTransform(position, direction.ToDegree()); } -FTransform FDungeonPartsTransform::CalculateWorldTransform(dungeon::Random& random, const FTransform& transform, const EDungeonPartsPlacementDirection placementDirection) const noexcept +FTransform FDungeonPartsTransform::CalculateWorldTransform(const std::shared_ptr& random, const FTransform& transform, const EDungeonPartsPlacementDirection placementDirection) const noexcept { if (static_cast(placementDirection) < static_cast(EDungeonPartsPlacementDirection::West)) { @@ -59,7 +60,7 @@ FTransform FDungeonPartsTransform::CalculateWorldTransform(dungeon::Random& rand } else if (placementDirection == EDungeonPartsPlacementDirection::RandomDirection) { - const FRotator rotator(0., static_cast(random.Get(4)) * 90., 0.); + const FRotator rotator(0., static_cast(random->Get(4)) * 90., 0.); FTransform result(transform); result.SetRotation(rotator.Quaternion()); return CalculateWorldTransform_(result, RelativeTransform); @@ -73,61 +74,61 @@ FTransform FDungeonPartsTransform::CalculateWorldTransform(dungeon::Random& rand return transform; } -FTransform FDungeonPartsTransform::CalculateWorldTransform(dungeon::Random& random, const FVector& position, const FRotator& rotator, const EDungeonPartsPlacementDirection placementDirection) const noexcept +FTransform FDungeonPartsTransform::CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const FRotator& rotator, const EDungeonPartsPlacementDirection placementDirection) const noexcept { const FTransform transform(rotator, position); return CalculateWorldTransform(random, transform, placementDirection); } -FTransform FDungeonPartsTransform::CalculateWorldTransform(dungeon::Random& random, const FVector& position, const float yaw, const EDungeonPartsPlacementDirection placementDirection) const noexcept +FTransform FDungeonPartsTransform::CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const float yaw, const EDungeonPartsPlacementDirection placementDirection) const noexcept { const FRotator rotator(0, yaw, 0); return CalculateWorldTransform(random, position, rotator, placementDirection); } -FTransform FDungeonPartsTransform::CalculateWorldTransform(dungeon::Random& random, const FVector& position, const dungeon::Direction& direction, const EDungeonPartsPlacementDirection placementDirection) const noexcept +FTransform FDungeonPartsTransform::CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const dungeon::Direction& direction, const EDungeonPartsPlacementDirection placementDirection) const noexcept { return CalculateWorldTransform(random, position, direction.ToDegree(), placementDirection); } //////////////////////////////////////////////////////////////////////////////////////////////////// -FTransform FDungeonActorPartsWithDirection::CalculateWorldTransform(dungeon::Random& random, const FTransform& transform) const noexcept +FTransform FDungeonActorPartsWithDirection::CalculateWorldTransform(const std::shared_ptr& random, const FTransform& transform) const noexcept { return FDungeonPartsTransform::CalculateWorldTransform(random, transform, PlacementDirection); } -FTransform FDungeonActorPartsWithDirection::CalculateWorldTransform(dungeon::Random& random, const FVector& position, const FRotator& rotator) const noexcept +FTransform FDungeonActorPartsWithDirection::CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const FRotator& rotator) const noexcept { return FDungeonPartsTransform::CalculateWorldTransform(random, position, rotator, PlacementDirection); } -FTransform FDungeonActorPartsWithDirection::CalculateWorldTransform(dungeon::Random& random, const FVector& position, const float yaw) const noexcept +FTransform FDungeonActorPartsWithDirection::CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const float yaw) const noexcept { return FDungeonPartsTransform::CalculateWorldTransform(random, position, yaw, PlacementDirection); } -FTransform FDungeonActorPartsWithDirection::CalculateWorldTransform(dungeon::Random& random, const FVector& position, const dungeon::Direction& direction) const noexcept +FTransform FDungeonActorPartsWithDirection::CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const dungeon::Direction& direction) const noexcept { return FDungeonPartsTransform::CalculateWorldTransform(random, position, direction, PlacementDirection); } //////////////////////////////////////////////////////////////////////////////////////////////////// -FTransform FDungeonMeshPartsWithDirection::CalculateWorldTransform(dungeon::Random& random, const FTransform& transform) const noexcept +FTransform FDungeonMeshPartsWithDirection::CalculateWorldTransform(const std::shared_ptr& random, const FTransform& transform) const noexcept { return FDungeonPartsTransform::CalculateWorldTransform(random, transform, PlacementDirection); } -FTransform FDungeonMeshPartsWithDirection::CalculateWorldTransform(dungeon::Random& random, const FVector& position, const FRotator& rotator) const noexcept +FTransform FDungeonMeshPartsWithDirection::CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const FRotator& rotator) const noexcept { return FDungeonPartsTransform::CalculateWorldTransform(random, position, rotator, PlacementDirection); } -FTransform FDungeonMeshPartsWithDirection::CalculateWorldTransform(dungeon::Random& random, const FVector& position, const float yaw) const noexcept +FTransform FDungeonMeshPartsWithDirection::CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const float yaw) const noexcept { return FDungeonPartsTransform::CalculateWorldTransform(random, position, yaw, PlacementDirection); } -FTransform FDungeonMeshPartsWithDirection::CalculateWorldTransform(dungeon::Random& random, const FVector& position, const dungeon::Direction& direction) const noexcept +FTransform FDungeonMeshPartsWithDirection::CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const dungeon::Direction& direction) const noexcept { return FDungeonPartsTransform::CalculateWorldTransform(random, position, direction, PlacementDirection); } @@ -138,202 +139,11 @@ UDungeonGenerateParameter::UDungeonGenerateParameter(const FObjectInitializer& O { } -int32 UDungeonGenerateParameter::SelectDungeonMeshPartsIndex(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random, const int32 size, const EDungeonPartsSelectionMethod partsSelectionMethod) const -{ - switch (partsSelectionMethod) - { - case EDungeonPartsSelectionMethod::GridIndex: - return gridIndex % size; - - case EDungeonPartsSelectionMethod::Direction: - return grid.GetDirection().Get() % size; - - case EDungeonPartsSelectionMethod::Random: - return random.Get(size); - - default: - return gridIndex % size; - } -} - -// aka: SelectParts, SelectRandomActorParts -FDungeonActorParts* UDungeonGenerateParameter::SelectActorParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random, const TArray& parts, const EDungeonPartsSelectionMethod partsSelectionMethod) const -{ - const int32 size = parts.Num(); - if (size <= 0) - return nullptr; - - const int32 index = SelectDungeonMeshPartsIndex(gridIndex, grid, random, size, partsSelectionMethod); - FDungeonActorParts* actorParts = const_cast(&parts[index]); - return IsValid(actorParts->ActorClass) ? actorParts : nullptr; -} - -// aka: SelectParts, SelectActorParts -FDungeonRandomActorParts* UDungeonGenerateParameter::SelectRandomActorParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random, const TArray& parts, const EDungeonPartsSelectionMethod partsSelectionMethod) const -{ - const int32 size = parts.Num(); - if (size <= 0) - return nullptr; - - const int32 index = SelectDungeonMeshPartsIndex(gridIndex, grid, random, size, partsSelectionMethod); - FDungeonRandomActorParts* actorParts = const_cast(&parts[index]); - if (!IsValid(actorParts->ActorClass)) - return nullptr; - - const float value = random.Get(); - if (value > actorParts->Frequency) - return nullptr; - - return actorParts; -} - -int32 UDungeonGenerateParameter::GetRandomSeed() const -{ - return RandomSeed; -} - -int32 UDungeonGenerateParameter::GetGeneratedRandomSeed() const -{ - return GeneratedRandomSeed; -} - -void UDungeonGenerateParameter::SetGeneratedRandomSeed(const int32 generatedRandomSeed) -{ - GeneratedRandomSeed = generatedRandomSeed; -} - -int32 UDungeonGenerateParameter::GetNumberOfCandidateRooms() const -{ - return NumberOfCandidateRooms; -} - -const FInt32Interval& UDungeonGenerateParameter::GetRoomWidth() const noexcept -{ - return RoomWidth; -} - -const FInt32Interval& UDungeonGenerateParameter::GetRoomDepth() const noexcept -{ - return RoomDepth; -} - -const FInt32Interval& UDungeonGenerateParameter::GetRoomHeight() const noexcept -{ - return RoomHeight; -} - -int32 UDungeonGenerateParameter::GetHorizontalRoomMargin() const noexcept -{ - return RoomMargin; -} - -int32 UDungeonGenerateParameter::GetVerticalRoomMargin() const noexcept -{ - return VerticalRoomMargin; -} - -float UDungeonGenerateParameter::GetGridSize() const -{ - return GridSize; -} - -const FDungeonMeshPartsWithDirection* UDungeonGenerateParameter::SelectFloorParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const -{ - return SelectParts(gridIndex, grid, random, FloorParts, FloorPartsSelectionMethod); -} - -void UDungeonGenerateParameter::EachFloorParts(std::function func) const -{ - EachParts(FloorParts, func); -} - -const FDungeonMeshParts* UDungeonGenerateParameter::SelectSlopeParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const -{ - return SelectParts(gridIndex, grid, random, SlopeParts, SloopPartsSelectionMethod); -} - -void UDungeonGenerateParameter::EachSlopeParts(std::function func) const -{ - EachParts(SlopeParts, func); -} - -const FDungeonMeshParts* UDungeonGenerateParameter::SelectWallParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const -{ - return SelectParts(gridIndex, grid, random, WallParts, WallPartsSelectionMethod); -} - -void UDungeonGenerateParameter::EachWallParts(std::function func) const -{ - EachParts(WallParts, func); -} - -const FDungeonMeshPartsWithDirection* UDungeonGenerateParameter::SelectRoomRoofParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const -{ - return SelectParts(gridIndex, grid, random, RoomRoofParts, RoomRoofPartsSelectionMethod); -} - -void UDungeonGenerateParameter::EachRoomRoofParts(std::function func) const -{ - EachParts(RoomRoofParts, func); -} - -const FDungeonMeshPartsWithDirection* UDungeonGenerateParameter::SelectAisleRoofParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const -{ - return SelectParts(gridIndex, grid, random, AisleRoofParts, AisleRoofPartsSelectionMethod); -} - -void UDungeonGenerateParameter::EachAisleRoofParts(std::function func) const -{ - EachParts(AisleRoofParts, func); -} - -const FDungeonMeshParts* UDungeonGenerateParameter::SelectPillarParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const -{ - return SelectParts(gridIndex, grid, random, PillarParts, PillarPartsSelectionMethod); -} - -void UDungeonGenerateParameter::EachPillarParts(std::function func) const -{ - EachParts(PillarParts, func); -} - -const FDungeonRandomActorParts* UDungeonGenerateParameter::SelectTorchParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const -{ - return SelectRandomActorParts(gridIndex, grid, random, TorchParts, TorchPartsSelectionMethod); -} - -#if 0 -const FDungeonActorParts* UDungeonGenerateParameter::SelectChandelierParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const -{ - return SelectRandomActorParts(gridIndex, grid, random, ChandelierParts, ChandelierPartsSelectionMethod); -} -#endif - -const FDungeonDoorActorParts* UDungeonGenerateParameter::SelectDoorParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const -{ - return SelectParts(gridIndex, grid, random, DoorParts, DoorPartsSelectionMethod); -} - -bool UDungeonGenerateParameter::IsMovePlayerStartToStartingPoint() const noexcept -{ - return MovePlayerStartToStartingPoint; -} - -const FDungeonActorPartsWithDirection& UDungeonGenerateParameter::GetStartParts() const -{ - return StartParts; -} - -const FDungeonActorPartsWithDirection& UDungeonGenerateParameter::GetGoalParts() const -{ - return GoalParts; -} - void UDungeonGenerateParameter::EachDungeonRoomLocator(std::function func) const { - if (IsValid(DungeonRoomAsset)) + if (IsValid(DungeonRoomDatabase)) { - for (const FDungeonRoomLocator& dungeonRoomLocator : DungeonRoomAsset->GetDungeonRoomLocator()) + for (const FDungeonRoomLocator& dungeonRoomLocator : DungeonRoomDatabase->GetDungeonRoomLocator()) { if (dungeonRoomLocator.GetLevelPath().IsValid()) { @@ -343,11 +153,6 @@ void UDungeonGenerateParameter::EachDungeonRoomLocator(std::function DungeonRoomAsset; + // TArray DungeonRoomDatabase; jsonString += TEXT("Version:\"DUNGENERATOR_PLUGIN_VERSION_NAME\",\n"); - jsonString += TEXT("Beta:\"DUNGENERATOR_PLUGIN_BETA_VERSION\",\n"); jsonString += TEXT("Tag:\"JENKINS_JOB_TAG\",\n"); jsonString += TEXT("UUID:\"JENKINS_UUID\",\n"); jsonString += TEXT("License:\"JENKINS_LICENSE\"\n"); @@ -447,3 +251,163 @@ FString UDungeonGenerateParameter::GetJsonDefaultDirectory() const return FString(FPaths::ProjectSavedDir() / TEXT("DungeonGenerator")); } #endif + + + +const FDungeonMeshPartsWithDirection* UDungeonGenerateParameter::SelectFloorParts(const UDungeonMeshSetDatabase* dungeonPartsDatabase, const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const +{ + if (IsValid(dungeonPartsDatabase)) + { + if (const FDungeonRoomMeshSet* parts = dungeonPartsDatabase->SelectImplement(random)) + { + if (const FDungeonMeshPartsWithDirection* result = parts->SelectFloorParts(gridIndex, grid, random)) + return result; + } + } + return nullptr; +} + +const FDungeonMeshParts* UDungeonGenerateParameter::SelectWallParts(const UDungeonMeshSetDatabase* dungeonPartsDatabase, const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const +{ + if (IsValid(dungeonPartsDatabase)) + { + if (const FDungeonRoomMeshSet* parts = dungeonPartsDatabase->SelectImplement(random)) + { + if (const FDungeonMeshParts* result = parts->SelectWallParts(gridIndex, grid, random)) + return result; + } + } + return nullptr; +} + +const FDungeonMeshPartsWithDirection* UDungeonGenerateParameter::SelectRoofParts(const UDungeonMeshSetDatabase* dungeonPartsDatabase, const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const +{ + if (IsValid(dungeonPartsDatabase)) + { + if (const FDungeonRoomMeshSet* parts = dungeonPartsDatabase->SelectImplement(random)) + { + if (const FDungeonMeshPartsWithDirection* result = parts->SelectRoofParts(gridIndex, grid, random)) + return result; + } + } + return nullptr; +} + +const FDungeonMeshParts* UDungeonGenerateParameter::SelectSlopeParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const +{ + if (IsValid(DungeonAislePartsDatabase)) + { + if (const FDungeonAisleMeshSet* parts = DungeonAislePartsDatabase->Select(random)) + { + if (const FDungeonMeshParts* result = parts->SelectSlopeParts(gridIndex, grid, random)) + return result; + } + } + return nullptr; +} + +const FDungeonMeshParts* UDungeonGenerateParameter::SelectPillarParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const +{ + return FDungeonRoomMeshSet::SelectParts(gridIndex, grid, random, PillarParts, PillarPartsSelectionMethod); +} + +const FDungeonRandomActorParts* UDungeonGenerateParameter::SelectTorchParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const +{ + return FDungeonRoomMeshSet::SelectRandomActorParts(gridIndex, grid, random, TorchParts, TorchPartsSelectionMethod); +} + +const FDungeonDoorActorParts* UDungeonGenerateParameter::SelectDoorParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const +{ + return FDungeonRoomMeshSet::SelectParts(gridIndex, grid, random, DoorParts, DoorPartsSelectionMethod); +} + + + +void UDungeonGenerateParameter::EachRoomFloorParts(std::function func) const +{ + if (IsValid(DungeonRoomPartsDatabase)) + { + DungeonRoomPartsDatabase->Each([&func](const FDungeonRoomMeshSet& parts) + { + parts.EachFloorParts(func); + } + ); + } +} + +void UDungeonGenerateParameter::EachAisleFloorParts(std::function func) const +{ + if (IsValid(DungeonAislePartsDatabase)) + { + DungeonAislePartsDatabase->Each([&func](const FDungeonRoomMeshSet& parts) + { + parts.EachFloorParts(func); + } + ); + } +} + +void UDungeonGenerateParameter::EachRoomWallParts(std::function func) const +{ + if (IsValid(DungeonRoomPartsDatabase)) + { + DungeonRoomPartsDatabase->Each([&func](const FDungeonRoomMeshSet& parts) + { + parts.EachWallParts(func); + } + ); + } +} + +void UDungeonGenerateParameter::EachAisleWallParts(std::function func) const +{ + if (IsValid(DungeonAislePartsDatabase)) + { + DungeonAislePartsDatabase->Each([&func](const FDungeonRoomMeshSet& parts) + { + parts.EachWallParts(func); + } + ); + } +} + +void UDungeonGenerateParameter::EachSlopeParts(std::function func) const +{ + if (IsValid(DungeonAislePartsDatabase)) + { + DungeonAislePartsDatabase->Each([&func](const FDungeonAisleMeshSet& parts) + { + parts.EachSlopeParts(func); + } + ); + } +} + +void UDungeonGenerateParameter::EachPillarParts(std::function func) const +{ + FDungeonRoomMeshSet::EachParts(PillarParts, func); +} + +void UDungeonGenerateParameter::EachRoomRoofParts(std::function func) const +{ + if (IsValid(DungeonRoomPartsDatabase)) + { + DungeonRoomPartsDatabase->Each([&func](const FDungeonRoomMeshSet& parts) + { + parts.EachRoofParts(func); + } + ); + } +} + +void UDungeonGenerateParameter::EachAisleRoofParts(std::function func) const +{ + if (IsValid(DungeonAislePartsDatabase)) + { + DungeonAislePartsDatabase->Each([&func](const FDungeonRoomMeshSet& parts) + { + parts.EachRoofParts(func); + } + ); + } +} diff --git a/Source/DungeonGenerator/Private/DungeonGeneratorCore.cpp b/Source/DungeonGenerator/Private/DungeonGeneratorCore.cpp index 8955d4f..b95fda1 100644 --- a/Source/DungeonGenerator/Private/DungeonGeneratorCore.cpp +++ b/Source/DungeonGenerator/Private/DungeonGeneratorCore.cpp @@ -6,31 +6,41 @@ All Rights Reserved. #include "DungeonGeneratorCore.h" #include "DungeonGenerateParameter.h" -#include "DungeonDoor.h" -#include "DungeonLevelStreamingDynamic.h" -#include "DungeonRoomProps.h" -#include "DungeonRoomSensor.h" #include "Core/Debug/Debug.h" #include "Core/Debug/BuildInfomation.h" #include "Core/Identifier.h" #include "Core/Generator.h" #include "Core/Voxel.h" +#include "Core/Debug/Stopwatch.h" #include "Core/Math/Math.h" +#include "Core/Math/Random.h" +#include "Decorator/DungeonInteriorUnplaceableBounds.h" +#include "Mission/DungeonRoomProps.h" +#include "SubActor/DungeonRoomSensor.h" +#include "SubActor/DungeonDoor.h" +#include "SubLevel/DungeonLevelStreamingDynamic.h" +#include "SubLevel/DungeonRoomLocator.h" +#include "PluginInfomation.h" #include +#include #include #include +#include #include #include #include #include +#include #include #include #include +#include #include #if WITH_EDITOR // UnrealEd +#include #include #include #endif @@ -55,6 +65,7 @@ const FName& CDungeonGeneratorCore::GetDungeonGeneratorTag() CDungeonGeneratorCore::CDungeonGeneratorCore(const TWeakObjectPtr& world) : mWorld(world) + , mInteriorUnplaceableBounds(std::make_shared()) { AddStaticMeshEvent addStaticMeshEvent = [this](UStaticMesh* staticMesh, const FTransform& transform) { @@ -68,13 +79,25 @@ CDungeonGeneratorCore::CDungeonGeneratorCore(const TWeakObjectPtr& world mOnAddFloor = addStaticMeshEvent; mOnAddSlope = addStaticMeshEvent; mOnAddWall = addStaticMeshEvent; - mOnAddRoomRoof = addStaticMeshEvent; - mOnAddAisleRoof = addStaticMeshEvent; + mOnAddRoof = addStaticMeshEvent; mOnResetPillar = addPillarStaticMeshEvent; } +CDungeonGeneratorCore::~CDungeonGeneratorCore() +{ + DestroySpawnedActors(); + UnloadStreamLevels(); +} + bool CDungeonGeneratorCore::Create(const UDungeonGenerateParameter* parameter) { + DUNGEON_GENERATOR_LOG(TEXT("version '%s', license '%s', uuid '%s', commit '%s'"), + TEXT(DUNGENERATOR_PLUGIN_VERSION_NAME), + TEXT(JENKINS_LICENSE), + TEXT(JENKINS_UUID), + TEXT(JENKINS_GIT_COMMIT) + ); + // Conversion from UDungeonGenerateParameter to dungeon::GenerateParameter if (!IsValid(parameter)) { @@ -86,9 +109,8 @@ bool CDungeonGeneratorCore::Create(const UDungeonGenerateParameter* parameter) int32 randomSeed = parameter->GetRandomSeed(); if (parameter->GetRandomSeed() == 0) randomSeed = static_cast(time(nullptr)); - generateParameter.mRandom.SetSeed(randomSeed); + generateParameter.mRandom->SetSeed(randomSeed); const_cast(parameter)->SetGeneratedRandomSeed(randomSeed); - generateParameter.mNumberOfCandidateFloors = parameter->NumberOfCandidateFloors; generateParameter.mNumberOfCandidateRooms = parameter->NumberOfCandidateRooms; generateParameter.mMinRoomWidth = parameter->RoomWidth.Min; generateParameter.mMaxRoomWidth = parameter->RoomWidth.Max; @@ -96,19 +118,61 @@ bool CDungeonGeneratorCore::Create(const UDungeonGenerateParameter* parameter) generateParameter.mMaxRoomDepth = parameter->RoomDepth.Max; generateParameter.mMinRoomHeight = parameter->RoomHeight.Min; generateParameter.mMaxRoomHeight = parameter->RoomHeight.Max; - generateParameter.mHorizontalRoomMargin = parameter->RoomMargin; - generateParameter.mVerticalRoomMargin = parameter->VerticalRoomMargin; + // TODO:UDungeonGenerateParameter + if (parameter->MergeRooms) + { + generateParameter.mHorizontalRoomMargin = 0; + generateParameter.mVerticalRoomMargin = 0; + generateParameter.mNumberOfCandidateFloors = 1; + } + else if (parameter->RoomMargin <= 1) + { + generateParameter.mHorizontalRoomMargin = parameter->RoomMargin; + generateParameter.mVerticalRoomMargin = 0; + generateParameter.mNumberOfCandidateFloors = 1; + } + else + { + generateParameter.mHorizontalRoomMargin = parameter->RoomMargin; + generateParameter.mVerticalRoomMargin = parameter->VerticalRoomMargin; + generateParameter.mNumberOfCandidateFloors = static_cast(std::min( + static_cast(generateParameter.mMaxRoomHeight + 2), + static_cast(std::numeric_limits::max()) + )); + } + if (parameter->GetStartRoom().IsValid()) + { + generateParameter.mStartRoomSize = parameter->GetStartRoom().GetSize(); + } + if (parameter->GetGoalRoom().IsValid()) + { + generateParameter.mGoalRoomSize = parameter->GetStartRoom().GetSize(); + } mParameter = parameter; - mAisleInteriorDecorator.Initialize(mParameter->DungeonInteriorAsset); - mSlopeInteriorDecorator.Initialize(mParameter->DungeonInteriorAsset); - mRoomInteriorDecorator.Initialize(mParameter->DungeonInteriorAsset); + mAisleInteriorDecorator.Initialize(mParameter->DungeonInteriorDatabase); + mSlopeInteriorDecorator.Initialize(mParameter->DungeonInteriorDatabase); + mRoomInteriorDecorator.Initialize(mParameter->DungeonInteriorDatabase); mGenerator = std::make_shared(); mGenerator->OnQueryParts([this, parameter](const std::shared_ptr& room) { - CreateImpl_AddRoomAsset(parameter, room); + CreateImplement_AddRoomAsset(parameter, room); }); + if (parameter->GetStartRoom().IsValid()) + { + mGenerator->OnStartParts([this, parameter](const std::shared_ptr& room) + { + CreateImplement_AddRoomAsset(parameter->GetStartRoom(), room, parameter->GetGridSize()); + }); + } + if (parameter->GetGoalRoom().IsValid()) + { + mGenerator->OnGoalParts([this, parameter](const std::shared_ptr& room) + { + CreateImplement_AddRoomAsset(parameter->GetGoalRoom(), room, parameter->GetGridSize()); + }); + } mGenerator->Generate(generateParameter); // デバッグ情報を出力 @@ -139,22 +203,25 @@ bool CDungeonGeneratorCore::Create(const UDungeonGenerateParameter* parameter) } else { - AddTerrain(); - AddObject(); + CreateImplement_AddTerrain(); DUNGEON_GENERATOR_LOG(TEXT("Done.")); + return true; } } -bool CDungeonGeneratorCore::CreateImpl_AddRoomAsset(const UDungeonGenerateParameter* parameter, const std::shared_ptr& room) +bool CDungeonGeneratorCore::CreateImplement_AddRoomAsset(const UDungeonGenerateParameter* parameter, const std::shared_ptr& room) { parameter->EachDungeonRoomLocator([this, parameter, &room](const FDungeonRoomLocator& dungeonRoomLocator) { - if (dungeonRoomLocator.GetDungeonParts() != EDungeonRoomParts::Any) + if (room->GetParts() == dungeon::Room::Parts::Start || room->GetParts() == dungeon::Room::Parts::Goal) + return; + + if (dungeonRoomLocator.GetDungeonParts() != EDungeonRoomLocatorParts::Any) { // Match the order of EDungeonRoomParts and dungeon::Room::Parts - if (static_cast(dungeonRoomLocator.GetDungeonParts()) != room->GetParts()) + if (Equal(dungeonRoomLocator.GetDungeonParts(), static_cast(room->GetParts())) == false) return; } @@ -199,373 +266,200 @@ bool CDungeonGeneratorCore::CreateImpl_AddRoomAsset(const UDungeonGenerateParame if (!IsStreamLevelRequested(dungeonRoomLocator.GetLevelPath())) { + // TODO CreateImplement_AddRoomAssetと共通化できるか検討して下さい + const float gridSize = parameter->GetGridSize(); + + check(room->GetWidth() >= dungeonRoomLocator.GetWidth()); + check(room->GetDepth() >= dungeonRoomLocator.GetDepth()); + check(room->GetHeight() >= dungeonRoomLocator.GetHeight()); room->SetDataSize(dungeonRoomLocator.GetWidth(), dungeonRoomLocator.GetDepth(), dungeonRoomLocator.GetHeight()); FIntVector min, max; room->GetDataBounds(min, max); room->SetNoMeshGeneration(!dungeonRoomLocator.IsGenerateRoofMesh(), !dungeonRoomLocator.IsGenerateFloorMesh()); + RequestStreamLevel(dungeonRoomLocator.GetLevelPath(), FVector(min) * gridSize); - const float halfGridSize = parameter->GetGridSize() * 0.5f; - const FVector halfOffset(halfGridSize, halfGridSize, 0); - - RequestStreamLevel(dungeonRoomLocator.GetLevelPath(), FVector(min) * parameter->GetGridSize() + halfOffset); + // add un-place able bounds + { + const FBox unplacatableBounds(FVector(min) * gridSize, FVector(max) * gridSize); + mInteriorUnplaceableBounds->Add(unplacatableBounds); + } } }); return true; } -void CDungeonGeneratorCore::AddTerrain() +bool CDungeonGeneratorCore::CreateImplement_AddRoomAsset(const FDungeonRoomRegister& roomRegister, const std::shared_ptr& room, const float gridSize) { - if (mGenerator == nullptr) + if (room == nullptr) + return false; + + check(room->GetWidth() == roomRegister.GetWidth()); + check(room->GetDepth() == roomRegister.GetDepth()); + check(room->GetHeight() == roomRegister.GetHeight()); + room->SetDataSize(roomRegister.GetWidth(), roomRegister.GetDepth(), roomRegister.GetHeight()); + + FIntVector min, max; + room->GetDataBounds(min, max); + room->SetNoMeshGeneration(!roomRegister.IsGenerateRoofMesh(), !roomRegister.IsGenerateFloorMesh()); + RequestStreamLevel(roomRegister.GetLevelPath(), FVector(min) * gridSize); + + // add un-place able bounds { - DUNGEON_GENERATOR_ERROR(TEXT("CDungeonGeneratorCore::Createを呼び出してください")); - return; + const FBox unplacatableBounds(FVector(min) * gridSize, FVector(max) * gridSize); + mInteriorUnplaceableBounds->Add(unplacatableBounds); } + return true; +} + +void CDungeonGeneratorCore::CreateImplement_AddTerrain() +{ const UDungeonGenerateParameter* parameter = mParameter.Get(); if (!IsValid(parameter)) { - DUNGEON_GENERATOR_ERROR(TEXT("DungeonGenerateParameterを設定してください")); + DUNGEON_GENERATOR_ERROR(TEXT("DungeonGenerateParameter is not set. Please set it.")); return; } - mAisleInteriorDecorator.ClearDecorationLocation(); - mSlopeInteriorDecorator.ClearDecorationLocation(); - mRoomInteriorDecorator.ClearDecorationLocation(); + mAisleInteriorDecorator.ClearDecorationLocation(mInteriorUnplaceableBounds); + mSlopeInteriorDecorator.ClearDecorationLocation(mInteriorUnplaceableBounds); + mRoomInteriorDecorator.ClearDecorationLocation(mInteriorUnplaceableBounds); - mGenerator->GetVoxel()->Each([this, parameter](const FIntVector& location, const dungeon::Grid& grid) - { - const size_t gridIndex = mGenerator->GetVoxel()->Index(location); - const float gridSize = parameter->GetGridSize(); - const float halfGridSize = gridSize * 0.5f; - const FVector halfOffset(halfGridSize, halfGridSize, 0); - const FVector position = parameter->ToWorld(location); - const FVector centerPosition = position + halfOffset; - - if (mOnAddSlope && grid.CanBuildSlope()) - { - /* - スロープのメッシュを生成 - メッシュは原点からX軸とY軸方向に伸びており、面はZ軸が上面になっています。 - */ - if (const FDungeonMeshParts* parts = parameter->SelectSlopeParts(gridIndex, grid, dungeon::Random::Instance())) - { - mOnAddSlope(parts->StaticMesh, parts->CalculateWorldTransform(centerPosition, grid.GetDirection())); - } - } - else if (mOnAddFloor && grid.CanBuildFloor(mGenerator->GetVoxel()->Get(location.X, location.Y, location.Z - 1), true)) + std::vector spawnedTorchBounds; + { + dungeon::Stopwatch stopwatch; + mGenerator->GetVoxel()->Each([this, parameter, &spawnedTorchBounds](const FIntVector& location, const dungeon::Grid& grid) { - /* - 床のメッシュを生成 - メッシュは原点からX軸とY軸方向に伸びており、面はZ軸が上面になっています。 - */ - if (const FDungeonMeshParts* parts = parameter->SelectFloorParts(gridIndex, grid, dungeon::Random::Instance())) - { - mOnAddFloor(parts->StaticMesh, parts->CalculateWorldTransform(centerPosition, grid.GetDirection())); - } - } + // Generate floor and slope meshes + CreateImplement_AddFloorAndSlope(parameter, location); - /* - 壁のメッシュを生成 - メッシュは原点からY軸とZ軸方向に伸びており、面はX軸が正面(北側の壁)になっています。 - */ - if (mOnAddWall) - { - if (const FDungeonMeshParts* parts = parameter->SelectWallParts(gridIndex, grid, dungeon::Random::Instance())) - { - if (grid.CanBuildWall(mGenerator->GetVoxel()->Get(location.X, location.Y - 1, location.Z), dungeon::Direction::North, parameter->MergeRooms)) - { - // 北側の壁 - FVector wallPosition = centerPosition; - wallPosition.Y -= halfGridSize; - mOnAddWall(parts->StaticMesh, parts->CalculateWorldTransform(wallPosition, 0.f)); - - if (grid.IsKindOfAisleType()) - mAisleInteriorDecorator.AddDecorationLocation(wallPosition, 0.f); - else if (grid.GetType() == dungeon::Grid::Type::Deck) - mRoomInteriorDecorator.AddDecorationLocation(wallPosition, 0.f); - } - if (grid.CanBuildWall(mGenerator->GetVoxel()->Get(location.X, location.Y + 1, location.Z), dungeon::Direction::South, parameter->MergeRooms)) - { - // 南側の壁 - FVector wallPosition = centerPosition; - wallPosition.Y += halfGridSize; - mOnAddWall(parts->StaticMesh, parts->CalculateWorldTransform(wallPosition, 180.f)); - - if (grid.IsKindOfAisleType()) - mAisleInteriorDecorator.AddDecorationLocation(wallPosition, 180.f); - else if (grid.GetType() == dungeon::Grid::Type::Deck) - mRoomInteriorDecorator.AddDecorationLocation(wallPosition, 180.f); - } - if (grid.CanBuildWall(mGenerator->GetVoxel()->Get(location.X + 1, location.Y, location.Z), dungeon::Direction::East, parameter->MergeRooms)) - { - // 東側の壁 - FVector wallPosition = centerPosition; - wallPosition.X += halfGridSize; - mOnAddWall(parts->StaticMesh, parts->CalculateWorldTransform(wallPosition, 90.f)); - - if (grid.IsKindOfAisleType()) - mAisleInteriorDecorator.AddDecorationLocation(wallPosition, 90.f); - else if (grid.GetType() == dungeon::Grid::Type::Deck) - mRoomInteriorDecorator.AddDecorationLocation(wallPosition, 90.f); - } - if (grid.CanBuildWall(mGenerator->GetVoxel()->Get(location.X - 1, location.Y, location.Z), dungeon::Direction::West, parameter->MergeRooms)) - { - // 西側の壁 - FVector wallPosition = centerPosition; - wallPosition.X -= halfGridSize; - mOnAddWall(parts->StaticMesh, parts->CalculateWorldTransform(wallPosition, -90.f)); - - if (grid.IsKindOfAisleType()) - mAisleInteriorDecorator.AddDecorationLocation(wallPosition, -90.f); - else if (grid.GetType() == dungeon::Grid::Type::Deck) - mRoomInteriorDecorator.AddDecorationLocation(wallPosition, -90.f); - } - } - } + // Generate wall mesh + CreateImplement_AddWall(parameter, location); - /* - 柱のメッシュを生成 - メッシュは原点からY軸とZ軸方向に伸びており、面はX軸が正面になっています。 - */ - if (mOnResetPillar) - { - FVector wallVector(0.f); - uint8_t wallCount = 0; - bool onFloor = false; - uint32_t pillarGridHeight = 1; - for (int_fast8_t dy = -1; dy <= 0; ++dy) - { - for (int_fast8_t dx = -1; dx <= 0; ++dx) - { - // 壁の数を調べます - const auto& result = mGenerator->GetVoxel()->Get(location.X + dx, location.Y + dy, location.Z); - if (grid.CanBuildPillar(result)) - { - wallVector += FVector(static_cast(dx) + 0.5f, static_cast(dy) + 0.5f, 0.f); - ++wallCount; - } + // Generate mesh for pillars and torches + CreateImplement_AddPillarAndTorch(spawnedTorchBounds, parameter, location); - // 床を調べます - const auto& baseFloorGrid = mGenerator->GetVoxel()->Get(location.X + dx, location.Y + dy, location.Z); - const auto& underFloorGrid = mGenerator->GetVoxel()->Get(location.X + dx, location.Y + dy, location.Z - 1); - if (baseFloorGrid.CanBuildSlope() || baseFloorGrid.CanBuildFloor(underFloorGrid, false)) - { - onFloor = true; - - // 天井の高さを調べます - uint32_t gridHeight = 1; - while (true) - { - const auto& roofGrid = mGenerator->GetVoxel()->Get(location.X + dx, location.Y + dy, location.Z + gridHeight); - if (roofGrid.GetType() == dungeon::Grid::Type::OutOfBounds) - break; - if (!grid.CanBuildRoof(roofGrid, false)) - break; - ++gridHeight; - } - if (pillarGridHeight < gridHeight) - pillarGridHeight = gridHeight; - } - } - } - if (onFloor && 0 < wallCount && wallCount < 4) - { - wallVector.Normalize(); + // Generate door mesh + CreateImplement_AddDoor(parameter, location); - const FTransform transform(wallVector.Rotation(), position); - if (const FDungeonMeshParts* parts = parameter->SelectPillarParts(gridIndex, grid, dungeon::Random::Instance())) - { - mOnResetPillar(pillarGridHeight, parts->StaticMesh, parts->CalculateWorldTransform(transform)); - } + // Generate roof mesh + CreateImplement_AddRoof(parameter, location); - // 水平以外に対応が必要? - if (wallCount == 2) - { - if (const FDungeonActorParts* parts = parameter->SelectTorchParts(gridIndex, grid, dungeon::Random::Instance())) - { -#if 0 - const FTransform worldTransform = transform * parts->RelativeTransform; - //const FTransform worldTransform = parts->RelativeTransform * transform; -#else - const FVector rotaedLocation = transform.Rotator().RotateVector(parts->RelativeTransform.GetLocation()); - const FTransform worldTransform( - transform.Rotator() + parts->RelativeTransform.Rotator(), - transform.GetLocation() + rotaedLocation, - transform.GetScale3D() * parts->RelativeTransform.GetScale3D() - ); -#endif - SpawnActor(parts->ActorClass, TEXT("Dungeon/Actors"), worldTransform); - } - } - } + return true; } + ); + DUNGEON_GENERATOR_LOG(TEXT("Spawn meshs and actors: %lf seconds"), stopwatch.Lap()); + } + { + dungeon::Stopwatch stopwatch; + mAisleInteriorDecorator.ShuffleDecorationLocation(GetRandom()); + mAisleInteriorDecorator.TrySpawnActors(mWorld.Get(), TArray({ TEXT("aisle"), TEXT("wall"), TEXT("major") }), GetRandom()); + mAisleInteriorDecorator.TrySpawnActors(mWorld.Get(), TArray({ TEXT("aisle"), TEXT("wall") }), GetRandom()); + mAisleInteriorDecorator.ClearDecorationLocation(); + DUNGEON_GENERATOR_LOG(TEXT("Spawn aisle interior decorate actors: %lf seconds"), stopwatch.Lap()); + } + + { + dungeon::Stopwatch stopwatch; + mSlopeInteriorDecorator.ShuffleDecorationLocation(GetRandom()); + mSlopeInteriorDecorator.TrySpawnActors(mWorld.Get(), TArray({ TEXT("slope"), TEXT("wall"), TEXT("major") }), GetRandom()); + mSlopeInteriorDecorator.TrySpawnActors(mWorld.Get(), TArray({ TEXT("slope"), TEXT("wall") }), GetRandom()); + mSlopeInteriorDecorator.ClearDecorationLocation(); + DUNGEON_GENERATOR_LOG(TEXT("Spawn slope interior decorate actors: %lf seconds"), stopwatch.Lap()); + } - // 扉の生成通知 - if (const FDungeonDoorActorParts* parts = parameter->SelectDoorParts(gridIndex, grid, dungeon::Random::Instance())) + // RoomSensorActorを生成 + { + dungeon::Stopwatch stopwatch; + mGenerator->ForEach([this, parameter](const std::shared_ptr& room) { - const EDungeonRoomProps props = static_cast(grid.GetProps()); + const FVector center = room->GetCenter() * parameter->GetGridSize(); + const FVector extent = room->GetExtent() * parameter->GetGridSize(); + + // RoomSensor + ADungeonRoomSensor* roomSensorActor = SpawnRoomSensorActor( + parameter->GetRoomSensorClass(), + room->GetIdentifier(), + center, + extent, + static_cast(room->GetParts()), + static_cast(room->GetItem()), + room->GetBranchId(), + room->GetDepthFromStart(), + mGenerator->GetDeepestDepthFromStart() //!< TODO:適切な関数名に変えて下さい + ); - if (grid.CanBuildGate(mGenerator->GetVoxel()->Get(location.X, location.Y - 1, location.Z), dungeon::Direction::North)) - { - // 北側の扉 - FVector doorPosition = position; - doorPosition.X += parameter->GridSize * 0.5f; - SpawnDoorActor(parts->ActorClass, parts->CalculateWorldTransform(doorPosition, 0.f), props); - } - if (grid.CanBuildGate(mGenerator->GetVoxel()->Get(location.X, location.Y + 1, location.Z), dungeon::Direction::South)) - { - // 南側の扉 - FVector doorPosition = position; - doorPosition.X += parameter->GridSize * 0.5f; - doorPosition.Y += parameter->GridSize; - SpawnDoorActor(parts->ActorClass, parts->CalculateWorldTransform(doorPosition, 180.f), props); - } - if (grid.CanBuildGate(mGenerator->GetVoxel()->Get(location.X + 1, location.Y, location.Z), dungeon::Direction::East)) + if ((room->GetParts() == dungeon::Room::Parts::Start) || (room->GetParts() == dungeon::Room::Parts::Goal)) { - // 東側の扉 - FVector doorPosition = position; - doorPosition.X += parameter->GridSize; - doorPosition.Y += parameter->GridSize * 0.5f; - SpawnDoorActor(parts->ActorClass, parts->CalculateWorldTransform(doorPosition, 90.f), props); + int i = 0; } - if (grid.CanBuildGate(mGenerator->GetVoxel()->Get(location.X - 1, location.Y, location.Z), dungeon::Direction::West)) - { - // 西側の扉 - FVector doorPosition = position; - doorPosition.Y += parameter->GridSize * 0.5f; - SpawnDoorActor(parts->ActorClass, parts->CalculateWorldTransform(doorPosition, -90.f), props); - } - } - // 屋根のメッシュ生成通知 - if (grid.CanBuildRoof(mGenerator->GetVoxel()->Get(location.X, location.Y, location.Z + 1), true)) - { - /* - 壁のメッシュを生成 - メッシュは原点からY軸とZ軸方向に伸びており、面はX軸が正面になっています。 - */ - const FTransform transform(centerPosition); - //if (grid.CanBuildWall(mGenerator->GetVoxel()->Get(location.X, location.Y - 1, location.Z), dungeon::Direction::North, parameter->MergeRooms)) - if (grid.IsKindOfRoomType()) + // center interior { - if (mOnAddRoomRoof) + FVector C = center + FVector(0, 0, -extent.Z); + FIntVector location = parameter->ToGrid(C); + const auto& grid = mGenerator->GetVoxel()->Get(location.X, location.Y, location.Z); + if (grid.IsKindOfRoomTypeWithoutGate()) { - if (const FDungeonMeshPartsWithDirection* parts = parameter->SelectRoomRoofParts(gridIndex, grid, dungeon::Random::Instance())) + FDungeonInteriorDecorator localRoomCenterInteriorDecorator( + mRoomInteriorDecorator.GetAsset(), + mInteriorUnplaceableBounds + ); + + localRoomCenterInteriorDecorator.AddDecorationLocation(C, 0.f); + { - mOnAddRoomRoof( - parts->StaticMesh, - parts->CalculateWorldTransform(dungeon::Random::Instance(), transform) - ); + TArray tags = { TEXT("center"), TEXT("major") }; + tags.Add(FString(room->GetPartsName().size(), room->GetPartsName().data())); + if (IsValid(roomSensorActor)) + tags.Append(roomSensorActor->GetInquireInteriorTags()); + localRoomCenterInteriorDecorator.TrySpawnActors(mWorld.Get(), tags, GetRandom()); } - } - } - else - { - if (mOnAddAisleRoof) - { - if (const FDungeonMeshPartsWithDirection* parts = parameter->SelectAisleRoofParts(gridIndex, grid, dungeon::Random::Instance())) { - mOnAddAisleRoof( - parts->StaticMesh, - parts->CalculateWorldTransform(dungeon::Random::Instance(), transform) - ); + TArray tags = { TEXT("center") }; + tags.Add(FString(room->GetPartsName().size(), room->GetPartsName().data())); + if (IsValid(roomSensorActor)) + tags.Append(roomSensorActor->GetInquireInteriorTags()); + localRoomCenterInteriorDecorator.TrySpawnActors(mWorld.Get(), tags, GetRandom()); } } } - -#if 0 - if (mOnResetChandelier) + // wall interior { - if (const FDungeonActorParts* parts = parameter->SelectChandelierParts(dungeon::Random::Instance())) + const std::shared_ptr localRoomWallInteriorDecorator + = mRoomInteriorDecorator.CreateInteriorDecorator(FBox(center - extent, center + extent)); + localRoomWallInteriorDecorator->ShuffleDecorationLocation(GetRandom()); { - mOnResetChandelier(parts->ActorClass, worldTransform); - } - } -#endif - } - - return true; - } - ); - { - mAisleInteriorDecorator.ShuffleDecorationLocation(dungeon::Random::Instance()); - mAisleInteriorDecorator.SpawnActors(mWorld.Get(), TArray({ TEXT("aisle"), TEXT("wall"), TEXT("major") }), dungeon::Random::Instance()); - mAisleInteriorDecorator.SpawnActors(mWorld.Get(), TArray({ TEXT("aisle"), TEXT("wall") }), dungeon::Random::Instance()); - mAisleInteriorDecorator.ClearDecorationLocation(); - } - - { - mSlopeInteriorDecorator.ShuffleDecorationLocation(dungeon::Random::Instance()); - mSlopeInteriorDecorator.SpawnActors(mWorld.Get(), TArray({ TEXT("slope"), TEXT("wall"), TEXT("major") }), dungeon::Random::Instance()); - mSlopeInteriorDecorator.SpawnActors(mWorld.Get(), TArray({ TEXT("slope"), TEXT("wall") }), dungeon::Random::Instance()); - mSlopeInteriorDecorator.ClearDecorationLocation(); - } - - // RoomSensorActorを生成 - mGenerator->ForEach([this, parameter](const std::shared_ptr& room) - { - const FVector center = room->GetCenter() * parameter->GetGridSize(); - const FVector extent = room->GetExtent() * parameter->GetGridSize(); - ADungeonRoomSensor* roomSensorActor = SpawnRoomSensorActor( - parameter->GetRoomSensorClass(), - room->GetIdentifier(), - center, - extent, - static_cast(room->GetParts()), - static_cast(room->GetItem()), - room->GetBranchId(), - room->GetDepthFromStart(), - mGenerator->GetDeepestDepthFromStart() //!< TODO:適切な関数名に変えて下さい - ); - - // center - { - FVector C = center + FVector(0, 0, -extent.Z); - FIntVector location = parameter->ToGrid(C); - const auto& grid = mGenerator->GetVoxel()->Get(location.X, location.Y, location.Z); - if (grid.IsKindOfRoomTypeWithoutGate()) - { - FDungeonInteriorDecorator localRoomCenterInteriorDecorator(mRoomInteriorDecorator.GetAsset()); - localRoomCenterInteriorDecorator.AddDecorationLocation(C, 0.f); - { - TArray tags = { TEXT("center"), TEXT("major") }; + TArray tags = { TEXT("wall"), TEXT("major") }; tags.Add(FString(room->GetPartsName().size(), room->GetPartsName().data())); - tags.Append(roomSensorActor->OnDecorateInteria()); - localRoomCenterInteriorDecorator.SpawnActors(mWorld.Get(), tags, dungeon::Random::Instance()); + if (IsValid(roomSensorActor)) + tags.Append(roomSensorActor->GetInquireInteriorTags()); + localRoomWallInteriorDecorator->TrySpawnActors(mWorld.Get(), tags, GetRandom()); } { - TArray tags = { TEXT("center") }; + TArray tags = { TEXT("wall") }; tags.Add(FString(room->GetPartsName().size(), room->GetPartsName().data())); - tags.Append(roomSensorActor->OnDecorateInteria()); - localRoomCenterInteriorDecorator.SpawnActors(mWorld.Get(), tags, dungeon::Random::Instance()); + if (IsValid(roomSensorActor)) + tags.Append(roomSensorActor->GetInquireInteriorTags()); + localRoomWallInteriorDecorator->TrySpawnActors(mWorld.Get(), tags, GetRandom()); } } - } - // wall - { - const std::shared_ptr localRoomWallInteriorDecorator = mRoomInteriorDecorator.Room(FBox(center - extent, center + extent)); - localRoomWallInteriorDecorator->ShuffleDecorationLocation(dungeon::Random::Instance()); - { - TArray tags = { TEXT("wall"), TEXT("major") }; - tags.Add(FString(room->GetPartsName().size(), room->GetPartsName().data())); - tags.Append(roomSensorActor->OnDecorateInteria()); - localRoomWallInteriorDecorator->SpawnActors(mWorld.Get(), tags, dungeon::Random::Instance()); - } + + if ((room->GetParts() == dungeon::Room::Parts::Start) || (room->GetParts() == dungeon::Room::Parts::Goal)) { - TArray tags = { TEXT("wall") }; - tags.Add(FString(room->GetPartsName().size(), room->GetPartsName().data())); - tags.Append(roomSensorActor->OnDecorateInteria()); - localRoomWallInteriorDecorator->SpawnActors(mWorld.Get(), tags, dungeon::Random::Instance()); + int i = 0; } } - } - ); + ); + DUNGEON_GENERATOR_LOG(TEXT("Spawn DungeonRoomSensor and room interior decorate actors: %lf seconds"), stopwatch.Lap()); + } mRoomInteriorDecorator.ClearDecorationLocation(); SpawnRecastNavMesh(); + #if 0 //SpawnRecastNavMesh(); SpawnNavMeshBoundsVolume(parameter); @@ -579,6 +473,7 @@ void CDungeonGeneratorCore::AddTerrain() if (USceneComponent* rootComponent = navMeshBoundsVolume->GetRootComponent()) { + const EComponentMobility::Type mobility = rootComponent->Mobility; rootComponent->SetMobility(EComponentMobility::Stationary); navMeshBoundsVolume->SetActorLocation(boundingCenter); @@ -613,7 +508,7 @@ void CDungeonGeneratorCore::AddTerrain() } else { - DUNGEON_GENERATOR_ERROR(TEXT("CDungeonGeneratorCore: CubeBuilderの生成に失敗しました")); + DUNGEON_GENERATOR_ERROR(TEXT("CubeBuilder generation failed in CDungeonGeneratorCore")); } #else /* @@ -625,61 +520,326 @@ void CDungeonGeneratorCore::AddTerrain() navMeshBoundsVolume->SetActorScale3D(boundingScale); #endif - rootComponent->SetMobility(EComponentMobility::Static); + rootComponent->SetMobility(mobility); } else { - DUNGEON_GENERATOR_ERROR(TEXT("NavMeshBoundsVolumeのRootComponentを設定して下さい")); + DUNGEON_GENERATOR_ERROR(TEXT("Set the RootComponent of the NavMeshBoundsVolume")); } } #endif } -void CDungeonGeneratorCore::SpawnRecastNavMesh() +void CDungeonGeneratorCore::CreateImplement_AddWall(const UDungeonGenerateParameter* parameter, const FIntVector& location) { - if (ARecastNavMesh* navMeshBoundsVolume = FindActor()) + /* + 壁のメッシュを生成 + メッシュは原点からY軸とZ軸方向に伸びており、面はX軸が正面(北側の壁)になっています。 + */ + if (mOnAddWall) { - const auto mode = navMeshBoundsVolume->GetRuntimeGenerationMode(); - if (mode != ERuntimeGenerationType::Dynamic && mode != ERuntimeGenerationType::DynamicModifiersOnly) + const size_t gridIndex = mGenerator->GetVoxel()->Index(location); + const dungeon::Grid& currentGrid = mGenerator->GetVoxel()->Get(gridIndex); + const dungeon::Grid& underGrid = mGenerator->GetVoxel()->Get(location.X, location.Y, location.Z - 1); + const float gridSize = parameter->GetGridSize(); + const float halfGridSize = gridSize * 0.5f; + const FVector halfOffset(halfGridSize, halfGridSize, 0); + const FVector position = parameter->ToWorld(location); + const FVector centerPosition = position + halfOffset; + + const UDungeonMeshSetDatabase* dungeonMeshSetDatabase; + if (currentGrid.IsKindOfRoomType()) + dungeonMeshSetDatabase = parameter->GetDungeonRoomPartsDatabase(); + else + dungeonMeshSetDatabase = parameter->GetDungeonAislePartsDatabase(); + + if (const FDungeonMeshParts* parts = parameter->SelectWallParts(dungeonMeshSetDatabase, gridIndex, currentGrid, GetRandom())) { - DUNGEON_GENERATOR_ERROR(TEXT("RecastNavMeshのRuntimeGenerationModeをDynamicに設定してください")); + if (currentGrid.CanBuildWall(mGenerator->GetVoxel()->Get(location.X, location.Y - 1, location.Z), underGrid, dungeon::Direction::North, parameter->MergeRooms)) + { + // 北側の壁 + FVector wallPosition = centerPosition; + wallPosition.Y -= halfGridSize; + mOnAddWall(parts->StaticMesh, parts->CalculateWorldTransform(wallPosition, 0.f)); + + if (currentGrid.IsKindOfAisleType()) + mAisleInteriorDecorator.AddDecorationLocation(wallPosition, 0.f); + else if (currentGrid.GetType() == dungeon::Grid::Type::Deck) + mRoomInteriorDecorator.AddDecorationLocation(wallPosition, 0.f); + } + if (currentGrid.CanBuildWall(mGenerator->GetVoxel()->Get(location.X, location.Y + 1, location.Z), underGrid, dungeon::Direction::South, parameter->MergeRooms)) + { + // 南側の壁 + FVector wallPosition = centerPosition; + wallPosition.Y += halfGridSize; + mOnAddWall(parts->StaticMesh, parts->CalculateWorldTransform(wallPosition, 180.f)); + + if (currentGrid.IsKindOfAisleType()) + mAisleInteriorDecorator.AddDecorationLocation(wallPosition, 180.f); + else if (currentGrid.GetType() == dungeon::Grid::Type::Deck) + mRoomInteriorDecorator.AddDecorationLocation(wallPosition, 180.f); + } + if (currentGrid.CanBuildWall(mGenerator->GetVoxel()->Get(location.X + 1, location.Y, location.Z), underGrid, dungeon::Direction::East, parameter->MergeRooms)) + { + // 東側の壁 + FVector wallPosition = centerPosition; + wallPosition.X += halfGridSize; + mOnAddWall(parts->StaticMesh, parts->CalculateWorldTransform(wallPosition, 90.f)); + + if (currentGrid.IsKindOfAisleType()) + mAisleInteriorDecorator.AddDecorationLocation(wallPosition, 90.f); + else if (currentGrid.GetType() == dungeon::Grid::Type::Deck) + mRoomInteriorDecorator.AddDecorationLocation(wallPosition, 90.f); + } + if (currentGrid.CanBuildWall(mGenerator->GetVoxel()->Get(location.X - 1, location.Y, location.Z), underGrid, dungeon::Direction::West, parameter->MergeRooms)) + { + // 西側の壁 + FVector wallPosition = centerPosition; + wallPosition.X -= halfGridSize; + mOnAddWall(parts->StaticMesh, parts->CalculateWorldTransform(wallPosition, -90.f)); + + if (currentGrid.IsKindOfAisleType()) + mAisleInteriorDecorator.AddDecorationLocation(wallPosition, -90.f); + else if (currentGrid.GetType() == dungeon::Grid::Type::Deck) + mRoomInteriorDecorator.AddDecorationLocation(wallPosition, -90.f); + } } } } -void CDungeonGeneratorCore::AddObject() +void CDungeonGeneratorCore::CreateImplement_AddDoor(const UDungeonGenerateParameter* parameter, const FIntVector& location) { - if (mGenerator == nullptr) + const size_t gridIndex = mGenerator->GetVoxel()->Index(location); + const dungeon::Grid& grid = mGenerator->GetVoxel()->Get(gridIndex); + const float gridSize = parameter->GetGridSize(); + const float halfGridSize = gridSize * 0.5f; + const FVector halfOffset(halfGridSize, halfGridSize, 0); + const FVector position = parameter->ToWorld(location); + const FVector centerPosition = position + halfOffset; + + if (const FDungeonDoorActorParts* parts = parameter->SelectDoorParts(gridIndex, grid, GetRandom())) { - DUNGEON_GENERATOR_ERROR(TEXT("CDungeonGeneratorCore::Createを呼び出してください")); - return; + const EDungeonRoomProps props = static_cast(grid.GetProps()); + + if (grid.CanBuildGate(mGenerator->GetVoxel()->Get(location.X, location.Y - 1, location.Z), dungeon::Direction::North)) + { + // 北側の扉 + FVector doorPosition = position; + doorPosition.X += parameter->GridSize * 0.5f; + SpawnDoorActor(parts->ActorClass, parts->CalculateWorldTransform(doorPosition, 0.f), props); + } + if (grid.CanBuildGate(mGenerator->GetVoxel()->Get(location.X, location.Y + 1, location.Z), dungeon::Direction::South)) + { + // 南側の扉 + FVector doorPosition = position; + doorPosition.X += parameter->GridSize * 0.5f; + doorPosition.Y += parameter->GridSize; + SpawnDoorActor(parts->ActorClass, parts->CalculateWorldTransform(doorPosition, 180.f), props); + } + if (grid.CanBuildGate(mGenerator->GetVoxel()->Get(location.X + 1, location.Y, location.Z), dungeon::Direction::East)) + { + // 東側の扉 + FVector doorPosition = position; + doorPosition.X += parameter->GridSize; + doorPosition.Y += parameter->GridSize * 0.5f; + SpawnDoorActor(parts->ActorClass, parts->CalculateWorldTransform(doorPosition, 90.f), props); + } + if (grid.CanBuildGate(mGenerator->GetVoxel()->Get(location.X - 1, location.Y, location.Z), dungeon::Direction::West)) + { + // 西側の扉 + FVector doorPosition = position; + doorPosition.Y += parameter->GridSize * 0.5f; + SpawnDoorActor(parts->ActorClass, parts->CalculateWorldTransform(doorPosition, -90.f), props); + } } +} - const UDungeonGenerateParameter* parameter = mParameter.Get(); - if (!IsValid(parameter)) +void CDungeonGeneratorCore::CreateImplement_AddFloorAndSlope(const UDungeonGenerateParameter* parameter, const FIntVector& location) +{ + const size_t gridIndex = mGenerator->GetVoxel()->Index(location); + const dungeon::Grid& grid = mGenerator->GetVoxel()->Get(gridIndex); + const float gridSize = parameter->GetGridSize(); + const float halfGridSize = gridSize * 0.5f; + const FVector halfOffset(halfGridSize, halfGridSize, 0); + const FVector position = parameter->ToWorld(location); + const FVector centerPosition = position + halfOffset; + + if (mOnAddSlope && grid.CanBuildSlope()) { - DUNGEON_GENERATOR_ERROR(TEXT("DungeonGenerateParameterを設定してください")); - return; + /* + スロープのメッシュを生成 + メッシュは原点からX軸とY軸方向に伸びており、面はZ軸が上面になっています。 + */ + if (const FDungeonMeshParts* parts = parameter->SelectSlopeParts(gridIndex, grid, GetRandom())) + { + mOnAddSlope(parts->StaticMesh, parts->CalculateWorldTransform(centerPosition, grid.GetDirection())); + } } + else if (mOnAddFloor && grid.CanBuildFloor(mGenerator->GetVoxel()->Get(location.X, location.Y, location.Z - 1), true)) + { + const UDungeonMeshSetDatabase* dungeonMeshSetDatabase; + if (grid.IsKindOfRoomType()) + dungeonMeshSetDatabase = parameter->GetDungeonRoomPartsDatabase(); + else + dungeonMeshSetDatabase = parameter->GetDungeonAislePartsDatabase(); - if (parameter->GetStartParts().ActorClass) + if (const FDungeonMeshParts* parts = parameter->SelectFloorParts(dungeonMeshSetDatabase, gridIndex, grid, GetRandom())) + { + /* + 床のメッシュを生成 + メッシュは原点からX軸とY軸方向に伸びており、面はZ軸が上面になっています。 + */ + mOnAddFloor(parts->StaticMesh, parts->CalculateWorldTransform(centerPosition, grid.GetDirection())); + } + } +} + +void CDungeonGeneratorCore::CreateImplement_AddPillarAndTorch(std::vector& spawnedTorchBounds, const UDungeonGenerateParameter* parameter, const FIntVector& location) +{ + /* + 柱のメッシュを生成 + メッシュは原点からY軸とZ軸方向に伸びており、面はX軸が正面になっています。 + */ + if (mOnResetPillar) { - /** - TODO:パーツによる回転指定 PlacementDirection に対応して下さい - */ - const FTransform wallTransform(*mGenerator->GetStartPoint() * parameter->GetGridSize()); - const FTransform worldTransform = wallTransform * parameter->GetStartParts().RelativeTransform; - SpawnActorOnFloor(parameter->GetStartParts().ActorClass, worldTransform); + const size_t gridIndex = mGenerator->GetVoxel()->Index(location); + const dungeon::Grid& grid = mGenerator->GetVoxel()->Get(gridIndex); + const float gridSize = parameter->GetGridSize(); + const float halfGridSize = gridSize * 0.5f; + const FVector halfOffset(halfGridSize, halfGridSize, 0); + const FVector position = parameter->ToWorld(location); + const FVector centerPosition = position + halfOffset; + + FVector wallVector(0.f); + uint8_t wallCount = 0; + uint8_t deckCount = 0; + bool onFloor = false; + uint32_t pillarGridHeight = 1; + for (int_fast8_t dy = -1; dy <= 0; ++dy) + { + for (int_fast8_t dx = -1; dx <= 0; ++dx) + { + // Check the number of walls. + const auto& result = mGenerator->GetVoxel()->Get(location.X + dx, location.Y + dy, location.Z); + if (grid.CanBuildPillar(result)) + { + wallVector += FVector(static_cast(dx) + 0.5f, static_cast(dy) + 0.5f, 0.f); + ++wallCount; + } + // Check the number of floors. + if ( + (result.GetType() == dungeon::Grid::Type::Deck) || + (result.GetType() == dungeon::Grid::Type::Gate) || + (result.GetType() == dungeon::Grid::Type::Aisle) || + (result.GetType() == dungeon::Grid::Type::Slope) /* || TODO:スロープの上部分を判別する方法を検討してください + (result.GetType() == dungeon::Grid::Type::Atrium)*/) + { + ++deckCount; + } + + // 床を調べます TODO:dungeon::Grid::Type::Deckで調べられるか調べてください + const auto& baseFloorGrid = mGenerator->GetVoxel()->Get(location.X + dx, location.Y + dy, location.Z); + const auto& underFloorGrid = mGenerator->GetVoxel()->Get(location.X + dx, location.Y + dy, location.Z - 1); + if (baseFloorGrid.IsKindOfSlopeType() || baseFloorGrid.CanBuildFloor(underFloorGrid, false)) + { + onFloor = true; + + // 天井の高さを調べます + uint32_t gridHeight = 1; + while (true) + { + const auto& roofGrid = mGenerator->GetVoxel()->Get(location.X + dx, location.Y + dy, location.Z + gridHeight); + if (roofGrid.GetType() == dungeon::Grid::Type::OutOfBounds) + break; + if (!grid.CanBuildRoof(roofGrid, false)) + break; + ++gridHeight; + } + if (pillarGridHeight < gridHeight) + pillarGridHeight = gridHeight; + } + } + } + + if (onFloor && (1 <= wallCount && wallCount <= 3)) + { + wallVector.Normalize(); + FRotator wallRotation(wallVector.Rotation()); + //wallRotation.Yaw = static_cast(static_cast(std::round(wallRotation.Yaw)) / 90) * 90.0; + const FTransform transform(wallRotation, position); + + if (const FDungeonMeshParts* parts = parameter->SelectPillarParts(gridIndex, grid, GetRandom())) + { + mOnResetPillar(pillarGridHeight, parts->StaticMesh, parts->CalculateWorldTransform(transform)); + } + + // Need wall torch? + if (deckCount >= 2) + { + if (const FDungeonActorParts* parts = parameter->SelectTorchParts(gridIndex, grid, GetRandom())) + { +#if 0 + const FTransform worldTransform = parts->RelativeTransform * transform; +#else + const FVector rotatedLocation = transform.Rotator().RotateVector(parts->RelativeTransform.GetLocation()); + const FTransform worldTransform( + transform.Rotator() + parts->RelativeTransform.Rotator(), + transform.GetLocation() + rotatedLocation, + transform.GetScale3D() * parts->RelativeTransform.GetScale3D() + ); +#endif + SpawnTorchActor(spawnedTorchBounds, wallVector, parts->ActorClass, TEXT("Dungeon/Actors"), worldTransform); + } + } + } } +} - if (parameter->GetGoalParts().ActorClass) +void CDungeonGeneratorCore::CreateImplement_AddRoof(const UDungeonGenerateParameter* parameter, const FIntVector& location) +{ + if (mOnAddRoof) { - /** - TODO:パーツによる回転指定 PlacementDirection に対応して下さい - */ - const FTransform wallTransform(*mGenerator->GetGoalPoint() * parameter->GetGridSize()); - const FTransform worldTransform = wallTransform * parameter->GetGoalParts().RelativeTransform; - SpawnActorOnFloor(parameter->GetGoalParts().ActorClass, worldTransform); + const size_t gridIndex = mGenerator->GetVoxel()->Index(location); + const dungeon::Grid& grid = mGenerator->GetVoxel()->Get(gridIndex); + const float gridSize = parameter->GetGridSize(); + const float halfGridSize = gridSize * 0.5f; + const FVector halfOffset(halfGridSize, halfGridSize, 0); + const FVector position = parameter->ToWorld(location); + const FVector centerPosition = position + halfOffset; + + if (grid.CanBuildRoof(mGenerator->GetVoxel()->Get(location.X, location.Y, location.Z + 1), true)) + { + const UDungeonMeshSetDatabase* dungeonMeshSetDatabase; + if (grid.IsKindOfRoomType()) + dungeonMeshSetDatabase = parameter->GetDungeonRoomPartsDatabase(); + else + dungeonMeshSetDatabase = parameter->GetDungeonAislePartsDatabase(); + + /* + 壁のメッシュを生成 + メッシュは原点からY軸とZ軸方向に伸びており、面はX軸が正面になっています。 + */ + const FTransform transform(centerPosition); + if (const FDungeonMeshPartsWithDirection* parts = parameter->SelectRoofParts(dungeonMeshSetDatabase, gridIndex, grid, GetRandom())) + { + mOnAddRoof( + parts->StaticMesh, + parts->CalculateWorldTransform(GetRandom(), transform) + ); + } + } + } +} + +void CDungeonGeneratorCore::SpawnRecastNavMesh() +{ + if (ARecastNavMesh* navMeshBoundsVolume = FindActor()) + { + const auto mode = navMeshBoundsVolume->GetRuntimeGenerationMode(); + if (mode != ERuntimeGenerationType::Dynamic && mode != ERuntimeGenerationType::DynamicModifiersOnly) + { + DUNGEON_GENERATOR_ERROR(TEXT("Set RuntimeGenerationMode of RecastNavMesh to Dynamic")); + } } } @@ -697,9 +857,7 @@ FTransform CDungeonGeneratorCore::GetStartTransform() const const UDungeonGenerateParameter* parameter = mParameter.Get(); if (IsValid(parameter) && mGenerator != nullptr && mGenerator->GetLastError() == dungeon::Generator::Error::Success) { - const FTransform wallTransform(*mGenerator->GetStartPoint() * parameter->GetGridSize()); - const FTransform worldTransform = wallTransform * parameter->GetStartParts().RelativeTransform; - return worldTransform; + return FTransform(*mGenerator->GetStartPoint() * parameter->GetGridSize()); } return FTransform::Identity; } @@ -709,9 +867,7 @@ FTransform CDungeonGeneratorCore::GetGoalTransform() const const UDungeonGenerateParameter* parameter = mParameter.Get(); if (IsValid(parameter) && mGenerator != nullptr && mGenerator->GetLastError() == dungeon::Generator::Error::Success) { - const FTransform wallTransform(*mGenerator->GetGoalPoint() * parameter->GetGridSize()); - const FTransform worldTransform = wallTransform * parameter->GetGoalParts().RelativeTransform; - return worldTransform; + return FTransform(*mGenerator->GetGoalPoint() * parameter->GetGridSize()); } return FTransform::Identity; } @@ -762,55 +918,96 @@ FBox CDungeonGeneratorCore::CalculateBoundingBox() const void CDungeonGeneratorCore::MovePlayerStart() { - if (APlayerStart* playerStart = FindActor()) + const UDungeonGenerateParameter* parameter = mParameter.Get(); + if (IsValid(parameter) == false) + return; + + if (parameter->IsMovePlayerStartToStartingPoint() == false) + return; + + /* + if (parameter->GetStartRoom().IsValid() == true) + return; + */ + + size_t totalPlayerStart = 0; + EachActors([&totalPlayerStart](APlayerStart* playerStart) + { + // APlayerStartはコリジョンが無効になっているので、GetSimpleCollisionCylinderを利用する事ができない + if (USceneComponent* rootComponent = playerStart->GetRootComponent()) + ++totalPlayerStart; + } + ); + if (totalPlayerStart == 0) { - // APlayerStartはコリジョンが無効になっているので、GetSimpleCollisionCylinderを利用する事ができない - if (USceneComponent* rootComponent = playerStart->GetRootComponent()) + DUNGEON_GENERATOR_WARNING(TEXT("PlayerStart could not be found from the level")); + return; + } + + // Calculate the position to shift APlayerStart + const double placementRadius = totalPlayerStart > 1 ? parameter->GetGridSize() : 0; + const double placementAngle = (3.1415926535897932384626433832795 * 2.) / static_cast(totalPlayerStart); + + size_t index = 0; + EachActors([this, parameter, placementRadius, placementAngle, &index](APlayerStart* playerStart) { - // 接地しない様に少しだけ余白を作る - static constexpr float heightMargine = 10.f; + // "Play from Here" PlayerStart, if we find one while in PIE mode + if (Cast(playerStart)) + return; - const EComponentMobility::Type mobility = rootComponent->Mobility; - rootComponent->SetMobility(EComponentMobility::Movable); + // APlayerStart cannot use GetSimpleCollisionCylinder because collision is disabled. + if (USceneComponent* rootComponent = playerStart->GetRootComponent()) { - float cylinderRadius, cylinderHalfHeight; - rootComponent->CalcBoundingCylinder(cylinderRadius, cylinderHalfHeight); - - FVector location = GetStartLocation(); + // Create a small margin to avoid grounding. + static constexpr float heightMargin = 10.f; - const UDungeonGenerateParameter* parameter = mParameter.Get(); - const auto offsetZ = IsValid(parameter) ? parameter->GetGridSize() : (cylinderHalfHeight * 2); - FHitResult hitResult; - if (playerStart->GetWorld()->LineTraceSingleByChannel(hitResult, location + FVector(0, 0, offsetZ), location, ECollisionChannel::ECC_Pawn)) + // Temporarily set EComponentMobility to Movable + const EComponentMobility::Type mobility = rootComponent->Mobility; + rootComponent->SetMobility(EComponentMobility::Movable); { - location = hitResult.ImpactPoint; - } - location.Z += cylinderHalfHeight + heightMargine; + float cylinderRadius, cylinderHalfHeight; + rootComponent->CalcBoundingCylinder(cylinderRadius, cylinderHalfHeight); + + // If there are multiple APlayerStart, shift APlayerStart from the center of the room + FVector location = GetStartLocation(); + location.X += placementRadius * std::cos(placementAngle * static_cast(index)); + location.Y += placementRadius * std::sin(placementAngle * static_cast(index)); - playerStart->SetActorLocation(location); + // Grounding the APlayerStart + FHitResult hitResult; + if (playerStart->GetWorld()->LineTraceSingleByChannel(hitResult, location + FVector(0, 0, parameter->GetGridSize()), location, ECollisionChannel::ECC_Pawn)) + { + location = hitResult.ImpactPoint; + } + location.Z += cylinderHalfHeight + heightMargin; + playerStart->SetActorLocation(location); - FCollisionShape collisionShape; +#if WITH_EDITOR + FCollisionShape collisionShape; #if UE_VERSION_OLDER_THAN(5, 0, 0) - collisionShape.SetBox(FVector(cylinderRadius, cylinderRadius, cylinderHalfHeight)); + collisionShape.SetBox(FVector(cylinderRadius, cylinderRadius, cylinderHalfHeight)); #else - collisionShape.SetBox(FVector3f(cylinderRadius, cylinderRadius, cylinderHalfHeight)); + collisionShape.SetBox(FVector3f(cylinderRadius, cylinderRadius, cylinderHalfHeight)); +#endif + if (playerStart->GetWorld()->OverlapBlockingTestByChannel(location, playerStart->GetActorQuat(), ECollisionChannel::ECC_Pawn, collisionShape)) + { + DUNGEON_GENERATOR_ERROR(TEXT("PlayerStart(%s) is in contact with something"), *playerStart->GetName()); + } #endif - if (playerStart->GetWorld()->OverlapBlockingTestByChannel(location, playerStart->GetActorQuat(), ECollisionChannel::ECC_Pawn, collisionShape)) - { - DUNGEON_GENERATOR_ERROR(TEXT("%s(PlayerStart)が何かに接触しています"), *playerStart->GetName()); } + // Undo EComponentMobility + rootComponent->SetMobility(mobility); + + ++index; } - rootComponent->SetMobility(mobility); - } - else - { - DUNGEON_GENERATOR_ERROR(TEXT("%s(PlayerStart)のRootComponentを設定して下さい"), *playerStart->GetName()); +#if WITH_EDITOR + else + { + DUNGEON_GENERATOR_ERROR(TEXT("Set RootComponent of PlayerStart(%s)"), *playerStart->GetName()); + } +#endif } - } - else - { - DUNGEON_GENERATOR_WARNING(TEXT("PlayerStartは発見できませんでした")); - } + ); } //////////////////////////////////////////////////////////////////////////////// @@ -837,6 +1034,7 @@ AStaticMeshActor* CDungeonGeneratorCore::SpawnStaticMeshActor(UStaticMesh* stati mesh->SetStaticMesh(staticMesh); actor->FinishSpawning(transform); + return actor; } @@ -869,6 +1067,52 @@ void CDungeonGeneratorCore::SpawnDoorActor(UClass* actorClass, const FTransform& } } +AActor* CDungeonGeneratorCore::SpawnTorchActor(std::vector& spawnedTorchBounds, const FVector& wallNormal, UClass* actorClass, const FName& folderPath, const FTransform& transform, const ESpawnActorCollisionHandlingMethod spawnActorCollisionHandlingMethod) const +{ + AActor* actor = SpawnActor(actorClass, folderPath, transform, spawnActorCollisionHandlingMethod); + if (!IsValid(actor)) + return nullptr; +#if 0 + return actor; +#else + FSphere localLightBounds(EForceInit::ForceInit); + { + TArray localLightComponents; + actor->GetComponents(localLightComponents); + for (const auto& localLightComponent : localLightComponents) + { + localLightBounds += localLightComponent->GetBoundingSphere(); + } + } + + // Since there is no light influence behind the wall, + // move the influence sphere about half of its radius away from the wall. + { + localLightBounds.W *= 0.5; + localLightBounds.W *= 0.5; + localLightBounds.Center += wallNormal * localLightBounds.W; + } + + const auto i = std::find_if(spawnedTorchBounds.begin(), spawnedTorchBounds.end(), [&localLightBounds](const FSphere& bounds) + { + return bounds.Intersects(localLightBounds); + } + ); + if (i == spawnedTorchBounds.end()) + { + // Registers a light range of torch actor + spawnedTorchBounds.emplace_back(localLightBounds); + + return actor; + } + else + { + actor->Destroy(); + return nullptr; + } +#endif +} + ADungeonRoomSensor* CDungeonGeneratorCore::SpawnRoomSensorActor( UClass* actorClass, const dungeon::Identifier& identifier, @@ -884,7 +1128,7 @@ ADungeonRoomSensor* CDungeonGeneratorCore::SpawnRoomSensorActor( ADungeonRoomSensor* actor = SpawnActorDeferred(actorClass, TEXT("Dungeon/Sensors"), transform, ESpawnActorCollisionHandlingMethod::AlwaysSpawn); if (IsValid(actor)) { - actor->Initialize(identifier.Get(), extent, parts, item, branchId, depthFromStart, deepestDepthFromStart); + actor->Initialize(GetRandom(), identifier.Get(), extent, parts, item, branchId, depthFromStart, deepestDepthFromStart); actor->FinishSpawning(transform); } return actor; @@ -913,6 +1157,27 @@ void CDungeonGeneratorCore::DestroySpawnedActors(UWorld* world) } actor->Destroy(); } + +#if WITH_EDITOR +#if UE_VERSION_NEWER_THAN(5, 0, 0) + { + TArray deleteFolders; + FActorFolders::Get().ForEachFolder(*world, [&deleteFolders](const FFolder& folder) + { + const FName pathName = folder.GetPath(); + if (pathName.ToString().StartsWith("Dungeon")) + { + deleteFolders.Add(folder); + } + return true; + }); + for (FFolder& folder : deleteFolders) + { + FActorFolders::Get().DeleteFolder(*world, folder); + } + } +#endif +#endif } //////////////////////////////////////////////////////////////////////////////// @@ -1041,7 +1306,7 @@ UTexture2D* CDungeonGeneratorCore::GenerateMiniMapTexture(uint32_t worldToTextur { const auto& grid = voxel->Get(x, y, z); // slope - if (grid.CanBuildSlope() || grid.GetType() == dungeon::Grid::Type::Atrium) + if (grid.IsKindOfSlopeType()) { rect(x, y, floorColor); } @@ -1052,19 +1317,20 @@ UTexture2D* CDungeonGeneratorCore::GenerateMiniMapTexture(uint32_t worldToTextur } // wall - if (grid.CanBuildWall(voxel->Get(x, y - 1, z), dungeon::Direction::North, parameter->MergeRooms)) + static const dungeon::Grid underGrid(dungeon::Grid::Type::Empty); + if (grid.CanBuildWall(voxel->Get(x, y - 1, z), underGrid, dungeon::Direction::North, parameter->MergeRooms)) { line(x, y, dungeon::Direction::North, wallColor); } - if (grid.CanBuildWall(voxel->Get(x, y + 1, z), dungeon::Direction::South, parameter->MergeRooms)) + if (grid.CanBuildWall(voxel->Get(x, y + 1, z), underGrid, dungeon::Direction::South, parameter->MergeRooms)) { line(x, y, dungeon::Direction::South, wallColor); } - if (grid.CanBuildWall(voxel->Get(x + 1, y, z), dungeon::Direction::East, parameter->MergeRooms)) + if (grid.CanBuildWall(voxel->Get(x + 1, y, z), underGrid, dungeon::Direction::East, parameter->MergeRooms)) { line(x, y, dungeon::Direction::East, wallColor); } - if (grid.CanBuildWall(voxel->Get(x - 1, y, z), dungeon::Direction::West, parameter->MergeRooms)) + if (grid.CanBuildWall(voxel->Get(x - 1, y, z), underGrid, dungeon::Direction::West, parameter->MergeRooms)) { line(x, y, dungeon::Direction::West, wallColor); } @@ -1093,6 +1359,11 @@ UTexture2D* CDungeonGeneratorCore::GenerateMiniMapTexture(uint32_t worldToTextur return generateTexture; } +uint32_t CDungeonGeneratorCore::CalculateCRC32() const noexcept +{ + return mGenerator ? mGenerator->CalculateCRC32() : 0; +} + std::shared_ptr CDungeonGeneratorCore::GetGenerator() const { return mGenerator; @@ -1114,52 +1385,74 @@ void CDungeonGeneratorCore::RequestStreamLevel(const FSoftObjectPath& levelPath, mRequestLoadStreamLevels.emplace_back(levelPath, levelLocation); } -void CDungeonGeneratorCore::AsyncLoadStreamLevels() +void CDungeonGeneratorCore::FlushLoadStreamLevels() { - if (!mRequestLoadStreamLevels.empty()) + UWorld* world = mWorld.Get(); + if (IsValid(world)) { - LoadStreamLevelParameter requestStreamLevel = mRequestLoadStreamLevels.front(); - mRequestLoadStreamLevels.pop_front(); + while (!mRequestLoadStreamLevels.empty()) + { + AsyncLoadStreamLevels(true); + } + world->UpdateLevelStreaming(); + } +} - if (FindLoadedStreamLevel(requestStreamLevel.mPath) == nullptr) +void CDungeonGeneratorCore::AsyncLoadStreamLevels(const bool bShouldBlockOnLoad) +{ + UWorld* world = mWorld.Get(); + if (IsValid(world)) + { + if (!mRequestLoadStreamLevels.empty()) { - UWorld* world = mWorld.Get(); - if (IsValid(world)) - { - bool bSuccess = false; + LoadStreamLevelParameter requestStreamLevel = mRequestLoadStreamLevels.front(); + mRequestLoadStreamLevels.pop_front(); + + 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(); - ULevelStreamingDynamic* levelStreaming = ULevelStreamingDynamic::LoadLevelInstance(parameter, bSuccess); + const FTransform transform(FRotator::ZeroRotator, requestStreamLevel.mLocation); + ULevelStreamingDynamic::FLoadLevelInstanceParams parameter(world, requestStreamLevel.mPath.GetLongPackageName(), transform); + parameter.OptionalLevelStreamingClass = UDungeonLevelStreamingDynamic::StaticClass(); + 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); + 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); + ULevelStreamingDynamic* levelStreaming = ULevelStreamingDynamic::LoadLevelInstance( + world, + requestStreamLevel.mPath.GetLongPackageName(), + requestStreamLevel.mLocation, + FRotator::ZeroRotator, + bSuccess); #endif - if (bSuccess && IsValid(levelStreaming)) - { - mLoadedStreamLevels.Add(levelStreaming); + if (bSuccess && IsValid(levelStreaming)) + { + mLoadedStreamLevels.Add(levelStreaming); - DUNGEON_GENERATOR_LOG(TEXT("Load Level (%s)"), *requestStreamLevel.mPath.GetLongPackageName()); - } - else + if (bShouldBlockOnLoad) { - DUNGEON_GENERATOR_ERROR(TEXT("Failed to Load Level (%s)"), *requestStreamLevel.mPath.GetLongPackageName()); + levelStreaming->bShouldBlockOnLoad = true; + world->UpdateStreamingLevelShouldBeConsidered(levelStreaming); +#if UE_VERSION_NEWER_THAN(5, 2, 0) + if (levelStreaming->GetLevelStreamingState() == ELevelStreamingState::Removed) +#endif + { + world->AddStreamingLevel(levelStreaming); + } } + + DUNGEON_GENERATOR_LOG(TEXT("Load Level (%s)"), *requestStreamLevel.mPath.GetLongPackageName()); + } + else + { + DUNGEON_GENERATOR_ERROR(TEXT("Failed to Load Level (%s)"), *requestStreamLevel.mPath.GetLongPackageName()); } } } @@ -1172,12 +1465,10 @@ void CDungeonGeneratorCore::SyncLoadStreamLevels() if (IsValid(world)) { TArray moveActors; + int32 index = 0; for (const auto& requestStreamLevel : mRequestLoadStreamLevels) { - if (FindLoadedStreamLevel(requestStreamLevel.mPath)) - continue; - bool bSuccess = false; #if UE_VERSION_NEWER_THAN(5, 1, 0) @@ -1212,22 +1503,24 @@ void CDungeonGeneratorCore::SyncLoadStreamLevels() ULevel* loadedLevel = levelStreaming->GetLoadedLevel(); if (IsValid(loadedLevel)) { - 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); - + FString folderName = levelStreaming->PackageNameToLoad.ToString(); + folderName.RemoveFromStart("/Game/", ESearchCase::IgnoreCase); + folderName.RemoveFromStart("Map/", ESearchCase::IgnoreCase); + folderName.RemoveFromStart("Maps/", ESearchCase::IgnoreCase); + folderName.RemoveFromStart("Level/", ESearchCase::IgnoreCase); + folderName.RemoveFromStart("Levels/", ESearchCase::IgnoreCase); + folderName.RemoveFromStart("/", ESearchCase::IgnoreCase); + const FString folderPath = TEXT("Dungeon/Levels/") + folderName; + const FName folderPathName(folderPath, index); + for (AActor* actor : loadedLevel->Actors) { actor->Tags.Add(GetDungeonGeneratorTag()); - - const FName folderPath(FString(TEXT("Dungeon/Levels/")) + folder); - actor->SetFolderPath(folderPath); + actor->SetFolderPath(folderPathName); } moveActors.Append(loadedLevel->Actors); + ++index; } mLoadedStreamLevels.Add(levelStreaming); @@ -1265,25 +1558,6 @@ void CDungeonGeneratorCore::UnloadStreamLevels() } } -TSoftObjectPtr CDungeonGeneratorCore::FindLoadedStreamLevel(const FSoftObjectPath& levelPath) const -{ - for (const TSoftObjectPtr& loadedStreamLevel : mLoadedStreamLevels) - { - if (loadedStreamLevel.IsValid()) - { -#if UE_VERSION_OLDER_THAN(5, 1, 0) - if (loadedStreamLevel->PackageNameToLoad == levelPath.GetAssetPathName()) -#else - if (loadedStreamLevel->PackageNameToLoad == levelPath.GetAssetPath().GetPackageName()) -#endif - { - return loadedStreamLevel; - } - } - } - return nullptr; -} - void CDungeonGeneratorCore::LoadStreamLevelImplement(UWorld* world, const FSoftObjectPath& path, const FTransform& transform) { #if UE_VERSION_NEWER_THAN(5, 0, 0) @@ -1459,3 +1733,13 @@ void CDungeonGeneratorCore::DrawVoxelGridType() const ); } #endif + +std::shared_ptr CDungeonGeneratorCore::GetRandom() noexcept +{ + return mGenerator->GetGenerateParameter().GetRandom(); +} + +std::shared_ptr CDungeonGeneratorCore::GetRandom() const noexcept +{ + return mGenerator->GetGenerateParameter().GetRandom(); +} diff --git a/Source/DungeonGenerator/Private/DungeonInteriorAsset.cpp b/Source/DungeonGenerator/Private/DungeonInteriorAsset.cpp deleted file mode 100644 index e41b5e1..0000000 --- a/Source/DungeonGenerator/Private/DungeonInteriorAsset.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/** -\author Shun Moriya -\copyright 2023- Shun Moriya -All Rights Reserved. -*/ - -#include "DungeonInteriorAsset.h" -#include "Core/Math/Random.h" - -TArray UDungeonInteriorAsset::Select(const TArray& interiorTags, dungeon::Random& random) const -{ - TArray selects; - - if (interiorTags.IsEmpty()) - { - for (const FDungeonInteriorParts& parts : Parts) - { - if (parts.Frequency >= random.Get(100)) - { - selects.Emplace(parts); - } - } - } - else - { - for (const FDungeonInteriorParts& parts : Parts) - { - if (parts.Frequency >= random.Get(100) && - parts.Match(interiorTags)) - { - selects.Emplace(parts); - } - } - } - - return selects; -} - -void UDungeonInteriorAsset::EachInteriorLocation(const AActor* actor, std::function function) const -{ - actor->ForEachComponent(false, [this, &function](UDungeonInteriorLocationComponent* interiorLocationComponent) - { - if (IsValid(interiorLocationComponent)) - { - TArray partsResult; - for (const FDungeonInteriorParts& parts : Parts) - { - if (parts.Match(interiorLocationComponent->GetInteriorTags())) - partsResult.Emplace(parts); - } - function(interiorLocationComponent); - } - } - ); -} diff --git a/Source/DungeonGenerator/Private/DungeonInteriorDecorator.cpp b/Source/DungeonGenerator/Private/DungeonInteriorDecorator.cpp deleted file mode 100644 index 3908de5..0000000 --- a/Source/DungeonGenerator/Private/DungeonInteriorDecorator.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/** -\author Shun Moriya -\copyright 2023- Shun Moriya -All Rights Reserved. -*/ - -#include "DungeonInteriorDecorator.h" -#include "DungeonGeneratorCore.h" -#include "Core/Math/Random.h" -#include "Core/Debug/Debug.h" -#include - -FDungeonInteriorDecorator::FDungeonInteriorDecorator(UDungeonInteriorAsset* asset) - : Asset(asset) -{ -} - -FDungeonInteriorDecorator::~FDungeonInteriorDecorator() -{ - Finalize(); -} - -void FDungeonInteriorDecorator::Initialize(UDungeonInteriorAsset* asset) -{ - Asset = asset; -} - -void FDungeonInteriorDecorator::Finalize() -{ - Asset = nullptr; -} - -bool FDungeonInteriorDecorator::IsValid() const noexcept -{ - return Asset != nullptr; -} - -UDungeonInteriorAsset* FDungeonInteriorDecorator::GetAsset() -{ - return Asset; -} - -const UDungeonInteriorAsset* FDungeonInteriorDecorator::GetAsset() const -{ - return Asset; -} - -void FDungeonInteriorDecorator::ClearDecorationLocation() -{ - mDecorationLocations.clear(); -} - -void FDungeonInteriorDecorator::AddDecorationLocation(const FVector& location, const double yaw) -{ - mDecorationLocations.push_back(std::make_shared(location, yaw)); -} - -size_t FDungeonInteriorDecorator::GetDecorationLocationSize() const noexcept -{ - return mDecorationLocations.size(); -} - -void FDungeonInteriorDecorator::ShuffleDecorationLocation(dungeon::Random& random) -{ - size_t count = mDecorationLocations.size() / 2; - while (count > 0) - { - const size_t a = random.Get(mDecorationLocations.size()); - const size_t b = random.Get(mDecorationLocations.size()); - if (a != b) - { - std::swap(mDecorationLocations[a], mDecorationLocations[b]); - } - --count; - } -} - -void FDungeonInteriorDecorator::SpawnActors(UWorld* world, const TArray& tags, dungeon::Random& random) -{ - for (const std::shared_ptr& decorationLocation : mDecorationLocations) - { - const FTransform transform(FRotator(0, decorationLocation->Yaw, 0), decorationLocation->Position); - SpawnActorsImplement(world, tags, transform, random, 0); - } -} - -void FDungeonInteriorDecorator::SpawnActorsImplement(UWorld* world, const TArray& tags, const FTransform& parentTransform, dungeon::Random& random, const size_t depth) -{ - if (depth >= 5) - return; - - if (!::IsValid(world)) - return; - - // Select the parts to be placed - const TArray parts = Select(tags, random); - if (parts.IsEmpty()) - return; - - const int32 partsIndex = random.Get(parts.Num()); - const FDungeonInteriorParts& selectedParts = parts[partsIndex]; - if (selectedParts.Class == nullptr) - { - DUNGEON_GENERATOR_LOG(TEXT("Set the class of interior parts")); - return; - } - - // Placement angle error - double spawnYaw = 0; - switch (selectedParts.DungeonAngleTypeOfPlacement) - { - case EDungeonAngleTypeOfPlacement::None: - break; - case EDungeonAngleTypeOfPlacement::Every180Degrees: - if (random.Get() & 1) - spawnYaw = 180.; - break; - case EDungeonAngleTypeOfPlacement::Every90Degrees: - spawnYaw = (static_cast(random.Get() & 3) * 90.f); - break; - case EDungeonAngleTypeOfPlacement::Free: - spawnYaw = random.Get(-180.f, 180.f); - break; - } - const FQuat spawnRotation = FRotator(0, spawnYaw, 0).Quaternion(); - - // Calculate placement transforms - FTransform spawnTransform(spawnRotation); - spawnTransform *= parentTransform; - - // Spawn actor - FActorSpawnParameters actorSpawnParameters; - actorSpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; - //actorSpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; - //actorSpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding; - //actorSpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::DontSpawnIfColliding; - actorSpawnParameters.bDeferConstruction = true; - AActor* actor = world->SpawnActor(selectedParts.Class, &spawnTransform, actorSpawnParameters); - if (!::IsValid(actor)) - { - DUNGEON_GENERATOR_LOG(TEXT("Failed to spawn interior actor %s"), *selectedParts.Class->GetName()); - return; - } - - actor->Tags.Add(CDungeonGeneratorCore::GetDungeonGeneratorTag()); -#if WITH_EDITOR - actor->SetFolderPath(TEXT("Dungeon/Interiors")); -#endif - - actor->FinishSpawning(spawnTransform, true); - - FBox bounds = actor->GetComponentsBoundingBox(); - if (!bounds.IsValid) - { - DUNGEON_GENERATOR_LOG(TEXT("Overlap with other collisions, destroy %s"), *actor->GetName()); - actor->Destroy(); - return; - } - - { - // Bounding box size away from the generation position - FVector boundsCenter = bounds.GetCenter(); - const FVector& boundsExtent = bounds.GetExtent(); - const FVector& wallPosition = parentTransform.GetLocation(); - FVector movementDirection = (depth == 0) - ? boundsExtent * parentTransform.GetRotation().GetRightVector() - : FVector::Zero(); - movementDirection.Z = boundsExtent.Z; - - // Placement position error - if (selectedParts.PercentageOfErrorInPlacement > 0) - { - const float percentageOfErrorInPlacement = static_cast(selectedParts.PercentageOfErrorInPlacement) / 100.f; - boundsCenter.X += boundsExtent.X * (random.Get() * percentageOfErrorInPlacement); - boundsCenter.Y += boundsExtent.Y * (random.Get() * percentageOfErrorInPlacement); - } - - // Calculate new transform - const FVector targetPosition = wallPosition + movementDirection; - const FVector spawnOffset = targetPosition - boundsCenter; - bounds = bounds.ShiftBy(spawnOffset); - } - - { - // Collision check - FCollisionQueryParams queryParams(TEXT("FDungeonInteriorDecorator:SpawnActorsImplement"), false, actor); - queryParams.bFindInitialOverlaps = true; - if (selectedParts.OverlapCheck && world->OverlapBlockingTestByChannel(bounds.GetCenter(), spawnRotation, ECC_Visibility, FCollisionShape::MakeBox(bounds.GetExtent() - FVector(10)), queryParams)) - { - DUNGEON_GENERATOR_LOG(TEXT("Overlap with other collisions, destroy %s"), *actor->GetName()); - actor->Destroy(); - } - else - { - // Decorating Decorations (Fillables) - // http://www.archmagerises.com/news/2021/6/12/how-to-procedurally-generate-and-decorate-3d-dungeon-rooms-in-unity-c - - if (selectedParts.AdditionalExtentToProhibitPlacement > 0) - { - bounds.Min.X -= selectedParts.AdditionalExtentToProhibitPlacement; - bounds.Min.Y -= selectedParts.AdditionalExtentToProhibitPlacement; - bounds.Max.X += selectedParts.AdditionalExtentToProhibitPlacement; - bounds.Max.Y += selectedParts.AdditionalExtentToProhibitPlacement; - } - - if (Intersects(bounds)) - { - DUNGEON_GENERATOR_LOG(TEXT("Location of prohibited placement of surrounding interior actors, destroy %s"), *actor->GetName()); - actor->Destroy(); - } - else - { - actor->SetActorLocation(bounds.GetCenter()); - - Asset->EachInteriorLocation(actor, [this, world, &tags, &random, depth](UDungeonInteriorLocationComponent* component) - { - SpawnActorsImplement(world, component->GetInteriorTags(), component->GetComponentTransform(), random, depth + 1); - } - ); - - mSensors.Emplace(bounds); - } - } - } -} - -TArray FDungeonInteriorDecorator::Select(const TArray& interiorTags, dungeon::Random& random) const -{ - return ::IsValid(Asset) ? Asset->Select(interiorTags, random) : TArray(); -} - -bool FDungeonInteriorDecorator::Intersects(const FBox& bounds) const -{ - for (const FBox& sensor : mSensors) - { - if (sensor.Intersect(bounds)) - return true; - } - - return false; -} - -std::shared_ptr FDungeonInteriorDecorator::Room(const FBox& bounds) const -{ - std::shared_ptr result = std::make_shared(Asset); - for (const auto& i : mDecorationLocations) - { - if (bounds.IsInsideOrOn(i->Position)) - { - result->mDecorationLocations.push_back(i); - } - } - return result; -} diff --git a/Source/DungeonGenerator/Private/DungeonLevelStreamingDynamic.cpp b/Source/DungeonGenerator/Private/DungeonLevelStreamingDynamic.cpp deleted file mode 100644 index 149b494..0000000 --- a/Source/DungeonGenerator/Private/DungeonLevelStreamingDynamic.cpp +++ /dev/null @@ -1,12 +0,0 @@ -/** -\author Shun Moriya -\copyright 2023- Shun Moriya -All Rights Reserved. -*/ - -#include "DungeonLevelStreamingDynamic.h" - -UDungeonLevelStreamingDynamic::UDungeonLevelStreamingDynamic(const FObjectInitializer& initializer) - : Super(initializer) -{ -} diff --git a/Source/DungeonGenerator/Private/DungeonMeshSet.cpp b/Source/DungeonGenerator/Private/DungeonMeshSet.cpp new file mode 100644 index 0000000..3006bed --- /dev/null +++ b/Source/DungeonGenerator/Private/DungeonMeshSet.cpp @@ -0,0 +1,59 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "DungeonMeshSet.h" +#include "Core/Direction.h" +#include "Core/Grid.h" +#include "Core/Math/Random.h" + +int32 FDungeonMeshSet::SelectDungeonMeshPartsIndex(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random, const int32 size, const EDungeonPartsSelectionMethod partsSelectionMethod) +{ + switch (partsSelectionMethod) + { + case EDungeonPartsSelectionMethod::GridIndex: + return gridIndex % size; + + case EDungeonPartsSelectionMethod::Direction: + return grid.GetDirection().Get() % size; + + case EDungeonPartsSelectionMethod::Random: + return random->Get(size); + + default: + return gridIndex % size; + } +} + +// aka: SelectParts, SelectRandomActorParts +FDungeonActorParts* FDungeonMeshSet::SelectActorParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random, const TArray& parts, const EDungeonPartsSelectionMethod partsSelectionMethod) +{ + const int32 size = parts.Num(); + if (size <= 0) + return nullptr; + + const int32 index = SelectDungeonMeshPartsIndex(gridIndex, grid, random, size, partsSelectionMethod); + FDungeonActorParts* actorParts = const_cast(&parts[index]); + return IsValid(actorParts->ActorClass) ? actorParts : nullptr; +} + +// aka: SelectParts, SelectActorParts +FDungeonRandomActorParts* FDungeonMeshSet::SelectRandomActorParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random, const TArray& parts, const EDungeonPartsSelectionMethod partsSelectionMethod) +{ + const int32 size = parts.Num(); + if (size <= 0) + return nullptr; + + const int32 index = SelectDungeonMeshPartsIndex(gridIndex, grid, random, size, partsSelectionMethod); + FDungeonRandomActorParts* actorParts = const_cast(&parts[index]); + if (!IsValid(actorParts->ActorClass)) + return nullptr; + + const float value = random->Get(); + if (value > actorParts->Frequency) + return nullptr; + + return actorParts; +} diff --git a/Source/DungeonGenerator/Private/DungeonInteriorLocationComponent.cpp b/Source/DungeonGenerator/Private/DungeonMeshSetDatabase.cpp similarity index 63% rename from Source/DungeonGenerator/Private/DungeonInteriorLocationComponent.cpp rename to Source/DungeonGenerator/Private/DungeonMeshSetDatabase.cpp index bbace12..4cc6422 100644 --- a/Source/DungeonGenerator/Private/DungeonInteriorLocationComponent.cpp +++ b/Source/DungeonGenerator/Private/DungeonMeshSetDatabase.cpp @@ -4,4 +4,4 @@ All Rights Reserved. */ -#include "DungeonInteriorLocationComponent.h" +#include "DungeonMeshSetDatabase.h" diff --git a/Source/DungeonGenerator/Private/DungeonRoomMeshSet.cpp b/Source/DungeonGenerator/Private/DungeonRoomMeshSet.cpp new file mode 100644 index 0000000..a2443c1 --- /dev/null +++ b/Source/DungeonGenerator/Private/DungeonRoomMeshSet.cpp @@ -0,0 +1,7 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "DungeonRoomMeshSet.h" diff --git a/Source/DungeonGenerator/Private/DungeonRoomMeshSetDatabase.cpp b/Source/DungeonGenerator/Private/DungeonRoomMeshSetDatabase.cpp new file mode 100644 index 0000000..86e562c --- /dev/null +++ b/Source/DungeonGenerator/Private/DungeonRoomMeshSetDatabase.cpp @@ -0,0 +1,23 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "DungeonRoomMeshSetDatabase.h" +#include "Core/Math/Random.h" + +const FDungeonRoomMeshSet* UDungeonRoomMeshSetDatabase::Select(const std::shared_ptr& random) const +{ + const int32 size = Parts.Num(); + if (size <= 0) + return nullptr; + + const uint32_t index = random->Get(size); + return &Parts[index]; +} + +const FDungeonRoomMeshSet* UDungeonRoomMeshSetDatabase::SelectImplement(const std::shared_ptr& random) const +{ + return Select(random); +} diff --git a/Source/DungeonGenerator/Private/MiniMap/DungeonMiniMap.cpp b/Source/DungeonGenerator/Private/MiniMap/DungeonMiniMap.cpp new file mode 100644 index 0000000..60f275a --- /dev/null +++ b/Source/DungeonGenerator/Private/MiniMap/DungeonMiniMap.cpp @@ -0,0 +1,18 @@ +/* +A mini-map of the dungeon generated in the editor +It contains the mini-map textures of each floor and the information +about the conversion from world coordinates to texture coordinates. +*/ + +#include "MiniMap/DungeonMiniMap.h" + +int32 UDungeonMiniMap::TransformWorldToLayer(const float worldElevation) const noexcept +{ + const int32 voxelHeight = static_cast(worldElevation * WorldToVoxelScale); + for (int32 i = 0; i < Elevations.Num(); ++i) + { + if (voxelHeight <= Elevations[i]) + return i; + } + return 0; +} diff --git a/Source/DungeonGenerator/Private/DungeonMiniMapTexture.cpp b/Source/DungeonGenerator/Private/MiniMap/DungeonMiniMapTexture.cpp similarity index 95% rename from Source/DungeonGenerator/Private/DungeonMiniMapTexture.cpp rename to Source/DungeonGenerator/Private/MiniMap/DungeonMiniMapTexture.cpp index 41f6670..fd6d4a6 100644 --- a/Source/DungeonGenerator/Private/DungeonMiniMapTexture.cpp +++ b/Source/DungeonGenerator/Private/MiniMap/DungeonMiniMapTexture.cpp @@ -4,8 +4,9 @@ All Rights Reserved. */ -#include "DungeonMiniMapTexture.h" +#include "MiniMap/DungeonMiniMapTexture.h" #include "DungeonGeneratorCore.h" +#include bool UDungeonMiniMapTexture::GenerateMiniMapTextureWithSize(const std::shared_ptr& dungeonGeneratorCore, const uint32_t textureWidth, const uint8 currentFloor) { diff --git a/Source/DungeonGenerator/Private/DungeonMiniMapTextureLayer.cpp b/Source/DungeonGenerator/Private/MiniMap/DungeonMiniMapTextureLayer.cpp similarity index 83% rename from Source/DungeonGenerator/Private/DungeonMiniMapTextureLayer.cpp rename to Source/DungeonGenerator/Private/MiniMap/DungeonMiniMapTextureLayer.cpp index 100a758..170df04 100644 --- a/Source/DungeonGenerator/Private/DungeonMiniMapTextureLayer.cpp +++ b/Source/DungeonGenerator/Private/MiniMap/DungeonMiniMapTextureLayer.cpp @@ -4,8 +4,8 @@ All Rights Reserved. */ -#include "DungeonMiniMapTextureLayer.h" -#include "DungeonMiniMapTexture.h" +#include "MiniMap/DungeonMiniMapTextureLayer.h" +#include "MiniMap/DungeonMiniMapTexture.h" #include "DungeonGeneratorCore.h" #include "Core/Generator.h" @@ -35,7 +35,7 @@ bool UDungeonMiniMapTextureLayer::GenerateMiniMapTextureWithSize(const std::shar return false; const std::vector& floorHeight = mGenerator->GetFloorHeight(); - DungeonMiniMapTextures.Reset(floorHeight.size()); + DungeonMiniMapTextures.Empty(); for (uint8_t i = 0; i < floorHeight.size(); ++i) { @@ -64,7 +64,7 @@ bool UDungeonMiniMapTextureLayer::GenerateMiniMapTextureWithScale(const std::sha return false; const std::vector& floorHeight = mGenerator->GetFloorHeight(); - DungeonMiniMapTextures.Reset(floorHeight.size()); + DungeonMiniMapTextures.Empty(); for (uint8_t i = 0; i < floorHeight.size(); ++i) { @@ -97,17 +97,25 @@ UDungeonMiniMapTexture* UDungeonMiniMapTextureLayer::GetByHeight(const float hei return GetByFloor(mGenerator->FindFloor(voxelHeight)); } +float UDungeonMiniMapTextureLayer::GetGeneratedScale() const noexcept +{ + return (DungeonMiniMapTextures.Num() > 0) ? DungeonMiniMapTextures[0]->GetGeneratedScale() : 1.f; +} + +float UDungeonMiniMapTextureLayer::GetScaleWorldToTexture() const noexcept +{ + return 1.f / mGridSize * GetGeneratedScale(); +} + +const std::vector& UDungeonMiniMapTextureLayer::GetFloorElevations() const noexcept +{ + return mGenerator->GetFloorHeight(); +} + FVector2D UDungeonMiniMapTextureLayer::ToRelative(const FVector& location) const { - if (DungeonMiniMapTextures.Num() <= 0) - { - return FVector2D::ZeroVector; - } - else - { - const float ratio = 1.f / mGridSize * DungeonMiniMapTextures[0]->GetGeneratedScale(); - return FVector2D(location.X, location.Y) * ratio; - } + const float ratio = GetScaleWorldToTexture(); + return FVector2D(location.X, location.Y) * ratio; } FVector2D UDungeonMiniMapTextureLayer::ToRelativeAndFloor(uint8& floor, const FVector& location) const diff --git a/Source/DungeonGenerator/Private/DungeonRoomItem.cpp b/Source/DungeonGenerator/Private/Mission/DungeonRoomItem.cpp similarity index 90% rename from Source/DungeonGenerator/Private/DungeonRoomItem.cpp rename to Source/DungeonGenerator/Private/Mission/DungeonRoomItem.cpp index 3bd258f..70287c8 100644 --- a/Source/DungeonGenerator/Private/DungeonRoomItem.cpp +++ b/Source/DungeonGenerator/Private/Mission/DungeonRoomItem.cpp @@ -4,7 +4,7 @@ All Rights Reserved. */ -#include "DungeonRoomItem.h" +#include "Mission/DungeonRoomItem.h" const FString& GetDungeonRoomItemName(const EDungeonRoomItem item) { diff --git a/Source/DungeonGenerator/Private/DungeonRoomParts.cpp b/Source/DungeonGenerator/Private/Mission/DungeonRoomParts.cpp similarity index 90% rename from Source/DungeonGenerator/Private/DungeonRoomParts.cpp rename to Source/DungeonGenerator/Private/Mission/DungeonRoomParts.cpp index 4bea3cf..e3a4249 100644 --- a/Source/DungeonGenerator/Private/DungeonRoomParts.cpp +++ b/Source/DungeonGenerator/Private/Mission/DungeonRoomParts.cpp @@ -4,17 +4,16 @@ All Rights Reserved. */ -#pragma once -#include "DungeonRoomParts.h" +#include "Mission/DungeonRoomParts.h" const FString& GetDungeonRoomPartsName(const EDungeonRoomParts parts) { static const FString names[] = { TEXT("Any"), - TEXT("Start"), - TEXT("Goal"), TEXT("Hall"), TEXT("Hanare"), + TEXT("Start"), + TEXT("Goal"), }; static_assert(DungeonRoomPartsSize == (sizeof(names) / sizeof(names[0]))); return names[static_cast(parts)]; diff --git a/Source/DungeonGenerator/Private/DungeonRoomProps.cpp b/Source/DungeonGenerator/Private/Mission/DungeonRoomProps.cpp similarity index 90% rename from Source/DungeonGenerator/Private/DungeonRoomProps.cpp rename to Source/DungeonGenerator/Private/Mission/DungeonRoomProps.cpp index 5ba4b4e..e617415 100644 --- a/Source/DungeonGenerator/Private/DungeonRoomProps.cpp +++ b/Source/DungeonGenerator/Private/Mission/DungeonRoomProps.cpp @@ -4,7 +4,7 @@ All Rights Reserved. */ -#include "DungeonRoomProps.h" +#include "Mission/DungeonRoomProps.h" const FString& GetDungeonRoomPropsName(const EDungeonRoomProps props) { diff --git a/Source/DungeonGenerator/Private/PluginInfomation.h b/Source/DungeonGenerator/Private/PluginInfomation.h index 32839e7..9a34709 100644 --- a/Source/DungeonGenerator/Private/PluginInfomation.h +++ b/Source/DungeonGenerator/Private/PluginInfomation.h @@ -1,15 +1,19 @@ -/** +/*! This is automatically generated file. DO NOT EDIT THIS FILE. \author Shun Moriya \copyright 2023- Shun Moriya +All Rights Reserved. */ #pragma once +#if !defined(DUNGENERATOR_PLUGIN_INFOMATION_DETECTED) +#define DUNGENERATOR_PLUGIN_INFOMATION_DETECTED #define DUNGENERATOR_PLUGIN_FILE_VERSION "3" -#define DUNGENERATOR_PLUGIN_VERSION "1" -#define DUNGENERATOR_PLUGIN_VERSION_NAME "0.1" -#define DUNGENERATOR_PLUGIN_FRIENDLY_NAME "DungeonGenerator" -#define DUNGENERATOR_PLUGIN_DESCRIPTION "Generates a three-dimensional dungeon." -#define DUNGENERATOR_PLUGIN_DOCS_URL "https://github.com/shun126/DungeonGenerator/wiki/" -#define DUNGENERATOR_PLUGIN_SUPPORT_URL "https://github.com/shun126/DungeonGenerator/issues/" +#define DUNGENERATOR_PLUGIN_VERSION "14" +#define DUNGENERATOR_PLUGIN_VERSION_NAME "1.5.0" +#define DUNGENERATOR_PLUGIN_FRIENDLY_NAME "Procedural 3D Dungeon Generator" +#define DUNGENERATOR_PLUGIN_DESCRIPTION "Procedural 3D dungeon generator plugin. Easy generation of levels, mini-maps and missions." +#define DUNGENERATOR_PLUGIN_DOCS_URL "https://github.com/shun126/UE5-DungeonGeneratorDemo/wiki" +#define DUNGENERATOR_PLUGIN_SUPPORT_URL "https://github.com/shun126/UE5-DungeonGeneratorDemo/discussions/" #define DUNGENERATOR_PLUGIN_BETA_VERSION "true" +#endif diff --git a/Source/DungeonGenerator/Private/DungeonDoor.cpp b/Source/DungeonGenerator/Private/SubActor/DungeonDoor.cpp similarity index 91% rename from Source/DungeonGenerator/Private/DungeonDoor.cpp rename to Source/DungeonGenerator/Private/SubActor/DungeonDoor.cpp index cc13ad4..c45c669 100644 --- a/Source/DungeonGenerator/Private/DungeonDoor.cpp +++ b/Source/DungeonGenerator/Private/SubActor/DungeonDoor.cpp @@ -4,7 +4,7 @@ All Rights Reserved. */ -#include "DungeonDoor.h" +#include "SubActor/DungeonDoor.h" void ADungeonDoor::Initialize(const EDungeonRoomProps props) { diff --git a/Source/DungeonGenerator/Private/DungeonRoomSensor.cpp b/Source/DungeonGenerator/Private/SubActor/DungeonRoomSensor.cpp similarity index 89% rename from Source/DungeonGenerator/Private/DungeonRoomSensor.cpp rename to Source/DungeonGenerator/Private/SubActor/DungeonRoomSensor.cpp index 41dbc9b..0a5c8c4 100644 --- a/Source/DungeonGenerator/Private/DungeonRoomSensor.cpp +++ b/Source/DungeonGenerator/Private/SubActor/DungeonRoomSensor.cpp @@ -4,11 +4,11 @@ All Rights Reserved. */ -#include "DungeonRoomSensor.h" +#include "SubActor/DungeonRoomSensor.h" #include "DungeonGeneratorCore.h" #include "Core/Identifier.h" +#include "Core/Math/Random.h" #include -#include #if WITH_EDITOR #include @@ -37,7 +37,6 @@ ADungeonRoomSensor::ADungeonRoomSensor(const FObjectInitializer& initializer) : Super(initializer) { #if WITH_EDITOR - // ティック有効化 PrimaryActorTick.bCanEverTick = PrimaryActorTick.bStartWithTickEnabled = true; #endif @@ -71,7 +70,6 @@ void ADungeonRoomSensor::Tick(float DeltaSeconds) output.Add(TEXT("BranchId:") + FString::FromInt(BranchId)); output.Add(TEXT("DepthFromStart:") + FString::FromInt(DepthFromStart)); - // 文字列を展開 FString message; for (const FString& line : output) { @@ -89,6 +87,7 @@ void ADungeonRoomSensor::Tick(float DeltaSeconds) #endif void ADungeonRoomSensor::Initialize( + const std::shared_ptr& random, const int32 identifier, const FVector& extents, const EDungeonRoomParts parts, @@ -99,6 +98,7 @@ void ADungeonRoomSensor::Initialize( { Finalize(); + mRandom = random; Identifier = identifier; //Bounding->InitBoxExtent(extents); Bounding->SetBoxExtent(extents); @@ -126,7 +126,7 @@ void ADungeonRoomSensor::Finalize() } //////////////////////////////////////////////////////////////////////////////////////////////////// -// BluePrint便利関数 +// BluePrint functions int32 ADungeonRoomSensor::IdealNumberOfActor(const float areaRequiredPerPerson, const int32 maxNumberOfActor) const { const FBox& bounds = Bounding->Bounds.GetBox(); @@ -140,13 +140,8 @@ int32 ADungeonRoomSensor::IdealNumberOfActor(const float areaRequiredPerPerson, bool ADungeonRoomSensor::RandomPoint(FVector& result, const float offsetHeight) const { - static std::random_device seed; - static std::mt19937 engine(seed()); // メルセンヌ・ツイスター法 - //static std::minstd_rand0 engine(seed()); // 線形合同法 - //static std::ranlux24_base engine(seed()); // キャリー付き減算法 - std::normal_distribution<> distribution(0., 1.); - double ratioX = distribution(engine); - double ratioY = distribution(engine); + double ratioX = mRandom->Get(); + double ratioY = mRandom->Get(); static constexpr double range = 0.8; ratioX = std::max(-range, std::min(ratioX, range)); @@ -165,7 +160,7 @@ bool ADungeonRoomSensor::RandomPoint(FVector& result, const float offsetHeight) center.Z - extent.Z * 2. ); - // ホームを最も近い地上の位置を調べる + // Find the nearest ground location return FindFloorHeightPosition(result, startPosition, endPosition, offsetHeight); } @@ -193,6 +188,7 @@ bool ADungeonRoomSensor::GetFloorHeightPosition(FVector& result, FVector startPo FVector endPosition = startPosition; endPosition.Z -= (bounds.GetExtent().Z * 2); + // Find the nearest ground location return FindFloorHeightPosition(result, startPosition, endPosition, offsetHeight); } @@ -212,7 +208,5 @@ bool ADungeonRoomSensor::FindFloorHeightPosition(FVector& result, const FVector& float ADungeonRoomSensor::GetDepthRatioFromStart() const { - if (DeepestDepthFromStart <= 0.f) - return 0.f; - return static_cast(DepthFromStart) / static_cast(DeepestDepthFromStart); + return (DeepestDepthFromStart == 0) ? 0.f : static_cast(DepthFromStart) / static_cast(DeepestDepthFromStart); } diff --git a/Source/DungeonGenerator/Private/DungeonLevelStreamingActor.cpp b/Source/DungeonGenerator/Private/SubLevel/DungeonLevelStreamingActor.cpp similarity index 97% rename from Source/DungeonGenerator/Private/DungeonLevelStreamingActor.cpp rename to Source/DungeonGenerator/Private/SubLevel/DungeonLevelStreamingActor.cpp index 9c035ba..b50b115 100644 --- a/Source/DungeonGenerator/Private/DungeonLevelStreamingActor.cpp +++ b/Source/DungeonGenerator/Private/SubLevel/DungeonLevelStreamingActor.cpp @@ -4,7 +4,7 @@ All Rights Reserved. */ -#include "DungeonLevelStreamingActor.h" +#include "SubLevel/DungeonLevelStreamingActor.h" #include #include #include diff --git a/Source/DungeonGenerator/Private/SubLevel/DungeonLevelStreamingDynamic.cpp b/Source/DungeonGenerator/Private/SubLevel/DungeonLevelStreamingDynamic.cpp new file mode 100644 index 0000000..5315465 --- /dev/null +++ b/Source/DungeonGenerator/Private/SubLevel/DungeonLevelStreamingDynamic.cpp @@ -0,0 +1,7 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "SubLevel/DungeonLevelStreamingDynamic.h" diff --git a/Source/DungeonGenerator/Public/DungeonInteriorAsset.h b/Source/DungeonGenerator/Public/Decorator/DungeonInteriorDatabase.h similarity index 55% rename from Source/DungeonGenerator/Public/DungeonInteriorAsset.h rename to Source/DungeonGenerator/Public/Decorator/DungeonInteriorDatabase.h index 452aa56..dcb76f1 100644 --- a/Source/DungeonGenerator/Public/DungeonInteriorAsset.h +++ b/Source/DungeonGenerator/Public/Decorator/DungeonInteriorDatabase.h @@ -8,7 +8,8 @@ All Rights Reserved. #include "DungeonInteriorLocationComponent.h" #include "DungeonInteriorParts.h" #include -#include "DungeonInteriorAsset.generated.h" +#include +#include "DungeonInteriorDatabase.generated.h" namespace dungeon { @@ -16,7 +17,7 @@ namespace dungeon } UCLASS(Blueprintable, BlueprintType) -class DUNGEONGENERATOR_API UDungeonInteriorAsset : public UObject +class DUNGEONGENERATOR_API UDungeonInteriorDatabase : public UObject { GENERATED_BODY() @@ -24,17 +25,17 @@ class DUNGEONGENERATOR_API UDungeonInteriorAsset : public UObject /* constructor */ - explicit UDungeonInteriorAsset(const FObjectInitializer& ObjectInitializer); + explicit UDungeonInteriorDatabase(const FObjectInitializer& ObjectInitializer); /* destructor */ - virtual ~UDungeonInteriorAsset() = default; + virtual ~UDungeonInteriorDatabase() = default; /* Selects FDungeonInteriorParts that match interiorTags */ - TArray Select(const TArray& interiorTags, dungeon::Random& random) const; + TArray Select(const TArray& interiorTags, const std::shared_ptr& random) const; /* Locate the actor's own UDungeonInteriorLocationComponent and select the interior you need. @@ -46,7 +47,13 @@ class DUNGEONGENERATOR_API UDungeonInteriorAsset : public UObject TArray Parts; }; -inline UDungeonInteriorAsset::UDungeonInteriorAsset(const FObjectInitializer& ObjectInitializer) +inline UDungeonInteriorDatabase::UDungeonInteriorDatabase(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { } + +UCLASS(Blueprintable, BlueprintType, Deprecated, meta = (DeprecationMessage = "UDungeonInteriorAsset is deprecated, use UDungeonInteriorDatabase.")) +class DUNGEONGENERATOR_API UDEPRECATED_DungeonInteriorAsset : public UDungeonInteriorDatabase +{ + GENERATED_BODY() +}; diff --git a/Source/DungeonGenerator/Public/Decorator/DungeonInteriorDecorator.h b/Source/DungeonGenerator/Public/Decorator/DungeonInteriorDecorator.h new file mode 100644 index 0000000..e61ad5e --- /dev/null +++ b/Source/DungeonGenerator/Public/Decorator/DungeonInteriorDecorator.h @@ -0,0 +1,213 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include "Decorator/DungeonInteriorUnplaceableBounds.h" +#include +#include "DungeonInteriorDecorator.generated.h" + +struct FDungeonInteriorParts; +class UDungeonInteriorDatabase; +namespace dungeon +{ + class Random; +} + +/* +Interior decorator class +http://www.archmagerises.com/news/2021/6/12/how-to-procedurally-generate-and-decorate-3d-dungeon-rooms-in-unity-c + +interior filter tags + room: aisle, slope, hall, hanare, start, goal + location: center, wall, roof + priority: major +*/ +USTRUCT(Blueprintable, BlueprintType) +struct DUNGEONGENERATOR_API FDungeonInteriorDecorator +{ + GENERATED_BODY() + +public: + // constructor + FDungeonInteriorDecorator() = default; + + // constructor + explicit FDungeonInteriorDecorator(UDungeonInteriorDatabase* asset); + + // constructor + FDungeonInteriorDecorator(UDungeonInteriorDatabase* asset, const std::shared_ptr& unplaceableBounds); + + // destructor + virtual ~FDungeonInteriorDecorator(); + + // initialize + void Initialize(UDungeonInteriorDatabase* asset); + + // finalize + void Finalize(); + + // check valid + bool IsValid() const noexcept; + + UDungeonInteriorDatabase* GetAsset(); + const UDungeonInteriorDatabase* GetAsset() const; + + + void ResetUnplacatableBounds(const std::shared_ptr& interiorUnplaceableBounds); + std::shared_ptr GetUnplacatableBounds() const noexcept; + bool IntersectUnplacatableBounds(const FBox& bounds) const; + bool IsInsideOrOnUnplacatableBounds(const FVector& location) const; + + + void ClearDecorationLocation(const std::shared_ptr& interiorUnplaceableBounds = nullptr); + void AddDecorationLocation(const FVector& location, const double yaw); + size_t GetDecorationLocationSize() const noexcept; + void ShuffleDecorationLocation(const std::shared_ptr& random); + + /* + Spawns an actor at the added decoration position. + Decoration positions that have been successfully spawned are deleted. + */ + void TrySpawnActors(UWorld* world, const TArray& tags, const std::shared_ptr& random); + + + + + /* + Selects FDungeonInteriorParts that match interiorTags + */ + TArray Select(const TArray& interiorTags, const std::shared_ptr& random) const; + + + + /* + Extract DecorationLocation in bounds + */ + std::shared_ptr CreateInteriorDecorator(const FBox& placeableBounds) const; + +private: + bool TrySpawnActorsImplement(UWorld* world, const TArray& tags, const FTransform& parentTransform, const std::shared_ptr& random, const size_t depth); + bool Intersects(const FBox& bounds) const; + +public: + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator") + UDungeonInteriorDatabase* Asset; + // TObjectPtr not used for UE4 compatibility + +private: + struct DecorationLocation final + { + FVector Position; + double Yaw; + + DecorationLocation(const FVector& position, const double yaw) + : Position(position), Yaw(yaw) + {} + DecorationLocation(const DecorationLocation& other) noexcept + : Position(other.Position), Yaw(other.Yaw) + {} + DecorationLocation(DecorationLocation&& other) noexcept + : Position(std::move(other.Position)), Yaw(other.Yaw) + {} + DecorationLocation& operator=(const DecorationLocation& other) noexcept + { + Position = other.Position; + Yaw = other.Yaw; + return *this; + } + DecorationLocation& operator=(DecorationLocation&& other) noexcept + { + Position = std::move(other.Position); + Yaw = other.Yaw; + return *this; + } + }; + std::vector> mDecorationLocations; + + // Placed actor bounds + TArray mPlacedBounds; + + // Unplaceable actor bounds + std::shared_ptr mUnplaceableBounds; +}; + +inline FDungeonInteriorDecorator::FDungeonInteriorDecorator(UDungeonInteriorDatabase* asset) + : Asset(asset) + , mUnplaceableBounds(std::make_shared()) +{ +} + +inline FDungeonInteriorDecorator::FDungeonInteriorDecorator(UDungeonInteriorDatabase* asset, const std::shared_ptr& unplaceableBounds) + : Asset(asset) + , mUnplaceableBounds(unplaceableBounds) +{ +} + +inline FDungeonInteriorDecorator::~FDungeonInteriorDecorator() +{ + Finalize(); +} + +inline void FDungeonInteriorDecorator::Initialize(UDungeonInteriorDatabase* asset) +{ + Asset = asset; +} + +inline void FDungeonInteriorDecorator::Finalize() +{ + Asset = nullptr; +} + +inline bool FDungeonInteriorDecorator::IsValid() const noexcept +{ + return Asset != nullptr; +} + +inline UDungeonInteriorDatabase* FDungeonInteriorDecorator::GetAsset() +{ + return Asset; +} + +inline const UDungeonInteriorDatabase* FDungeonInteriorDecorator::GetAsset() const +{ + return Asset; +} + +inline void FDungeonInteriorDecorator::ResetUnplacatableBounds(const std::shared_ptr& interiorUnplaceableBounds) +{ + mUnplaceableBounds = interiorUnplaceableBounds; +} + +inline std::shared_ptr FDungeonInteriorDecorator::GetUnplacatableBounds() const noexcept +{ + return mUnplaceableBounds; +} + +inline bool FDungeonInteriorDecorator::IntersectUnplacatableBounds(const FBox& bounds) const +{ + return mUnplaceableBounds ? mUnplaceableBounds->Intersect(bounds) : false; +} + +inline bool FDungeonInteriorDecorator::IsInsideOrOnUnplacatableBounds(const FVector& location) const +{ + return mUnplaceableBounds ? mUnplaceableBounds->IsInsideOrOn(location) : false; +} + +inline void FDungeonInteriorDecorator::ClearDecorationLocation(const std::shared_ptr& interiorUnplaceableBounds) +{ + ResetUnplacatableBounds(interiorUnplaceableBounds); + mDecorationLocations.clear(); +} + +inline void FDungeonInteriorDecorator::AddDecorationLocation(const FVector& location, const double yaw) +{ + mDecorationLocations.push_back(std::make_shared(location, yaw)); +} + +inline size_t FDungeonInteriorDecorator::GetDecorationLocationSize() const noexcept +{ + return mDecorationLocations.size(); +} diff --git a/Source/DungeonGenerator/Public/DungeonInteriorLocationComponent.h b/Source/DungeonGenerator/Public/Decorator/DungeonInteriorLocationComponent.h similarity index 70% rename from Source/DungeonGenerator/Public/DungeonInteriorLocationComponent.h rename to Source/DungeonGenerator/Public/Decorator/DungeonInteriorLocationComponent.h index 1eaa2f3..7bacb8e 100644 --- a/Source/DungeonGenerator/Public/DungeonInteriorLocationComponent.h +++ b/Source/DungeonGenerator/Public/Decorator/DungeonInteriorLocationComponent.h @@ -5,6 +5,7 @@ All Rights Reserved. */ #pragma once +#include #include "DungeonInteriorLocationComponent.generated.h" /* @@ -32,12 +33,16 @@ class DUNGEONGENERATOR_API UDungeonInteriorLocationComponent : public USceneComp /* Get interior tags */ - const TArray GetInteriorTags() const noexcept; + const TArray GetInquireInteriorTags() const noexcept; protected: - // interior tags (filter) + /* + interior tags (filter) + For example, if the creator returns the tag `kitchen`, the interior with the kitchen tag will be selected. + If multiple tags are set, actors containing all tags will be selected as spawn candidates. + */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DungeonGenerator") - TArray InteriorTags; + TArray InquireInteriorTags; }; inline UDungeonInteriorLocationComponent::UDungeonInteriorLocationComponent(const FObjectInitializer& ObjectInitializer) @@ -45,7 +50,7 @@ inline UDungeonInteriorLocationComponent::UDungeonInteriorLocationComponent(cons { } -inline const TArray UDungeonInteriorLocationComponent::GetInteriorTags() const noexcept +inline const TArray UDungeonInteriorLocationComponent::GetInquireInteriorTags() const noexcept { - return InteriorTags; + return InquireInteriorTags; } diff --git a/Source/DungeonGenerator/Public/DungeonInteriorParts.h b/Source/DungeonGenerator/Public/Decorator/DungeonInteriorParts.h similarity index 79% rename from Source/DungeonGenerator/Public/DungeonInteriorParts.h rename to Source/DungeonGenerator/Public/Decorator/DungeonInteriorParts.h index 20f7d0f..01cfb46 100644 --- a/Source/DungeonGenerator/Public/DungeonInteriorParts.h +++ b/Source/DungeonGenerator/Public/Decorator/DungeonInteriorParts.h @@ -5,7 +5,7 @@ All Rights Reserved. */ #pragma once -#include +#include "DungeonInteriorSystemTags.h" #include "DungeonInteriorParts.generated.h" // Angle of freedom @@ -33,26 +33,33 @@ struct DUNGEONGENERATOR_API FDungeonInteriorParts bool Match(const TArray& tags) const noexcept; public: - // -location : center, wall, roof - // - priority : major /* - If decorator contains a query tag, this part will be selected as a candidate. - If the InteriorTags of the DuneInteriorLocationComponent is set to 'cup', - The part with 'cup' set will be selected as a candidate for spawning. + Actor to spawn as interior + */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator", meta = (AllowedClasses = "Actor")) + UClass* Class; + // TObjectPtr not used for UE4 compatibility + /* List of tags to be set from the decorator + - priority : major - room parts: aisle, slope, hall, hanare, start, goal - location : center, wall - - priority : major + + Please let us know your opinion on whether additional tags, such as ceilings, are needed. */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator") - TSet InteriorTags = { TEXT("aisle"), TEXT("slope"), TEXT("hall"), TEXT("hanare"), TEXT("start"), TEXT("goal"), TEXT("center"), TEXT("wall") }; + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator|InteriorTags") + FDungeonInteriorSystemTags InteriorSystemTags; /* - Actor to spawn as interior + If decorator contains a query tag, this part will be selected as a candidate. + If the InteriorTags of the DuneInteriorLocationComponent is set to 'cup', + The part with 'cup' set will be selected as a candidate for spawning. + + You can set a tag for a room type, e.g., 'kitchen'. This can be specified from the DungeonRoomSensor class. */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator", meta = (AllowedClasses = "Actor")) - UClass* Class = nullptr; + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator|InteriorTags") + TSet InteriorAdditionalTags; /* Additional Extent to prohibit placement @@ -72,7 +79,7 @@ struct DUNGEONGENERATOR_API FDungeonInteriorParts After a query that contains InteriorTags, a lottery is drawn to see if spawning is possible. If 100, the lottery always succeeds. */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator", meta = (ClampMin = 0, ClampMax = 100)) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator", meta = (ClampMin = 1, ClampMax = 100)) uint8 Frequency = 100; /* @@ -80,7 +87,7 @@ struct DUNGEONGENERATOR_API FDungeonInteriorParts Specify the ratio of collision size. 0 means the center of the collision, 100 means collision size. */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator", meta = (ClampMin = 0, ClampMax = 100)) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator", meta = (ClampMin = 0, ClampMax = 200)) uint8 PercentageOfErrorInPlacement = 10; /* diff --git a/Source/DungeonGenerator/Public/Decorator/DungeonInteriorSystemTags.h b/Source/DungeonGenerator/Public/Decorator/DungeonInteriorSystemTags.h new file mode 100644 index 0000000..80449e6 --- /dev/null +++ b/Source/DungeonGenerator/Public/Decorator/DungeonInteriorSystemTags.h @@ -0,0 +1,132 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include +#include "DungeonInteriorSystemTags.generated.h" + +USTRUCT(Blueprintable, BlueprintType) +struct FDungeonInteriorSystemTags +{ + GENERATED_BODY() + +public: + const TSet& InteriorSystemTags() const + { + const_cast(this)->CacheInteriorSystemTags(); + return mCachedInteriorSystemTags; + } + + void TagMajor(const bool enable) + { + mDirtyInteriorSystemTags = major != enable; + major = enable; + } + + void TagAisle(const bool enable) + { + mDirtyInteriorSystemTags = aisle != enable; + aisle = enable; + } + + void TagSlope(const bool enable) + { + mDirtyInteriorSystemTags = slope != enable; + slope = true; + } + + void TagHall(const bool enable) + { + mDirtyInteriorSystemTags = hall != enable; + hall = enable; + } + + void TagHanare(const bool enable) + { + mDirtyInteriorSystemTags = hanare != enable; + hanare = enable; + } + + void TagStart(const bool enable) + { + mDirtyInteriorSystemTags = start != enable; + start = enable; + } + + void TagGoal(const bool enable) + { + mDirtyInteriorSystemTags = goal != enable; + goal = enable; + } + + void TagCenter(const bool enable) + { + mDirtyInteriorSystemTags = center != enable; + center = enable; + } + + void TagWall(const bool enable) + { + mDirtyInteriorSystemTags = wall != enable; + wall = enable; + } + +private: + void CacheInteriorSystemTags() + { +#if !WITH_EDITOR + if (mDirtyInteriorSystemTags) +#endif + { + mDirtyInteriorSystemTags = false; + + mCachedInteriorSystemTags.Empty(); +#define __ADD__(value) if (value) mCachedInteriorSystemTags.Add(TEXT(#value)) + __ADD__(major); + __ADD__(aisle); + __ADD__(slope); + __ADD__(hall); + __ADD__(hanare); + __ADD__(start); + __ADD__(goal); + __ADD__(center); + __ADD__(wall); +#undef __ADD__ + } + } + +protected: + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator|Priority") + bool major = false; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator|Location") + bool aisle = true; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator|Location") + bool slope = true; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator|Location") + bool hall = true; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator|Location") + bool hanare = true; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator|Location") + bool start = false; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator|Location") + bool goal = false; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator|Place") + bool center = false; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator|Place") + bool wall = true; + +private: + bool mDirtyInteriorSystemTags = true; + TSet mCachedInteriorSystemTags; +}; diff --git a/Source/DungeonGenerator/Public/Decorator/DungeonInteriorUnplaceableBounds.h b/Source/DungeonGenerator/Public/Decorator/DungeonInteriorUnplaceableBounds.h new file mode 100644 index 0000000..0b929b1 --- /dev/null +++ b/Source/DungeonGenerator/Public/Decorator/DungeonInteriorUnplaceableBounds.h @@ -0,0 +1,83 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include +#include + +class CDungeonInteriorUnplaceableBounds final +{ +public: + CDungeonInteriorUnplaceableBounds(); + explicit CDungeonInteriorUnplaceableBounds(const CDungeonInteriorUnplaceableBounds& other); + explicit CDungeonInteriorUnplaceableBounds(CDungeonInteriorUnplaceableBounds&& other) noexcept; + + CDungeonInteriorUnplaceableBounds& operator=(const CDungeonInteriorUnplaceableBounds& other); + CDungeonInteriorUnplaceableBounds& operator=(CDungeonInteriorUnplaceableBounds&& other) noexcept; + + void Add(const FBox& box); + void Clear(); + + bool Intersect(const FBox& bounds) const noexcept; + bool IsInsideOrOn(const FVector& location) const noexcept; + +private: + std::vector mUnplaceableBounds; +}; + +inline CDungeonInteriorUnplaceableBounds::CDungeonInteriorUnplaceableBounds() +{ +} + +inline CDungeonInteriorUnplaceableBounds::CDungeonInteriorUnplaceableBounds(const CDungeonInteriorUnplaceableBounds& other) + : mUnplaceableBounds(other.mUnplaceableBounds) +{ +} + +inline CDungeonInteriorUnplaceableBounds::CDungeonInteriorUnplaceableBounds(CDungeonInteriorUnplaceableBounds&& other) noexcept + : mUnplaceableBounds(std::move(other.mUnplaceableBounds)) +{ +} + +inline CDungeonInteriorUnplaceableBounds& CDungeonInteriorUnplaceableBounds::operator=(const CDungeonInteriorUnplaceableBounds& other) +{ + mUnplaceableBounds = other.mUnplaceableBounds; +} + +inline CDungeonInteriorUnplaceableBounds& CDungeonInteriorUnplaceableBounds::operator=(CDungeonInteriorUnplaceableBounds&& other) noexcept +{ + mUnplaceableBounds = std::move(other.mUnplaceableBounds); +} + +inline void CDungeonInteriorUnplaceableBounds::Add(const FBox& box) +{ + mUnplaceableBounds.emplace_back(box); +} + +inline void CDungeonInteriorUnplaceableBounds::Clear() +{ + mUnplaceableBounds.clear(); +} + +inline bool CDungeonInteriorUnplaceableBounds::Intersect(const FBox& bounds) const noexcept +{ + for (const FBox& unplacatableBounds : mUnplaceableBounds) + { + if (unplacatableBounds.Intersect(bounds)) + return true; + } + return false; +} + +inline bool CDungeonInteriorUnplaceableBounds::IsInsideOrOn(const FVector& location) const noexcept +{ + for (const FBox& unplacatableBounds : mUnplaceableBounds) + { + if (unplacatableBounds.IsInsideOrOn(location)) + return true; + } + return false; +} diff --git a/Source/DungeonGenerator/Public/DungeonAisleMeshSet.h b/Source/DungeonGenerator/Public/DungeonAisleMeshSet.h new file mode 100644 index 0000000..e750a23 --- /dev/null +++ b/Source/DungeonGenerator/Public/DungeonAisleMeshSet.h @@ -0,0 +1,28 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include "DungeonRoomMeshSet.h" +#include "DungeonAisleMeshSet.generated.h" + +USTRUCT(Blueprintable, BlueprintType) +struct DUNGEONGENERATOR_API FDungeonAisleMeshSet : public FDungeonRoomMeshSet +{ + GENERATED_BODY() + +public: + const FDungeonMeshParts* SelectSlopeParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const; + void EachSlopeParts(std::function func) const; + +protected: + // How to generate parts for stairs and ramps + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Parts|Sloop", BlueprintReadWrite) + EDungeonPartsSelectionMethod SloopPartsSelectionMethod = EDungeonPartsSelectionMethod::Random; + + // Stairs and ramp parts + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Parts|Sloop", BlueprintReadWrite) + TArray SlopeParts; +}; diff --git a/Source/DungeonGenerator/Public/DungeonAisleMeshSetDatabase.h b/Source/DungeonGenerator/Public/DungeonAisleMeshSetDatabase.h new file mode 100644 index 0000000..d0d0567 --- /dev/null +++ b/Source/DungeonGenerator/Public/DungeonAisleMeshSetDatabase.h @@ -0,0 +1,43 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include "DungeonMeshSetDatabase.h" +#include "DungeonAisleMeshSet.h" +#include "DungeonAisleMeshSetDatabase.generated.h" + +UCLASS(Blueprintable, BlueprintType) +class DUNGEONGENERATOR_API UDungeonAisleMeshSetDatabase : public UDungeonMeshSetDatabase +{ + GENERATED_BODY() + +public: + explicit UDungeonAisleMeshSetDatabase(const FObjectInitializer& ObjectInitializer); + virtual ~UDungeonAisleMeshSetDatabase() = default; + + const FDungeonAisleMeshSet* Select(const std::shared_ptr& random) const; + void Each(std::function func) const; + + // UDungeonMeshSetDatabase overrides + virtual const FDungeonRoomMeshSet* SelectImplement(const std::shared_ptr& random) const override; + +protected: + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator") + TArray Parts; +}; + +inline UDungeonAisleMeshSetDatabase::UDungeonAisleMeshSetDatabase(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +} + +inline void UDungeonAisleMeshSetDatabase::Each(std::function func) const +{ + for (const FDungeonAisleMeshSet& parts : Parts) + { + func(parts); + } +} diff --git a/Source/DungeonGenerator/Public/DungeonBlueprint.h b/Source/DungeonGenerator/Public/DungeonBlueprint.h index a88533c..922d90a 100644 --- a/Source/DungeonGenerator/Public/DungeonBlueprint.h +++ b/Source/DungeonGenerator/Public/DungeonBlueprint.h @@ -17,4 +17,28 @@ class UDungeonBlueprint : public UBlueprintFunctionLibrary GENERATED_BODY() public: + UFUNCTION(BlueprintCallable, Category = "DungeonGenerator") + static const FString& GetPluginVersion() noexcept; + + UFUNCTION(BlueprintCallable, Category = "DungeonGenerator") + static const FString& GetDocumentURL() noexcept; + + UFUNCTION(BlueprintCallable, Category = "DungeonGenerator") + static const FString& GetSupportURL() noexcept; + + UFUNCTION(BlueprintCallable, Category = "DungeonGenerator") + static const FString& GetBuildTag() noexcept; + + UFUNCTION(BlueprintCallable, Category = "DungeonGenerator") + static const FString& GetCommitID() noexcept; + + UFUNCTION(BlueprintCallable, Category = "DungeonGenerator") + static const FString& GetIdentifier() noexcept; + + UFUNCTION(BlueprintCallable, Category = "DungeonGenerator") + static const FString& GetLicenseType() noexcept; + + // Transform from world location to texture location + UFUNCTION(BlueprintCallable, Category = "DungeonGenerator/Minimap") + static FVector2D TransformWorldToTexture(const FVector worldLocation, const float worldToTextureScale) noexcept; }; diff --git a/Source/DungeonGenerator/Public/DungeonGenerateActor.h b/Source/DungeonGenerator/Public/DungeonGenerateActor.h index 65500b8..1c76a4c 100644 --- a/Source/DungeonGenerator/Public/DungeonGenerateActor.h +++ b/Source/DungeonGenerator/Public/DungeonGenerateActor.h @@ -5,7 +5,8 @@ All Rights Reserved. */ #pragma once -#include "DungeonRoomParts.h" +#include "Mission/DungeonRoomParts.h" +#include "Mission/DungeonRoomProps.h" #include #include #include "DungeonGenerateActor.generated.h" @@ -22,7 +23,6 @@ namespace dungeon DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDungeonGeneratorActorSignature, const FTransform&, transform); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDungeonGeneratorDoorSignature, AActor*, doorActor, EDungeonRoomProps, props); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDungeonGeneratorPlayerStartSignature, const FVector&, location); DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FDungeonGeneratorDelegete, bool, StreamingLevel, EDungeonRoomParts, DungeonRoomParts, const FBox&, RoomRect); /** @@ -50,6 +50,12 @@ class DUNGEONGENERATOR_API ADungeonGenerateActor : public AActor UFUNCTION(BlueprintCallable, Category = "DungeonGenerator") void GenerateDungeon(); + /** + Generate new dungeon + */ + UFUNCTION(BlueprintCallable, Category = "DungeonGenerator") + void GenerateDungeonWithSeed(const int32 generatedRandomSeed); + /** Destroy dungeon */ @@ -90,14 +96,24 @@ class DUNGEONGENERATOR_API ADungeonGenerateActor : public AActor UFUNCTION(BlueprintCallable, Category = "DungeonGenerator") UDungeonMiniMapTextureLayer* GetGeneratedMiniMapTextureLayer() const; + /** + Calculate CRC32 + \return CRC32 + */ + UFUNCTION(BlueprintCallable, Category = "DungeonGenerator") + int32 CalculateCRC32() const noexcept; + // AActor overrides virtual void PreInitializeComponents() override; + virtual void PostInitializeComponents() override; + virtual void BeginPlay() override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; virtual void Tick(float DeltaSeconds) override; #if WITH_EDITOR virtual bool ShouldTickIfViewportsOnly() const override; #endif + virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; private: static void BeginAddInstance(TArray& meshs); @@ -115,52 +131,65 @@ class DUNGEONGENERATOR_API ADungeonGenerateActor : public AActor protected: UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator") - UDungeonGenerateParameter* DungeonGenerateParameter = nullptr; + UDungeonGenerateParameter* DungeonGenerateParameter; + // TObjectPtr not used for UE4 compatibility #if WITH_EDITORONLY_DATA - //! Random number seeds + //! Generated random number seeds UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Transient, Category = "DungeonGenerator") int32 GeneratedRandomSeed = 0; + + //! Generated dungeon hash + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Transient, Category = "DungeonGenerator") + int32 GeneratedDungeonCRC32 = 0; #endif + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator") + bool ReplicatedServerDungeon = false; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator") + bool AutoGenerateAtStart = true; + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator") bool InstancedStaticMesh = false; - UPROPERTY(Transient, BlueprintReadOnly, Category = "DungeonGenerator") + UPROPERTY(Transient, BlueprintReadOnly, Category = "DungeonGenerator|InstancedStaticMesh") TArray FloorMeshs; + // TObjectPtr not used for UE4 compatibility - UPROPERTY(Transient, BlueprintReadOnly, Category = "DungeonGenerator") + UPROPERTY(Transient, BlueprintReadOnly, Category = "DungeonGenerator|InstancedStaticMesh") TArray SlopeMeshs; + // TObjectPtr not used for UE4 compatibility - UPROPERTY(Transient, BlueprintReadOnly, Category = "DungeonGenerator") + UPROPERTY(Transient, BlueprintReadOnly, Category = "DungeonGenerator|InstancedStaticMesh") TArray WallMeshs; + // TObjectPtr not used for UE4 compatibility - UPROPERTY(Transient, BlueprintReadOnly, Category = "DungeonGenerator") - TArray RoomRoofMeshs; + UPROPERTY(Transient, BlueprintReadOnly, Category = "DungeonGenerator|InstancedStaticMesh") + TArray RoofMeshs; + // TObjectPtr not used for UE4 compatibility - UPROPERTY(Transient, BlueprintReadOnly, Category = "DungeonGenerator") - TArray AisleRoofMeshs; - - UPROPERTY(Transient, BlueprintReadOnly, Category = "DungeonGenerator") + UPROPERTY(Transient, BlueprintReadOnly, Category = "DungeonGenerator|InstancedStaticMesh") TArray PillarMeshs; + // TObjectPtr not used for UE4 compatibility // event - UPROPERTY(BlueprintAssignable, Category = "Event") + UPROPERTY(BlueprintAssignable, Category = "Event", meta = (DeprecatedProperty, DeprecationMessage = "remove it in the next version.")) FDungeonGeneratorActorSignature OnCreateFloor; - UPROPERTY(BlueprintAssignable, Category = "Event") + UPROPERTY(BlueprintAssignable, Category = "Event", meta = (DeprecatedProperty, DeprecationMessage = "remove it in the next version.")) FDungeonGeneratorActorSignature OnCreateSlope; - UPROPERTY(BlueprintAssignable, Category = "Event") + UPROPERTY(BlueprintAssignable, Category = "Event", meta = (DeprecatedProperty, DeprecationMessage = "remove it in the next version.")) FDungeonGeneratorActorSignature OnCreateWall; - UPROPERTY(BlueprintAssignable, Category = "Event") + UPROPERTY(BlueprintAssignable, Category = "Event", meta = (DeprecatedProperty, DeprecationMessage = "remove it in the next version.")) FDungeonGeneratorActorSignature OnCreateRoomRoof; - UPROPERTY(BlueprintAssignable, Category = "Event") + UPROPERTY(BlueprintAssignable, Category = "Event", meta = (DeprecatedProperty, DeprecationMessage = "remove it in the next version.")) FDungeonGeneratorActorSignature OnCreateAisleRoof; - UPROPERTY(BlueprintAssignable, Category = "Event") + UPROPERTY(BlueprintAssignable, Category = "Event", meta = (DeprecatedProperty, DeprecationMessage = "remove it in the next version.")) FDungeonGeneratorActorSignature OnCreatePillar; @@ -171,12 +200,6 @@ class DUNGEONGENERATOR_API ADungeonGenerateActor : public AActor - /* - Notification to move the player to the starting position - Called at the timing of PreInitializeComponents - */ - UPROPERTY(BlueprintAssignable, Category = "Event") - FDungeonGeneratorPlayerStartSignature OnMovePlayerStart; /* 部屋の生成通知 @@ -190,8 +213,7 @@ class DUNGEONGENERATOR_API ADungeonGenerateActor : public AActor // Cache of the UIDungeonMiniMapTextureLayer UPROPERTY(BlueprintReadOnly, Transient, Category = "DungeonGenerator") UDungeonMiniMapTextureLayer* DungeonMiniMapTextureLayer; - // TObjectPtr not used for UE4 compatibility - + // TObjectPtr not used for UE4 compatibility // build job tag UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Transient, Category = "DungeonGenerator|Detail") diff --git a/Source/DungeonGenerator/Public/DungeonGenerateParameter.h b/Source/DungeonGenerator/Public/DungeonGenerateParameter.h index 5becccf..05f9e56 100644 --- a/Source/DungeonGenerator/Public/DungeonGenerateParameter.h +++ b/Source/DungeonGenerator/Public/DungeonGenerateParameter.h @@ -5,159 +5,19 @@ All Rights Reserved. */ #pragma once -#include "DungeonRoomAsset.h" -#include "DungeonRoomSensor.h" +#include "DungeonAisleMeshSetDatabase.h" +#include "DungeonRoomMeshSetDatabase.h" +#include "SubActor/DungeonRoomSensor.h" +#include "SubLevel/DungeonRoomLocator.h" +#include "SubLevel/DungeonRoomRegister.h" #include +#include #include "DungeonGenerateParameter.generated.h" // forward declaration -class UDungeonInteriorAsset; -class UDungeonRoomAsset; - -namespace dungeon -{ - class Direction; - class Grid; - class Random; -} - -/** -Part placement direction -*/ -UENUM(BlueprintType) -enum class EDungeonPartsPlacementDirection : uint8 -{ - North, - East, - South, - West, - FollowGridDirection, - RandomDirection -}; - -/** -Part Selection Method -*/ -UENUM(BlueprintType) -enum class EDungeonPartsSelectionMethod : uint8 -{ - Random, - GridIndex, - Direction, -}; - -/** -Parts transform -*/ -USTRUCT(BlueprintType) -struct DUNGEONGENERATOR_API FDungeonPartsTransform -{ - GENERATED_BODY() - -public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DungeonGenerator") - FTransform RelativeTransform; - - FTransform CalculateWorldTransform(const FTransform& transform) const noexcept; - FTransform CalculateWorldTransform(const FVector& position, const FRotator& rotator) const noexcept; - FTransform CalculateWorldTransform(const FVector& position, const float yaw) const noexcept; - FTransform CalculateWorldTransform(const FVector& position, const dungeon::Direction& direction) const noexcept; - -protected: - FTransform CalculateWorldTransform(dungeon::Random& random, const FTransform& transform, const EDungeonPartsPlacementDirection placementDirection) const noexcept; - FTransform CalculateWorldTransform(dungeon::Random& random, const FVector& position, const FRotator& rotator, const EDungeonPartsPlacementDirection placementDirection) const noexcept; - FTransform CalculateWorldTransform(dungeon::Random& random, const FVector& position, const float yaw, const EDungeonPartsPlacementDirection placementDirection) const noexcept; - FTransform CalculateWorldTransform(dungeon::Random& random, const FVector& position, const dungeon::Direction& direction, const EDungeonPartsPlacementDirection placementDirection) const noexcept; -}; - -/** -Actor Parts -*/ -USTRUCT(BlueprintType) -struct DUNGEONGENERATOR_API FDungeonActorParts : public FDungeonPartsTransform -{ - GENERATED_BODY() - -public: - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator", meta = (AllowedClasses = "Actor")) - UClass* ActorClass = nullptr; -}; - -/** -Door actor Parts -*/ -USTRUCT(BlueprintType) -struct DUNGEONGENERATOR_API FDungeonDoorActorParts : public FDungeonPartsTransform -{ - GENERATED_BODY() - -public: - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator", meta = (AllowedClasses = "DungeonDoor")) - UClass* ActorClass = nullptr; -}; - -/** -Mesh Parts -*/ -USTRUCT(BlueprintType) -struct DUNGEONGENERATOR_API FDungeonMeshParts : public FDungeonPartsTransform -{ - GENERATED_BODY() - -public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DungeonGenerator") - UStaticMesh* StaticMesh = nullptr; -}; - -/** -Actor parts with direction specification -*/ -USTRUCT(BlueprintType) -struct DUNGEONGENERATOR_API FDungeonActorPartsWithDirection : public FDungeonActorParts -{ - GENERATED_BODY() - -public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DungeonGenerator") - EDungeonPartsPlacementDirection PlacementDirection = EDungeonPartsPlacementDirection::RandomDirection; - - FTransform CalculateWorldTransform(dungeon::Random& random, const FTransform& transform) const noexcept; - FTransform CalculateWorldTransform(dungeon::Random& random, const FVector& position, const FRotator& rotator) const noexcept; - FTransform CalculateWorldTransform(dungeon::Random& random, const FVector& position, const float yaw) const noexcept; - FTransform CalculateWorldTransform(dungeon::Random& random, const FVector& position, const dungeon::Direction& direction) const noexcept; -}; - -/** -Mesh parts with direction specification -*/ -USTRUCT(BlueprintType) -struct DUNGEONGENERATOR_API FDungeonMeshPartsWithDirection : public FDungeonMeshParts -{ - GENERATED_BODY() - -public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DungeonGenerator") - EDungeonPartsPlacementDirection PlacementDirection = EDungeonPartsPlacementDirection::RandomDirection; - - FTransform CalculateWorldTransform(dungeon::Random& random, const FTransform& transform) const noexcept; - FTransform CalculateWorldTransform(dungeon::Random& random, const FVector& position, const FRotator& rotator) const noexcept; - FTransform CalculateWorldTransform(dungeon::Random& random, const FVector& position, const float yaw) const noexcept; - FTransform CalculateWorldTransform(dungeon::Random& random, const FVector& position, const dungeon::Direction& direction) const noexcept; -}; - -/** -Actor parts with Probability -*/ -USTRUCT(BlueprintType) -struct DUNGEONGENERATOR_API FDungeonRandomActorParts : public FDungeonActorPartsWithDirection -{ - GENERATED_BODY() - -public: - // Production frequency - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DungeonGenerator", meta = (ClampMin = "0.", ClampMax = "1.")) - float Frequency = 1.f; -}; +class UDungeonInteriorDatabase; +class UDungeonRoomDatabase; +struct FDungeonRoomLocator; /** Dungeon generation parameter class @@ -168,19 +28,11 @@ class DUNGEONGENERATOR_API UDungeonGenerateParameter : public UObject GENERATED_BODY() public: - /* - constructor - */ explicit UDungeonGenerateParameter(const FObjectInitializer& ObjectInitializer); - - /* - destructor - */ virtual ~UDungeonGenerateParameter() = default; int32 GetRandomSeed() const; int32 GetGeneratedRandomSeed() const; - void SetGeneratedRandomSeed(const int32 generatedRandomSeed); int32 GetNumberOfCandidateRooms() const; @@ -193,34 +45,10 @@ class DUNGEONGENERATOR_API UDungeonGenerateParameter : public UObject float GetGridSize() const; - const FDungeonMeshPartsWithDirection* SelectFloorParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const; - void EachFloorParts(std::function func) const; - - const FDungeonMeshParts* SelectSlopeParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const; - void EachSlopeParts(std::function func) const; - - const FDungeonMeshParts* SelectWallParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const; - void EachWallParts(std::function func) const; - - const FDungeonMeshPartsWithDirection* SelectRoomRoofParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const; - void EachRoomRoofParts(std::function func) const; - - const FDungeonMeshPartsWithDirection* SelectAisleRoofParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const; - void EachAisleRoofParts(std::function func) const; - - const FDungeonMeshParts* SelectPillarParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const; - void EachPillarParts(std::function func) const; - - const FDungeonRandomActorParts* SelectTorchParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const; - //const FDungeonRandomActorParts* SelectChandelierParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const; - - const FDungeonDoorActorParts* SelectDoorParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random) const; - bool IsMovePlayerStartToStartingPoint() const noexcept; - const FDungeonActorPartsWithDirection& GetStartParts() const; - - const FDungeonActorPartsWithDirection& GetGoalParts() const; + const FDungeonRoomRegister& GetStartRoom() const; + const FDungeonRoomRegister& GetGoalRoom() const; void EachDungeonRoomLocator(std::function func) const; @@ -235,26 +63,42 @@ class DUNGEONGENERATOR_API UDungeonGenerateParameter : public UObject // Converts from a grid coordinate system to a world coordinate system FIntVector ToGrid(const FVector& location) const; +private: + void SetRandomSeed(const int32 generateRandomSeed); + void SetGeneratedRandomSeed(const int32 generatedRandomSeed); + #if WITH_EDITOR void DumpToJson() const; + FString GetJsonDefaultDirectory() const; #endif private: - int32 SelectDungeonMeshPartsIndex(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random, const int32 size, const EDungeonPartsSelectionMethod partsSelectionMethod) const; - - template - T* SelectParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random, const TArray& parts, const EDungeonPartsSelectionMethod partsSelectionMethod) const; + const UDungeonRoomMeshSetDatabase* GetDungeonRoomPartsDatabase() const noexcept; + const UDungeonAisleMeshSetDatabase* GetDungeonAislePartsDatabase() const noexcept; - FDungeonActorParts* SelectActorParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random, const TArray& parts, const EDungeonPartsSelectionMethod partsSelectionMethod) const; + const FDungeonMeshPartsWithDirection* SelectFloorParts(const UDungeonMeshSetDatabase* dungeonPartsDatabase, const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const; + const FDungeonMeshParts* SelectWallParts(const UDungeonMeshSetDatabase* dungeonPartsDatabase, const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const; + const FDungeonMeshPartsWithDirection* SelectRoofParts(const UDungeonMeshSetDatabase* dungeonPartsDatabase, const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const; + const FDungeonMeshParts* SelectSlopeParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const; + const FDungeonMeshParts* SelectPillarParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const; + const FDungeonRandomActorParts* SelectTorchParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const; + const FDungeonDoorActorParts* SelectDoorParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const; - FDungeonRandomActorParts* SelectRandomActorParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random, const TArray& parts, const EDungeonPartsSelectionMethod partsSelectionMethod) const; + void EachSlopeParts(std::function func) const; + void EachPillarParts(std::function func) const; - template - void EachParts(const TArray& parts, std::function func) const; +private: + void EachFloorParts(std::function func) const; + void EachWallParts(std::function func) const; + void EachRoofParts(std::function func) const; -#if WITH_EDITOR - FString GetJsonDefaultDirectory() const; -#endif + void EachRoomFloorParts(std::function func) const; + void EachRoomWallParts(std::function func) const; + void EachRoomRoofParts(std::function func) const; + + void EachAisleFloorParts(std::function func) const; + void EachAisleWallParts(std::function func) const; + void EachAisleRoofParts(std::function func) const; protected: //! Seed of random number (if 0, auto-generated) @@ -265,37 +109,37 @@ class DUNGEONGENERATOR_API UDungeonGenerateParameter : public UObject UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Transient, Category = "DungeonGenerator") int32 GeneratedRandomSeed = 0; - // dungeon level - UPROPERTY(EditAnywhere, Category = "DungeonGenerator", BlueprintReadWrite, meta = (ClampMin = "1")) - uint8 NumberOfCandidateFloors = 3; - /** Candidate number of rooms to be generated This is the initial number of rooms to be generated, not the final number of rooms to be generated. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DungeonGenerator", meta = (ClampMin = "1")) - uint8 NumberOfCandidateRooms = 8; + uint8 NumberOfCandidateRooms = 20; //! Room Width UPROPERTY(EditAnywhere, Category = "DungeonGenerator", meta = (UIMin = 1, ClampMin = 1)) - FInt32Interval RoomWidth = { 4, 8 }; + FInt32Interval RoomWidth = { 3, 8 }; //! Room depth UPROPERTY(EditAnywhere, Category = "DungeonGenerator", meta = (UIMin = 1, ClampMin = 1)) - FInt32Interval RoomDepth = { 4, 8 }; + FInt32Interval RoomDepth = { 3, 8 }; //! Room height UPROPERTY(EditAnywhere, Category = "DungeonGenerator", meta = (UIMin = 1, ClampMin = 1)) - FInt32Interval RoomHeight = { 2, 3 }; + FInt32Interval RoomHeight = { 2, 4 }; //! Horizontal room margins UPROPERTY(EditAnywhere, Category = "DungeonGenerator", BlueprintReadWrite, meta = (ClampMin = "0"), DisplayName = "Room Horizontal Margin") - int32 RoomMargin = 1; + int32 RoomMargin = 2; //! Horizontal room-to-room coupling UPROPERTY(EditAnywhere, Category = "DungeonGenerator", BlueprintReadWrite) bool MergeRooms = false; + // Move PlayerStart to the starting point. + UPROPERTY(EditAnywhere, Category = "DungeonGenerator", BlueprintReadWrite) + bool MovePlayerStartToStartingPoint = true; + //! Vertical room-to-room margins UPROPERTY(EditAnywhere, Category = "DungeonGenerator", BlueprintReadWrite, meta = (ClampMin = "0")) int32 VerticalRoomMargin = 0; @@ -304,122 +148,170 @@ class DUNGEONGENERATOR_API UDungeonGenerateParameter : public UObject UPROPERTY(EditAnywhere, Category = "DungeonGenerator", BlueprintReadOnly) float GridSize = 100.f; - //! How to generate floor parts - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Floor", BlueprintReadWrite) - EDungeonPartsSelectionMethod FloorPartsSelectionMethod = EDungeonPartsSelectionMethod::Random; + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Parts", BlueprintReadWrite) + UDungeonRoomMeshSetDatabase* DungeonRoomPartsDatabase; + // TObjectPtr not used for UE4 compatibility - //! Floor parts - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Floor", BlueprintReadWrite) - TArray FloorParts; + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Parts", BlueprintReadWrite) + UDungeonAisleMeshSetDatabase* DungeonAislePartsDatabase; + // TObjectPtr not used for UE4 compatibility - //! How to generate parts for stairs and ramps - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Sloop", BlueprintReadWrite) - EDungeonPartsSelectionMethod SloopPartsSelectionMethod = EDungeonPartsSelectionMethod::Random; + // How to generate parts of pillar + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Parts|Pillar", BlueprintReadWrite) + EDungeonPartsSelectionMethod PillarPartsSelectionMethod = EDungeonPartsSelectionMethod::Random; - //! Stairs and ramp parts - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Sloop", BlueprintReadWrite) - TArray SlopeParts; + // Pillar Parts + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Parts|Pillar", BlueprintReadWrite) + TArray PillarParts; - //! How to generate wall parts - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Wall", BlueprintReadWrite) - EDungeonPartsSelectionMethod WallPartsSelectionMethod = EDungeonPartsSelectionMethod::Random; + // How to generate parts for torch + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Parts|Torch", BlueprintReadWrite) + EDungeonPartsSelectionMethod TorchPartsSelectionMethod = EDungeonPartsSelectionMethod::Random; - //! Wall parts - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Wall", BlueprintReadWrite) - TArray WallParts; + // Torch (pillar lighting) parts + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Parts|Torch", BlueprintReadWrite) + TArray TorchParts; - //! How to generate room parts - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|RoomRoof", BlueprintReadWrite) - EDungeonPartsSelectionMethod RoomRoofPartsSelectionMethod = EDungeonPartsSelectionMethod::Random; + // How to generate door parts + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Parts|Door", BlueprintReadWrite) + EDungeonPartsSelectionMethod DoorPartsSelectionMethod = EDungeonPartsSelectionMethod::Random; - //! Room Roof Parts - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|RoomRoof", BlueprintReadWrite) - TArray RoomRoofParts; + // Door Parts + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Parts|Door", BlueprintReadWrite) + TArray DoorParts; - //! How to generate aisle parts - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|AisleRoof", BlueprintReadWrite) - EDungeonPartsSelectionMethod AisleRoofPartsSelectionMethod = EDungeonPartsSelectionMethod::Random; + // Interior Asset + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Interior") + UDungeonInteriorDatabase* DungeonInteriorDatabase; + // TObjectPtr not used for UE4 compatibility - //! Roof parts of aisle - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|AisleRoof", BlueprintReadWrite) - TArray AisleRoofParts; + //! sublevel placement + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|SubLevel") + UDungeonRoomDatabase* DungeonRoomDatabase; + // TObjectPtr not used for UE4 compatibility - //! How to generate parts of columns - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Pillar", BlueprintReadWrite) - EDungeonPartsSelectionMethod PillarPartsSelectionMethod = EDungeonPartsSelectionMethod::Random; + /* + Places the specified sublevel in the starting room + If LevelPath is not set, the room is automatically generated. + */ + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|SubLevel", BlueprintReadWrite) + FDungeonRoomRegister StartRoom; - //! Pillar Parts - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Pillar", BlueprintReadWrite) - TArray PillarParts; + /* + Places the specified sublevel in the goal room + If LevelPath is not set, the room is automatically generated. + */ + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|SubLevel", BlueprintReadWrite) + FDungeonRoomRegister GoalRoom; - //! How to generate parts for torch (pillar lighting) - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Torch", BlueprintReadWrite) - EDungeonPartsSelectionMethod TorchPartsSelectionMethod = EDungeonPartsSelectionMethod::Random; + // Room Sensor Class + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|RoomSensor", BlueprintReadWrite, meta = (AllowedClasses = "DungeonRoomSensor")) + UClass* DungeonRoomSensorClass; + // TObjectPtr not used for UE4 compatibility - //! Torch (pillar lighting) parts - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Torch", BlueprintReadWrite) - TArray TorchParts; + friend class ADungeonGenerateActor; + friend class CDungeonGeneratorCore; +}; -/* - シャンデリア(天井の照明)のパーツ - UPROPERTY(EditAnywhere, Category="DungeonGenerator", BlueprintReadWrite) - TArray ChandelierParts; -*/ +inline int32 UDungeonGenerateParameter::GetRandomSeed() const +{ + return RandomSeed; +} - //! How to generate door parts - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Door", BlueprintReadWrite) - EDungeonPartsSelectionMethod DoorPartsSelectionMethod = EDungeonPartsSelectionMethod::Random; +inline int32 UDungeonGenerateParameter::GetGeneratedRandomSeed() const +{ + return GeneratedRandomSeed; +} - // Door Parts - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Door", BlueprintReadWrite) - TArray DoorParts; +inline void UDungeonGenerateParameter::SetRandomSeed(const int32 generateRandomSeed) +{ + RandomSeed = generateRandomSeed; +} - // Move PlayerStart to the starting point. - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Start", BlueprintReadWrite) - bool MovePlayerStartToStartingPoint = true; +inline void UDungeonGenerateParameter::SetGeneratedRandomSeed(const int32 generatedRandomSeed) +{ + GeneratedRandomSeed = generatedRandomSeed; +} - // starting point - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Start", BlueprintReadWrite) - FDungeonActorPartsWithDirection StartParts; +inline int32 UDungeonGenerateParameter::GetNumberOfCandidateRooms() const +{ + return NumberOfCandidateRooms; +} - // goal position - UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Goal", BlueprintReadWrite) - FDungeonActorPartsWithDirection GoalParts; +inline const FInt32Interval& UDungeonGenerateParameter::GetRoomWidth() const noexcept +{ + return RoomWidth; +} - // Room Sensor Class - UPROPERTY(EditAnywhere, Category = "DungeonGenerator", BlueprintReadWrite, meta = (AllowedClasses = "DungeonRoomSensor")) - UClass* DungeonRoomSensorClass = ADungeonRoomSensor::StaticClass(); +inline const FInt32Interval& UDungeonGenerateParameter::GetRoomDepth() const noexcept +{ + return RoomDepth; +} - // Interior Asset - UPROPERTY(EditAnywhere, Category = "DungeonGenerator") - UDungeonInteriorAsset* DungeonInteriorAsset; - // TObjectPtr not used for UE4 compatibility +inline const FInt32Interval& UDungeonGenerateParameter::GetRoomHeight() const noexcept +{ + return RoomHeight; +} - //! sublevel placement - UPROPERTY(EditAnywhere, Category = "DungeonGenerator") - UDungeonRoomAsset* DungeonRoomAsset; - // TObjectPtr not used for UE4 compatibility +inline int32 UDungeonGenerateParameter::GetHorizontalRoomMargin() const noexcept +{ + return RoomMargin; +} - friend class CDungeonGeneratorCore; -}; +inline int32 UDungeonGenerateParameter::GetVerticalRoomMargin() const noexcept +{ + return VerticalRoomMargin; +} -// aka: SelectActorParts, SelectRandomActorParts -template -inline T* UDungeonGenerateParameter::SelectParts(const size_t gridIndex, const dungeon::Grid& grid, dungeon::Random& random, const TArray& parts, const EDungeonPartsSelectionMethod partsSelectionMethod) const +inline float UDungeonGenerateParameter::GetGridSize() const { - const int32 size = parts.Num(); - if (size <= 0) - return nullptr; + return GridSize; +} + +inline bool UDungeonGenerateParameter::IsMovePlayerStartToStartingPoint() const noexcept +{ + return MovePlayerStartToStartingPoint; +} + +inline const UDungeonRoomMeshSetDatabase* UDungeonGenerateParameter::GetDungeonRoomPartsDatabase() const noexcept +{ + return DungeonRoomPartsDatabase; +} + +inline const UDungeonAisleMeshSetDatabase* UDungeonGenerateParameter::GetDungeonAislePartsDatabase() const noexcept +{ + return DungeonAislePartsDatabase; +} - const int32 index = SelectDungeonMeshPartsIndex(gridIndex, grid, random, size, partsSelectionMethod); - return const_cast(&parts[index]); +inline const FDungeonRoomRegister& UDungeonGenerateParameter::GetStartRoom() const +{ + return StartRoom; +} + +inline const FDungeonRoomRegister& UDungeonGenerateParameter::GetGoalRoom() const +{ + return GoalRoom; +} + +inline UClass* UDungeonGenerateParameter::GetRoomSensorClass() const +{ + return DungeonRoomSensorClass; +} + +inline void UDungeonGenerateParameter::EachFloorParts(std::function func) const +{ + EachRoomFloorParts(func); + EachAisleFloorParts(func); +} + +inline void UDungeonGenerateParameter::EachWallParts(std::function func) const +{ + EachRoomWallParts(func); + EachAisleWallParts(func); } -template -inline void UDungeonGenerateParameter::EachParts(const TArray& parts, std::function func) const +inline void UDungeonGenerateParameter::EachRoofParts(std::function func) const { - for (const auto& part : parts) - { - func(part); - } + EachRoomRoofParts(func); + EachAisleRoofParts(func); } diff --git a/Source/DungeonGenerator/Public/DungeonGeneratorCore.h b/Source/DungeonGenerator/Public/DungeonGeneratorCore.h index 1c15e48..efafc23 100644 --- a/Source/DungeonGenerator/Public/DungeonGeneratorCore.h +++ b/Source/DungeonGenerator/Public/DungeonGeneratorCore.h @@ -5,10 +5,11 @@ All Rights Reserved. */ #pragma once -#include "DungeonInteriorDecorator.h" -#include "DungeonRoomItem.h" -#include "DungeonRoomParts.h" -#include "DungeonRoomProps.h" +#include "Decorator/DungeonInteriorDecorator.h" +#include "Mission/DungeonRoomItem.h" +#include "Mission/DungeonRoomParts.h" +#include "Mission/DungeonRoomProps.h" +#include "SubLevel/DungeonRoomRegister.h" #include #include #include @@ -16,6 +17,7 @@ All Rights Reserved. #include // Forward declaration +class CDungeonInteriorUnplaceableBounds; class UDungeonGenerateParameter; class ULevelStreamingDynamic; class UStaticMesh; @@ -27,6 +29,7 @@ namespace dungeon { class Identifier; class Generator; + class Random; class Room; } @@ -58,14 +61,13 @@ class DUNGEONGENERATOR_API CDungeonGeneratorCore final : public std::enable_shar /** destructor */ - ~CDungeonGeneratorCore() = default; + ~CDungeonGeneratorCore(); // event void OnAddFloor(const AddStaticMeshEvent& func); void OnAddSlope(const AddStaticMeshEvent& func); void OnAddWall(const AddStaticMeshEvent& func); - void OnAddRoomRoof(const AddStaticMeshEvent& func); - void OnAddAisleRoof(const AddStaticMeshEvent& func); + void OnAddRoof(const AddStaticMeshEvent& func); void OnAddPillar(const AddPillarStaticMeshEvent& func); void OnResetTorch(const ResetActorEvent& func); //void OnAddChandelier(const ResetActorEvent& func); @@ -137,6 +139,12 @@ class DUNGEONGENERATOR_API CDungeonGeneratorCore final : public std::enable_shar */ UTexture2D* GenerateMiniMapTextureWithScale(uint32_t& worldToTextureScale, uint32_t textureScale, uint32_t currentLevel) const; + /** + Calculate CRC32 + \return CRC32 + */ + uint32_t CalculateCRC32() const noexcept; + /** Get dungeon generation core object. \return dungeon::Generator @@ -148,9 +156,14 @@ class DUNGEONGENERATOR_API CDungeonGeneratorCore final : public std::enable_shar #endif private: - bool CreateImpl_AddRoomAsset(const UDungeonGenerateParameter* parameter, const std::shared_ptr& room); - void AddTerrain(); - void AddObject(); + bool CreateImplement_AddRoomAsset(const UDungeonGenerateParameter* parameter, const std::shared_ptr& room); + bool CreateImplement_AddRoomAsset(const FDungeonRoomRegister& roomRegister, const std::shared_ptr& room, const float gridSize); + void CreateImplement_AddTerrain(); + void CreateImplement_AddFloorAndSlope(const UDungeonGenerateParameter* parameter, const FIntVector& location); + void CreateImplement_AddWall(const UDungeonGenerateParameter* parameter, const FIntVector& location); + void CreateImplement_AddPillarAndTorch(std::vector& spawnedTorchBounds, const UDungeonGenerateParameter* parameter, const FIntVector& location); + void CreateImplement_AddDoor(const UDungeonGenerateParameter* parameter, const FIntVector& location); + void CreateImplement_AddRoof(const UDungeonGenerateParameter* parameter, const FIntVector& location); //////////////////////////////////////////////////////////////////////////// // Interior @@ -163,6 +176,7 @@ class DUNGEONGENERATOR_API CDungeonGeneratorCore final : public std::enable_shar AStaticMeshActor* SpawnStaticMeshActor(UStaticMesh* staticMesh, const FName& folderPath, const FTransform& transform, const ESpawnActorCollisionHandlingMethod spawnActorCollisionHandlingMethod = ESpawnActorCollisionHandlingMethod::AlwaysSpawn) const; void SpawnActorOnFloor(UClass* actorClass, const FTransform& transform) const; void SpawnDoorActor(UClass* actorClass, const FTransform& transform, EDungeonRoomProps props) const; + AActor* SpawnTorchActor(std::vector& spawnedTorchBounds, const FVector& wallNormal, UClass* actorClass, const FName& folderPath, const FTransform& transform, ESpawnActorCollisionHandlingMethod spawnActorCollisionHandlingMethod = ESpawnActorCollisionHandlingMethod::AlwaysSpawn) const; ADungeonRoomSensor* SpawnRoomSensorActor( UClass* actorClass, const dungeon::Identifier& identifier, @@ -179,18 +193,23 @@ class DUNGEONGENERATOR_API CDungeonGeneratorCore final : public std::enable_shar void DestroySpawnedActors() const; static void DestroySpawnedActors(UWorld* world); + //////////////////////////////////////////////////////////////////////////// template T* FindActor(); template const T* FindActor() const; + //////////////////////////////////////////////////////////////////////////// + template void EachActors(std::function function); + template void EachActors(std::function function) const; + //////////////////////////////////////////////////////////////////////////// bool IsStreamLevelRequested(const FSoftObjectPath& levelPath) const; void RequestStreamLevel(const FSoftObjectPath& levelPath, const FVector& levelLocation); - void AsyncLoadStreamLevels(); + void FlushLoadStreamLevels(); + void AsyncLoadStreamLevels(const bool bShouldBlockOnLoad = false); #if WITH_EDITOR void SyncLoadStreamLevels(); #endif void UnloadStreamLevels(); - TSoftObjectPtr FindLoadedStreamLevel(const FSoftObjectPath& levelPath) const; void LoadStreamLevelImplement(UWorld* world, const FSoftObjectPath& path, const FTransform& transform); void UnloadStreamLevelImplement(UWorld* world, const FSoftObjectPath& path, const bool shouldBlockOnUnload); @@ -198,6 +217,10 @@ class DUNGEONGENERATOR_API CDungeonGeneratorCore final : public std::enable_shar //////////////////////////////////////////////////////////////////////////// UTexture2D* GenerateMiniMapTexture(uint32_t worldToTextureScale, uint32_t textureWidthHeight, uint32_t currentLevel) const; + //////////////////////////////////////////////////////////////////////////// + std::shared_ptr GetRandom() noexcept; + std::shared_ptr GetRandom() const noexcept; + //////////////////////////////////////////////////////////////////////////// #if WITH_EDITOR void DrawRoomAisleInformation() const; @@ -212,18 +235,18 @@ class DUNGEONGENERATOR_API CDungeonGeneratorCore final : public std::enable_shar AddStaticMeshEvent mOnAddFloor; AddStaticMeshEvent mOnAddSlope; AddStaticMeshEvent mOnAddWall; - AddStaticMeshEvent mOnAddRoomRoof; - AddStaticMeshEvent mOnAddAisleRoof; + AddStaticMeshEvent mOnAddRoof; AddPillarStaticMeshEvent mOnResetPillar; ResetActorEvent mOnResetTorch; ResetDoorEvent mOnResetDoor; + std::shared_ptr mInteriorUnplaceableBounds; FDungeonInteriorDecorator mAisleInteriorDecorator; FDungeonInteriorDecorator mSlopeInteriorDecorator; FDungeonInteriorDecorator mRoomInteriorDecorator; - struct LoadStreamLevelParameter + struct LoadStreamLevelParameter final { FSoftObjectPath mPath; FVector mLocation; @@ -259,14 +282,9 @@ inline void CDungeonGeneratorCore::OnAddWall(const AddStaticMeshEvent& func) mOnAddWall = func; } -inline void CDungeonGeneratorCore::OnAddRoomRoof(const AddStaticMeshEvent& func) +inline void CDungeonGeneratorCore::OnAddRoof(const AddStaticMeshEvent& func) { - mOnAddRoomRoof = func; -} - -inline void CDungeonGeneratorCore::OnAddAisleRoof(const AddStaticMeshEvent& func) -{ - mOnAddAisleRoof = func; + mOnAddRoof = func; } inline void CDungeonGeneratorCore::OnAddPillar(const AddPillarStaticMeshEvent& func) @@ -342,3 +360,29 @@ inline const T* CDungeonGeneratorCore::FindActor() const } return nullptr; } + +template +inline void CDungeonGeneratorCore::EachActors(std::function function) +{ + UWorld* world = mWorld.Get(); + if (IsValid(world)) + { + for (TActorIterator iterator(world); iterator; ++iterator) + { + function(*iterator); + } + } +} + +template +inline void CDungeonGeneratorCore::EachActors(std::function function) const +{ + UWorld* world = mWorld.Get(); + if (IsValid(world)) + { + for (TActorIterator iterator(world); iterator; ++iterator) + { + function(*iterator); + } + } +} diff --git a/Source/DungeonGenerator/Public/DungeonInteriorDecorator.h b/Source/DungeonGenerator/Public/DungeonInteriorDecorator.h deleted file mode 100644 index 3618f2b..0000000 --- a/Source/DungeonGenerator/Public/DungeonInteriorDecorator.h +++ /dev/null @@ -1,115 +0,0 @@ -/** -\author Shun Moriya -\copyright 2023- Shun Moriya -All Rights Reserved. -*/ - -#pragma once -#include "DungeonInteriorAsset.h" -#include -#include -#include "DungeonInteriorDecorator.generated.h" - -namespace dungeon -{ - class Random; -} - -/* -Interior decorator class -http://www.archmagerises.com/news/2021/6/12/how-to-procedurally-generate-and-decorate-3d-dungeon-rooms-in-unity-c - -interior filter tags - room: aisle, slope, hall, hanare, start, goal - location: center, wall, roof - priority: major -*/ -USTRUCT(Blueprintable, BlueprintType) -struct DUNGEONGENERATOR_API FDungeonInteriorDecorator -{ - GENERATED_BODY() - -public: - // constructor - FDungeonInteriorDecorator() = default; - - // constructor - explicit FDungeonInteriorDecorator(UDungeonInteriorAsset* asset); - - // destructor - virtual ~FDungeonInteriorDecorator(); - - // initialize - void Initialize(UDungeonInteriorAsset* asset); - - // finalize - void Finalize(); - - // check valid - bool IsValid() const noexcept; - - UDungeonInteriorAsset* GetAsset(); - const UDungeonInteriorAsset* GetAsset() const; - - - void ClearDecorationLocation(); - void AddDecorationLocation(const FVector& location, const double yaw); - size_t GetDecorationLocationSize() const noexcept; - void ShuffleDecorationLocation(dungeon::Random& random); - - - void SpawnActors(UWorld* world, const TArray& tags, dungeon::Random& random); - - - - - /* - Selects FDungeonInteriorParts that match interiorTags - */ - TArray Select(const TArray& interiorTags, dungeon::Random& random) const; - - - - /* - Extract DecorationLocation in bounds - */ - std::shared_ptr Room(const FBox& bounds) const; - -private: - void SpawnActorsImplement(UWorld* world, const TArray& tags, const FTransform& parentTransform, dungeon::Random& random, const size_t depth); - bool Intersects(const FBox& bounds) const; - -public: - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator") - UDungeonInteriorAsset* Asset = nullptr; - -private: - struct DecorationLocation final - { - FVector Position; - double Yaw; - - DecorationLocation(const FVector& position, const double yaw) - : Position(position), Yaw(yaw) - {} - DecorationLocation(const DecorationLocation& other) noexcept - : Position(other.Position), Yaw(other.Yaw) - {} - DecorationLocation(DecorationLocation&& other) noexcept - : Position(std::move(other.Position)), Yaw(other.Yaw) - {} - DecorationLocation& operator=(const DecorationLocation& other) noexcept - { - Position = other.Position; - Yaw = other.Yaw; - } - DecorationLocation& operator=(DecorationLocation&& other) noexcept - { - Position = std::move(other.Position); - Yaw = other.Yaw; - } - }; - std::vector> mDecorationLocations; - - TArray mSensors; -}; diff --git a/Source/DungeonGenerator/Public/DungeonMeshSet.h b/Source/DungeonGenerator/Public/DungeonMeshSet.h new file mode 100644 index 0000000..908b691 --- /dev/null +++ b/Source/DungeonGenerator/Public/DungeonMeshSet.h @@ -0,0 +1,200 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include +#include +#include +#include "DungeonMeshSet.generated.h" + +// forward declaration +class UClass; +class UStaticMesh; + +namespace dungeon +{ + class Direction; + class Grid; + class Random; +} + +/** +Part placement direction +*/ +UENUM(BlueprintType) +enum class EDungeonPartsPlacementDirection : uint8 +{ + North, + East, + South, + West, + FollowGridDirection, + RandomDirection +}; + +/** +Part Selection Method +*/ +UENUM(BlueprintType) +enum class EDungeonPartsSelectionMethod : uint8 +{ + Random, + GridIndex, + Direction, +}; + +/** +Parts transform +*/ +USTRUCT(BlueprintType) +struct DUNGEONGENERATOR_API FDungeonPartsTransform +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DungeonGenerator") + FTransform RelativeTransform; + + FTransform CalculateWorldTransform(const FTransform& transform) const noexcept; + FTransform CalculateWorldTransform(const FVector& position, const FRotator& rotator) const noexcept; + FTransform CalculateWorldTransform(const FVector& position, const float yaw) const noexcept; + FTransform CalculateWorldTransform(const FVector& position, const dungeon::Direction& direction) const noexcept; + +protected: + FTransform CalculateWorldTransform(const std::shared_ptr& random, const FTransform& transform, const EDungeonPartsPlacementDirection placementDirection) const noexcept; + FTransform CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const FRotator& rotator, const EDungeonPartsPlacementDirection placementDirection) const noexcept; + FTransform CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const float yaw, const EDungeonPartsPlacementDirection placementDirection) const noexcept; + FTransform CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const dungeon::Direction& direction, const EDungeonPartsPlacementDirection placementDirection) const noexcept; +}; + +/** +Actor Parts +*/ +USTRUCT(BlueprintType) +struct DUNGEONGENERATOR_API FDungeonActorParts : public FDungeonPartsTransform +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator", meta = (AllowedClasses = "Actor")) + UClass* ActorClass = nullptr; +}; + +/** +Door actor Parts +*/ +USTRUCT(BlueprintType) +struct DUNGEONGENERATOR_API FDungeonDoorActorParts : public FDungeonPartsTransform +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator", meta = (AllowedClasses = "DungeonDoor")) + UClass* ActorClass = nullptr; +}; + +/** +Mesh Parts +*/ +USTRUCT(BlueprintType) +struct DUNGEONGENERATOR_API FDungeonMeshParts : public FDungeonPartsTransform +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DungeonGenerator") + UStaticMesh* StaticMesh = nullptr; +}; + +/** +Actor parts with direction specification +*/ +USTRUCT(BlueprintType) +struct DUNGEONGENERATOR_API FDungeonActorPartsWithDirection : public FDungeonActorParts +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DungeonGenerator") + EDungeonPartsPlacementDirection PlacementDirection = EDungeonPartsPlacementDirection::RandomDirection; + + FTransform CalculateWorldTransform(const std::shared_ptr& random, const FTransform& transform) const noexcept; + FTransform CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const FRotator& rotator) const noexcept; + FTransform CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const float yaw) const noexcept; + FTransform CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const dungeon::Direction& direction) const noexcept; +}; + +/** +Mesh parts with direction specification +*/ +USTRUCT(BlueprintType) +struct DUNGEONGENERATOR_API FDungeonMeshPartsWithDirection : public FDungeonMeshParts +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DungeonGenerator") + EDungeonPartsPlacementDirection PlacementDirection = EDungeonPartsPlacementDirection::RandomDirection; + + FTransform CalculateWorldTransform(const std::shared_ptr& random, const FTransform& transform) const noexcept; + FTransform CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const FRotator& rotator) const noexcept; + FTransform CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const float yaw) const noexcept; + FTransform CalculateWorldTransform(const std::shared_ptr& random, const FVector& position, const dungeon::Direction& direction) const noexcept; +}; + +/** +Actor parts with Probability +*/ +USTRUCT(BlueprintType) +struct DUNGEONGENERATOR_API FDungeonRandomActorParts : public FDungeonActorPartsWithDirection +{ + GENERATED_BODY() + +public: + // Production frequency + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DungeonGenerator", meta = (ClampMin = "0.", ClampMax = "1.")) + float Frequency = 1.f; +}; + +USTRUCT(Blueprintable, BlueprintType) +struct DUNGEONGENERATOR_API FDungeonMeshSet +{ + GENERATED_BODY() + +public: + static FDungeonRandomActorParts* SelectRandomActorParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random, const TArray& parts, const EDungeonPartsSelectionMethod partsSelectionMethod); + + template + static T* SelectParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random, const TArray& parts, const EDungeonPartsSelectionMethod partsSelectionMethod); + + template + static void EachParts(const TArray& parts, std::function func); + +private: + static int32 SelectDungeonMeshPartsIndex(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random, const int32 size, const EDungeonPartsSelectionMethod partsSelectionMethod); + static FDungeonActorParts* SelectActorParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random, const TArray& parts, const EDungeonPartsSelectionMethod partsSelectionMethod); +}; + +// aka: SelectActorParts, SelectRandomActorParts +template +inline T* FDungeonMeshSet::SelectParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random, const TArray& parts, const EDungeonPartsSelectionMethod partsSelectionMethod) +{ + const int32 size = parts.Num(); + if (size <= 0) + return nullptr; + + const int32 index = SelectDungeonMeshPartsIndex(gridIndex, grid, random, size, partsSelectionMethod); + return const_cast(&parts[index]); +} + +template +inline void FDungeonMeshSet::EachParts(const TArray& parts, std::function func) +{ + for (const auto& part : parts) + { + func(part); + } +} diff --git a/Source/DungeonGenerator/Public/DungeonMeshSetDatabase.h b/Source/DungeonGenerator/Public/DungeonMeshSetDatabase.h new file mode 100644 index 0000000..4a7e0c6 --- /dev/null +++ b/Source/DungeonGenerator/Public/DungeonMeshSetDatabase.h @@ -0,0 +1,33 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include "DungeonMeshSet.h" +#include "DungeonMeshSetDatabase.generated.h" + +struct FDungeonRoomMeshSet; + +UCLASS(Blueprintable, BlueprintType) +class DUNGEONGENERATOR_API UDungeonMeshSetDatabase : public UObject +{ + GENERATED_BODY() + +public: + explicit UDungeonMeshSetDatabase(const FObjectInitializer& objectInitializer); + virtual ~UDungeonMeshSetDatabase() = default; + + virtual const FDungeonRoomMeshSet* SelectImplement(const std::shared_ptr& random) const; +}; + +inline UDungeonMeshSetDatabase::UDungeonMeshSetDatabase(const FObjectInitializer& objectInitializer) + : Super(objectInitializer) +{ +} + +inline const FDungeonRoomMeshSet* UDungeonMeshSetDatabase::SelectImplement(const std::shared_ptr& random) const +{ + return nullptr; +} diff --git a/Source/DungeonGenerator/Public/DungeonRoomLocator.h b/Source/DungeonGenerator/Public/DungeonRoomLocator.h deleted file mode 100644 index 288ab1d..0000000 --- a/Source/DungeonGenerator/Public/DungeonRoomLocator.h +++ /dev/null @@ -1,168 +0,0 @@ -/** -\author Shun Moriya -\copyright 2023- Shun Moriya -All Rights Reserved. -*/ - -#pragma once -#include "DungeonRoomParts.h" -#include "DungeonRoomLocator.generated.h" - -// cppcheck-suppress [unknownMacro] -UENUM(BlueprintType) -enum class EDungeonRoomSizeCondition : uint8 -{ - Equal, - EqualGreater, -}; - -/** -Sub-level information to replace dungeon rooms class -*/ -USTRUCT(Blueprintable, BlueprintType) -struct DUNGEONGENERATOR_API FDungeonRoomLocator -{ - GENERATED_BODY() - -public: - /* - Get sublevel paths - */ - const FSoftObjectPath& GetLevelPath() const noexcept; - - /* - Gets the sub-level grid size - */ - FIntVector GetSize() const noexcept; - - /* - Gets the grid width of the sublevel - */ - int32 GetWidth() const noexcept; - - /* - Gets the grid depth of the sublevel - */ - int32 GetDepth() const noexcept; - - /* - Gets the grid height of the sublevel - */ - int32 GetHeight() const noexcept; - - /* - Gets the width condition for placing sublevels - */ - EDungeonRoomSizeCondition GetWidthCondition() const noexcept; - - /* - Gets the depth condition for placing sublevels - */ - EDungeonRoomSizeCondition GetDepthCondition() const noexcept; - - /* - Gets the height condition for placing sublevels - */ - EDungeonRoomSizeCondition GetHeightCondition() const noexcept; - - /* - Get the parts of the dungeon (room roles) where the sublevels will be placed - */ - EDungeonRoomParts GetDungeonParts() const noexcept; - - /* - Generates or retrieves the ceiling mesh of the grid for sublevel placement - */ - bool IsGenerateRoofMesh() const noexcept; - - /* - Generates or retrieves the floor mesh of the grid for sublevel placement - */ - bool IsGenerateFloorMesh() const noexcept; - -protected: - UPROPERTY(EditAnywhere, Category = "DungeonGenerator", meta = (AllowedClasses = "World")) - FSoftObjectPath LevelPath; - - UPROPERTY(EditAnywhere, Category = "DungeonGenerator", meta = (ClampMin = 1)) - int32 Width = 1; - - UPROPERTY(EditAnywhere, Category = "DungeonGenerator", meta = (ClampMin = 1)) - int32 Depth = 1; - - UPROPERTY(EditAnywhere, Category = "DungeonGenerator", meta = (ClampMin = 1)) - int32 Height = 1; - - UPROPERTY(EditAnywhere, Category = "DungeonGenerator") - EDungeonRoomSizeCondition WidthCondition = EDungeonRoomSizeCondition::Equal; - - UPROPERTY(EditAnywhere, Category = "DungeonGenerator") - EDungeonRoomSizeCondition DepthCondition = EDungeonRoomSizeCondition::Equal; - - UPROPERTY(EditAnywhere, Category = "DungeonGenerator") - EDungeonRoomSizeCondition HeightCondition = EDungeonRoomSizeCondition::EqualGreater; - - UPROPERTY(EditAnywhere, Category = "DungeonGenerator") - EDungeonRoomParts DungeonParts = EDungeonRoomParts::Any; - - UPROPERTY(EditAnywhere, Category = "DungeonGenerator") - bool GenerateRoofMesh = true; - - UPROPERTY(EditAnywhere, Category = "DungeonGenerator") - bool GenerateFloorMesh = false; -}; - -inline const FSoftObjectPath& FDungeonRoomLocator::GetLevelPath() const noexcept -{ - return LevelPath; -} - -inline FIntVector FDungeonRoomLocator::GetSize() const noexcept -{ - return FIntVector(Width, Depth, Height); -} - -inline int32 FDungeonRoomLocator::GetWidth() const noexcept -{ - return Width; -} - -inline int32 FDungeonRoomLocator::GetDepth() const noexcept -{ - return Depth; -} - -inline int32 FDungeonRoomLocator::GetHeight() const noexcept -{ - return Height; -} - -inline EDungeonRoomSizeCondition FDungeonRoomLocator::GetWidthCondition() const noexcept -{ - return WidthCondition; -} - -inline EDungeonRoomSizeCondition FDungeonRoomLocator::GetDepthCondition() const noexcept -{ - return DepthCondition; -} - -inline EDungeonRoomSizeCondition FDungeonRoomLocator::GetHeightCondition() const noexcept -{ - return HeightCondition; -} - -inline EDungeonRoomParts FDungeonRoomLocator::GetDungeonParts() const noexcept -{ - return DungeonParts; -} - -inline bool FDungeonRoomLocator::IsGenerateRoofMesh() const noexcept -{ - return GenerateRoofMesh; -} - -inline bool FDungeonRoomLocator::IsGenerateFloorMesh() const noexcept -{ - return GenerateFloorMesh; -} diff --git a/Source/DungeonGenerator/Public/DungeonRoomMeshSet.h b/Source/DungeonGenerator/Public/DungeonRoomMeshSet.h new file mode 100644 index 0000000..fc43a8f --- /dev/null +++ b/Source/DungeonGenerator/Public/DungeonRoomMeshSet.h @@ -0,0 +1,80 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include "DungeonMeshSet.h" +#include "DungeonRoomMeshSet.generated.h" + +USTRUCT(Blueprintable, BlueprintType) +struct DUNGEONGENERATOR_API FDungeonRoomMeshSet : public FDungeonMeshSet +{ + GENERATED_BODY() + +public: + const FDungeonMeshPartsWithDirection* SelectFloorParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const; + void EachFloorParts(std::function func) const; + + const FDungeonMeshParts* SelectWallParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const; + void EachWallParts(std::function func) const; + + const FDungeonMeshPartsWithDirection* SelectRoofParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const; + void EachRoofParts(std::function func) const; + +protected: + //! How to generate floor parts + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Floor", BlueprintReadWrite) + EDungeonPartsSelectionMethod FloorPartsSelectionMethod = EDungeonPartsSelectionMethod::Random; + + //! Floor parts + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Floor", BlueprintReadWrite) + TArray FloorParts; + + //! How to generate wall parts + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Wall", BlueprintReadWrite) + EDungeonPartsSelectionMethod WallPartsSelectionMethod = EDungeonPartsSelectionMethod::Random; + + //! Wall parts + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Wall", BlueprintReadWrite) + TArray WallParts; + + //! How to generate room parts + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Roof", BlueprintReadWrite) + EDungeonPartsSelectionMethod RoofPartsSelectionMethod = EDungeonPartsSelectionMethod::Random; + + //! Room Roof Parts + UPROPERTY(EditAnywhere, Category = "DungeonGenerator|Roof", BlueprintReadWrite) + TArray RoofParts; +}; + +inline const FDungeonMeshPartsWithDirection* FDungeonRoomMeshSet::SelectFloorParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const +{ + return SelectParts(gridIndex, grid, random, FloorParts, FloorPartsSelectionMethod); +} + +inline void FDungeonRoomMeshSet::EachFloorParts(std::function func) const +{ + EachParts(FloorParts, func); +} + +inline const FDungeonMeshParts* FDungeonRoomMeshSet::SelectWallParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const +{ + return SelectParts(gridIndex, grid, random, WallParts, WallPartsSelectionMethod); +} + +inline void FDungeonRoomMeshSet::EachWallParts(std::function func) const +{ + EachParts(WallParts, func); +} + +inline const FDungeonMeshPartsWithDirection* FDungeonRoomMeshSet::SelectRoofParts(const size_t gridIndex, const dungeon::Grid& grid, const std::shared_ptr& random) const +{ + return SelectParts(gridIndex, grid, random, RoofParts, RoofPartsSelectionMethod); +} + +inline void FDungeonRoomMeshSet::EachRoofParts(std::function func) const +{ + EachParts(RoofParts, func); +} diff --git a/Source/DungeonGenerator/Public/DungeonRoomMeshSetDatabase.h b/Source/DungeonGenerator/Public/DungeonRoomMeshSetDatabase.h new file mode 100644 index 0000000..73c3141 --- /dev/null +++ b/Source/DungeonGenerator/Public/DungeonRoomMeshSetDatabase.h @@ -0,0 +1,43 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include "DungeonMeshSetDatabase.h" +#include "DungeonRoomMeshSet.h" +#include "DungeonRoomMeshSetDatabase.generated.h" + +UCLASS(Blueprintable, BlueprintType) +class DUNGEONGENERATOR_API UDungeonRoomMeshSetDatabase : public UDungeonMeshSetDatabase +{ + GENERATED_BODY() + +public: + explicit UDungeonRoomMeshSetDatabase(const FObjectInitializer& ObjectInitializer); + virtual ~UDungeonRoomMeshSetDatabase() = default; + + const FDungeonRoomMeshSet* Select(const std::shared_ptr& random) const; + void Each(std::function func) const; + + // UDungeonMeshSetDatabase overrides + virtual const FDungeonRoomMeshSet* SelectImplement(const std::shared_ptr& random) const override; + +protected: + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DungeonGenerator") + TArray Parts; +}; + +inline UDungeonRoomMeshSetDatabase::UDungeonRoomMeshSetDatabase(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +} + +inline void UDungeonRoomMeshSetDatabase::Each(std::function func) const +{ + for (const FDungeonRoomMeshSet& parts : Parts) + { + func(parts); + } +} diff --git a/Source/DungeonGenerator/Public/MiniMap/DungeonMiniMap.h b/Source/DungeonGenerator/Public/MiniMap/DungeonMiniMap.h new file mode 100644 index 0000000..6e2c3f6 --- /dev/null +++ b/Source/DungeonGenerator/Public/MiniMap/DungeonMiniMap.h @@ -0,0 +1,90 @@ +#pragma once +#include +#include "DungeonMiniMap.generated.h" + +/* +A mini-map of the dungeon generated in the editor +It contains the mini-map textures of each floor and the information +about the conversion from world coordinates to texture coordinates. +*/ +UCLASS(Blueprintable, BlueprintType) +class DUNGEONGENERATOR_API UDungeonMiniMap : public UObject +{ + GENERATED_BODY() + +public: + explicit UDungeonMiniMap(const FObjectInitializer& ObjectInitializer); + virtual ~UDungeonMiniMap() = default; + + void AddTexture(UTexture2D* texture); + + float GetGridSize() const noexcept; + void SetGridSize(const float gridSize); + + float GetWorldToTextureScale() const noexcept; + void SetWorldToTextureScale(const float worldToTextureScale); + + void Add(const int32 elevation); + + // Conversion from World space to texture space + UFUNCTION(BlueprintCallable, Category = "DungeonGenerator") + FVector2D TransformWorldToTexture(const FVector worldLocation) const noexcept; + + // Conversion from World space to texture layer + UFUNCTION(BlueprintCallable, Category = "DungeonGenerator") + int32 TransformWorldToLayer(const float worldElevation) const noexcept; + +protected: + UPROPERTY(EditAnywhere, Category = "DungeonGenerator", BlueprintReadOnly) + TArray Textures; + // TObjectPtr not used for UE4 compatibility + + UPROPERTY(EditAnywhere, Category = "DungeonGenerator", BlueprintReadOnly) + TArray Elevations; + + UPROPERTY(EditAnywhere, Category = "DungeonGenerator", BlueprintReadOnly) + float WorldToVoxelScale = 1.f; + + UPROPERTY(EditAnywhere, Category = "DungeonGenerator", BlueprintReadOnly) + float WorldToTextureScale = 1.f; +}; + +inline UDungeonMiniMap::UDungeonMiniMap(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +} + +inline void UDungeonMiniMap::AddTexture(UTexture2D* texture) +{ + Textures.Add(texture); +} + +inline float UDungeonMiniMap::GetGridSize() const noexcept +{ + return 1.f / WorldToVoxelScale; +} + +inline void UDungeonMiniMap::SetGridSize(const float gridSize) +{ + WorldToVoxelScale = 1.f / gridSize; +} + +inline float UDungeonMiniMap::GetWorldToTextureScale() const noexcept +{ + return WorldToTextureScale; +} + +inline void UDungeonMiniMap::SetWorldToTextureScale(const float worldToTextureScale) +{ + WorldToTextureScale = worldToTextureScale; +} + +inline void UDungeonMiniMap::Add(const int32 elevation) +{ + Elevations.Add(elevation); +} + +inline FVector2D UDungeonMiniMap::TransformWorldToTexture(const FVector worldLocation) const noexcept +{ + return FVector2D(worldLocation.X, worldLocation.Y) * WorldToTextureScale; +} diff --git a/Source/DungeonGenerator/Public/DungeonMiniMapTexture.h b/Source/DungeonGenerator/Public/MiniMap/DungeonMiniMapTexture.h similarity index 96% rename from Source/DungeonGenerator/Public/DungeonMiniMapTexture.h rename to Source/DungeonGenerator/Public/MiniMap/DungeonMiniMapTexture.h index 808cebf..dcedbcb 100644 --- a/Source/DungeonGenerator/Public/DungeonMiniMapTexture.h +++ b/Source/DungeonGenerator/Public/MiniMap/DungeonMiniMapTexture.h @@ -8,6 +8,7 @@ All Rights Reserved. #pragma once #include +#include #include "DungeonMiniMapTexture.generated.h" class CDungeonGeneratorCore; @@ -69,8 +70,8 @@ class DUNGEONGENERATOR_API UDungeonMiniMapTexture : public UObject protected: // Generated textures UPROPERTY(BlueprintReadOnly, Category = "DungeonGenerator") - UTexture2D* Texture = nullptr; - // TObjectPtr not used for UE4 compatibility + UTexture2D* Texture; + // TObjectPtr not used for UE4 compatibility // Transformation scale from Voxel space to texture space UPROPERTY(BlueprintReadOnly, Category = "DungeonGenerator") diff --git a/Source/DungeonGenerator/Public/DungeonMiniMapTextureLayer.h b/Source/DungeonGenerator/Public/MiniMap/DungeonMiniMapTextureLayer.h similarity index 84% rename from Source/DungeonGenerator/Public/DungeonMiniMapTextureLayer.h rename to Source/DungeonGenerator/Public/MiniMap/DungeonMiniMapTextureLayer.h index 757d66a..aa6f280 100644 --- a/Source/DungeonGenerator/Public/DungeonMiniMapTextureLayer.h +++ b/Source/DungeonGenerator/Public/MiniMap/DungeonMiniMapTextureLayer.h @@ -8,6 +8,8 @@ All Rights Reserved. #pragma once #include +#include +#include #include "DungeonMiniMapTextureLayer.generated.h" class ADungeonGenerateActor; @@ -21,7 +23,7 @@ namespace dungeon } /** -Minimap texture layer class +Mini-map texture layer class */ UCLASS(Blueprintable, BlueprintType) class DUNGEONGENERATOR_API UDungeonMiniMapTextureLayer : public UObject @@ -89,20 +91,37 @@ class DUNGEONGENERATOR_API UDungeonMiniMapTextureLayer : public UObject private: /** - Generates a minimap texture + Generates a mini-map texture */ bool GenerateMiniMapTextureWithSize(const std::shared_ptr& dungeonGeneratorCore, const uint32_t textureWidth, const float gridSize); /** - Generates a minimap texture + Generates a mini-map texture */ bool GenerateMiniMapTextureWithScale(const std::shared_ptr& dungeonGeneratorCore, const uint32_t textureScale, const float gridSize); + /** + Gets the length of a voxel edge. + */ + float GetGridScale() const noexcept; + + /** + Get scale value from world space to voxel space + */ + float GetGeneratedScale() const noexcept; + + /** + Get scale value from world space to texture space + */ + float GetScaleWorldToTexture() const noexcept; + + const std::vector& GetFloorElevations() const noexcept; + private: // Dungeon minimap textures for each level UPROPERTY(BlueprintReadOnly, Category = "DungeonGenerator", meta = (AllowPrivateAccess = "true")) TArray DungeonMiniMapTextures; - // TObjectPtr not used for UE4 compatibility + // TObjectPtr not used for UE4 compatibility // Reference to Generator std::shared_ptr mGenerator; @@ -128,3 +147,8 @@ inline FVector2D UDungeonMiniMapTextureLayer::ToRelativeWithScale(const FVector& { return ToRelative(location) * scale; } + +inline float UDungeonMiniMapTextureLayer::GetGridScale() const noexcept +{ + return mGridSize; +} diff --git a/Source/DungeonGenerator/Public/DungeonRoomItem.h b/Source/DungeonGenerator/Public/Mission/DungeonRoomItem.h similarity index 100% rename from Source/DungeonGenerator/Public/DungeonRoomItem.h rename to Source/DungeonGenerator/Public/Mission/DungeonRoomItem.h diff --git a/Source/DungeonGenerator/Public/DungeonRoomParts.h b/Source/DungeonGenerator/Public/Mission/DungeonRoomParts.h similarity index 50% rename from Source/DungeonGenerator/Public/DungeonRoomParts.h rename to Source/DungeonGenerator/Public/Mission/DungeonRoomParts.h index f9e4a6a..7ee727f 100644 --- a/Source/DungeonGenerator/Public/DungeonRoomParts.h +++ b/Source/DungeonGenerator/Public/Mission/DungeonRoomParts.h @@ -15,8 +15,20 @@ UENUM(BlueprintType) enum class EDungeonRoomParts : uint8 { Any, //!< Unspecified (equivalent to dungeon::Room::Parts::Unidentified) + Hall, //!< A hall (several corridors are connected) + Hanare, //!< separate room (with only one passageway connected to it) Start, //!< starting point Goal, //!< goal point +}; + +/** +Type of room parts +Same content as dungeon::Room::Parts +*/ +UENUM(BlueprintType) +enum class EDungeonRoomLocatorParts : uint8 +{ + Any, //!< Unspecified (equivalent to dungeon::Room::Parts::Unidentified) Hall, //!< A hall (several corridors are connected) Hanare, //!< separate room (with only one passageway connected to it) }; @@ -24,7 +36,7 @@ enum class EDungeonRoomParts : uint8 /** Number of different room parts */ -static constexpr uint8 DungeonRoomPartsSize = static_cast(EDungeonRoomParts::Hanare) + 1; +static constexpr uint8 DungeonRoomPartsSize = static_cast(EDungeonRoomParts::Goal) + 1; /** Get the symbol name of a room part. @@ -33,3 +45,19 @@ Same content as dungeon::Room::Parts \return Symbol name for EDungeonRoomParts */ extern const FString& GetDungeonRoomPartsName(const EDungeonRoomParts parts); + +/** +Compare EDungeonRoomParts and EDungeonRoomLocatorParts +*/ +inline constexpr bool Equal(const EDungeonRoomParts parts, const EDungeonRoomLocatorParts locatorParts) +{ + return static_cast(parts) == static_cast(locatorParts); +} + +/** +Compare EDungeonRoomParts and EDungeonRoomLocatorParts +*/ +inline constexpr bool Equal(const EDungeonRoomLocatorParts locatorParts, const EDungeonRoomParts parts) +{ + return Equal(parts, locatorParts); +} diff --git a/Source/DungeonGenerator/Public/DungeonRoomProps.h b/Source/DungeonGenerator/Public/Mission/DungeonRoomProps.h similarity index 100% rename from Source/DungeonGenerator/Public/DungeonRoomProps.h rename to Source/DungeonGenerator/Public/Mission/DungeonRoomProps.h diff --git a/Source/DungeonGenerator/Public/DungeonDoor.h b/Source/DungeonGenerator/Public/SubActor/DungeonDoor.h similarity index 98% rename from Source/DungeonGenerator/Public/DungeonDoor.h rename to Source/DungeonGenerator/Public/SubActor/DungeonDoor.h index 8db8274..a354de4 100644 --- a/Source/DungeonGenerator/Public/DungeonDoor.h +++ b/Source/DungeonGenerator/Public/SubActor/DungeonDoor.h @@ -5,7 +5,7 @@ All Rights Reserved. */ #pragma once -#include "DungeonRoomProps.h" +#include "Mission/DungeonRoomProps.h" #include #include "DungeonDoor.generated.h" diff --git a/Source/DungeonGenerator/Public/DungeonRoomSensor.h b/Source/DungeonGenerator/Public/SubActor/DungeonRoomSensor.h similarity index 91% rename from Source/DungeonGenerator/Public/DungeonRoomSensor.h rename to Source/DungeonGenerator/Public/SubActor/DungeonRoomSensor.h index 95a42f9..6a11325 100644 --- a/Source/DungeonGenerator/Public/DungeonRoomSensor.h +++ b/Source/DungeonGenerator/Public/SubActor/DungeonRoomSensor.h @@ -5,13 +5,20 @@ All Rights Reserved. */ #pragma once -#include "DungeonRoomItem.h" -#include "DungeonRoomParts.h" +#include "Mission/DungeonRoomItem.h" +#include "Mission/DungeonRoomParts.h" +#include +#include #include "DungeonRoomSensor.generated.h" // forward declaration class UBoxComponent; +namespace dungeon +{ + class Random; +} + /* Room Bounding Box Sensor Actor Class */ @@ -36,6 +43,7 @@ class DUNGEONGENERATOR_API ADungeonRoomSensor : public AActor TODO: Too many arguments, so please deal with them by putting them together in a structure, etc. */ void Initialize( + const std::shared_ptr& random, const int32 identifier, const FVector& extents, const EDungeonRoomParts parts, @@ -119,10 +127,11 @@ class DUNGEONGENERATOR_API ADungeonRoomSensor : public AActor /* Gets the tag of the interior of the room to be generated. - For example, if the creator returns the tag `kitchen`, the interior with the kitchen tag will be selected + For example, if the creator returns the tag `kitchen`, the interior with the kitchen tag will be selected. + If multiple tags are set, actors containing all tags will be selected as spawn candidates. */ UFUNCTION(BlueprintImplementableEvent) - TArray OnDecorateInteria() const; + TArray GetInquireInteriorTags() const; /* Get the DungeonGenerator tag name. @@ -148,7 +157,8 @@ class DUNGEONGENERATOR_API ADungeonRoomSensor : public AActor private: // Room Bounding Box UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DungeonGenerator", meta = (AllowPrivateAccess = "true")) - UBoxComponent* Bounding = nullptr; + UBoxComponent* Bounding; + // TObjectPtr not used for UE4 compatibility UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "DungeonGenerator", meta = (AllowPrivateAccess = "true")) int32 Identifier = -1; @@ -181,6 +191,10 @@ class DUNGEONGENERATOR_API ADungeonRoomSensor : public AActor Finalized, }; State mState = State::Invalid; + + std::shared_ptr mRandom; + + friend class CDungeonGeneratorCore; }; inline void ADungeonRoomSensor::OnInitialize_Implementation(const EDungeonRoomParts parts, const EDungeonRoomItem item, const uint8 depthFromStart, const float depthFromStartRatio) diff --git a/Source/DungeonGenerator/Public/DungeonLevelStreamingActor.h b/Source/DungeonGenerator/Public/SubLevel/DungeonLevelStreamingActor.h similarity index 96% rename from Source/DungeonGenerator/Public/DungeonLevelStreamingActor.h rename to Source/DungeonGenerator/Public/SubLevel/DungeonLevelStreamingActor.h index 8dc6293..0c9b06f 100644 --- a/Source/DungeonGenerator/Public/DungeonLevelStreamingActor.h +++ b/Source/DungeonGenerator/Public/SubLevel/DungeonLevelStreamingActor.h @@ -5,7 +5,6 @@ All Rights Reserved. */ #pragma once - #include #include "DungeonLevelStreamingActor.generated.h" @@ -41,6 +40,7 @@ class DUNGEONGENERATOR_API ADungeonLevelStreamingActor : public AActor // Overlap volume to trigger level streaming UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "DungeonGenerator") UBoxComponent* OverlapVolume; + // TObjectPtr not used for UE4 compatibility // Level streaming path UPROPERTY(EditAnywhere, Category = "DungeonGenerator", meta = (AllowedClasses = "World")) diff --git a/Source/DungeonGenerator/Public/DungeonLevelStreamingDynamic.h b/Source/DungeonGenerator/Public/SubLevel/DungeonLevelStreamingDynamic.h similarity index 82% rename from Source/DungeonGenerator/Public/DungeonLevelStreamingDynamic.h rename to Source/DungeonGenerator/Public/SubLevel/DungeonLevelStreamingDynamic.h index 8d32de3..09df5c9 100644 --- a/Source/DungeonGenerator/Public/DungeonLevelStreamingDynamic.h +++ b/Source/DungeonGenerator/Public/SubLevel/DungeonLevelStreamingDynamic.h @@ -5,7 +5,6 @@ All Rights Reserved. */ #pragma once - #include #include #include "DungeonLevelStreamingDynamic.generated.h" @@ -32,3 +31,8 @@ class DUNGEONGENERATOR_API UDungeonLevelStreamingDynamic : public ULevelStreamin */ virtual ~UDungeonLevelStreamingDynamic() = default; }; + +inline UDungeonLevelStreamingDynamic::UDungeonLevelStreamingDynamic(const FObjectInitializer& initializer) + : Super(initializer) +{ +} diff --git a/Source/DungeonGenerator/Public/DungeonRoomAsset.h b/Source/DungeonGenerator/Public/SubLevel/DungeonRoomDatabase.h similarity index 50% rename from Source/DungeonGenerator/Public/DungeonRoomAsset.h rename to Source/DungeonGenerator/Public/SubLevel/DungeonRoomDatabase.h index 90bc824..5b894a1 100644 --- a/Source/DungeonGenerator/Public/DungeonRoomAsset.h +++ b/Source/DungeonGenerator/Public/SubLevel/DungeonRoomDatabase.h @@ -6,13 +6,13 @@ All Rights Reserved. #pragma once #include "DungeonRoomLocator.h" -#include "DungeonRoomAsset.generated.h" +#include "DungeonRoomDatabase.generated.h" /** Sub-level information to replace dungeon rooms class */ UCLASS(Blueprintable, BlueprintType) -class DUNGEONGENERATOR_API UDungeonRoomAsset : public UObject +class DUNGEONGENERATOR_API UDungeonRoomDatabase : public UObject { GENERATED_BODY() @@ -20,12 +20,12 @@ class DUNGEONGENERATOR_API UDungeonRoomAsset : public UObject /* constructor */ - explicit UDungeonRoomAsset(const FObjectInitializer& ObjectInitializer); + explicit UDungeonRoomDatabase(const FObjectInitializer& ObjectInitializer); /* destructor */ - virtual ~UDungeonRoomAsset() = default; + virtual ~UDungeonRoomDatabase() = default; /* Get the dungeon room information array @@ -38,12 +38,18 @@ class DUNGEONGENERATOR_API UDungeonRoomAsset : public UObject TArray DungeonRoomLocator; }; -inline UDungeonRoomAsset::UDungeonRoomAsset(const FObjectInitializer& ObjectInitializer) +inline UDungeonRoomDatabase::UDungeonRoomDatabase(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { } -inline const TArray& UDungeonRoomAsset::GetDungeonRoomLocator() const noexcept +inline const TArray& UDungeonRoomDatabase::GetDungeonRoomLocator() const noexcept { return reinterpret_cast&>(DungeonRoomLocator); } + +UCLASS(Blueprintable, BlueprintType, Deprecated, meta = (DeprecationMessage = "UDungeonRoomAsset is deprecated, use UDungeonRoomDatabase.")) +class DUNGEONGENERATOR_API UDEPRECATED_DungeonRoomAsset : public UDungeonRoomDatabase +{ + GENERATED_BODY() +}; diff --git a/Source/DungeonGenerator/Public/SubLevel/DungeonRoomLocator.h b/Source/DungeonGenerator/Public/SubLevel/DungeonRoomLocator.h new file mode 100644 index 0000000..f995ca1 --- /dev/null +++ b/Source/DungeonGenerator/Public/SubLevel/DungeonRoomLocator.h @@ -0,0 +1,81 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include "DungeonRoomRegister.h" +#include "Mission/DungeonRoomParts.h" +#include "DungeonRoomLocator.generated.h" + +// cppcheck-suppress [unknownMacro] +UENUM(BlueprintType) +enum class EDungeonRoomSizeCondition : uint8 +{ + Equal, + EqualGreater, +}; + +/** +Sub-level information to replace dungeon rooms class +*/ +USTRUCT(Blueprintable, BlueprintType) +struct DUNGEONGENERATOR_API FDungeonRoomLocator : public FDungeonRoomRegister +{ + GENERATED_BODY() + +public: + /* + Gets the width condition for placing sublevels + */ + EDungeonRoomSizeCondition GetWidthCondition() const noexcept; + + /* + Gets the depth condition for placing sublevels + */ + EDungeonRoomSizeCondition GetDepthCondition() const noexcept; + + /* + Gets the height condition for placing sublevels + */ + EDungeonRoomSizeCondition GetHeightCondition() const noexcept; + + /* + Get the parts of the dungeon (room roles) where the sublevels will be placed + */ + EDungeonRoomLocatorParts GetDungeonParts() const noexcept; + +protected: + UPROPERTY(EditAnywhere, Category = "DungeonGenerator") + EDungeonRoomSizeCondition WidthCondition = EDungeonRoomSizeCondition::Equal; + + UPROPERTY(EditAnywhere, Category = "DungeonGenerator") + EDungeonRoomSizeCondition DepthCondition = EDungeonRoomSizeCondition::Equal; + + UPROPERTY(EditAnywhere, Category = "DungeonGenerator") + EDungeonRoomSizeCondition HeightCondition = EDungeonRoomSizeCondition::EqualGreater; + + UPROPERTY(EditAnywhere, Category = "DungeonGenerator") + EDungeonRoomLocatorParts DungeonParts = EDungeonRoomLocatorParts::Any; +}; + +inline EDungeonRoomSizeCondition FDungeonRoomLocator::GetWidthCondition() const noexcept +{ + return WidthCondition; +} + +inline EDungeonRoomSizeCondition FDungeonRoomLocator::GetDepthCondition() const noexcept +{ + return DepthCondition; +} + +inline EDungeonRoomSizeCondition FDungeonRoomLocator::GetHeightCondition() const noexcept +{ + return HeightCondition; +} + +inline EDungeonRoomLocatorParts FDungeonRoomLocator::GetDungeonParts() const noexcept +{ + return DungeonParts; +} diff --git a/Source/DungeonGenerator/Public/SubLevel/DungeonRoomRegister.h b/Source/DungeonGenerator/Public/SubLevel/DungeonRoomRegister.h new file mode 100644 index 0000000..7d99ecf --- /dev/null +++ b/Source/DungeonGenerator/Public/SubLevel/DungeonRoomRegister.h @@ -0,0 +1,112 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include +#include "DungeonRoomRegister.generated.h" + +USTRUCT(Blueprintable, BlueprintType) +struct DUNGEONGENERATOR_API FDungeonRoomRegister +{ + GENERATED_BODY() + +public: + bool IsValid() const noexcept; + + /* + Get sublevel paths + */ + const FSoftObjectPath& GetLevelPath() const noexcept; + + /* + Gets the sub-level grid size + */ + FIntVector GetSize() const noexcept; + + /* + Gets the grid width of the sublevel + */ + int32 GetWidth() const noexcept; + + /* + Gets the grid depth of the sublevel + */ + int32 GetDepth() const noexcept; + + /* + Gets the grid height of the sublevel + */ + int32 GetHeight() const noexcept; + + /* + Generates or retrieves the ceiling mesh of the grid for sublevel placement + */ + bool IsGenerateRoofMesh() const noexcept; + + /* + Generates or retrieves the floor mesh of the grid for sublevel placement + */ + bool IsGenerateFloorMesh() const noexcept; + +protected: + UPROPERTY(EditAnywhere, Category = "DungeonGenerator", meta = (AllowedClasses = "World")) + FSoftObjectPath LevelPath; + + UPROPERTY(EditAnywhere, Category = "DungeonGenerator", meta = (ClampMin = 1)) + int32 Width = 1; + + UPROPERTY(EditAnywhere, Category = "DungeonGenerator", meta = (ClampMin = 1)) + int32 Depth = 1; + + UPROPERTY(EditAnywhere, Category = "DungeonGenerator", meta = (ClampMin = 1)) + int32 Height = 1; + + UPROPERTY(EditAnywhere, Category = "DungeonGenerator") + bool GenerateRoofMesh = true; + + UPROPERTY(EditAnywhere, Category = "DungeonGenerator") + bool GenerateFloorMesh = false; +}; + +inline bool FDungeonRoomRegister::IsValid() const noexcept +{ + return LevelPath.IsValid(); +} + +inline const FSoftObjectPath& FDungeonRoomRegister::GetLevelPath() const noexcept +{ + return LevelPath; +} + +inline FIntVector FDungeonRoomRegister::GetSize() const noexcept +{ + return FIntVector(Width, Depth, Height); +} + +inline int32 FDungeonRoomRegister::GetWidth() const noexcept +{ + return Width; +} + +inline int32 FDungeonRoomRegister::GetDepth() const noexcept +{ + return Depth; +} + +inline int32 FDungeonRoomRegister::GetHeight() const noexcept +{ + return Height; +} + +inline bool FDungeonRoomRegister::IsGenerateRoofMesh() const noexcept +{ + return GenerateRoofMesh; +} + +inline bool FDungeonRoomRegister::IsGenerateFloorMesh() const noexcept +{ + return GenerateFloorMesh; +} diff --git a/Source/DungeonGeneratorEditor/Private/BuildInfomation.h b/Source/DungeonGeneratorEditor/Private/BuildInfomation.h index c374658..4e82212 100644 --- a/Source/DungeonGeneratorEditor/Private/BuildInfomation.h +++ b/Source/DungeonGeneratorEditor/Private/BuildInfomation.h @@ -3,6 +3,7 @@ This is automatically generated file. DO NOT EDIT THIS FILE. \author Shun Moriya \copyright 2023- Shun Moriya +All Rights Reserved. */ #pragma once #if !defined(JENKINS_BUILD_DETECTED) @@ -17,5 +18,6 @@ DO NOT EDIT THIS FILE. #define JENKINS_GIT_COMMIT "UNDEFINED COMMIT" #define JENKINS_UUID "3da61b40-1fba-7757-b07f-110de2a901e3" #define JENKINS_LICENSE "GPL v3" -#define JENKINS_FOR_DEVELOP 1 -#endif +#define JENKINS_FOR_DEVELOP 1 +#define JENKINS_FOR_RELEASE 0 +#endif diff --git a/Source/DungeonGeneratorEditor/Private/DungeonAisleMeshSetDatabaseFactory.cpp b/Source/DungeonGeneratorEditor/Private/DungeonAisleMeshSetDatabaseFactory.cpp new file mode 100644 index 0000000..9e6c536 --- /dev/null +++ b/Source/DungeonGeneratorEditor/Private/DungeonAisleMeshSetDatabaseFactory.cpp @@ -0,0 +1,20 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "DungeonAisleMeshSetDatabaseFactory.h" +#include "../../DungeonGenerator/Public/DungeonAisleMeshSetDatabase.h" + +UDungeonAisleMeshSetDatabaseFactory::UDungeonAisleMeshSetDatabaseFactory() +{ + SupportedClass = UDungeonAisleMeshSetDatabase::StaticClass(); + bCreateNew = true; + bEditAfterNew = true; +} + +UObject* UDungeonAisleMeshSetDatabaseFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ + return NewObject(InParent, InClass, InName, Flags); +} diff --git a/Source/DungeonGeneratorEditor/Private/DungeonAisleMeshSetDatabaseTypeActions.cpp b/Source/DungeonGeneratorEditor/Private/DungeonAisleMeshSetDatabaseTypeActions.cpp new file mode 100644 index 0000000..c6d1061 --- /dev/null +++ b/Source/DungeonGeneratorEditor/Private/DungeonAisleMeshSetDatabaseTypeActions.cpp @@ -0,0 +1,13 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "DungeonAisleMeshSetDatabaseTypeActions.h" +#include "../../DungeonGenerator/Public/DungeonAisleMeshSetDatabase.h" + +UClass* FDungeonAisleMeshSetDatabaseTypeActions::GetSupportedClass() const +{ + return UDungeonAisleMeshSetDatabase::StaticClass(); +} diff --git a/Source/DungeonGeneratorEditor/Private/DungeonGenerateParameterTypeActions.cpp b/Source/DungeonGeneratorEditor/Private/DungeonGenerateParameterTypeActions.cpp index b141e96..925a455 100644 --- a/Source/DungeonGeneratorEditor/Private/DungeonGenerateParameterTypeActions.cpp +++ b/Source/DungeonGeneratorEditor/Private/DungeonGenerateParameterTypeActions.cpp @@ -7,32 +7,7 @@ All Rights Reserved. #include "DungeonGenerateParameterTypeActions.h" #include "../../DungeonGenerator/Public/DungeonGenerateParameter.h" -UDungeonGenerateParameterTypeActions::UDungeonGenerateParameterTypeActions(EAssetTypeCategories::Type InAssetCategory) - : mAssetCategory(InAssetCategory) -{ -} - -FColor UDungeonGenerateParameterTypeActions::GetTypeColor() const -{ - return FColor::Emerald; -} - -void UDungeonGenerateParameterTypeActions::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) -{ - FSimpleAssetEditor::CreateEditor(EToolkitMode::Standalone, EditWithinLevelEditor, InObjects); -} - -FText UDungeonGenerateParameterTypeActions::GetName() const -{ - return FText::FromName(TEXT("DungeonGenerateParameter")); -} - UClass* UDungeonGenerateParameterTypeActions::GetSupportedClass() const { return UDungeonGenerateParameter::StaticClass(); } - -uint32 UDungeonGenerateParameterTypeActions::GetCategories() -{ - return mAssetCategory; -} diff --git a/Source/DungeonGeneratorEditor/Private/DungeonGeneratorEditorModule.cpp b/Source/DungeonGeneratorEditor/Private/DungeonGeneratorEditorModule.cpp index 5e46018..60c8768 100644 --- a/Source/DungeonGeneratorEditor/Private/DungeonGeneratorEditorModule.cpp +++ b/Source/DungeonGeneratorEditor/Private/DungeonGeneratorEditorModule.cpp @@ -8,13 +8,17 @@ All Rights Reserved. #include "DungeonGeneratorStyle.h" #include "DungeonGeneratorCommands.h" #include "DungeonGenerateParameterTypeActions.h" -#include "DungeonInteriorAssetTypeActions.h" -#include "DungeonRoomAssetTypeActions.h" +#include "DungeonInteriorDatabaseTypeActions.h" +#include "DungeonMiniMapTypeActions.h" +#include "DungeonAisleMeshSetDatabaseTypeActions.h" +#include "DungeonRoomMeshSetDatabaseTypeActions.h" +#include "DungeonRoomDatabaseTypeActions.h" #include "BuildInfomation.h" #include "../../DungeonGenerator/Public/DungeonGenerateParameter.h" #include "../../DungeonGenerator/Public/DungeonGeneratorCore.h" -#include "../../DungeonGenerator/Public/DungeonMiniMapTexture.h" -#include "../../DungeonGenerator/Public/dungeonMiniMapTextureLayer.h" +#include "../../DungeonGenerator/Public/MiniMap/DungeonMiniMap.h" +#include "../../DungeonGenerator/Public/MiniMap/DungeonMiniMapTexture.h" +#include "../../DungeonGenerator/Public/MiniMap/DungeonMiniMapTextureLayer.h" #include #include #include @@ -23,11 +27,15 @@ All Rights Reserved. #include #include #include +#include #include #include #include #include +//#define _ENABLE_UNREGISTER_MOUNT_POINT +//#define _ENABLE_SAVE_AND_CLOSE_PACKAGES + static const FName DungeonGeneratorTabName("DungeonGenerator"); #define LOCTEXT_NAMESPACE "FDungeonGenerateEditorModule" @@ -59,7 +67,7 @@ void FDungeonGenerateEditorModule::StartupModule() .SetDisplayName(LOCTEXT("FDungeonGeneratorTabTitle", "DungeonGenerator")) .SetMenuType(ETabSpawnerMenuType::Hidden); - // AssetToolsモジュールを取得 + // Get AssetTools module IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); // LegacyAsset @@ -69,19 +77,37 @@ void FDungeonGenerateEditorModule::StartupModule() FText::FromName(TEXT("DungeonGenerator")) ); - // FDungeonInteriorAssetTypeActionsを追加 + // Register FDungeonInteriorDatabaseTypeActions + { + TSharedPtr actionType = MakeShareable(new FDungeonInteriorDatabaseTypeActions(gameAssetCategory)); + AssetTools.RegisterAssetTypeActions(actionType.ToSharedRef()); + } + + // Register FDungeonAisleMeshSetDatabaseTypeActions + { + TSharedPtr actionType = MakeShareable(new FDungeonAisleMeshSetDatabaseTypeActions(gameAssetCategory)); + AssetTools.RegisterAssetTypeActions(actionType.ToSharedRef()); + } + + // Register FDungeonRoomMeshSetDatabaseTypeActions + { + TSharedPtr actionType = MakeShareable(new FDungeonRoomMeshSetDatabaseTypeActions(gameAssetCategory)); + AssetTools.RegisterAssetTypeActions(actionType.ToSharedRef()); + } + + // Register FDungeonRoomDatabaseTypeActions { - TSharedPtr actionType = MakeShareable(new FDungeonInteriorAssetTypeActions(gameAssetCategory)); + TSharedPtr actionType = MakeShareable(new FDungeonRoomDatabaseTypeActions(gameAssetCategory)); AssetTools.RegisterAssetTypeActions(actionType.ToSharedRef()); } - // FDungeonRoomAssetTypeActionsを追加 + // Register FDungeonMiniMapLayerInfoTypeActions { - TSharedPtr actionType = MakeShareable(new FDungeonRoomAssetTypeActions(gameAssetCategory)); + TSharedPtr actionType = MakeShareable(new FDungeonMiniMapTypeActions(gameAssetCategory)); AssetTools.RegisterAssetTypeActions(actionType.ToSharedRef()); } - // UDungeonGenerateParameterTypeActionsを追加 + // Register UDungeonGenerateParameterTypeActions { TSharedPtr actionType = MakeShareable(new UDungeonGenerateParameterTypeActions(gameAssetCategory)); AssetTools.RegisterAssetTypeActions(actionType.ToSharedRef()); @@ -101,179 +127,214 @@ void FDungeonGenerateEditorModule::ShutdownModule() FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(DungeonGeneratorTabName); } - +/* +aka: https://docs.unrealengine.com/5.2/ja/slate-overview-for-unreal-engine/ +*/ TSharedRef FDungeonGenerateEditorModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs) { - TSharedRef dockTab = SNew(SDockTab) - .TabRole(ETabRole::NomadTab) - [ - SNew(SVerticalBox) - // UDungeonGenerateParameter - + SVerticalBox::Slot() - .VAlign(VAlign_Center) - .FillHeight(1.f) - .Padding(2.f) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .VAlign(VAlign_Center) - .AutoWidth() - .Padding(2.f) - [ - SNew(STextBlock) - .Text(LOCTEXT("DungeonGenerateParameterLabel", "DungeonGenerateParameter")) - ] - + SHorizontalBox::Slot() - .AutoWidth() - [ - //SAssignNew(EntryBox, SObjectPropertyEntryBox) - SNew(SObjectPropertyEntryBox) - .AllowedClass(UDungeonGenerateParameter::StaticClass()) - .ObjectPath_Raw(this, &FDungeonGenerateEditorModule::GetObjectPath) - .OnObjectChanged_Raw(this, &FDungeonGenerateEditorModule::SetAssetData) - ] - ] - + SVerticalBox::Slot() - .VAlign(VAlign_Center) - .FillHeight(1.f) - .Padding(2.f) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .VAlign(VAlign_Center) - .AutoWidth() - .Padding(2.f) - [ - SNew(STextBlock) - .Text(LOCTEXT("RandomSeedLabel", "Random seed")) - ] - + SHorizontalBox::Slot() - .AutoWidth() - [ - SAssignNew(mRandomSeedValue, SEditableTextBox) - .Text(LOCTEXT("RandomSeedValue", "")) - ] - ] - - + SVerticalBox::Slot() - .VAlign(VAlign_Center) - .FillHeight(1.f) - .Padding(2.f) - [ - SAssignNew(mGenerateDungeonButton, SButton) - .Text(LOCTEXT("GenerateDungeonButton", "Generate dungeon")) - .OnClicked_Raw(this, &FDungeonGenerateEditorModule::OnClickedGenerateButton) - ] - - + SVerticalBox::Slot() - .VAlign(VAlign_Center) - .FillHeight(1.f) - .Padding(2.f) - [ - SNew(SButton) - .Text(LOCTEXT("ClearDungeonButton", "Clear dungeon")) - .OnClicked_Raw(this, &FDungeonGenerateEditorModule::OnClickedClearButton) - ] - + // dungeon generation settings + auto dungeonBox = SNew(SVerticalBox); + { + dungeonBox->AddSlot() + .VAlign(VAlign_Center) + .FillHeight(1.f) + .Padding(2.f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .AutoWidth() + .Padding(2.f) + [ + SNew(STextBlock) + .Text(LOCTEXT("DungeonGenerateParameterLabel", "DungeonGenerateParameter")) + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + //SAssignNew(EntryBox, SObjectPropertyEntryBox) + SNew(SObjectPropertyEntryBox) + .AllowedClass(UDungeonGenerateParameter::StaticClass()) + .ObjectPath_Raw(this, &FDungeonGenerateEditorModule::GetObjectPath) + .OnObjectChanged_Raw(this, &FDungeonGenerateEditorModule::SetAssetData) + ] + ]; + + dungeonBox->AddSlot() + .VAlign(VAlign_Center) + .FillHeight(1.f) + .Padding(2.f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .AutoWidth() + .Padding(2.f) + [ + SNew(STextBlock) + .Text(LOCTEXT("RandomSeedLabel", "Random seed")) + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + SAssignNew(mRandomSeedValue, SEditableTextBox) + .Text(LOCTEXT("RandomSeedValue", "")) + ] + ]; + + dungeonBox->AddSlot() + .VAlign(VAlign_Center) + .FillHeight(1.f) + .Padding(2.f) + [ + SAssignNew(mGenerateDungeonButton, SButton) + .Text(LOCTEXT("GenerateDungeonButton", "Generate dungeon")) + .OnClicked_Raw(this, &FDungeonGenerateEditorModule::OnClickedGenerateButton) + ]; + + dungeonBox->AddSlot() + .VAlign(VAlign_Center) + .FillHeight(1.f) + .Padding(2.f) + [ + SNew(SButton) + .Text(LOCTEXT("ClearDungeonButton", "Clear dungeon")) + .OnClicked_Raw(this, &FDungeonGenerateEditorModule::OnClickedClearButton) + ]; + } - // Texture output size - + SVerticalBox::Slot() - .VAlign(VAlign_Center) - .FillHeight(1.f) - .Padding(2.f) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .VAlign(VAlign_Center) - .AutoWidth() - .Padding(2.f) - [ - SNew(STextBlock) - .Text(LOCTEXT("TextureOutputSizeLabel", "Texture output size")) - ] - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(SNumericEntryBox) - .AllowSpin(true) - .MinValue(1) - .MinSliderValue(1) - .Value(512) - .Value_Raw(this, &FDungeonGenerateEditorModule::OnGetGenerateTextureSize) - .OnValueChanged_Raw(this, &FDungeonGenerateEditorModule::OnSetGenerateTextureSize) - ] - ] - - + SVerticalBox::Slot() - .VAlign(VAlign_Center) - .FillHeight(1.f) - .Padding(2.f) - [ - SAssignNew(mGenerateTextureSizeButton, SButton) - .Text(LOCTEXT("GenerateTextureSizeButton", "Generate texture with size")) - .OnClicked_Raw(this, &FDungeonGenerateEditorModule::OnClickedGenerateTextureWithSizeButton) - ] - - // Texture output scale - + SVerticalBox::Slot() - .VAlign(VAlign_Center) - .FillHeight(1.f) - .Padding(2.f) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .VAlign(VAlign_Center) - .AutoWidth() - .Padding(2.f) - [ - SNew(STextBlock) - .Text(LOCTEXT("TextureOutputScaleLabel", "Texture output scale")) - ] - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(SNumericEntryBox) - .AllowSpin(true) - .MinValue(1) - .MinSliderValue(1) - .Value(1) - .Value_Raw(this, &FDungeonGenerateEditorModule::OnGetGenerateTextureScale) - .OnValueChanged_Raw(this, &FDungeonGenerateEditorModule::OnSetGenerateTextureScale) - ] - ] - - + SVerticalBox::Slot() - .VAlign(VAlign_Center) - .FillHeight(1.f) - .Padding(2.f) - [ - SAssignNew(mGenerateTextureScaleButton, SButton) - .Text(LOCTEXT("GenerateTextureScaleButton", "Generate texture with scale")) - .OnClicked_Raw(this, &FDungeonGenerateEditorModule::OnClickedGenerateTextureWithScaleButton) - ] + // mini-map texture settings + //auto textureBox = SNew(SVerticalBox); + auto textureBox = dungeonBox; + { + textureBox->AddSlot() + .VAlign(VAlign_Center) + .FillHeight(1.f) + .Padding(2.f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .AutoWidth() + .Padding(2.f) + [ + SNew(STextBlock) + .Text(LOCTEXT("TextureOutputSizeLabel", "Texture output size")) + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SNumericEntryBox) + .AllowSpin(true) + .MinValue(1) + .MinSliderValue(1) + .Value(512) + .Value_Raw(this, &FDungeonGenerateEditorModule::OnGetGenerateTextureSize) + .OnValueChanged_Raw(this, &FDungeonGenerateEditorModule::OnSetGenerateTextureSize) + ] + ]; + + textureBox->AddSlot() + .VAlign(VAlign_Center) + .FillHeight(1.f) + .Padding(2.f) + [ + SAssignNew(mGenerateTextureSizeButton, SButton) + .Text(LOCTEXT("GenerateTextureSizeButton", "Generate texture with size")) + .OnClicked_Raw(this, &FDungeonGenerateEditorModule::OnClickedGenerateTextureWithSizeButton) + ]; + + // Texture output scale + textureBox->AddSlot() + .VAlign(VAlign_Center) + .FillHeight(1.f) + .Padding(2.f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .AutoWidth() + .Padding(2.f) + [ + SNew(STextBlock) + .Text(LOCTEXT("TextureOutputScaleLabel", "Texture output scale")) + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SNumericEntryBox) + .AllowSpin(true) + .MinValue(1) + .MinSliderValue(1) + .Value(1) + .Value_Raw(this, &FDungeonGenerateEditorModule::OnGetGenerateTextureScale) + .OnValueChanged_Raw(this, &FDungeonGenerateEditorModule::OnSetGenerateTextureScale) + ] + ]; + + textureBox->AddSlot() + .VAlign(VAlign_Center) + .FillHeight(1.f) + .Padding(2.f) + [ + SAssignNew(mGenerateTextureScaleButton, SButton) + .Text(LOCTEXT("GenerateTextureScaleButton", "Generate texture with scale")) + .OnClicked_Raw(this, &FDungeonGenerateEditorModule::OnClickedGenerateTextureWithScaleButton) + ]; + + textureBox->AddSlot() + .VAlign(VAlign_Center) + .FillHeight(1.f) + .Padding(2.f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .AutoWidth() + .Padding(2.f) + [ + SNew(STextBlock) + .Text(LOCTEXT("WorldToTextureScaleLabel", "World to texture space scale")) + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + SAssignNew(mWorldToTextureScale, SEditableTextBox) + .Text(LOCTEXT("WorldToTextureScale", "")) + ] + ]; + } + // Plug-in infomation + //auto infomationBox = SNew(SVerticalBox); + auto infomationBox = dungeonBox; + { + infomationBox->AddSlot() + .VAlign(VAlign_Center) + .FillHeight(1.f) + .Padding(2.f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .AutoWidth() + .Padding(2.f) + [ + SNew(STextBlock) + .Text(LOCTEXT("JenkinsBuildTagLabel", "Build tag")) + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(STextBlock) + .Text(LOCTEXT("JenkinsBuildTagValue", JENKINS_JOB_TAG)) + ] + ]; + } - +SVerticalBox::Slot() - .VAlign(VAlign_Center) - .FillHeight(1.f) - .Padding(2.f) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .VAlign(VAlign_Center) - .AutoWidth() - .Padding(2.f) - [ - SNew(STextBlock) - .Text(LOCTEXT("JenkinsBuildTagLabel", "Build tag")) - ] - + SHorizontalBox::Slot() - .AutoWidth() + TSharedRef dockTab = SNew(SDockTab).TabRole(ETabRole::NomadTab) [ - SNew(STextBlock) - .Text(LOCTEXT("JenkinsBuildTagValue", JENKINS_JOB_TAG)) - ] - ] + dungeonBox /* textureBox , infomationBox*/ ]; SetAssetData(FAssetData()); @@ -358,20 +419,17 @@ FReply FDungeonGenerateEditorModule::OnClickedGenerateButton() mDungeonGeneratorCore = std::make_shared(world); if (mDungeonGeneratorCore == nullptr) { - DUNGEON_GENERATOR_ERROR(TEXT("Failed to create dungeon generator object")); + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("Message", "Failed to create dungeon generator object")); return FReply::Unhandled(); } if (!mDungeonGeneratorCore->Create(dungeonGenerateParameter)) { - DUNGEON_GENERATOR_ERROR(TEXT("Failed to generate dungeon")); + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("Message", "Failed to generate dungeon")); OnClickedClearButton(); return FReply::Unhandled(); } mDungeonGeneratorCore->SyncLoadStreamLevels(); - if (dungeonGenerateParameter->IsMovePlayerStartToStartingPoint()) - { - mDungeonGeneratorCore->MovePlayerStart(); - } + mDungeonGeneratorCore->MovePlayerStart(); // Set random seeds for generated dungeons const int32 value = dungeonGenerateParameter->GetGeneratedRandomSeed(); @@ -411,20 +469,20 @@ FReply FDungeonGenerateEditorModule::OnClickedGenerateTextureWithSizeButton() const UDungeonGenerateParameter* dungeonGenerateParameter = mDungeonGenerateParameter.Get(); if (dungeonGenerateParameter == nullptr) { - DUNGEON_GENERATOR_ERROR(TEXT("Set the DungeonGenerateParameter")); + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("Message", "Set the DungeonGenerateParameter")); return FReply::Unhandled(); } if (mDungeonGeneratorCore == nullptr) { - DUNGEON_GENERATOR_ERROR(TEXT("Generate the dungeon first.")); + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("Message", "Generate the dungeon first.")); return FReply::Unhandled(); } UDungeonMiniMapTextureLayer* dungeonMiniMapTextureLayer = NewObject(); if (!IsValid(dungeonMiniMapTextureLayer)) { - DUNGEON_GENERATOR_ERROR(TEXT("Failed to create DungeonMiniMapTextureLayer object")); + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("Message", "Failed to create DungeonMiniMapTextureLayer object")); return FReply::Unhandled(); } @@ -433,7 +491,12 @@ FReply FDungeonGenerateEditorModule::OnClickedGenerateTextureWithSizeButton() return FReply::Unhandled(); } - return SaveTextures(dungeonMiniMapTextureLayer); + if (!SavePackages(dungeonMiniMapTextureLayer)) + { + return FReply::Unhandled(); + } + + return FReply::Handled(); } FReply FDungeonGenerateEditorModule::OnClickedGenerateTextureWithScaleButton() @@ -441,20 +504,20 @@ FReply FDungeonGenerateEditorModule::OnClickedGenerateTextureWithScaleButton() const UDungeonGenerateParameter* dungeonGenerateParameter = mDungeonGenerateParameter.Get(); if (dungeonGenerateParameter == nullptr) { - DUNGEON_GENERATOR_ERROR(TEXT("Set the DungeonGenerateParameter")); + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("Message", "Set the DungeonGenerateParameter")); return FReply::Unhandled(); } if (mDungeonGeneratorCore == nullptr) { - DUNGEON_GENERATOR_ERROR(TEXT("Generate the dungeon first.")); + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("Message", "Generate the dungeon first.")); return FReply::Unhandled(); } UDungeonMiniMapTextureLayer* dungeonMiniMapTextureLayer = NewObject(); if (!IsValid(dungeonMiniMapTextureLayer)) { - DUNGEON_GENERATOR_ERROR(TEXT("Failed to create DungeonMiniMapTextureLayer object")); + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("Message", "Failed to create DungeonMiniMapTextureLayer object")); return FReply::Unhandled(); } @@ -463,11 +526,18 @@ FReply FDungeonGenerateEditorModule::OnClickedGenerateTextureWithScaleButton() return FReply::Unhandled(); } - return SaveTextures(dungeonMiniMapTextureLayer); + if (!SavePackages(dungeonMiniMapTextureLayer)) + { + return FReply::Unhandled(); + } + + return FReply::Handled(); } -FReply FDungeonGenerateEditorModule::SaveTextures(const UDungeonMiniMapTextureLayer* dungeonMiniMapTextureLayer) const +bool FDungeonGenerateEditorModule::SavePackages(const UDungeonMiniMapTextureLayer* dungeonMiniMapTextureLayer) { +#if defined(_ENABLE_UNREGISTER_MOUNT_POINT) + // Finalizer class class Finalizer final { public: @@ -476,88 +546,180 @@ FReply FDungeonGenerateEditorModule::SaveTextures(const UDungeonMiniMapTextureLa private: std::function mFunction; }; - +#endif // Mount the package destination - const TCHAR* basePath = TEXT("/Game/ProceduralTextures/"); - const FString absoluteBasePath = FPaths::ProjectContentDir() + TEXT("/ProceduralTextures/"); + static const FString baseName = TEXT("/ProceduralTextures/"); + static const FString basePath = TEXT("/Game") + baseName; + const FString absoluteBasePath = FPaths::ProjectContentDir() + baseName; FPackageName::RegisterMountPoint(basePath, absoluteBasePath); - +#if defined(_ENABLE_UNREGISTER_MOUNT_POINT) // Reserve unmounted package destination - Finalizer unmount([basePath, &absoluteBasePath]() + Finalizer unmount([&absoluteBasePath]() { FPackageName::UnRegisterMountPoint(basePath, absoluteBasePath); } ); +#endif + // Initialize world name + FString worldName; + if (const UWorld* world = GetWorldFromGameViewport()) + worldName = world->GetName() + TEXT("_"); - // Save textures as assets - { - TArray packages; + // Save Texture layer infomation + if (!SaveLayerInfoPackage(basePath, worldName, dungeonMiniMapTextureLayer)) + return false; - // Generate texture assets per floors - const int32 floorHeight = dungeonMiniMapTextureLayer->GetFloorHeight(); - for (int32 floor = 0; floor < floorHeight; ++floor) - { - // Create package - const FString packageName = FString::Printf(TEXT("Floor%d"), floor); - const FString packagePath = basePath + packageName; - UPackage* package = CreatePackage(*packagePath); - if (!IsValid(package)) - { - DUNGEON_GENERATOR_ERROR(TEXT("Failed to create Package")); - return FReply::Unhandled(); - } + return true; +} - // Load package - package->FullyLoad(); - package->MarkPackageDirty(); +// Texture layer infomation +bool FDungeonGenerateEditorModule::SaveLayerInfoPackage(const FString& basePath, const FString& worldName, const UDungeonMiniMapTextureLayer* dungeonMiniMapTextureLayer) +{ + const FString packageName = worldName + TEXT("MiniMap"); + const FString packagePath = basePath + packageName; - // Get minimap texture - UDungeonMiniMapTexture* dungeonMiniMapTexture = dungeonMiniMapTextureLayer->GetByFloor(floor); + // Create package + UPackage* package = CreatePackage(*packagePath); + if (!IsValid(package)) + { + DUNGEON_GENERATOR_ERROR(TEXT("Failed to create DungeonMiniMap package")); + return false; + } + package->FullyLoad(); - // Duplicate texture object and change to saveable + // Create UDungeonMiniMap object #if UE_VERSION_NEWER_THAN(5, 1, 0) - FName textureName = MakeUniqueObjectName(package, UTexture2D::StaticClass(), FName(*packageName), EUniqueObjectNameOptions::GloballyUnique); + FName objectName = MakeUniqueObjectName(package, UDungeonMiniMap::StaticClass(), FName(*packageName), EUniqueObjectNameOptions::GloballyUnique); #else - FName textureName = MakeUniqueObjectName(package, UTexture2D::StaticClass(), FName(*packageName)); + FName objectName = MakeUniqueObjectName(package, UDungeonMiniMap::StaticClass(), FName(*packageName)); #endif - UTexture2D* texture = DuplicateObject(dungeonMiniMapTexture->GetTexture(), package, textureName); - texture->SetFlags(RF_Public | RF_Standalone); - texture->ClearFlags(RF_Transient); + UDungeonMiniMap* dungeonMiniMap = NewObject(package, objectName, RF_Public | RF_Standalone); + if (!IsValid(dungeonMiniMap)) + { + DUNGEON_GENERATOR_ERROR(TEXT("Failed to create DungeonMiniMap object")); + return false; + } - // Register texture objects as assets - FAssetRegistryModule::AssetCreated(texture); + // Initialize UDungeonMiniMap object + dungeonMiniMap->SetGridSize(dungeonMiniMapTextureLayer->GetGridScale()); + dungeonMiniMap->SetWorldToTextureScale(dungeonMiniMapTextureLayer->GetScaleWorldToTexture()); + for (int32_t elevation : dungeonMiniMapTextureLayer->GetFloorElevations()) + { + dungeonMiniMap->Add(elevation); + } + + // Notifies that a new asset has been created. + FAssetRegistryModule::AssetCreated(dungeonMiniMap); + dungeonMiniMap->MarkPackageDirty(); + dungeonMiniMap->PostEditChange(); + dungeonMiniMap->AddToRoot(); + package->MarkPackageDirty(); + + // Save texture assets by floors + TArray packages {package}; + if (!SaveTexturePackage(dungeonMiniMap, packages, basePath, worldName, dungeonMiniMapTextureLayer)) + { + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("Message", "Failed to create texture asset")); + return false; + } - // Package and save texture objects - const FString longPackageName = FPackageName::LongPackageNameToFilename(packagePath, FPackageName::GetAssetPackageExtension()); +#if defined(_ENABLE_SAVE_AND_CLOSE_PACKAGES) + // Package and save UDungeonMiniMap objects + const FString longPackageName = FPackageName::LongPackageNameToFilename(package->GetName(), FPackageName::GetAssetPackageExtension()); #if UE_VERSION_NEWER_THAN(5, 1, 0) - FSavePackageArgs saveArgs; - saveArgs.TopLevelFlags = RF_Public | RF_Standalone; - saveArgs.SaveFlags = SAVE_NoError; - const bool saved = UPackage::SavePackage(package, texture, *longPackageName, saveArgs); + FSavePackageArgs saveArgs; + saveArgs.TopLevelFlags = RF_Public | RF_Standalone; + saveArgs.SaveFlags = SAVE_NoError; + const bool saved = UPackage::SavePackage(package, dungeonMiniMap, *longPackageName, saveArgs); #else - const bool saved = UPackage::SavePackage(package, texture, RF_Public | RF_Standalone, *longPackageName); + const bool saved = UPackage::SavePackage(package, dungeonMiniMap, RF_Public | RF_Standalone, *longPackageName); #endif - if (saved == false) - { - DUNGEON_GENERATOR_ERROR(TEXT("Failed to save Package '%s'"), *longPackageName); - return FReply::Unhandled(); - } + if (saved == false) + { + DUNGEON_GENERATOR_ERROR(TEXT("Failed to save Package '%s'"), *longPackageName); + return false; + } - // Record saved packages - packages.Add(package); + // Unload packages + if (!UPackageTools::UnloadPackages(packages)) + { + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("Message", "Failed to unload Packages")); + return false; + } +#endif + + return true; +} + +bool FDungeonGenerateEditorModule::SaveTexturePackage(UDungeonMiniMap* dungeonMiniMap, TArray& packages, const FString& basePath, const FString& worldName, const UDungeonMiniMapTextureLayer* dungeonMiniMapTextureLayer) +{ + // Generate texture assets per floors + const int32 floorHeight = dungeonMiniMapTextureLayer->GetFloorHeight(); + for (int32 floor = 0; floor < floorHeight; ++floor) + { + // Create package + const FString packageName = FString::Printf(TEXT("%sFloor%d"), *worldName, floor); + const FString packagePath = basePath + packageName; + UPackage* package = CreatePackage(*packagePath); + if (!IsValid(package)) + { + DUNGEON_GENERATOR_ERROR(TEXT("Failed to create Package")); + return false; } + package->FullyLoad(); - // Unload packages - if (!PackageTools::UnloadPackages(packages)) + // Duplicate texture object and change to saveable +#if UE_VERSION_NEWER_THAN(5, 1, 0) + FName objectName = MakeUniqueObjectName(package, UTexture2D::StaticClass(), FName(*packageName), EUniqueObjectNameOptions::GloballyUnique); +#else + FName objectName = MakeUniqueObjectName(package, UTexture2D::StaticClass(), FName(*packageName)); +#endif + + // Get minimap texture + UDungeonMiniMapTexture* dungeonMiniMapTexture = dungeonMiniMapTextureLayer->GetByFloor(floor); + UTexture2D* texture = DuplicateObject(dungeonMiniMapTexture->GetTexture(), package, objectName); + texture->SetFlags(RF_Public | RF_Standalone); + texture->ClearFlags(RF_Transient); + + // Notifies that a new asset has been created. + FAssetRegistryModule::AssetCreated(texture); + texture->MarkPackageDirty(); + texture->PostEditChange(); + texture->AddToRoot(); + package->MarkPackageDirty(); + +#if defined(_ENABLE_SAVE_AND_CLOSE_PACKAGES) + // Record saved packages + packages.Add(package); + + // Package and save texture objects + const FString longPackageName = FPackageName::LongPackageNameToFilename(package->GetName(), FPackageName::GetAssetPackageExtension()); +#if UE_VERSION_NEWER_THAN(5, 1, 0) + FSavePackageArgs saveArgs; + saveArgs.TopLevelFlags = RF_Public | RF_Standalone; + saveArgs.SaveFlags = SAVE_NoError; + const bool saved = UPackage::SavePackage(package, texture, *longPackageName, saveArgs); +#else + const bool saved = UPackage::SavePackage(package, texture, RF_Public | RF_Standalone, *longPackageName); +#endif + if (saved == false) { - DUNGEON_GENERATOR_ERROR(TEXT("Failed to unload Packages")); - return FReply::Unhandled(); + DUNGEON_GENERATOR_ERROR(TEXT("Failed to save Package '%s'"), *longPackageName); + return false; } +#endif - packages.Empty(); + // Add texture into UDungeonMiniMap + dungeonMiniMap->AddTexture(texture); } - return FReply::Handled(); + // Set scale value to texture space + { + const float ratio = dungeonMiniMapTextureLayer->GetScaleWorldToTexture(); + mWorldToTextureScale->SetText(FText::FromString(FString::SanitizeFloat(ratio))); + } + + return true; } UWorld* FDungeonGenerateEditorModule::GetWorldFromGameViewport() diff --git a/Source/DungeonGeneratorEditor/Private/DungeonInteriorAssetFactory.cpp b/Source/DungeonGeneratorEditor/Private/DungeonInteriorAssetFactory.cpp deleted file mode 100644 index c184119..0000000 --- a/Source/DungeonGeneratorEditor/Private/DungeonInteriorAssetFactory.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/** -\author Shun Moriya -\copyright 2023- Shun Moriya -All Rights Reserved. -*/ - -#include "DungeonInteriorAssetFactory.h" -#include "../../DungeonGenerator/Public/DungeonInteriorAsset.h" - -UDungeonInteriorAssetFactory::UDungeonInteriorAssetFactory() -{ - SupportedClass = UDungeonInteriorAsset::StaticClass(); - bCreateNew = true; - bEditAfterNew = true; -} - -UObject* UDungeonInteriorAssetFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) -{ - return NewObject(InParent, InClass, InName, Flags); -} diff --git a/Source/DungeonGeneratorEditor/Private/DungeonInteriorAssetTypeActions.cpp b/Source/DungeonGeneratorEditor/Private/DungeonInteriorAssetTypeActions.cpp deleted file mode 100644 index 1cb4050..0000000 --- a/Source/DungeonGeneratorEditor/Private/DungeonInteriorAssetTypeActions.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/** -\author Shun Moriya -\copyright 2023- Shun Moriya -All Rights Reserved. -*/ - -#include "DungeonInteriorAssetTypeActions.h" -#include "../../DungeonGenerator/Public/DungeonInteriorAsset.h" - -FDungeonInteriorAssetTypeActions::FDungeonInteriorAssetTypeActions(EAssetTypeCategories::Type InAssetCategory) - : mAssetCategory(InAssetCategory) -{ -} - -FColor FDungeonInteriorAssetTypeActions::GetTypeColor() const -{ - return FColor::Purple; -} - -void FDungeonInteriorAssetTypeActions::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) -{ - FSimpleAssetEditor::CreateEditor(EToolkitMode::Standalone, EditWithinLevelEditor, InObjects); -} - -FText FDungeonInteriorAssetTypeActions::GetName() const -{ - return FText::FromName(TEXT("DungeonInteriorAsset")); -} - -UClass* FDungeonInteriorAssetTypeActions::GetSupportedClass() const -{ - return UDungeonInteriorAsset::StaticClass(); -} - -uint32 FDungeonInteriorAssetTypeActions::GetCategories() -{ - return mAssetCategory; -} diff --git a/Source/DungeonGeneratorEditor/Private/DungeonInteriorDatabaseFactory.cpp b/Source/DungeonGeneratorEditor/Private/DungeonInteriorDatabaseFactory.cpp new file mode 100644 index 0000000..4fac836 --- /dev/null +++ b/Source/DungeonGeneratorEditor/Private/DungeonInteriorDatabaseFactory.cpp @@ -0,0 +1,20 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "DungeonInteriorDatabaseFactory.h" +#include "../../DungeonGenerator/Public/Decorator/DungeonInteriorDatabase.h" + +UDungeonInteriorDatabaseFactory::UDungeonInteriorDatabaseFactory() +{ + SupportedClass = UDungeonInteriorDatabase::StaticClass(); + bCreateNew = true; + bEditAfterNew = true; +} + +UObject* UDungeonInteriorDatabaseFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ + return NewObject(InParent, InClass, InName, Flags); +} diff --git a/Source/DungeonGeneratorEditor/Private/DungeonInteriorDatabaseTypeActions.cpp b/Source/DungeonGeneratorEditor/Private/DungeonInteriorDatabaseTypeActions.cpp new file mode 100644 index 0000000..2bd6df8 --- /dev/null +++ b/Source/DungeonGeneratorEditor/Private/DungeonInteriorDatabaseTypeActions.cpp @@ -0,0 +1,13 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "DungeonInteriorDatabaseTypeActions.h" +#include "../../DungeonGenerator/Public/Decorator/DungeonInteriorDatabase.h" + +UClass* FDungeonInteriorDatabaseTypeActions::GetSupportedClass() const +{ + return UDungeonInteriorDatabase::StaticClass(); +} diff --git a/Source/DungeonGeneratorEditor/Private/DungeonMiniMapFactory.cpp b/Source/DungeonGeneratorEditor/Private/DungeonMiniMapFactory.cpp new file mode 100644 index 0000000..45ef7af --- /dev/null +++ b/Source/DungeonGeneratorEditor/Private/DungeonMiniMapFactory.cpp @@ -0,0 +1,20 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "DungeonMiniMapFactory.h" +#include "../../DungeonGenerator/Public/MiniMap/DungeonMiniMap.h" + +UDungeonMiniMapFactory::UDungeonMiniMapFactory() +{ + SupportedClass = UDungeonMiniMap::StaticClass(); + bCreateNew = true; + bEditAfterNew = true; +} + +UObject* UDungeonMiniMapFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ + return NewObject(InParent, InClass, InName, Flags); +} diff --git a/Source/DungeonGeneratorEditor/Private/DungeonMiniMapTypeActions.cpp b/Source/DungeonGeneratorEditor/Private/DungeonMiniMapTypeActions.cpp new file mode 100644 index 0000000..3f344d1 --- /dev/null +++ b/Source/DungeonGeneratorEditor/Private/DungeonMiniMapTypeActions.cpp @@ -0,0 +1,13 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "DungeonMiniMapTypeActions.h" +#include "../../DungeonGenerator/Public/MiniMap/DungeonMiniMap.h" + +UClass* FDungeonMiniMapTypeActions::GetSupportedClass() const +{ + return UDungeonMiniMap::StaticClass(); +} diff --git a/Source/DungeonGeneratorEditor/Private/DungeonRoomAssetFactory.cpp b/Source/DungeonGeneratorEditor/Private/DungeonRoomAssetFactory.cpp deleted file mode 100644 index 33f1496..0000000 --- a/Source/DungeonGeneratorEditor/Private/DungeonRoomAssetFactory.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/** -\author Shun Moriya -\copyright 2023- Shun Moriya -All Rights Reserved. -*/ - -#include "DungeonRoomAssetFactory.h" -#include "../../DungeonGenerator/Public/DungeonRoomAsset.h" - -UDungeonRoomAssetFactory::UDungeonRoomAssetFactory() -{ - SupportedClass = UDungeonRoomAsset::StaticClass(); - bCreateNew = true; - bEditAfterNew = true; -} - -UObject* UDungeonRoomAssetFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) -{ - return NewObject(InParent, InClass, InName, Flags); -} diff --git a/Source/DungeonGeneratorEditor/Private/DungeonRoomAssetTypeActions.cpp b/Source/DungeonGeneratorEditor/Private/DungeonRoomAssetTypeActions.cpp deleted file mode 100644 index 41d964a..0000000 --- a/Source/DungeonGeneratorEditor/Private/DungeonRoomAssetTypeActions.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/** -\author Shun Moriya -\copyright 2023- Shun Moriya -All Rights Reserved. -*/ - -#include "DungeonRoomAssetTypeActions.h" -#include "../../DungeonGenerator/Public/DungeonRoomAsset.h" - -FDungeonRoomAssetTypeActions::FDungeonRoomAssetTypeActions(EAssetTypeCategories::Type InAssetCategory) - : mAssetCategory(InAssetCategory) -{ -} - -FColor FDungeonRoomAssetTypeActions::GetTypeColor() const -{ - return FColor::Emerald; -} - -void FDungeonRoomAssetTypeActions::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) -{ - FSimpleAssetEditor::CreateEditor(EToolkitMode::Standalone, EditWithinLevelEditor, InObjects); -} - -FText FDungeonRoomAssetTypeActions::GetName() const -{ - return FText::FromName(TEXT("DungeonRoomAsset")); -} - -UClass* FDungeonRoomAssetTypeActions::GetSupportedClass() const -{ - return UDungeonRoomAsset::StaticClass(); -} - -uint32 FDungeonRoomAssetTypeActions::GetCategories() -{ - return mAssetCategory; -} diff --git a/Source/DungeonGeneratorEditor/Private/DungeonRoomDatabaseFactory.cpp b/Source/DungeonGeneratorEditor/Private/DungeonRoomDatabaseFactory.cpp new file mode 100644 index 0000000..67f46c4 --- /dev/null +++ b/Source/DungeonGeneratorEditor/Private/DungeonRoomDatabaseFactory.cpp @@ -0,0 +1,20 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "DungeonRoomDatabaseFactory.h" +#include "../../DungeonGenerator/Public/SubLevel/DungeonRoomDatabase.h" + +UDungeonRoomDatabaseFactory::UDungeonRoomDatabaseFactory() +{ + SupportedClass = UDungeonRoomDatabase::StaticClass(); + bCreateNew = true; + bEditAfterNew = true; +} + +UObject* UDungeonRoomDatabaseFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ + return NewObject(InParent, InClass, InName, Flags); +} diff --git a/Source/DungeonGeneratorEditor/Private/DungeonRoomDatabaseTypeActions.cpp b/Source/DungeonGeneratorEditor/Private/DungeonRoomDatabaseTypeActions.cpp new file mode 100644 index 0000000..3f0fcf3 --- /dev/null +++ b/Source/DungeonGeneratorEditor/Private/DungeonRoomDatabaseTypeActions.cpp @@ -0,0 +1,13 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "DungeonRoomDatabaseTypeActions.h" +#include "../../DungeonGenerator/Public/SubLevel/DungeonRoomDatabase.h" + +UClass* FDungeonRoomDatabaseTypeActions::GetSupportedClass() const +{ + return UDungeonRoomDatabase::StaticClass(); +} diff --git a/Source/DungeonGeneratorEditor/Private/DungeonRoomMeshSetDatabaseFactory.cpp b/Source/DungeonGeneratorEditor/Private/DungeonRoomMeshSetDatabaseFactory.cpp new file mode 100644 index 0000000..1f180d7 --- /dev/null +++ b/Source/DungeonGeneratorEditor/Private/DungeonRoomMeshSetDatabaseFactory.cpp @@ -0,0 +1,20 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "DungeonRoomMeshSetDatabaseFactory.h" +#include "../../DungeonGenerator/Public/DungeonRoomMeshSetDatabase.h" + +UDungeonRoomMeshSetDatabaseFactory::UDungeonRoomMeshSetDatabaseFactory() +{ + SupportedClass = UDungeonRoomMeshSetDatabase::StaticClass(); + bCreateNew = true; + bEditAfterNew = true; +} + +UObject* UDungeonRoomMeshSetDatabaseFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ + return NewObject(InParent, InClass, InName, Flags); +} diff --git a/Source/DungeonGeneratorEditor/Private/DungeonRoomMeshSetDatabaseTypeActions.cpp b/Source/DungeonGeneratorEditor/Private/DungeonRoomMeshSetDatabaseTypeActions.cpp new file mode 100644 index 0000000..fafe62d --- /dev/null +++ b/Source/DungeonGeneratorEditor/Private/DungeonRoomMeshSetDatabaseTypeActions.cpp @@ -0,0 +1,13 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#include "DungeonRoomMeshSetDatabaseTypeActions.h" +#include "../../DungeonGenerator/Public/DungeonRoomMeshSetDatabase.h" + +UClass* FDungeonRoomMeshSetDatabaseTypeActions::GetSupportedClass() const +{ + return UDungeonRoomMeshSetDatabase::StaticClass(); +} diff --git a/Source/DungeonGeneratorEditor/Private/PluginInfomation.h b/Source/DungeonGeneratorEditor/Private/PluginInfomation.h index 32839e7..9a34709 100644 --- a/Source/DungeonGeneratorEditor/Private/PluginInfomation.h +++ b/Source/DungeonGeneratorEditor/Private/PluginInfomation.h @@ -1,15 +1,19 @@ -/** +/*! This is automatically generated file. DO NOT EDIT THIS FILE. \author Shun Moriya \copyright 2023- Shun Moriya +All Rights Reserved. */ #pragma once +#if !defined(DUNGENERATOR_PLUGIN_INFOMATION_DETECTED) +#define DUNGENERATOR_PLUGIN_INFOMATION_DETECTED #define DUNGENERATOR_PLUGIN_FILE_VERSION "3" -#define DUNGENERATOR_PLUGIN_VERSION "1" -#define DUNGENERATOR_PLUGIN_VERSION_NAME "0.1" -#define DUNGENERATOR_PLUGIN_FRIENDLY_NAME "DungeonGenerator" -#define DUNGENERATOR_PLUGIN_DESCRIPTION "Generates a three-dimensional dungeon." -#define DUNGENERATOR_PLUGIN_DOCS_URL "https://github.com/shun126/DungeonGenerator/wiki/" -#define DUNGENERATOR_PLUGIN_SUPPORT_URL "https://github.com/shun126/DungeonGenerator/issues/" +#define DUNGENERATOR_PLUGIN_VERSION "14" +#define DUNGENERATOR_PLUGIN_VERSION_NAME "1.5.0" +#define DUNGENERATOR_PLUGIN_FRIENDLY_NAME "Procedural 3D Dungeon Generator" +#define DUNGENERATOR_PLUGIN_DESCRIPTION "Procedural 3D dungeon generator plugin. Easy generation of levels, mini-maps and missions." +#define DUNGENERATOR_PLUGIN_DOCS_URL "https://github.com/shun126/UE5-DungeonGeneratorDemo/wiki" +#define DUNGENERATOR_PLUGIN_SUPPORT_URL "https://github.com/shun126/UE5-DungeonGeneratorDemo/discussions/" #define DUNGENERATOR_PLUGIN_BETA_VERSION "true" +#endif diff --git a/Source/DungeonGeneratorEditor/Public/DungeonInteriorAssetFactory.h b/Source/DungeonGeneratorEditor/Public/DungeonAisleMeshSetDatabaseFactory.h similarity index 58% rename from Source/DungeonGeneratorEditor/Public/DungeonInteriorAssetFactory.h rename to Source/DungeonGeneratorEditor/Public/DungeonAisleMeshSetDatabaseFactory.h index 3bcfa3f..ebc9208 100644 --- a/Source/DungeonGeneratorEditor/Public/DungeonInteriorAssetFactory.h +++ b/Source/DungeonGeneratorEditor/Public/DungeonAisleMeshSetDatabaseFactory.h @@ -7,23 +7,16 @@ All Rights Reserved. #pragma once #include #include -#include "DungeonInteriorAssetFactory.generated.h" +#include "DungeonAisleMeshSetDatabaseFactory.generated.h" UCLASS() -class DUNGEONGENERATOREDITOR_API UDungeonInteriorAssetFactory : public UFactory +class DUNGEONGENERATOREDITOR_API UDungeonAisleMeshSetDatabaseFactory : public UFactory { GENERATED_BODY() public: - /* - Constructor - */ - UDungeonInteriorAssetFactory(); - - /* - Destructor - */ - virtual ~UDungeonInteriorAssetFactory() = default; + UDungeonAisleMeshSetDatabaseFactory(); + virtual ~UDungeonAisleMeshSetDatabaseFactory() = default; // UFactory overrides virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; diff --git a/Source/DungeonGeneratorEditor/Public/DungeonAisleMeshSetDatabaseTypeActions.h b/Source/DungeonGeneratorEditor/Public/DungeonAisleMeshSetDatabaseTypeActions.h new file mode 100644 index 0000000..2fa557c --- /dev/null +++ b/Source/DungeonGeneratorEditor/Public/DungeonAisleMeshSetDatabaseTypeActions.h @@ -0,0 +1,53 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include +#include + +class FDungeonAisleMeshSetDatabaseTypeActions : public FAssetTypeActions_Base +{ +public: + FDungeonAisleMeshSetDatabaseTypeActions(EAssetTypeCategories::Type InAssetCategory); + virtual ~FDungeonAisleMeshSetDatabaseTypeActions() = default; + + // FAssetTypeActions_Base overrides + virtual FColor GetTypeColor() const override; + virtual void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr()) override; + + // IAssetTypeActions Implementation + virtual FText GetName() const override; + virtual UClass* GetSupportedClass() const override; + virtual uint32 GetCategories() override; + +private: + EAssetTypeCategories::Type mAssetCategory; +}; + +inline FDungeonAisleMeshSetDatabaseTypeActions::FDungeonAisleMeshSetDatabaseTypeActions(EAssetTypeCategories::Type InAssetCategory) + : mAssetCategory(InAssetCategory) +{ +} + +inline FColor FDungeonAisleMeshSetDatabaseTypeActions::GetTypeColor() const +{ + return FColor::Emerald; +} + +inline void FDungeonAisleMeshSetDatabaseTypeActions::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) +{ + FSimpleAssetEditor::CreateEditor(EToolkitMode::Standalone, EditWithinLevelEditor, InObjects); +} + +inline FText FDungeonAisleMeshSetDatabaseTypeActions::GetName() const +{ + return FText::FromName(TEXT("DungeonAisleMeshSetDatabase")); +} + +inline uint32 FDungeonAisleMeshSetDatabaseTypeActions::GetCategories() +{ + return mAssetCategory; +} diff --git a/Source/DungeonGeneratorEditor/Public/DungeonGenerateParameterFactory.h b/Source/DungeonGeneratorEditor/Public/DungeonGenerateParameterFactory.h index 5ff55f5..cadccf5 100644 --- a/Source/DungeonGeneratorEditor/Public/DungeonGenerateParameterFactory.h +++ b/Source/DungeonGeneratorEditor/Public/DungeonGenerateParameterFactory.h @@ -15,14 +15,7 @@ class DUNGEONGENERATOREDITOR_API UDungeonGenerateParameterFactory : public UFact GENERATED_BODY() public: - /* - constructor - */ UDungeonGenerateParameterFactory(); - - /* - destructor - */ virtual ~UDungeonGenerateParameterFactory() = default; // UFactory overrides diff --git a/Source/DungeonGeneratorEditor/Public/DungeonGenerateParameterTypeActions.h b/Source/DungeonGeneratorEditor/Public/DungeonGenerateParameterTypeActions.h index b9f8d29..e082f15 100644 --- a/Source/DungeonGeneratorEditor/Public/DungeonGenerateParameterTypeActions.h +++ b/Source/DungeonGeneratorEditor/Public/DungeonGenerateParameterTypeActions.h @@ -26,3 +26,28 @@ class UDungeonGenerateParameterTypeActions : public FAssetTypeActions_Base private: EAssetTypeCategories::Type mAssetCategory; }; + +inline UDungeonGenerateParameterTypeActions::UDungeonGenerateParameterTypeActions(EAssetTypeCategories::Type InAssetCategory) + : mAssetCategory(InAssetCategory) +{ +} + +inline FColor UDungeonGenerateParameterTypeActions::GetTypeColor() const +{ + return FColor::Emerald; +} + +inline void UDungeonGenerateParameterTypeActions::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) +{ + FSimpleAssetEditor::CreateEditor(EToolkitMode::Standalone, EditWithinLevelEditor, InObjects); +} + +inline FText UDungeonGenerateParameterTypeActions::GetName() const +{ + return FText::FromName(TEXT("DungeonGenerateParameter")); +} + +inline uint32 UDungeonGenerateParameterTypeActions::GetCategories() +{ + return mAssetCategory; +} diff --git a/Source/DungeonGeneratorEditor/Public/DungeonGeneratorEditorModule.h b/Source/DungeonGeneratorEditor/Public/DungeonGeneratorEditorModule.h index bf71d37..f19c313 100644 --- a/Source/DungeonGeneratorEditor/Public/DungeonGeneratorEditorModule.h +++ b/Source/DungeonGeneratorEditor/Public/DungeonGeneratorEditorModule.h @@ -13,6 +13,7 @@ class FToolBarBuilder; class FMenuBuilder; class CDungeonGeneratorCore; class UDungeonGenerateParameter; +class UDungeonMiniMap; class UDungeonMiniMapTextureLayer; class UStaticMesh; @@ -47,7 +48,9 @@ class FDungeonGenerateEditorModule : public IModuleInterface FReply OnClickedGenerateTextureWithSizeButton(); FReply OnClickedGenerateTextureWithScaleButton(); - FReply SaveTextures(const UDungeonMiniMapTextureLayer* dungeonMiniMapTextureLayer) const; + bool SavePackages(const UDungeonMiniMapTextureLayer* dungeonMiniMapTextureLayer); + bool SaveLayerInfoPackage(const FString& basePath, const FString& worldName, const UDungeonMiniMapTextureLayer* dungeonMiniMapTextureLayer); + bool SaveTexturePackage(UDungeonMiniMap* dungeonMiniMapLayerInfo, TArray& packages, const FString& basePath, const FString& worldName, const UDungeonMiniMapTextureLayer* dungeonMiniMapTextureLayer); static UWorld* GetWorldFromGameViewport(); @@ -61,7 +64,8 @@ class FDungeonGenerateEditorModule : public IModuleInterface TSharedPtr mGenerateTextureScaleButton; TWeakObjectPtr mDungeonGenerateParameter = nullptr; int32 mGenerateTextureSize = 512; - int32 mGenerateTextureScale = 1; + int32 mGenerateTextureScale = 3; + TSharedPtr mWorldToTextureScale; }; inline TOptional FDungeonGenerateEditorModule::OnGetGenerateTextureSize() const diff --git a/Source/DungeonGeneratorEditor/Public/DungeonInteriorAssetTypeActions.h b/Source/DungeonGeneratorEditor/Public/DungeonInteriorAssetTypeActions.h deleted file mode 100644 index 41713dc..0000000 --- a/Source/DungeonGeneratorEditor/Public/DungeonInteriorAssetTypeActions.h +++ /dev/null @@ -1,28 +0,0 @@ -/** -\author Shun Moriya -\copyright 2023- Shun Moriya -All Rights Reserved. -*/ - -#pragma once -#include -#include - -class FDungeonInteriorAssetTypeActions : public FAssetTypeActions_Base -{ -public: - FDungeonInteriorAssetTypeActions(EAssetTypeCategories::Type InAssetCategory); - virtual ~FDungeonInteriorAssetTypeActions() = default; - - // FAssetTypeActions_Base overrides - virtual FColor GetTypeColor() const override; - virtual void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr()) override; - - // IAssetTypeActions Implementation - virtual FText GetName() const override; - virtual UClass* GetSupportedClass() const override; - virtual uint32 GetCategories() override; - -private: - EAssetTypeCategories::Type mAssetCategory; -}; diff --git a/Source/DungeonGeneratorEditor/Public/DungeonInteriorDatabaseFactory.h b/Source/DungeonGeneratorEditor/Public/DungeonInteriorDatabaseFactory.h new file mode 100644 index 0000000..9d65376 --- /dev/null +++ b/Source/DungeonGeneratorEditor/Public/DungeonInteriorDatabaseFactory.h @@ -0,0 +1,29 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include +#include +#include "DungeonInteriorDatabaseFactory.generated.h" + +UCLASS() +class DUNGEONGENERATOREDITOR_API UDungeonInteriorDatabaseFactory : public UFactory +{ + GENERATED_BODY() + +public: + UDungeonInteriorDatabaseFactory(); + virtual ~UDungeonInteriorDatabaseFactory() = default; + + // UFactory overrides + virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; +}; + +UCLASS(Deprecated, meta = (DeprecationMessage = "UDungeonInteriorAssetFactory is deprecated, use UDungeonInteriorDatabaseFactory.")) +class DUNGEONGENERATOREDITOR_API UDEPRECATED_DungeonInteriorAssetFactory : public UDungeonInteriorDatabaseFactory +{ + GENERATED_BODY() +}; diff --git a/Source/DungeonGeneratorEditor/Public/DungeonInteriorDatabaseTypeActions.h b/Source/DungeonGeneratorEditor/Public/DungeonInteriorDatabaseTypeActions.h new file mode 100644 index 0000000..f7464ea --- /dev/null +++ b/Source/DungeonGeneratorEditor/Public/DungeonInteriorDatabaseTypeActions.h @@ -0,0 +1,53 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include +#include + +class FDungeonInteriorDatabaseTypeActions : public FAssetTypeActions_Base +{ +public: + explicit FDungeonInteriorDatabaseTypeActions(EAssetTypeCategories::Type InAssetCategory); + virtual ~FDungeonInteriorDatabaseTypeActions() = default; + + // FAssetTypeActions_Base overrides + virtual FColor GetTypeColor() const override; + virtual void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr()) override; + + // IAssetTypeActions Implementation + virtual FText GetName() const override; + virtual UClass* GetSupportedClass() const override; + virtual uint32 GetCategories() override; + +private: + EAssetTypeCategories::Type mAssetCategory; +}; + +inline FDungeonInteriorDatabaseTypeActions::FDungeonInteriorDatabaseTypeActions(EAssetTypeCategories::Type InAssetCategory) + : mAssetCategory(InAssetCategory) +{ +} + +inline FColor FDungeonInteriorDatabaseTypeActions::GetTypeColor() const +{ + return FColor::Emerald; +} + +inline void FDungeonInteriorDatabaseTypeActions::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) +{ + FSimpleAssetEditor::CreateEditor(EToolkitMode::Standalone, EditWithinLevelEditor, InObjects); +} + +inline FText FDungeonInteriorDatabaseTypeActions::GetName() const +{ + return FText::FromName(TEXT("DungeonInteriorDatabase")); +} + +inline uint32 FDungeonInteriorDatabaseTypeActions::GetCategories() +{ + return mAssetCategory; +} diff --git a/Source/DungeonGeneratorEditor/Public/DungeonRoomAssetFactory.h b/Source/DungeonGeneratorEditor/Public/DungeonMiniMapFactory.h similarity index 64% rename from Source/DungeonGeneratorEditor/Public/DungeonRoomAssetFactory.h rename to Source/DungeonGeneratorEditor/Public/DungeonMiniMapFactory.h index 8725e20..65eead2 100644 --- a/Source/DungeonGeneratorEditor/Public/DungeonRoomAssetFactory.h +++ b/Source/DungeonGeneratorEditor/Public/DungeonMiniMapFactory.h @@ -7,16 +7,16 @@ All Rights Reserved. #pragma once #include #include -#include "DungeonRoomAssetFactory.generated.h" +#include "DungeonMiniMapFactory.generated.h" UCLASS() -class DUNGEONGENERATOREDITOR_API UDungeonRoomAssetFactory : public UFactory +class DUNGEONGENERATOREDITOR_API UDungeonMiniMapFactory : public UFactory { GENERATED_BODY() public: - UDungeonRoomAssetFactory(); - virtual ~UDungeonRoomAssetFactory() = default; + UDungeonMiniMapFactory(); + virtual ~UDungeonMiniMapFactory() = default; // UFactory overrides virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; diff --git a/Source/DungeonGeneratorEditor/Public/DungeonMiniMapTypeActions.h b/Source/DungeonGeneratorEditor/Public/DungeonMiniMapTypeActions.h new file mode 100644 index 0000000..bba9a61 --- /dev/null +++ b/Source/DungeonGeneratorEditor/Public/DungeonMiniMapTypeActions.h @@ -0,0 +1,53 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include +#include + +class FDungeonMiniMapTypeActions : public FAssetTypeActions_Base +{ +public: + FDungeonMiniMapTypeActions(EAssetTypeCategories::Type InAssetCategory); + virtual ~FDungeonMiniMapTypeActions() = default; + + // FAssetTypeActions_Base overrides + virtual FColor GetTypeColor() const override; + virtual void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr()) override; + + // IAssetTypeActions Implementation + virtual FText GetName() const override; + virtual UClass* GetSupportedClass() const override; + virtual uint32 GetCategories() override; + +private: + EAssetTypeCategories::Type mAssetCategory; +}; + +inline FDungeonMiniMapTypeActions::FDungeonMiniMapTypeActions(EAssetTypeCategories::Type InAssetCategory) + : mAssetCategory(InAssetCategory) +{ +} + +inline FColor FDungeonMiniMapTypeActions::GetTypeColor() const +{ + return FColor::Emerald; +} + +inline void FDungeonMiniMapTypeActions::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) +{ + FSimpleAssetEditor::CreateEditor(EToolkitMode::Standalone, EditWithinLevelEditor, InObjects); +} + +inline FText FDungeonMiniMapTypeActions::GetName() const +{ + return FText::FromName(TEXT("DungeonMiniMap")); +} + +inline uint32 FDungeonMiniMapTypeActions::GetCategories() +{ + return mAssetCategory; +} diff --git a/Source/DungeonGeneratorEditor/Public/DungeonRoomAssetTypeActions.h b/Source/DungeonGeneratorEditor/Public/DungeonRoomAssetTypeActions.h deleted file mode 100644 index d3925c8..0000000 --- a/Source/DungeonGeneratorEditor/Public/DungeonRoomAssetTypeActions.h +++ /dev/null @@ -1,28 +0,0 @@ -/** -\author Shun Moriya -\copyright 2023- Shun Moriya -All Rights Reserved. -*/ - -#pragma once -#include -#include - -class FDungeonRoomAssetTypeActions : public FAssetTypeActions_Base -{ -public: - FDungeonRoomAssetTypeActions(EAssetTypeCategories::Type InAssetCategory); - virtual ~FDungeonRoomAssetTypeActions() = default; - - // FAssetTypeActions_Base overrides - virtual FColor GetTypeColor() const override; - virtual void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr()) override; - - // IAssetTypeActions Implementation - virtual FText GetName() const override; - virtual UClass* GetSupportedClass() const override; - virtual uint32 GetCategories() override; - -private: - EAssetTypeCategories::Type mAssetCategory; -}; diff --git a/Source/DungeonGeneratorEditor/Public/DungeonRoomDatabaseFactory.h b/Source/DungeonGeneratorEditor/Public/DungeonRoomDatabaseFactory.h new file mode 100644 index 0000000..c5837da --- /dev/null +++ b/Source/DungeonGeneratorEditor/Public/DungeonRoomDatabaseFactory.h @@ -0,0 +1,29 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include +#include +#include "DungeonRoomDatabaseFactory.generated.h" + +UCLASS() +class DUNGEONGENERATOREDITOR_API UDungeonRoomDatabaseFactory : public UFactory +{ + GENERATED_BODY() + +public: + UDungeonRoomDatabaseFactory(); + virtual ~UDungeonRoomDatabaseFactory() = default; + + // UFactory overrides + virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; +}; + +UCLASS(Deprecated, meta = (DeprecationMessage = "UDungeonRoomAssetFactory is deprecated, use UDungeonRoomDatabaseFactory.")) +class DUNGEONGENERATOREDITOR_API UDEPRECATED_DungeonRoomAssetFactory : public UDungeonRoomDatabaseFactory +{ + GENERATED_BODY() +}; diff --git a/Source/DungeonGeneratorEditor/Public/DungeonRoomDatabaseTypeActions.h b/Source/DungeonGeneratorEditor/Public/DungeonRoomDatabaseTypeActions.h new file mode 100644 index 0000000..ede853f --- /dev/null +++ b/Source/DungeonGeneratorEditor/Public/DungeonRoomDatabaseTypeActions.h @@ -0,0 +1,53 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include +#include + +class FDungeonRoomDatabaseTypeActions : public FAssetTypeActions_Base +{ +public: + FDungeonRoomDatabaseTypeActions(EAssetTypeCategories::Type InAssetCategory); + virtual ~FDungeonRoomDatabaseTypeActions() = default; + + // FAssetTypeActions_Base overrides + virtual FColor GetTypeColor() const override; + virtual void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr()) override; + + // IAssetTypeActions Implementation + virtual FText GetName() const override; + virtual UClass* GetSupportedClass() const override; + virtual uint32 GetCategories() override; + +private: + EAssetTypeCategories::Type mAssetCategory; +}; + +inline FDungeonRoomDatabaseTypeActions::FDungeonRoomDatabaseTypeActions(EAssetTypeCategories::Type InAssetCategory) + : mAssetCategory(InAssetCategory) +{ +} + +inline FColor FDungeonRoomDatabaseTypeActions::GetTypeColor() const +{ + return FColor::Emerald; +} + +inline void FDungeonRoomDatabaseTypeActions::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) +{ + FSimpleAssetEditor::CreateEditor(EToolkitMode::Standalone, EditWithinLevelEditor, InObjects); +} + +inline FText FDungeonRoomDatabaseTypeActions::GetName() const +{ + return FText::FromName(TEXT("DungeonRoomDatabase")); +} + +inline uint32 FDungeonRoomDatabaseTypeActions::GetCategories() +{ + return mAssetCategory; +} diff --git a/Source/DungeonGeneratorEditor/Public/DungeonRoomMeshSetDatabaseFactory.h b/Source/DungeonGeneratorEditor/Public/DungeonRoomMeshSetDatabaseFactory.h new file mode 100644 index 0000000..649753d --- /dev/null +++ b/Source/DungeonGeneratorEditor/Public/DungeonRoomMeshSetDatabaseFactory.h @@ -0,0 +1,23 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include +#include +#include "DungeonRoomMeshSetDatabaseFactory.generated.h" + +UCLASS() +class DUNGEONGENERATOREDITOR_API UDungeonRoomMeshSetDatabaseFactory : public UFactory +{ + GENERATED_BODY() + +public: + UDungeonRoomMeshSetDatabaseFactory(); + virtual ~UDungeonRoomMeshSetDatabaseFactory() = default; + + // UFactory overrides + virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; +}; diff --git a/Source/DungeonGeneratorEditor/Public/DungeonRoomMeshSetDatabaseTypeActions.h b/Source/DungeonGeneratorEditor/Public/DungeonRoomMeshSetDatabaseTypeActions.h new file mode 100644 index 0000000..c6e2d38 --- /dev/null +++ b/Source/DungeonGeneratorEditor/Public/DungeonRoomMeshSetDatabaseTypeActions.h @@ -0,0 +1,53 @@ +/** +\author Shun Moriya +\copyright 2023- Shun Moriya +All Rights Reserved. +*/ + +#pragma once +#include +#include + +class FDungeonRoomMeshSetDatabaseTypeActions : public FAssetTypeActions_Base +{ +public: + FDungeonRoomMeshSetDatabaseTypeActions(EAssetTypeCategories::Type InAssetCategory); + virtual ~FDungeonRoomMeshSetDatabaseTypeActions() = default; + + // FAssetTypeActions_Base overrides + virtual FColor GetTypeColor() const override; + virtual void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr()) override; + + // IAssetTypeActions Implementation + virtual FText GetName() const override; + virtual UClass* GetSupportedClass() const override; + virtual uint32 GetCategories() override; + +private: + EAssetTypeCategories::Type mAssetCategory; +}; + +inline FDungeonRoomMeshSetDatabaseTypeActions::FDungeonRoomMeshSetDatabaseTypeActions(EAssetTypeCategories::Type InAssetCategory) + : mAssetCategory(InAssetCategory) +{ +} + +inline FColor FDungeonRoomMeshSetDatabaseTypeActions::GetTypeColor() const +{ + return FColor::Emerald; +} + +inline void FDungeonRoomMeshSetDatabaseTypeActions::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) +{ + FSimpleAssetEditor::CreateEditor(EToolkitMode::Standalone, EditWithinLevelEditor, InObjects); +} + +inline FText FDungeonRoomMeshSetDatabaseTypeActions::GetName() const +{ + return FText::FromName(TEXT("DungeonRoomMeshSetDatabase")); +} + +inline uint32 FDungeonRoomMeshSetDatabaseTypeActions::GetCategories() +{ + return mAssetCategory; +}