Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workflow for working with prefab entities #1446

Closed
alice-i-cecile opened this issue Feb 14, 2021 · 11 comments
Closed

Workflow for working with prefab entities #1446

alice-i-cecile opened this issue Feb 14, 2021 · 11 comments
Labels
A-ECS Entities, components, systems, and events A-Reflection Runtime information about types C-Enhancement A new feature S-Needs-Design-Doc This issue or PR is particularly complex, and needs an approved design doc before it can be merged

Comments

@alice-i-cecile
Copy link
Member

What problem does this solve or what need does it fill?

Prefabs allow users to rapidly add copies of predefined entities to their game, and create variations on them.

What solution would you like?

The basic workflow would be:

  1. Create some interesting prefab entities in their own World either using its own systems, directly from code or by reading in a serialized format.
  2. Turn an entity into its component bundle.
  3. Create a new entity from that component bundle, with variations as needed.
  4. Use addition-detecting systems (or maybe State on_enter systems?) to finish initializing the entity in the main game world.

There are a few things we'd need to have this work:

  1. Multiple, interacting Worlds.
  2. A way to turn a created entity back into a bundle.
  3. A nice pattern for cloning an entity that allows you to change some of its components. If you have a bundle from 2., you could probably get away with .spawn(cloned_entity_bundle).with(modified_component), but it would need to be clear that .with is overwriting.

What alternative(s) have you considered?

Use component bundles with the struct-update Default pattern used in e.g. SpriteBundle.

This works quite well for statically defined prefabs, which can each get their own type. I'm not sure it works correctly (or naturally) for prefabs that are created dynamically or loaded from a serialized format. You would need to create new Bundles at runtime, which I'm not sure you could do.

The functionality needed all seems useful for other purposes though, and the pattern seems quite natural, so it may still be worthing pushing users towards it even if those barriers can be overcome.

Additional context

This issue ties into discussion in #255, although it doesn't actually use Scenes to accomplish its goals.

This issue was prompted by this conversation on Discord.

Multiple interacting Worlds was mentioned in #1343 for decoupling graphics and physics.

@alice-i-cecile
Copy link
Member Author

alice-i-cecile commented Feb 14, 2021

In order to turn a created entity into a bundle again, we can borrow from the approach taken in DynamicScene.

@TheRawMeatball
Copy link
Member

Something to consider is how this would interact with #1442.

@Davier
Copy link
Contributor

Davier commented Feb 15, 2021

I believe that scenes are the intended solution for that, what feature are they missing?

@DarkMara
Copy link

DarkMara commented Feb 15, 2021

I believe that scenes are the intended solution for that, what feature are they missing?

That's what I tried initially, but when you spawn a scene, there is no way to edit components on the entity, or even to know what entity was spawned. Scenes are spawned through SceneSpawner::spawn_dynamic.

For example, there is no way to spawn a scene at a specific location, which is a common thing to do with prefabs.

@Davier
Copy link
Contributor

Davier commented Feb 15, 2021

That's what I tried initially, but when you spawn a scene, there is no way to edit components on the entity, or even to know what entity was spawned. Scenes are spawned through SceneSpawner::spawn_dynamic.

For example, there is no way to spawn a scene at a specific location, which is a common thing to do with prefabs.

You can do the following:

  1. load a Scene from a file
  2. use DynamicScene::from_scene
  3. the dynamic scene contains all entities that will be spawned and their components, they can be reflected on and modified (for instance to change their Transform)
  4. use SceneSpawner::spawn_dynamic to actually spawn the entities

Step 3 definitely needs a better API, but it's already possible. How would you want to do it? How about a method to get an entity from its name, and a component from its type?

Aside from that, there are still some issues when spawning a deserialized scene. I'm working on it in #1395 but it's waiting for review.

@Davier
Copy link
Contributor

Davier commented Feb 15, 2021

Actually, it seems it would be easier to use Scene instead of DynamicScene. They contain their own world that you can query and modify, and when done you spawn them with a SpawnScene or SpawnSceneAsChild command.

@DarkMara
Copy link

I am loading my prefabs from a JSON file and then converting each one into a dynamic scene at runtime. I am doing this because Bevy does not have an editor, so I use an editor to set up my entities and then save it as JSON. Is it possible to do this with a Scene instead of DynamicScene?

I think the idea is that prefabs always have a root entity. In Unity they do at least. Not sure about Godot. Would it be worth it to have a special type of scene for prefabs, that are always guaranteed to have a single root entity? Then you won't have to get the entity by name at least.

@DarkMara
Copy link

Also, would that workflow work if you wanted to spawn hundreds of copies of a prefab? It seems like you'd have to make hundreds of scenes (so that you can modify the Transform on each one), and then spawn a single entity from each one. I could be wrong but it doesn't sound efficient.

@Davier
Copy link
Contributor

Davier commented Feb 15, 2021

Is it possible to do this with a Scene instead of DynamicScene?

You can convert a DynamicScene to a Scene with get_scene. At a glance I also don't see why you couldn't directly convert your JSON into a Scene.

I think the idea is that prefabs always have a root entity.

You can use the SpawnSceneAsChild command to spawn a Scene as a child of the current entity.

Also, would that workflow work if you wanted to spawn hundreds of copies of a prefab?

The scene is not changed when you spawn it, its entities are just copied into the app's World, so it can be reused. However, if you want to spawn a lot of copies at the same time, and modify each of them, you will need to use an exclusive system and call SceneSpawner::spawn_sync directly.

@DarkMara
Copy link

DarkMara commented Feb 15, 2021

I don't understand why SceneSpawner::spawn_sync returns an InstanceId, but spawn_dynamic_sync doesn't. At least now that I know I can use a Scene instead, I have a way to get the entities that were spawned.

I think there should be a way to do this without using an exclusive system though. It would be best to be able to do all of this with Commands and queue the modifications after spawning the prefab.

Edit: I just wanted to confirm that using SceneSpawner::spawn_sync works, so it is the best way to accomplish this for now.

@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Enhancement A new feature labels Feb 16, 2021
@alice-i-cecile alice-i-cecile added the A-Reflection Runtime information about types label Apr 14, 2021
@alice-i-cecile alice-i-cecile added the S-Needs-Design-Doc This issue or PR is particularly complex, and needs an approved design doc before it can be merged label Dec 12, 2021
@alice-i-cecile
Copy link
Member Author

Closing in favor of #3887.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events A-Reflection Runtime information about types C-Enhancement A new feature S-Needs-Design-Doc This issue or PR is particularly complex, and needs an approved design doc before it can be merged
Projects
None yet
Development

No branches or pull requests

4 participants