Handling multiple "levels" or "scenes" within a world

Started by
3 comments, last by frob 7 years, 8 months ago

I use the term “scene” and “level” interchangeably here.

So, I am a bit confused as to how to go about this. Is it normal to have game objects persist throughout many different scenes or levels - and if so - how are these scenes serialized? Just use an entity ID and set the entities' transform properties on loading the scene (ie a scene file would be this data)?

For example, say you have companions in an rpg that are traveling with you throughout the world. You have the world logically divided up in to scenes. Suppose, for simplicity, one huge “scene” for outside and a bunch of individual ones for inside areas. You and your companions go to a door and are transported and an inside scene is loaded.

If the entities live inside scenes, then you would have to copy from the outside scene to the inside scene – the ones outside would be destroyed while new copies are used inside. If the entities don’t live inside the scene, but in some greater world object that persists between scenes, then the current scene would only dictate which entities are to be drawn. In that case, it seems like the scene would be nothing more than a list of ent ids or something similar.

I like having the scene be nothing more than a list of ents basically to draw – and the ents living in a higher up world object - this is what I currently do. But currently I also store entities’ transform properties with the scene also. Basically loading a saved scene involves adding transform components to a bunch of entities if they don’t have them already, and if they do have them then setting them. When saving a scene to file you are saving a bunch of entity transform components, and when you save an entity to file you are saving everything else but the transform component.

I’m torn about the correctness of this. Would it make more sense to save the transform component as part of the entity file, and the scene would just be a list of entity ids – the location of these entities determined by their transform components as loaded from the entity file rather than the scene file?

It gets confusing to me because having the scene load the transform components means you can distribute the same entity among multiple scenes – which is starting to sound more like a type of prefab which is misleading.

Right now for example, I can place the same tree in several different scenes. Though only one of them will ever be visible at a time – if I reduce the health of the tree here it will be reduced in the other scenes also – this isn’t really good. I mean – your free to put a new tree entity in the other scenes, but my method almost encourages using entities this other pre-fab way. Except changing any one “instance” would change all of them throughout the world since they are the same thing.

If transform information is saved with the entity rather than the scene then you could only ever save an entity in one place at a time. Loading different scenes wouldn’t effectively move around entities.

If you made it here, thanks for bearing with me. Let me know what you think.

Advertisement

It depends quite a lot on how your world system operates and what your engine supports.

Most projects I've worked on have used a scene or world hierarchy of sorts.

You've got nodes in your hierarchy. The basic world leaves them mostly empty or filled with proxy or placeholder objects for the true content. This might be individual zones or lots or coordinate regions in a large world. This might be an arbitrary root world node that can have child nodes attached. When the time comes to load content into that area, you create a new node hierarchy, load all the data into it, and then attach the hierarchy by swapping out the proxy/placeholder and inserting the full bundle.

Regarding your confusion about dealing with copies and proxies of unloaded objects, that is resolved readily enough by using persistent ids and decoupling the representation of the data in the simulator from all the other data like models and textures and audio and animation and effects and world information.

For your example of a tree, the actual tree data is a tiny bit of data about the tree health, possibly a flyweight piece of data. That tiny piece of data indicates that it is a tree and that has a health level. The choice of model to display would invoke your tree renderer. Perhaps every tree is little more than world coordinates, an index of the type of tree, and a value for the tree health. These 64 bits of data per tree (or maybe even less) are small enough that you can have hundreds of trees visible in your world a few kilobytes of data.

You've put out a few options for ways you might save them, and they each have their own pros and cons. They might work well with the engine and tools you are using, or they might not. Your description of the scene being little more than a list of entity ids is essentially how many games do it. When it is stored to disk there is often one set of data that contains the scene hierarchy as a collection of iDs, and another set of data that contains whatever was inside the ID. Such a system lets all the items get persisted and replaced with their IDs as they are being written out; it ensures that if an item is a clone with multiple instances in the hierarchy then the contents are only written out once.

You've put out a few options for ways you might save them, and they each have their own pros and cons. They might work well with the engine and tools you are using, or they might not. Your description of the scene being little more than a list of entity ids is essentially how many games do it. When it is stored to disk there is often one set of data that contains the scene hierarchy as a collection of iDs, and another set of data that contains whatever was inside the ID. Such a system lets all the items get persisted and replaced with their IDs as they are being written out; it ensures that if an item is a clone with multiple instances in the hierarchy then the contents are only written out once.

