Is it possible to generate subscenes programmatically? And is that a bad idea.
Feel like I’d need to know why you want to do this before deciding if it’s a bad idea!
I can totally see there being use for an editor tool to setup new subscenes with default objects etc.
I had a usecase for this, converting to ECS a game that has a map editor / level design tool for a tile based game.
It use to generate the map tiles at runtime but could be so much more efficient to create subscene at edit time to benefit from the static optimizations…
I have tried to do the procedural approach to creating subscenes for my own open world project but with limited brain, limited time, rapid changes, and already knowing the scope of the project for now I just pooled a total number of subscenes based on map location. It works for now but is not flexible.
We are working on a sample where we do procedural dungeon generation.
Our approach is:
- A dungeon tile is an entity scene
- A procedural generator high level streaming rig (User code) has a list of SceneAsset references that define the possible tiles stored in a scriptable object.
At load time the scriptable objects defines all possible scenes, we load the meta data of each scene. (Eg. what connectivity of tiles do we have) Based on that it determines the placement of every possible tile in the world. Each represented by an entity representing the scene.
Then it uses normal streaming functionality to load the data. Eg. some scene sections are always loaded, other scene sections are loaded on demand based on distance.
To Unity.Entities we are adding the following things to support this:
- We are adding the ability to load & offset entity scenes (By letting users modify the loading staging world, before it is moved to the simulation world)
- We are adding the ability to load the same entity scene multiple times (Each one offset differently)
- We are adding the ability to generate meta data from the scene that can be used during placement
I expect those will land in a release in a couple of weeks in dots.
Wouldn’t happen to be this would it? https://github.com/Unity-Technologies/DOTS-StreamingSamples/tree/dungeon
Was just randomly looking at it the other day
(before anyone tries to run it, it requires access to releases that aren’t publicly available yet.)
What about a single subscene for the entire world, without the need to offset its entities, this is possible in the current state of things right ? just a matter of reusing what the editor already does to generate subscenes or is there more to it ?
That sounds more or less exactly what we’ve hacked around doing in our project. Everything works great for us in our editor mode, but we have been having issues with getting it working in builds. Hearing that the support for loading the same entity scene multiple times with offset becoming officially supported is thus great!
My use case wast procedural world
This is pretty much what my use case is. I want to build predefined tiles and load then on demand. My game is on a spherical board proceduraly generated at run time and the board size determinants the number of tiles and sub scenes I need. Im doing all my tests at 65k tiles so that a lot of sub-scenes but only 100 unique tiles. I have all the tile relationships and position on a blob graph and was thinking I could just load, position and swap a sub scene during runtime. Im just not sure of the limitation of this method and what number of tiles and unique tiles are realistic, targeting pc and mobile with the device setting the number of tiles
<3 Procedural gets love in DOTS <3
What a day! I don’t fancy tiles myself, it’s too limited, but will check the the code with great interest. thanks!
Wow… about 1000 line of code in vain for me
But for real, I love this! My code was such a hack anyways!
It would be kinda cool to have sub scene variants
More activity here now: GitHub - Unity-Technologies/DOTS-StreamingSamples at dungeon-hybridv2
Is there any information regarding when the required dependencies for the dungeon-hybridv2 will be in preview packages? I looks to do exactly what I am trying to do but I can see from loading it up that some key parts are missing from the preview packages.
Thanks
This functionality works since what I believe is Entities 0.13. I’ve started instancing subscenes and translating them individually. The only thing you should need is:
When you load scenes, load them with the SceneLoadFlags.NewInstance flag. You also need to attach a PostLoadCommandBuffer on the sceneEntity. This will be executed in the streaming world when the scene has been loaded, before your processing systems run. You use this buffer to inject the data needed to translate or process your scene instance. Something like this:
struct MyProcessingComponent : IComponentData
{
public float4x4 SceneInstanceOffset;
}
void LoadMyNewSceneInstance(SceneSystem sceneSystem, Unity.Entities.Hash128 sceneGuid, float4x4 sceneOffset)
{
var loadParams = new SceneSystem.LoadParameters()
{
Flags = SceneLoadFlags.NewInstance
};
Entity sceneEntity = _SceneSystem.LoadSceneAsync(sceneGuid, loadParams);
PostLoadCommandBuffer postLoadCommandBuffer = new PostLoadCommandBuffer();
postLoadCommandBuffer.CommandBuffer = new EntityCommandBuffer(Allocator.Persistent, PlaybackPolicy.MultiPlayback);
EntityManager.AddComponentData(sceneEntity, postLoadCommandBuffer);
Entity postLoadDataEntity = postLoadCommandBuffer.CommandBuffer.CreateEntity();
postLoadCommandBuffer.CommandBuffer.AddComponent(postLoadDataEntity, new MyProcessingComponent() { SceneInstanceOffset = sceneOffset });
}
The entity with the custom scene instance data (the “MyProcessingComponent”) that we created above will exist in the scene streaming world after we’ve loaded the scene. Since we’ve only created one instance, we can treat it as a singleton. You then inject Systems that handles the moving and/or other processing of your subscene using WorldSystemFilterFlags.ProcessAfterLoad:
[WorldSystemFilter(WorldSystemFilterFlags.ProcessAfterLoad)]
public class MoveMySceneAfterLoadSystem : SystemBase
{
protected override void OnCreate()
{
RequireSingletonForUpdate<MyProcessingComponent>();
}
protected override void OnUpdate()
{
var myProcessingComponent = GetSingleton<MyProcessingComponent>();
// Do your processing.
}
}
The sample project has unfortunately not been updated so it still relies on some internal Entities package branch, so it doesn’t work for us outsiders. I found the sample to be quite large and “messy” for the functionality it was trying to display. The core parts that are needed to get it working are what I wrote above.
Edit:
What you can extend this with is that you can load the scene template metadata as an entity, and then later on instantiate that entity with the NewInstance flag instead. This is what they do in the sample.
private Entity LoadInstantiableSceneTemplate(SceneSystem sceneSystem, Unity.Entities.Hash128 subSceneGuid)
{
SceneSystem.LoadParameters loadParams = new SceneSystem.LoadParameters()
{
Flags = SceneLoadFlags.DisableAutoLoad // Only loads the metadata entity
};
// You can load this entity to access general any metadata you've attached to the scene you want to instantiate.
var sceneTemplateEntity = sceneSystem.LoadSceneAsync(subSceneGuid, loadParams);
return sceneTemplateEntity;
}
private void InstantiateSceneTemplate(SceneSystem sceneSystem, Entity sceneTemplateEntity)
{
Entity sceneInstance = EntityManager.Instantiate(sceneTemplateEntity);
SceneSystem.LoadParameters loadParams = new SceneSystem.LoadParameters()
{
Flags = SceneLoadFlags.NewInstance
};
sceneSystem.LoadSceneAsync(sceneInstance, loadParams);
// And then you add the commandbuffer etc to the sceneInstance instead.
}
@Zec_1 thank you for this thorough response, probably the most detailed info on subscene/world instantiation I can find.
When moving between scenes, what is the order of world creation/entity instantiation? When in a scene with no entities and only a DefaultWorld and then switching to a scene that contains a subscene (with entities), how are the subscene entities loaded into existing worlds?
If when switching to a scene I would like to create worlds (like client/server worlds in netcode) and then have the subscene entities contained in both those worlds, what is the “order” to make this occur?
Running
SceneManager.LoadSceneAsync(m_SceneName);
m_SceneName has a script that runs on Awake:
ClientServerBootstrap.CreateServerWorld(world, “ServerWorld”);
ClientServerBootstrap.CreateClientWorld(world, “ClientWorld”);
Somehow the entities from the subscene in m_SceneName don’t make it into “ServerWorld” and “ClientWorld”.
How can I control the order of: scene loading, subscene entity loading, world creation?
We’re using a similar workflow where we create server and client worlds in OnEnable on a script, and then load SubScenes into both worlds. We’re not using the NetCode package though, so I don’t know how that package handles server/client worlds.
Are you using the “Auto Load Scene”-option in the SubScene? We’re not, and I believe it does not load SubScene into custom created worlds that are created after the SubScene monobehaviour has had it’s OnEnable executed. We don’t use that option, we’ve taken manual control over when the SubScenes are loaded. You can do the following:
public class BootstrapScript : MonoBehaviour
{
public SubScene MySubScene;
void Awake()
{
World defaultWorld = null;
// Don't know if you can get the server/client worlds like this, but you need a reference to them
// for the steps below
World serverWorld = ClientServerBootstrap.CreateServerWorld(defaultWorld, "ServerWorld");
World clientWorld = ClientServerBootstrap.CreateClientWorld(defaultWorld, "ClientWorld");
// I'm not sure if your ClientServerBootstrap includes the scene systems in the target worlds,
// but they're required to load SubScenes into worlds.
SceneSystem serverSceneSystem = serverWorld.GetExistingSystem<SceneSystem>();
SceneSystem clientSceneSystem = clientWorld.GetExistingSystem<SceneSystem>();
// If you need to unload the scene, these functions return an entity that acts as a scene instance reference.
// This function also contains an optional parameter to control how the scene is loaded (synchronous loading etc)
Entity serverScene = serverSceneSystem.LoadSceneAsync(MySubScene.SceneGUID);
Entity clientScene = clientSceneSystem.LoadSceneAsync(MySubScene.SceneGUID);
// If you need to unload scenes, you do this.
serverSceneSystem.UnloadScene(serverScene);
clientSceneSystem.UnloadScene(clientScene);
}
}
@Zec_1 you rock so hard, going to try this out thank you.
“We’re not, and I believe it does not load SubScene into custom created worlds that are created after the SubScene monobehaviour has had it’s OnEnable executed.”
I was wondering why the subscene wasn’t loaded into the custom created worlds, this makes sense.
This is how SubScene Monobehavior add Entity SubScene
void AddSceneEntities()
{
Assert.IsTrue(_AddedSceneGUID == default);
Assert.IsFalse(_SceneGUID == default);
var flags = AutoLoadScene ? 0 : SceneLoadFlags.DisableAutoLoad;
#if UNITY_EDITOR
flags |= EditorApplication.isPlaying ? SceneLoadFlags.BlockOnImport : 0;
#else
flags |= SceneLoadFlags.BlockOnImport;
#endif
foreach (var world in World.All)
{
var sceneSystem = world.GetExistingSystem<SceneSystem>();
if (sceneSystem != null)
{
var loadParams = new SceneSystem.LoadParameters
{
Flags = flags
};
var sceneEntity = sceneSystem.LoadSceneAsync(_SceneGUID, loadParams);
sceneSystem.EntityManager.AddComponentObject(sceneEntity, this);
_AddedSceneGUID = _SceneGUID;
}
}
}
and this part states only those world created with an active SceneSystem can receive new entity with from SubScene
foreach (var world in World.All)
{
var sceneSystem = world.GetExistingSystem<SceneSystem>();
.....
}
So check your custom world if it has SceneSystem. And you need these three systems in your custom world to load a subsence to the world.
SceneSystem: place the load command
Resovle: find all scene sections to load
Streaming: do the loading job
And the entity are loaded into these worlds first
if AutoLoadScene is not checked, the loaded entity will not be copied to the target world.
They are copied to the target world when a RequestSceneLoaded component is added to the
sceneEntity in target world.
Detail are all in the source code of the SceneSystems(in SceneSystemGroup)
@Lieene-Guo this is terrific, super appreciate the breakdown!