-
-
Couldn't load subscription status.
- Fork 4.2k
Description
Motivation
Recently I’ve been working on scene loading with bevy_trenchbroom, and something that’s come up is the way a user can customise how scenes are loaded. Often, a game may want to have some domain-specific metadata that can customise (for example) properties of materials, the components that get applied to newly-created entities, etc. The current solution is using "hooks" - basically a set of domain-specific linked lists of function pointers, inserted at specific points in the loading process with specific responsibilities. A design we came up with is to have all user code be handled by systems that run in the scene's inner World. For this, we’d like to be able to define phases, to allow users to ensure the order their code runs in comparison to the processing done in bevy_trenchbroom by default. However, in an ideal world we would be able to pass the LoadContext in to the systems, to allow customising loading external dependencies etc. Anything accessible to systems needs to either be added as a resource (in which case it needs to be 'static, which LoadContext isn’t), or passed in via run_system_with. Since the schedule graph uses Systems, which bounds systems to use In = (), this means we can’t use schedules to handle this.
Proposed solution
My proposal would simply be to have SystemWithAccess, Systems, and Schedule have type parameters for input and output, which would both default to (). These would not change the implementation at all (users are considered responsible for handling order w.r.t to inputs/outputs), the new method for relevant types would only be for the <(), ()> instantiation, so users would need to use Default::default() to instantiate other versions, and Schedule::run(..) would only be available for Schedule<(), ()>. For simplicity, instead of introducing Schedule::run_with, the way to actually run these systems would probably be to just use topsort_graph and run the systems in sequence. On the topic of simplicity, I also think it’s fine to require all systems in Systems/Schedule to have the same inputs and outputs, rather than letting them store dynamic system IDs. This is a very minimal change but would be a nice quality-of-life feature for anyone who uses schedules outside of the context of an App and/or wants to run a one-shot schedule.
For the purposes of scene loading, I do feel that loading scenes that include entities with behaviour has unique-enough requirements that something more dedicated built in to Bevy could be useful, but this would, at least, give us primitives that we can experiment with.
Alternatives
The standard way to handle the original motivation is, as far as I know, to load all the assets in some basic form, wait for them to be loaded, then process them in the main loop to create the relevant entities. This works, but has a few issues:
- The systems operate on the main world instead of in a completely separate world in a completely separate thread (potentially causing frame drops if significant processing is needed).
- It doesn’t have easy access to assets before handles have been created from them, for example if you want to edit an image before applying it to a material you need to mark it as being CPU-accessible
- Loading is a one-shot process, and you should ideally not have these systems in the main loop