I'm sure you've seen this: everything goes fine during your day, you're building next feature for your game when something doesn't feel right.
An object is out of place, moves not how it should. Nothing obvious mind you - it's not every frame, it doesn't even seem to be very specific. Bit random and nothing in your code seems to be doing that.
We've all been there, there's something modifying the transform behind the scenes. It could be some of the code in your assembly, could be some Unity stuff, some third party tool that doesn't work as you thought it would or a million other things that you simply do not know about.
With Unity transforms and their very public setters and modifying methods (like Transform.Translate) anything can move/rotate or scale the object - this as you've already found can lead to hours of wasted time, trying to pinpoint what's making the change.
Ok, so how do people go about detecting those changes?
Well if you are relatively new to Unity you'd be forgiven for thinking this should be straightforward. I mean it should, but it's not...
From my experience and advice found online there are few things to do:
1) Look in the docs to look for some sort of onChange callback
You'll likely going to find Transform.hasChanged. That's it - you're done. Well, not really. You'll quickly find, it'll tell you IF something has changed, not why. Little use in that scenario - especially if there are multiple changes being made in the same frame or multiple objects modifying.
2) Delete an object that's being moved
That's working well as when you remove the game object in question - code that was previously doing changes will now throw exceptions.
While it sounds good on paper, just try it. If you're working with anything that's non-trivial (which, let's face it - you are otherwise you'd never have this problem) then you'll see a ton of errors as there was a lot of existing code that was working fine but now is not. Looking for a needle in a haystack...
3) Isolate your issue in a smaller project
That's a good idea, combined with removing game objects you should get fewer errors and easily track the issue. The problem is - it takes time to set up and you're not necessarily debugging your exact issue.
4) Write better code :)
That's the next piece of advice you'll probably find. Write code that's more restrictive in modifying transforms. Perhaps also add a layer of abstraction, instead of:
myTransform.postion = newPositionuse:
myTransform.SetPositionViaCustomMethod(newPosition)perhaps even pass an object making the change:
This is actually great advice, you should always try to write better code!
Adding a layer of abstraction will allow you to put a breakpoint, and with the debugger, you can navigate stack trace to inspect who's changing the object.
That works great, still, there's just too much hassle with that:
- you have to get into the habit of using that new method every time and you have to get your team-mates to do the same
- there's a ton of code now using this method, and narrowing it down to your specific one is hard and lengthy (even with conditional breakpoints)
- 3rd party code will not use that method and if it's modifying your transform then you're in trouble
- abstraction like that will add a little bit overhead to your code
Those suck - is there anything better?
I believe I've covered the most mention-worthy methods and by now you probably realise that your options to find the issue, quickly - are slim. Give up, make a coffee and enjoy a day of slogging through a ton of code, removing objects like a madman and hope for a lucky find.
Or, you could try the tool I've put together. It gives you all the pros of the above methods without any of the cons!
Have a look at a quick, 1-min video that'll show you how it can help to debug those hard to find transform changes.
Finding Specific Change
Essentially it'll do step 4 for you, write better code - or rather edit your code. All on the fly, in memory, so you don't see a thing (don't worry your code is not actually modified in files :)).
With that, all changes are nicely captured - and you know not only if something changed like with Transform.hasChanges but also:
- which frame change occurred in
- which exact script, method and game object initiated the change actual value at the time
- original method call made, doesn't matter if it's Transform.Translate, Transform.postion = setter or any other 28 ways that are publicly available on Transform to modify position/rotation/scale
That's all nicely laid out in easy to use GUI.
Disabling Changes at Runtime
It goes even further and covers 'removing game object' step but it does it smarter. Instead of removing game object - it'll disable *specific* changes - with that you can see if the issue is gone (all at runtime, without recompile with a single click, quick!).
Viewing all TransformModifiers
This get's us to the next point. You'll see exactly which objects and which exact code (as in script and method) is causing changes - with that it'll be much simpler to get that pesky issue sorted.
Reply Previous Changes
To top it up, you can choose previous change from easy to use GUI and the object in question will instantly move to that state, if the change is causing issues - you'll see it straight away.
Full disclosure - I've spent a lot of time polishing it up and I'm certain it'll save you a lot of time - also it's a paid asset :)
If you don't want to spend the equivalence of a few coffees on it - don't fret. GUI is built on top of code API that's fully open-sourced and documented. You can use that to speed up you are debugging considerably. Everything of what's listed above is still possible, you just don't get a hugely-time saving GUI that'll make your life easier.
PS: did I mentioned GUI will save you a ton of time? :) - think I did, still, if you are unhappy with how it works I'll do a full refund for you, no questions asked.