anedumgottil / VR-Mazmorra

Procedurally generated VR Dungeon
Apache License 2.0
1 stars 0 forks source link

MapLoader relies on Editor-only classes to load Prefabs - unfortunately this is not easily fixable #5

Closed apodgo2 closed 6 years ago

apodgo2 commented 6 years ago

See: https://github.com/Anedumgottil/VR-Mazmorra/blob/5c023bf7adefb31f5f30000c00e8901ea337c77b/Technodungeon/Assets/Scripts/MapLoader.cs#L117

MapLoader relies on the BlockPrefabGenerator to generate prefabs that it will build it's Object pool out of. It uses these objects in this pool to Instantiate(clone) them and disperse them throughout the map. Since it needs them to be generated by the BlockPrefabGenerator, the game will not compile, as Prefab generation through text file can only be realized using the UnityEditor libraries. Prefabs cannot be generated and don't really exist during runtime, so we'll need to pre-generate them with this class and implement code to load them in and then build our classes out of the Prefabs.

Essentially, it means we have to Autogenerate a bunch of Blocks and their MicroBlocks, then convert them to Prefabs to save on disk. Then at Runtime we load the Prefabs and move backwards in the opposite direction from Prefab back to Block and MicroBlock. This is an absolutely massive waste of time and a huge pain in the butt, but we literally cannot do this any other way without serializing all of our GameObjects and classes... Which is a massive undertaking.

So we'll need this Block -> Prefab - > Block code and it needs to run fast.

apodgo2 commented 6 years ago

this is the worst thing ever

apodgo2 commented 6 years ago

Through many hours of deliberation I have come to a conclusion: The prefabs themselves, for blocks at least, are unnecessary. The time saved by pre-generating maybe 20 or 30 MicroBlock combinations at runtime is minimal. Furthermore, dealing with the Prefabs and attempting to generate blocks back from them as some sort of data storage solution is ridiculous.

The original idea was that we'd generate the Prefabs so they could be cloned, and the game would only need to store one reference to the Prefab in memory. Plus we can change them on the fly and have it affect all of the Prefabs. But UNITY DOESN'T STORE A SINGLE REFERENCE upon a call to instantiate. Instantiate literally clones the prefab game object, it has no concept of the prefab once it's built or compiled. So there is no optimization benefit at run time to having a prefab as your base for instantiation vs. just instantiating every part of the block by hand, manually.

It might be possible to either serialize all of our classes (serious bit of effort there) and load them in from either a binary or as ScriptableObjects from disk, which would mean we wouldn't need to clone by hand. It would also mean that we could save the entire map on disk assuming we could get the entire GridObject heirarchy to serialize (unlikely). This would have the benefit of allowing us to create combinations of GridObjects and storing them as GridSpaces on disk, for easier map design, but again the entire GridObject Heirarchy would need to be serializable and it contains a lot of extra code.

Plus just having them serialized doesn't provide any optimization benefits innately since we're still loading all of the files from disk and reinstantiating them from the binary. The main benefit to serialization would be saving massive chunks of the map to disk, along with all of their properties (something that was not available with Monobehaviour-based prefabs, no data from scripts are stored with prefabs AFAIK, which is why I almost tried reconstructing Blocks from the prefabs) and pulling those chunks up into the map whenever we wanted, which we already almost do with the GridSpaces layout.

The other (massive) advantage of Serialization is we could support converting our classes from Monobehaviours to ScriptableObjects. ScriptableObjects are essentially data storage classes that you can use to store fields, and since they're managed by Unity's internal engine, they are stored by reference. When you use them, hundreds and hundreds of times across Blocks all over the map, they are only loaded into memory once. This restricts you to using only one type of class (no instances, really, without considerable effort) along with a bunch of other caveats, but it's worth looking into in the future, especially with the MicroBlocks as with the current configuration a 100mx100m map will generate over 2 million instances of block primitives essentially identical to each other, which is WAYYYYY outside the range of what Unity can handle with it's clunky mesh renderer components. So it may actually be necessary with certain MicroBlocks, and would even allow us to store them as Assets with the BlockPrefabGenerator (may need to change it's name) but we would have to overcome all the complications of using a ScriptableObject.

The end result is I've just generated all of the Blocks at runtime, stored them in a dictionary so they'll be unique and we'll have a Pool of prefabs and blocks to grab from, and whenever you need a new Block or Prefab you have to instantiate it from the pool. The Blocks have the additional problem of needing to be instantiated piece-by-piece and their class deep-copy cloned since I didn't have the foresight of attaching the Block to the Prefab instead of the other way around.

apodgo2 commented 6 years ago

As of this commit: https://github.com/Anedumgottil/VR-Mazmorra/commit/87c8d30bdaeeae1d863e70a6baff10b9bad4502f

Blocks and MicroBlocks have been completely refactored and have their Copy Constructors, and therefore are ready to create deep-copies of the Prefab/Block Store pre-generated resources. These can, with only a few minor changes to our Grid code (need to change data structure type and we're good) be stored in our Grid inside their new special GridSpace class. So instead of the grid storing a flat array of GameObjects with position indices, we'll be storing GridSpaces which are 2mx2m and contain both the GameObject for each GridObject as well as the abstract class associated with it, and all of it's parameters and metadata.

One can even extend MicroBlocks, Blocks, and GridObjects to add functionality and fields, and then easily store them to the Grid seamlessly for display.

Soon we will have the 2D Gridview complete which will automate all of this. It will allow us to start creating these objects and storing them on a much larger scale using these mechanisms.