If so, did you fix the bug or remove the cache?
I have frequently had cache bugs and have always fixed them. I don’t know why I have been so averse to calculating on the spot, but it’s something I should do more often.
I usually cache when I have to find an object of some type and have had that type destroyed during runtime which basically left me with a null reference. It’s not pleasant, but luckily easy enough to fix.
This sound similar to the situation I was descripting with GetComponent. It’s fortunate that Unity clears up pointer when objects are destroyed, else this situation could be a lot messier!
I had this issue without knowing I had it (or that it was called a cache bug) .
In the RPG Combat - Scene Management, where we make the Portals I added a simple “In/Out” animation where I move the player from the spawn point to a destination point (Animated Warp Portal Transition ).
This involved getting the player and navmesh component multiple times, so I thought it would be “Smart” to get it in Awake . Turns out you get a nice “Null exception” since the cached “player” I was referencing was deleted once he stepped in the portal. Went back and simply referenced the player in both functions, and it fixed the issue.
I never knew caching was notoriously known for causing bugs, but as the lecture went on I remembered that I recently wrote a caching function to store some REST API data. The cache was warranted (imo) because it would save a lot of time by not gathering the same data each time my script was kicked off. However, I soon realised how much maintenance this routine injected into my code. I had to check for deleted or invalid records and prune that from my cache so it wouldn’t fill up with invalid data. I could see the potential for a lot of bugs. Even after I got it working, I always had the nagging feeling that I was forgetting something else that I should be accounting for that wouldn’t be revealed until the cache got a bit older. It definitely takes work to get it right.
Absolutely, and that’s the point! It’s often necessary and we need to include it. But it must be justified.
I usually use cache to store game stats, and update the cache by some events.
It is quite performant but it becomes clumsy and hard to use when system grows bigger.
The biggest cache bug is from the game I made in RPG Combat course. I cache the player stats and prevent getStats() is called per frame. It works well in combat, but cause bugs in the inventory and struggles me for weeks.
Had them a few times and it’s always from the same mistake. I cache a reference to a gameobject or component on Awake, then destroy said gameobject. Later, my code goes to fetch from the cache but discovers there’s nothing there
Had it recently on a 2D dogfight game I’m working on, but the debug error made my mistake pretty obvious. I ended up keeping the cache as I was accessing the component regularly, but I was able to refactor my code into multiple scripts. I put the script that stored the cache on the enemy gameobject I was destroying; meaning the script accessing the cache would be destroyed along with the contents of the cache = no more cache error.
So many times.
I am not so sure about GetComponent and caching. Normally I create my GameObjects with a very specific set of Components. Therefore I cache them in Awake and check if they’re actually there. Not caching them and calling GetComponent every time I require that component would also not solve the problem. I would have to check for the Component being null either way, not matter if I cached it or get it anew every time.
So I normally just assume, that all the components that I need are never destroyed at runtime, and only check them once in Awake and cache them. But if I actually get the situation, that a component could get destroyed, I normally use a Lazy Loader. For example if I have a CharacterMovement component, I would introduce a Movement property:
private CharacterMovement Movement
{
CharacterMovement movement = GetComponent<CharacterMovement>();
if (!movement)
{
throw new Exception("There was no CharacterMovement component attached or it got destroyed.");
}
return movement;
}
And most of the time I also cache the component in a private variable.
As a rule of thumb, I have no problem with caching components attached to the same GameObject. I like to ensure the component exists through a [RequireComponent] attribute. Caching external references on other GameObjects is another matter, and should always be done with great care.
ummm many things to cover:
-
generally I use caching with some sort of context and/or validation. Like beside the usual timestamp and TTL (time to live), I would also have a struct called somethingContext saved alongside it. So for example have a struct that returns a struct called CacheResult, inside of which there would be the result and also another struct called CacheContext… if anything it makes debugging much easier if something goes wrong. If single struct is too restrictive and/or I need something more complex, I would create an interface called ICacheContext, against which many different types of ICacheContext “subclasses” could be created appropriately. Having the right hierarchy and architecture would greatly help with preventing bugs and unexpected behaviours in the first place.
-
caching components to other GameObjects would probably be a red flag to me in most cases… if two separate objects are somewhat linked, then why wouldn’t they both have like a common ancestor that would manage this for them? Or a game system whose responsibilities would be the managing of such components…?
I can’t think of one. But I never recalculate caches when the input variables change, as the instructor kept saying in the video. The big advantage of a cache is not to do that, but just to mark it invalid, and only recalculate (if invalid) when it’s read.
I recently had a big cache bug working on the Zombie Runner game of the Complete C# Course, and it was quite frustrating heh…
I tried to do a different and more advanced approach than the one that’s in the course, for example I created my own FPS controller using the state machine pattern showed by Nathan in the 3rd Person Combat & Traversal Course , the same thing with the enemy AI… the thing is that I was using a lot of GetComponents in the Awake Method of the Enemy State Machine hahah, so I spent a lot of time trying to fix all the bugs.
Hi. . First post here. Just watched the " Naming Things & Cache Invalidation" video.
I guess I created an is_blocking
“cache bug” and have yet to fix it. Hoping going through this course will show me the way!