I have city blocks made of procedural buildings, which are in turn made with procedural elements (different facades, windows, wallpapers, and colors for example). I’m using level streaming and level instances, but I need the levels to remain consistent since it’s supposed to be an open-world.
This works fine the first time the level is created, but every time after that, it gets procedurally generated again. What should I be doing here?
Here is the BP for the level streaming volume:
And here’s an example of some buildings being inconsistent:
Hi. Actually you have to edit your random buildings generator. Things that needed to be changed is Random Integer, Random Float nodes and so on (Random * In Range too). You can just search it over blueprint using “Random” keyword. This nodes should be replaced by them equivalents with “From Stream” addition. Then you have to create a Stream variable somewhere (in your BP for example), this value will be the “Seed” of generation. And connect it to every stream input of random nodes you made. As result setting specific seed will give you the same building each time.
Ok, I understand the concept that it saves a seed number for the random gen. If I set all these seed numbers in a building, and then make a level with multiple buildings, will it automatically save the seeds that they rolled, or will I need to set up something to record these seeds in my level streamer or something?
You can add stream structure as an editable property of building - it will be visible as numeric seed in editor and will be saved for every building in a map.
In addition you can use following BP part to randomize building if it doesn’t fit (Stream seed still will be saved)
Rebuild is visible bool property, Random Seed is visible Random Stream property.
If you know a seed of some building you want you can enter this seed and get that building. Changes possible if your script analyzes landscape or some environment conditions arround and it changes. Also you will get a different building if trying to disable generating part of your building and skip some random requests as result.
I’ll give that a try and report back.
I put that BP snippet into my building blueprint, and it didn’t seem to do much. I’m trying to use that same concept to assign a seed from the Level Streaming Volume, but I’m not sure if there’s an elegant way to do that.
*Edit - I missed the part about adding the From Streaming node to the other parts of the BP. Will try and update.
Still no luck. What I’m trying to do now is create the Seed for the level in the Level Streaming Volume/ trigger box. It looks like it should work, but I can’t figure out how to pass the seed to the level or it’s children.
Probably you have to use customized game mode instead (If I understand it right). If you want to create match-wide randomness you have to create a custom game mode BP. It is a bplueprint with base class of GameMode or GameModeBase. GameMode will better if the default behavior is good for you. In this class you have to create an integer variable for game-wide seed and fill it in a construction script with random value.
Then you have to modify a construction script of your building
and generate a building based on the stream stored inside an RndStream variable.
You might have misunderstood what I’m trying to do, so I’ll try to explain it better:
Each game, the map should be different.
Each map area/ level is made up of a BP of a random set of buildings.
Each map area/level should stay the same when the levels are loaded or unloaded, but change if the game ends (if the player dies).
I’m basically trying to do a 3d Roguelike, where the map is made of blocks of level instances that are randomized. So for example, all of the wilderness blocks could use the same Level, but there would be variations. All of the city blocks should use the same level, but there are multiple BPs with randomized configurations it can use to generate them.
I’ll try your suggestion with the Game Seed, though I don’t see why I’d need to make a new game mode. Wouldn’t it be the same as just setting a random seed every time the game starts, and passing that into all of my BPs?
That’s what I was trying to do, yeah. I was using Level Instances and attempting to store the results in an array. What I tried was:
1 - Make a global BP with 3 arrays: LevelX, LevelY, and LevelSeed
2 - Add the LoadLevelVolume’s XY Coordinates to the global BP arrays
3 - Create a stream and add the scene to the global BP’s LevelSeed array
4 - Have the LoadLevelVolume spawn a level (the center of the volume aligns with the level)
5 - Have the Level Blueprint add the geometry BPs to the level and pass the seed into it
Somewhere along there, something wasn’t working. The idea is sound (I think), but I just couldn’t get it to work for some reason. I think it was mostly my ForLoops for checking the Arrays, I probably royally messed them up.
So, do you use LoadLevelInstance to spawn an instances of the same level multiple times? If you do you can try to create a Map array inside your game mode where key is a name of a newly created level object and value is a structure with properties that needed to the level generation. When level is loaded it checks if it’s name is added to that map and takes properties from there if possible. Otherwise it creates a new random property set and adds them to the map. This will stabilize your levels during reloading. You can even save random seeds on per-building basis by adding to the map an building object name appended by the level unique name.
Maybe you can use one tricky thing instead. You just have to use some combination of the object coordinates and an integer seed value to initialize builder random stream. As result you wouldn’t need to store something global because each building will be loaded at the coordinates you specified (or relative to at least). When level is streamed out and then loaded in these coordinates will not be changed. Random number stored inside an object will stay too.
If you want to create something like biomes (a region with red brick buildings or so on) you can use volumes on persistant level (and check if origin hits inside this volumes) or you can fill some array or map inside gamemode to describe region info. Second method is better if you want to allocate that areas dynamically.
Yes, but I think you have to choose more complex function to combine coordinates to an integer.
There is type hashes existing in UE and used internally for maps. Hashing gives better mixed result and you have really little chance to see any pattern. Unfortunately, it seems it’s not exposed to blueprint and available only for c++. Maybe you can find a formula of any hash function and implement it over blueprint.
That’s a great idea actually! I could do something like X*Y = seed or something. Only problem would be multiple things equalling the same seed, but there’s a pretty low chance there would be duplicates. I have 10 hand-made blocks right now, each with different buildings and stuff, so I think that could be a pretty solid solution.
My initial testing with your idea works fine. I’m using instances of just one level and spawning it multiple times using the World Coordinates X*Y as the Seed for the building’s construction script, and it works just as I imagined.
Thanks for all the help, I think this topic is wrapped up for now!