I am running into a bizarre bug that I’ve spent over 25 hours trying to fix. And I’ve only managed to eliminate possible causes, but I haven’t gotten close to figuring out why the issue occurs and how to fix/workaround it.
It can be summed up as: For 1 in 10 users, Resource.Load fails to find prefab game objects. It finds other asset types fine. For the other 9 out of 10, it works 100% of the time and cannot be reproduced. For those affected, it is broken 100% of the time and will never work.
What I’ve ruled out:
It is not unique to Resources.Load. I setup the project to use Addressables, and changed the code to load them as Addressables. Again with that approach, the same people/machine combos work fine, or are broken. Zero change in issue’s frequency or behavior.
There is no commonality between affected and non affected users beyond the fact that all affected users so far use Windows 11. That being said, the majority of people without any issues at all are also using Windows 11. Affected and non-affected users have a wide range of GFX cards, CPUs, and memory. One affected user has 32gb of RAM, a 4.7ghz 16 core intel processor, and a GTX 4090. Another has a really weak school laptop with an APU instead of a GFX card. Nothing seems to be consistent here.
The package was zipped using windows basic zip, WinRAR as both a rar and zip, and 7zip as a 7z. All delivery methods yield identical results.
Users who are affected repro with zero VPN or antivirus. One user has no vpn or antivirus at all, only using the Windows Defender that comes with the OS (and they turned it off to download and test, but they still reproduced the issue).
I’ve tried three Unity versions. 2021.2.11f1, 2022.3.29f1, and 2022.3.43f1. There is no change in frequency or behavior in the issue.
Hmm. This just a wild guess, but have you checked whether the affected users have put the build in a system folder like “Program Files” or “Program Files(x86)” that might prompt for admin access?
Thanks for the spitballing! Yes, we tried in multiple different directories. From a thumb drive, from the desktop, from program folder, etc. All yield identical results (though the game just runs slower on a thumb drive). We also had them “Run As Admin”.
Can you get them to send you the runtime logs? There might be an exception or issue before your Resources.Load() code runs.
The other thought is script execution order and having some rare interdependency you aren’t aware of.
ALSO: be sure you are using a typed version of Load:
Always use Resources.Load<T>(), never use Resources.Load() as T
And the same goes for Resources.LoadAll<T>()
Generally you want to avoid writing code that is simply waiting to mysteriously fail simply by the future appearance of an identically-named file of a different type.
ALSO: Always put your resources in type-ish subfolders if you use Resources.LoadAll(). Here’s why:
As of this writing, if you use Resources.LoadAll<T>("foldername/");, Unity will load ALL FOUND ASSETS at that path into memory, then check each type and only return to you the ones that meet type T. But Unity will have already loaded all the assets it finds in that path, so expect massive performance problems if you don’t use subdirectories, even crashes due to running out of memory if you have a lot of assets there.
The above code was the problem, setting the default value of “ChosenCardArtResourceName” to an empty string for some new users.
I still don’t understand why it EVER worked for some new players. This save data should have been an empty string for every single new player. It would only be set if you’ve played before.
It is saved in LocalLow, which I delete all of the time, so I should have reproduced it too. But somehow, it only affected 1 in 10 players. For everyone else, magically, it was not setting “ChosenCardArtResourceName” to an empty string. And this was a prefix used for several different prefabs throughout the game. So the resource load names would have been incorrect in several places, causing a cascade of errors.
I simply added a null/empty check before setting the value and it fixed the issue:
string cardArtResource = (string)ES3.Load(SaveGameDataKey.ChosenCardAsset.ToString(), settings);
if (!string.IsNullOrEmpty(cardArtResource))
SettingsData.ChosenCardArtResourceName = cardArtResource; //Critical that this is never set to an empty string. Game will completely break if it does.
I was sure it was going to be something simple and stupid on my part. I gradually became less confident in that assumption, but in the end, of course it was.