There recently was another thread talking about "spaces" and I think this is where I was getting somewhat confused. I don't really use a specific engine - I have conglomerated a bunch of usage code for different libraries that I have used - OpenGL, OpenAL, libsndfile, assimp, glfw, glew, and stb_image - and recently I have been trying to gather it all together in to a sort of engine (oh and recently I've been working with getting the bullet lib working). I made some different systems for ui, sound, selection, rendering, animation, and applying hierarchy transforms - much of it written during this last game jam, but I'm really struggling with how to organize the world - because all though only a small subset of things are on screen at once, sometimes I need logic to be done on game objects elsewhere.

It seems that I have been using "scenes" as what was referred to as "spaces". I'm not really sure how to relate them to each-other, or what the difference is. I'm also confused about whether I should allow game objects to share components to have an instancing effect, or create some prefab object - this is more of an editor concern though - as I'd like to be able to have some behavior similar to Unity as it gets annoying to change each object.

In the days before Unity, Unreal, etc., we always handled it this way: everything persists forever, except the current level/map/scene/whatever. Nothing needed to be serialised because it was in memory already.

Now, with these engines coming with their own idea of a 'scene', we've had to invert that - everything just exists within its scene, except certain things we mark as permanent. Unity has a 'dontdestroyonload' flag which tells it that certain game objects won't get deleted when the scene ends - again, there's no need for explicit serialisation because the object just stays there.

It's as simple or as difficult as you want to make it, but one thing that helps a lot is to separate out the View from the Model. Inside a scene, you load up 3D assets for your characters and give them a position within the scene - this is the View. But those objects have a link to the actual character data, which persists across scenes - this is the Model. The current transform isn't really an intrinsic property of the character; it's just data provided to the renderer when deciding how to draw it.

This is another reason why I don't like a lot of the currently trendy entity/component terminology - if you refer to everything as an Entity, it makes you think you're dealing with one single thing (regardless of how it's distributed across systems/components/whatever). But usually it makes a lot of sense to separate out the visual and presentation aspect from the logical aspect.

I can place the same tree in several different scenes. Though only one of them will ever be visible at a time – if I reduce the health of the tree here it will be reduced in the other scenes also – this isn’t really good.


That's a separate issue, really. This is the (common) problem of not having a clear abstraction between class/prototype/type objects and instance objects. Frob has mentioned the Flyweight approach which is designed for this sort of thing, but I prefer to either have an explicit Type Object where you need to be careful with memory usage, or just use the Prototype pattern where a full copy will suffice. In either case, you only need to serialise the instance-specific fields in your scene, providing the type/class/prototype data is stored somewhere centrally.

It seems that I have been using "scenes" as what was referred to as "spaces". I'm not really sure how to relate them to each-other, or what the difference is. I'm also confused about whether I should allow game objects to share components to have an instancing effect, or create some prefab object - this is more of an editor concern though - as I'd like to be able to have some behavior similar to Unity as it gets annoying to change each object.

I disagree with that thread's description of "spaces". Over my career I've always heard them discussed as levels, zones, or sometimes as rooms.

Implementation-wise, I've seen them implemented almost always as invisible markers, you could think of them as a plate that the instant it gets stepped on triggers the transition.

Using a Unity-like model, when a player makes contact with the trigger (just outside the edge of the room), the next room's level is loaded into memory as a GameObject tree positioned next to the player's current room, then the camera slides over to that new room. When the transition is complete, the old room's GameObject can be unloaded at a convenient time. Replace GameObject with whatever happens to be part of your scene graph or simulator world nodes or whatever you are using.

There are many options. I recommended a flyweight above as there are often an enormous number of things like trees, which were the example; many games can have hundreds or even thousands of trees in a zone/level/region, and while you want trees to be unique they generally don't need to be full objects. However, if you are building something else or don't have thousands of instances, other patterns may work better for you. If a cloned prototype is better for your situation, use that. If you need full objects, use that. A nice feature of game development is that there are generally many implementation options available.

This topic is closed to new replies.

Advertisement