I am making a 3D book. Each page has a high-res texture, and the entire book might consist of as much as 100 pages in total.
Although there is a large number of textures in the entire book, I only need at max four textures in memory at any given time (two for the page being flipped, two for the pages in the background). The other pages will be hidden.
What is the best practice for making sure Unity does not try to load or keep all 100 hires textures in memory at the same time in a worst case scenario?
At the moment, I am providing the page textures as
public List<Texture2D> pageTextures;
in my book script, which is populated through the Inspector. The script ensures only four textures are actually being used from this list of 100 at the same time.
But, I feel worried as I do not know what Unity is doing behind the scenes here. Will it automatically load and unload textures as I assign and unassign them to the relevant page materials (only four materials exists in the scene)? Or do I need to manage this manually, and if so, how?
to make sure you only have required textures in memory use resources.load when a texture is required and manually “destroy” it when done (assign null to all references).
But I thought unity will only include textures it flags as used, so if I load them directly from the assets in a script without having any references to them in Inspector fields, will they still be included?
Edit: Never mind, that is what the special Resources folder is for…
now i’m confused. i don’t know when the asset engine loads and unloads the data. thats why i sugested to use resources to have full controll over the process when it worries you. or you could still “profile” it by running your approach and checking the used memory of the application in windows task manager.
Yes, I was hoping to avoid having to do that though by asking here for what the best practice is based on how Unity operates in the background. I do not know how smart Unity is when it comes to unloading textures that are referenced but not actually used in the scene. Seems to implement my own texture caching system is a bit overkill, but better safe than sorry, so I will just do that.
I’m doing something very similar. The problem with using Resources.Load() is that you get a hitch anytime it happens, which will make your book feel jerky every time it loads a page, but it’s really the only way forward for you running Unity free.
If on Pro, the temptation would be to use AssetBundles, but the problem is that AssetBundles starts with “ass” for a reason. They’re a huge pain in the same to maintain, particularly if developing a cross-platform app. I also found the performance loading from them pretty abysmal, too. In the end, I spent a lot of time getting these working, and then I ripped it out.
What I’m doing now is going to sound a little circuitous, but I think it’s the best way forward. I’m putting each page into a material, and then I’m putting that material onto a quad, and I’m storing the quad on an object in a scene by itself called page1.unity, page2.unity, etc, and then I’m doing an Application.LoadLevelAdditiveAsync() to load it into the app asynchronously, pulling out the material, turning off the renderer, and then destroying scene’s page object when ready to unload. The scene files are pretty small, about 10k each.
I believe this might be the best way for you to approach it. Just use a LoadLevelAdditive() to pull them in, and then when/if you upgrade to Pro, it should be easier to migrate to loading them asynchronously.
I’m curious as to what issues you ran into with asset bundles. With some quick tests, asset bundles seem to work quite well if you use LoadAsync. Of course, it depends on how large your texture is - if it’s only 5 MB, then yes, you’ll see a hitch no matter what (due to uploading to GPU, I think). So I’m a bit surprised asset bundles didn’t work for you where separate scenes did. Details?
That’s not really true. AssetBundles are Pro-only, but they’re actually only useful for pretty specific cases, and loading images isn’t one of them.
I’d suggest looking at System.IO (I think) which is a part of the .NET/Mono standard library, and also at the WWW class which can be passed local addresses (“file://…”) as well as remote ones.
So I have been implementing a texture streaming system for our game, and actually, things have been working out quite well so far. Still some wrinkles to polish off, but I plan on writing a blog post series once it’s done and tested.
Although I’m not an “expert”, I don’t see anyone giving what I think is the correct answer, so I’ll venture forth.
Assuming the free version (no asset bundles), the only way to limit memory is to use the Resources functions.
However, as Dave Taylor said, you will get a “hitch” when it runs. Also loaded resources stay in memory until they are unloaded.
So, I recommend:
Load the page texture, using Resources.Load
While your readers are reading that page, load the previous and next pages in a coroutine, so they are ready to go when needed.
Be absolutely sure to Destroy old textures AND use Resources.UnloadAsset to free up that same resource texture from memory.
Unity doesn’t do it automatically.
There’s a LoadAssetAtPath, but it is Editor Only.
Did you mean Resources.Load?
To reduce “hitch” or “hang”, I would split a page texture into smaller chunk and load one chunk per frame. Sadly, Unity doesn’t appear to offer any asynchronous way to load local data.
Coroutine are not asynchrone, sadly. Coroutines are updated the same way MonoBehaviour “Update” method is. Unity is not really multi-thread friendly.