o.o Your … I spent 3 Years developing a Procedurally generated game in unity, City, Building, @.@ and had to Switch to unreal engine :X I cant wait to buy this. Lolz
So - Just got looking at the tutorial videos on YouTube for Dungeon Architect. Looks like I would have to define a Base City theme and then define a bunch of sub themes and overwrite the various bits of city that I want with them override volumes.
That said - The ability to connect multiple dungeons (or in my case Cities) together would be great. Would that be possible? I think that would be a great way to circumvent the problem you are facing with World Composition. So basically make the City Builder have an entrance or Exit which an artist can make it connect to another level manually.
Either way just a thought.
- HeadClot
@HeadClot that should be possible. I’ll have a demo of this soon and I’m open to suggestions
Thank you @MSTF. That is very useful. This works during runtime, however the editor gives errors since we change the package names as a workaround in that thread. So I’ve made two implementations, one for the editor and another for the runtime. If it is a runtime only build (e.g. standalone game) it would use the implementation mentioned in the thread. If we are in the editor, I use the editor services to clone the level files and map them to the world composition so we don’t get any warnings in the editor
This way it solves the problem in both editor and non-editor builds
's the code, if anyone is interested on getting something like this done
Non-Editor Builds (e.g. standalone)
ULevel* FDungeonNonEditorFallbackService::CreateStreamingLevelInstance(UWorld* InWorld, const FTransform& Transform, const TAssetPtr<UWorld>& WorldAsset, UClass* LevelStreamingClass) {
ULevel* NewLevel = NULL;
if (LevelStreamingClass == NULL) {
return NULL;
}
ULevelStreaming* StreamingLevel = NewObject<ULevelStreaming>(InWorld, LevelStreamingClass, NAME_None, RF_NoFlags, NULL);
FString PackageName = WorldAsset.GetLongPackageName();
FString UniquePackageName = PackageName + "_" + FGuid::NewGuid().ToString();
StreamingLevel->SetWorldAssetByPackageName(FName(*UniquePackageName));
StreamingLevel->LevelTransform = Transform;
StreamingLevel->PackageNameToLoad = FName(*PackageName);
StreamingLevel->bShouldBeLoaded = true;
StreamingLevel->bShouldBeVisible = true;
StreamingLevel->bShouldBlockOnLoad = false;
StreamingLevel->LevelColor = FLinearColor::MakeRandomColor();
// Add the new level to world.
InWorld->StreamingLevels.Add(StreamingLevel);
// Refresh just the newly created level.
TArray<ULevelStreaming*> LevelsForRefresh;
LevelsForRefresh.Add(StreamingLevel);
InWorld->RefreshStreamingLevels(LevelsForRefresh);
InWorld->MarkPackageDirty();
NewLevel = StreamingLevel->GetLoadedLevel();
if (NewLevel != nullptr)
{
//EditorLevelUtils::SetLevelVisibility(NewLevel, true, true);
// Levels migrated from other projects may fail to load their world settings
// If so we create a new AWorldSettings actor .
if (NewLevel->GetWorldSettings(false) == nullptr)
{
UWorld* SubLevelWorld = CastChecked<UWorld>(NewLevel->GetOuter());
FActorSpawnParameters SpawnInfo;
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
SpawnInfo.Name = GEngine->WorldSettingsClass->GetFName();
AWorldSettings* NewWorldSettings = SubLevelWorld->SpawnActor<AWorldSettings>(GEngine->WorldSettingsClass, SpawnInfo);
NewLevel->SetWorldSettings(NewWorldSettings);
}
}
return NewLevel;
}
Editor Builds
template<typename T>
static T* CloneAsset(const FString& TemplatePath, const FString& TargetName, const FString& TargetDirectory) {
T* Template = LoadObject<T>(NULL, *TemplatePath, NULL, LOAD_None, NULL);
IAssetTools& AssetTools = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools").Get();
UObject* AssetObject = AssetTools.DuplicateAsset(TargetName, TargetDirectory, Template);
T* Asset = Cast<T>(AssetObject);
if (!Asset) {
UE_LOG(LogEditorService, Warning, TEXT("Failed to clone asset at location %s/%s"), *TargetDirectory, *TargetName);
}
return Asset;
}
ULevel* FDungeonEditorService::CreateStreamingLevelInstance(UWorld* InWorld, const FTransform& Transform, const TAssetPtr<UWorld>& WorldAsset, UClass* LevelStreamingClass)
{
ULevel* NewLevel = nullptr;
if (LevelStreamingClass == NULL) {
return NULL;
}
ULevelStreaming* StreamingLevel = NewObject<ULevelStreaming>(InWorld, LevelStreamingClass, NAME_None, RF_NoFlags, NULL);
// Clone the streaming world
if (!InWorld || !InWorld->GetOutermost()) {
UE_LOG(LogEditorService, Error, TEXT("Cannot create streaming level. World context state is invalid"));
return nullptr;
}
FString ClonedAssetName = WorldAsset->GetName() + "_" + FGuid::NewGuid().ToString();
FString ClonedAssetPath = InWorld->GetOutermost()->GetName() + "_SnapStreamingInstances";
UWorld* ClonedWorld = CloneAsset<UWorld>(WorldAsset.GetLongPackageName(), ClonedAssetName, ClonedAssetPath);
if (!ClonedWorld) {
UE_LOG(LogEditorService, Error, TEXT("Failed to clone snap module"));
return nullptr;
}
FStringAssetReference ClonedWorldAssetRef(ClonedWorld);
FString ClonedWorldPath = ClonedWorldAssetRef.ToString();
StreamingLevel->SetWorldAssetByPackageName(FName(*ClonedWorldPath));
StreamingLevel->LevelTransform = Transform;
StreamingLevel->bShouldBeLoaded = true;
StreamingLevel->bShouldBeVisible = true;
StreamingLevel->bShouldBlockOnLoad = false;
StreamingLevel->LevelColor = FLinearColor::MakeRandomColor();
// Add the new level to world.
InWorld->StreamingLevels.Add(StreamingLevel);
// Refresh just the newly created level.
TArray<ULevelStreaming*> LevelsForRefresh;
LevelsForRefresh.Add(StreamingLevel);
InWorld->RefreshStreamingLevels(LevelsForRefresh);
InWorld->MarkPackageDirty();
NewLevel = StreamingLevel->GetLoadedLevel();
if (NewLevel != nullptr)
{
//EditorLevelUtils::SetLevelVisibility(NewLevel, true, true);
// Levels migrated from other projects may fail to load their world settings
// If so we create a new AWorldSettings actor .
if (NewLevel->GetWorldSettings(false) == nullptr)
{
UWorld* SubLevelWorld = CastChecked<UWorld>(NewLevel->GetOuter());
FActorSpawnParameters SpawnInfo;
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
SpawnInfo.Name = GEngine->WorldSettingsClass->GetFName();
AWorldSettings* NewWorldSettings = SubLevelWorld->SpawnActor<AWorldSettings>(GEngine->WorldSettingsClass, SpawnInfo);
NewLevel->SetWorldSettings(NewWorldSettings);
}
}
return NewLevel;
}
Can you throw me a Example, I think im Doing something wrong @.@ Everything I
ve Tried, that should work for allocation of information/Actors/Meshes in the Route arent working -_-
So I think you are saying this fixes the bug of having things get hung up and not allowing save. So in this dual case thing does that mean things like vegetation are only going to work in the editor? Also Where do things stand on Baked lighting and Vertex Painting blended materials? Will there be a binary build of this available on the site soon?
Everything works, foliage, lights vertex paint etc. I’ll have an update In 1 or 2 days. It also fixes the save bug
Thank you soooo much!
Hi,
Sorry for the very long post, i hope it can be a starting point for anyone interested in using DA with c++
My code seems to be working so is how it looks like:
watch?v=MffP7li8hMM
And 's how it’s done.
First i had to tell my project there is a new plugin in town:
Build.cs:
PublicDependencyModuleNames.AddRange(new string] { "DungeonArchitectRuntime" });
PublicIncludePaths.AddRange(new string] { "DungeonArchitectRuntime/Public", "DungeonArchitectRuntime/Classes" });
Then i created a class inheriting from ADungeon in which i included all the DA headers i needed.
I also created a structure containing all the variables i want to expose, because a structure is easily replicated and saved/reloaded.
DADungeon.h:
#include "DungeonThemeAsset.h"
#include "Builders/Grid/GridDungeonBuilder.h"
#include "Builders/Grid/GridDungeonConfig.h"
#include "Builders/FloorPlan/FloorPlanBuilder.h"
#include "Builders/FloorPlan/FloorPlanConfig.h"
#include "Core/Utils/DungeonModelHelper.h"
// Actor gameplay properties
USTRUCT()
struct FDAFdD{};
class YAG_API ADADungeon : public ADungeon
{
...]
UPROPERTY(ReplicatedUsing = OnRep_FdD)
FDAFdD FdD;
UFUNCTION()
void OnRep_FdD();
...]
};
I had to set its root component to movable to be able to teleport the dungeons in game:
ADungeon.cpp:
ADADungeon::ADADungeon(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
...]
GetRootComponent()->SetMobility(EComponentMobility::Movable);
...]
}
Don’t forget to replicate the structure:
void ADADungeon::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ADADungeon, FdD);
...]
}
The core of the class is the RebuildDungeon method in which i use the code provided by.
I put it entirely :
void ADADungeon::RebuildDungeon()
{
// Grab a reference to the theme asset and register it with the dungeon actor
FString StrDATheme = "/Game/DLCDaDungeons/Themes/" + FdD.DAThemeName / FdD.DAThemeName;
UDungeonThemeAsset* DATheme = Cast<UDungeonThemeAsset>(StaticLoadObject(UDungeonThemeAsset::StaticClass(), nullptr, *StrDATheme));
if (!DATheme) return;
// set the new theme
Themes.Empty();
Themes.Add(DATheme);
// Set the builder type
if (FdD.DADungeonTypeEnum == DADungeonEnum::DADUNGEON_GRID)
{
BuilderClass = UGridDungeonBuilder::StaticClass();
CreateBuilderInstance();
// Set the dungeon configuration
UGridDungeonConfig* GridConfig = Cast<UGridDungeonConfig>(GetConfig());
if (GridConfig) {
GridConfig->Seed = FdD.Seed;
GridConfig->Instanced = true;
GridConfig->NumCells = FdD.NumCells;
GridConfig->MinCellSize = FdD.MinCellSize;
GridConfig->MaxCellSize = FdD.MaxCellSize;
GridConfig->SpanningTreeLoopProbability = FdD.SpanningTreeLoopProbability;
GridConfig->HeightVariationProbability = FdD.HeightVariationProbability;
GridConfig->MaxAllowedStairHeight = FdD.MaxAllowedStairHeight;
}
}
if (FdD.DADungeonTypeEnum == DADungeonEnum::DADUNGEON_FLOOR)
{
BuilderClass = UFloorPlanBuilder::StaticClass();
CreateBuilderInstance();
// Set the dungeon configuration
UFloorPlanConfig* PlanConfig = Cast<UFloorPlanConfig>(GetConfig());
if (PlanConfig) {
PlanConfig->Seed = FdD.Seed;
PlanConfig->Instanced = true;
PlanConfig->BuildingSize = FdD.BuildingSize;
PlanConfig->MinRoomSize = FdD.MinRoomSize;
PlanConfig->MaxRoomSize = FdD.MaxRoomSize;
PlanConfig->HallWidth = FdD.HallWidth;
PlanConfig->MinRoomChunkArea = FdD.MinRoomChunkArea;
}
}
// build the dungeon
BuildDungeon();
}
This function is called whenever the FdD structure is changed, through the OnRep function:
void ADADungeon::OnRep_FdD()
{
...]
RebuildDungeon();
...]
}
The dungeon is spawned by the player controller in a server function:
void AyagPlayerController::ServerSpawnDADungeon_Implementation(const FString& NewDAThemeName)
{
// player location
AYagPlayer* YagDefaultPawn = Cast<AYagPlayer>(DefaultPawn);
if (!YagDefaultPawn) return;
// spawning location
// ground altitude relative to the player: -1/2 capsule
float DeltaZToGround = -DefaultPawn->GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
FVector DADungeonLocation = DefaultPawn->GetActorLocation() + FVector(0.f, 0.f, DeltaZToGround) + 500.f * GetActorForwardVector();
// rotation
FRotator DADungeonRotation = FRotator(0.f, 0.f, 0.f);
// spawn DADungeon
FActorSpawnParameters SpawnDADungeonParams;
ADADungeon* SpawnedDADungeon = GetWorld()->SpawnActor<ADADungeon>(ADADungeon::StaticClass(), DADungeonLocation, DADungeonRotation, SpawnDADungeonParams);
if (!SpawnedDADungeon) return;
// game state
AYagGameState* MyGameState = Cast<AYagGameState>(GetWorld()->GameState);
if (!MyGameState) return;
// set DADungeon type
FDAFdD NewFdD;
NewFdD.DAName = Display(TextEnum::DUNGEON) + " " + FString::FromInt(MyGameState->DADungeonArray.Num() + 1);
NewFdD.Seed = DungeonRandStream.GetUnsignedInt();
NewFdD.DAThemeName = "Default";
// This is the server function which replicates the FdD structure and triggers the call of the OnRep function on every clients
SpawnedDADungeon->SetFdD(NewFdD);
// add in DADungeon array
MyGameState->DADungeonArray.Add(SpawnedDADungeon);
...]
}
Destroying is done by calling the ADungeon::DestroyDungeon() method and then destroying the dungeon actor.
Saving and reloading are made through the FdD structure, nothing fancy.
When reloading, i just read the FdD, then spawn a new dungeon and assign the FdD to it.
Hope this helps !
Cheers
, your game is looking . I really like what you did with it and thank you for posting the code
A quick update on the marketplace integration for website customers, I waited for a few days for people to respond (about 45% have responded) and sent the list to Epic last week (on 20th). I’ll let you know if I hear from them
Thanks a lot
Version 2.1.11-beta.1
- Snap builder now uses world composition to build the scene. This retains all the other engine features in the modules like lightmaps, foliage, level blueprint scripts etc
@, I’ve have a sample soon
[MENTION=28980][/MENTION] Is there any way to tell the Isaac Builder to:
-
spawn a certain marker only once per room? (I think a selection logic would be applied, but how do I tell the engine to select only the middle cell, if my rooms are 3x3 cells?)
I tried also creating a new Marker Emitter live I’ve seen in this picture on the documentation page: http://coderespawn.github.io/dungeon-architect-user-guide-ue4/stage/assets/images/marker_emitter_eg1_1.png
But when I drag off the model pin, it does not return “break dungeonmodel” node. (pin color is blue, not darkblue like in your picture) -
spawn a marker with 100% probability only just once per seed?
PS.: For the Isaac Builder, there is literally no documentation
Ok this seems to work with the exception being that you can’t seem to do it with Source control active. on a non source control project it works as best as I cant tell but when you build in a project under source control. "PERFORCE 4 " as it is building the editor starts trying to add a ton of files to the source control and then crashes. Not sure if there is a way to flag these as cache so this doesn’t happen or if it has to do with the way it seem that now all of the temp files get written to a folder in the same folder as the map. Anyway it overwhelms the system and it crashes the editor every-time with source control on.
Is there a way to make it toss that stuff in the “Intermediate” folder or something so it isnt under the source monitored folders? or a way to flag it I guess either way
The only real "BUG I have noticed is in the world outliner all of the objects are getting thrown int he root of it instead of in the dungeon items folder. This makes it a nightmare to navigate.
Back to testing
[MENTION=28980][/MENTION] Really nice plugin, exactly what I needed. I got 2.1.10 installed on 4.13(OSX), are some questions:
-
When you have 2 or more dungeon actors on a level and use the paint mode, how do I choose which dungeon I’m painting on? I noticed in the videos and docs you have a dropdown to choose where to paint, I don’t have such option in my editor, just the brush size and floor index.
-
How do I paint a room? No matter how large I paint an area it always paints corridors. Can this only be done using the platform volume? Using the platform volume method doesn’t create door openings on rooms when manually paining from it, only creates door openings when procedural generated dungeon parts connect to it, If I set my number of cells to 0 and do all the creation manually I never get doors.
-
Is it possible to use snap doors in custom made dungeon pieces and fill the gaps with procedural generation? Say I create an intro section and a boss room in Maya, can I place them in the level and use snap doors to generate a grid based dungeon that connects the two?
-
Is there a roadmap for planned features?
Feature request:
Have a way to change generated marker types manually.
are some sample scenarios:
a. Generated dungeon is perfect, but some stairs are not desired, be able to remove those stairs and have the change persist when I regenerate the dungeon like it does when I manually add/remove cells. Basically change the stair marker for a ground marker in a case by case basis.
b. Be able to expand or remove door openings on a case by case basis. Basically change a door marker for a wall marker or vice versa.
I’m aware that I can modify the generated dungeon and remove actors and parts as a please, but when I regenerate the dungeon or manually paint anywhere those edits are lost. Say I create a dungeon graybox using basic shapes and materials much like you do in your tutorial, I edit and tweak for function, remove some stairs, make some door openings wider, etc, then I apply the template that has the final look, all my changes and edits should persist.
Compactly crashes on build EVERY time now. even cleared the dungeon and built a new one in a blank map with only a couple rooms and a connector. crashed. tried turning all of the setting like total rooms to 10 and safety limit to 50 and still crashed.
Had to roll back to 2.1.10 So it still has the saving but. so what is the properway to trigger the snap builder int he level blue print I would assume…
Tried This:
it doesnt seem to work.