What are you thinking about Game World approach where scene it is only small bunch of GameObjects that can be stored separately as a resource and can be loaded and unloaded in any time?
Unity already has something like this but:
- Scene is NOT a resource! It cannot be simply unloaded from Game World that has may already loaded scenes.
- We can not have a reference to the scene through which we can load, unload or check is scene loaded.
- We can not have a Bundle that has many scenes in it ant some sort of root Bundle resource that references to each of it.
- Light Mapping and Oclussion Culling data do not work for miltiple loaded scenes and we can’t manipulate it.
So my idea:
Asset Referece (AssetRef)
AssetRef type can store link to any asset in project and has abbility to load it, has info is asset loaded…
Unlike hard link i.e., public Texture2D myTexture, AssetRef only store some assetID and asset do not automatically loaded with the scene ( can be loaded if one script has hard link to it, and another AssetRef ).
AssetID can be key of global dictionary of all loaded assets; when AssetBundle was loaded all it asses will adds to global dictionary and thereafter become available for AssetRefs.
API of AssetRef can be like this:
class AssetRef<TAsset>
{
Int32 privateInternalID;
TAsset Asset { [internal call] } } //null if not loaded or unloaded
Boolean IsLoaded { [internal call] } }
TAsset Load ( ) { [internalcall] } //if an asset belongs to another bundle and bundle was not loaded before - throws exception. Do nothing if asset already loaded
void LoadAsync ( ) { [internalcall] } //start loading, also throws exception
TAsset LoadIntoGameWorld ( ) { [internalcall] } //Avaliable for prefabs. Loads Prefab Into GameWorld so we do not need to instantiate it and [B]Unlike[/B] Load( ) have only one instance of it exactly in scene (no prototype in memory). Calling this function will always result in populating one more object in scene, either from the prototype (if such exists), or from disk.
void LoadIntoGameWorldAsync ( ) { [internalcall] } //will load prefab into scene in any case except AssetNotFinded Exception
Single LoadProgress { [internal call] } //Can be used only if LoadAsync was called, otherwise returns 0 or 1.
event AssetLoaded Loaded { add{ [internalcall] } remove{ [internalcall] } }; // event that raise when asset will be loaded.
}
Unity can provide predefined AssetRefs for standart assets:
- Mesh → AssetRef → MeshRef
- Texture2D → AssetRef → Texture2DRef
- AnimationClip → AssetRef → AnimationClipRef
- TextAsset → AssetRef → TextAssetRef
- Material → AssetRef → MaterialRef
- Font → AssetRef → FontRef
- GameObject → AssetRef → PrefabRef //Prefabs can be referenced and loaded on demand, and then instantiated
- …
AssetRef - actually means weak reference to Prefab, root GameObject of with contains CustomScript component.
You can use AssetRef like this:
class BigBossRoomManager: Monobehaviour
{
public PrefabRef[] BigBosses;
public IEnumerator SpawnBigBoss ( ) { yield return BigBosses[someCalculatedIndex].LoadIntoGameWorldAsync ( ); }
}
BigBosses is array of few BigBoss Prefabs each 10 - 100+ MB of resources and you want to spawn only one of them (based on some logic) and only when player goes near BigBoss room. LoadIntoGameWorldAsync used because we do not need to have one more instance in memory i.e. prefab prototype.
How GameWorld will be constructed
- Unity GameWorld consists of Scenes and on game start it is Empty! No render settings data, light mapping, input manager settings… Just EMPTY
- All GameWorld information stored in Components that belongs to GameObjects
- Scene is Asset. So one more asset type Scene → AssetRef → SceneRef.
- Any scene has only one root GameObject in witch it holds scene specific components, e.g., Scene, LightmapSettings, NavMeshSettings, RenderSettings, SceneSettings, InputManagerSettings. Some of it may be absent, e.g., RenderSettings - because of some render settings was loaded already and we do not want to change it. All other GameObjects lie under Scene root. Scene is a component that describes scene ( name, description… ) and has ad last one important api: Boolean AutoAwake; and void Awake( ) - that can be used for deferred loading Scene into GameWorld after loading into memory. Also it can manipulate how other scene specific components will behaviour after loading the scene and in runtime.
- API Application.LoadScene/LoadSceneAdditive, DoNotDestoryOnLoad… is obsolete. Welkome SceneRef. Project has the only SceneRef to starting scene that will be loaded at game start, just how it has but not the first scene in build settings, and the reference to scene asset.
To be able to load next scene you must have SceneRef to it and call Load(), LoadAsync(), LoadIntoGameWorld(), LoadIntoGameWorldAsync().
For unloading scene just call Destroy( sceneObj.gameObject ); or if UT implement just Destroy( sceneObj );
Calling SceneRef.Load/LoadAsync() will load scene but not awake it!
Calling SceneRef.LoadIntoGameWorld/Async() will load scene and awake it if sceneObj.AutoAwake is true; Scene Transform can be modified between scene loaded into memory and scene awake (in that modification Static Object will be moved accordingly).
GameWorld has reference to all scenes loaded into it, All Scenes loads only into root of GameWorld so any game world root is Scene (not sure that this constrain is needed). - So after all this Scenes look like any other Prefab that supports Nesting (which coming) just with specific components in it root GameObject. So even internally it can be implemented like Prefab.
Also will be great to have ComponentRef where TComponent: MonoIDBehaviour…
MonoIDBehaviour is component that has unique ID and on Awake store itself in global dictionary of scene objects.
So we can reference Components cross scene. ComponentRefObj.Component will be available when Scene with that component was loaded and Awaked
For Endless Worlds Unity can provide API for moveing scenes e.g. GameWorld.MoveCenterOfWorld( Scene scene );, this call can move transforms of all GameWorld roots, so specified scene become in center of the world (need to move static objects, may be change something in collisions, physics, particle systems…, and procide Callback GameWorldCenter moved for side systems e.g. Bullet Physics).
After that manipulation (that can be huge) all global positions of Objects will be set relative to specified scene and Float precision do not affect logic in objects of this scene
Pros.:
- Scenes becomes assets, so it can simply be loaded and we always have reference to it root object. (No need workaround or hardcode for searching just loaded scene)
- No more hardcode like Application.LoadScene( “Literal” ) or Resource.Load(“Literal”), scenes and assets can be easily renamed or moved in project and nothing will be broken.
- One AssetBundle can has many scenes, prefabs and any other assets in it, and root asset can have references to each of them. So we can load only root asset and know which assets we just loaded dynamically, e.g. what new scenes we can go to
without actually loading any asset except root.
- We can create something like enemy database that can store all enemies of game and load and spawn part of them by some logic without memory overhead.
- Scenes can be implemented just lake Prefabs with Nesting support. Less code base, less bugs, more flexibility, more simplicity, easier to understand how it works.
- Support for Lightmaps and Oclussion Culling for additively loaded scenes
- transform.root will always reference on root Scene Object where is scene component, and maybe custom game specific component that responsible for scene logic.
Cons.:
- Can’t see any of them. May be some body else can see?
If I’am not the only who want such scene and asset management in Unity than maybe we can see it in Unity 5.0
It will be great if Unity System Architect says what he thinking about this.
Any Suggestions? Thoughts?
Do you want to have such a system?
feedback link: http://feedback.unity3d.com/unity/all-categories/1/hot/active/more-flexible-approach-of-asset-