Replies: 3 comments 7 replies
-
I am working on a turn based project where I use a separate ecs world for the logical state. My use case is pretty simple and probably different from what you're thinking of, but I just want to add it here so people are aware of it. I store my game's logical state in a resource that contains a world and has an api to get and perform actions. Instead of writing systems with queries, I have functions that use the world's The only systems I have for the logical world are things that happen between player turns. I have a schedule that I run once between each turn that runs these systems. I also have many kinds of relationships, such as mapping locations to entities, mapping logical entities to app entities, or keeping track of many different kinds of parent child relationships. These were all solvable, but some built in relationship support would have helped. So for a turn-based use case, the current world api already works well enough. For real time multiple worlds, the complexity is in scheduling systems that run on the other world so that they can run in parallel, and not just be called from inside an app-world system. For my use case it would be great if I could run a schedule on the logical world and not have to worry about that schedule finishing within a single app-world frame. |
Beta Was this translation helpful? Give feedback.
-
I don't know if it's a good practice but as I got no answer on my topic yet, and I think that it can be related to yours, I reference it here : #9732 (I was/am looking for a normal way to isolate plugins input/output data, and maybe having different worlds for different plugin logics as you suggest might be a solution to that) |
Beta Was this translation helpful? Give feedback.
-
This may be useful for the Editor too, where the editor would have a separate world instead of polluting the game world with editor related functionality. |
Beta Was this translation helpful? Give feedback.
-
I'm opening this as an "Idea" type of item to discuss the advantages of having multiple worlds. This is very similar to #2237, but that other item was opened as a Question, and focuses on a single aspect of multi-worlds, while I believe there's more to it. So I'd like to expand the scope and change the format into a discussion.
The idea proposed here would be to add support for (or, enhance and polish the existing support of) multiple
World
in the same Bevy application.Current status
Currently the entire Bevy application revolves around a single
World
. All features are added to that world: from low-level management systems like asset loading and raw inputs, to intermediate level ones like UI interactions, all the way to the actual game logic.Note: I use the term game here as usual as a terminology shortcut for the core application logic, so this applies equally to, say, a CAD application.
To be exhaustive, I should mention that internally Bevy also has a "render world". However, users rarely have to deal with it, and that world is used as a container for entities and components (for example, there's not component ticking nor time involved in the render world, and it's also cleared each frame), which semantically is different from what the main
World
is used for. Therefore I'm ignoring it in the discussion below.Scenarios
This section describes a variety of scenarios where using a separate dedicated
World
provides some net advantages.State management
Most applications have several layers of systems, from low-level hardware interaction to high-level "business logic" and user interaction. Often the low-level systems are provided by external libraries, although in a game engine like Bevy it's also frequent to write those systems directly inside the engine codebase itself. Those various layers generally have state, and the lifetime of those states dependent on the level.
Those two categories of state have very different requirements and lifetimes. Because of that, they are best separated into different worlds. Game logic state needs to be serialized as a whole for consistency, but do not need nor care about lower-level subsystem state. Saving the position of the player is critical, but saving the current time of the flashing animation of a menu is generally useless, as no user cares if the menu highlight will restart its animation on next run (and in fact, most users expects it to do so). Separating those states into different
World
containers ensures that all the game logic and only the game logic is persisted, without the risk to either forget about a game logic state or serialize extra unused state that doesn't need to be.As an example of this, Unity uses a common state resource for both the game serialization and the Editor-only serialization. This means often games are shipping with Editor-only data. Alternatively, the developers have to write complex systems to parse and strip Editor data before shipping.
Simulation time
Many simulation games (and apps) require very strict time / tick management for simulation accuracy. For example, the Factorio game has a separate dedicated time concept for the game simulation which is entirely decoupled from the "real" time (FPS / framerate) the application is running at. This ensures accuracy in simulation by decoupling the (always inaccurate and varying) application and rendering framerate from the precise ticks of the simulation of the game logic.
This simulation time often can be paused (the simulation stops) for the user/player to be able to inspect the game logic state. During that period however the rest of the application (asset management, UI, .etc) must continue to execute because the game needs to remain interactive; only the simulation is paused. In particular, things like UI animations continue to execute. Translated to Bevy, this means that any system associated with the game logic needs to be paused and not ticked anymore, while any system associated with other non-game-state functionalities need to continue ticking as usual.
Note: Time scaling (speed up / slow down) can be seen as a generalizing of pausing, and is affected in the same way.
Both features (separate simulation time, and pausing) can be achieved with a single
World
. However, there's a few major drawbacks in doing so:Time.time
(the simulation time affected by time scaling and pausing) andTime.unscaledTime
(the application time, which cannot be paused) when designing pausable components.Advantages of multi-world
The advantage of having multiple worlds for the above scenarios can be summarized into the concept of isolating states. Having a separate simulation world by design ensures that:
World
container maps 1:1 with the state to persist. There is no risk to persist extra state, nor forgetting to persist some state.Note that this isolation would provide strong guarantees by design that a future Bevy Editor would not mix its own state with the state of the game it edits. This is particularly valuable when thinking about a live editing ("play mode") scenario where both the Game application and the Editor application are running simultaneously, and their state must remain separated.
Relation to scenes
To some extent, the state persistence aspect relates to
Scene
s by the fact that deserializing scenes offers the same level of state persistence loading than deserializing aWorld
. Scenes can be used as "prefabs" to store a part of the game state. However scenes are more about reusability than state isolation, especially as they tend to "merge" into aWorld
when deserialized and do not (currently?) retain an understanding of their content so do not offer any way to easily (re-)serialize a subset of a singleWorld
. They also don't help with pausing simulation or simulating only a subset of aWorld
, as systems are associated withWorld
s and notScene
s.Relation to the
Subworlds
RFCThe
Subworlds
RFC covers a similar overall idea, and the discussion on the GitHub item goes through similar use cases as above. However there's a few distinctions:World
and should not be shared, or that would defeat the main design goal of isolating state.World
is used as the main game state to persist, and in that case is best left to the management of the application itself, as there's little incentive for the Bevy systems to know about this.World
, like is currently the case for the main and render worlds. The unclear part is the API to extract data of a world from a system of another world; this is wasExtract
does, but is quite annoying/limited at the minute.Open questions
World
API to achieve this?Extract
be improved for better usability?World
from anotherWorld
? Possibly not; the render world's systems have read-only access to the main world. And it might be in fact desirable to uphold this to enforce the isolation guarantee.Beta Was this translation helpful? Give feedback.
All reactions