Is there a callback, event, etc that allows changes to a GameObject’s transform (position, rotation) to be detected? It’s critical to be able to “trap” such changes on an object-by-object basis when trying to isolate problems (e.g., You have 30 players running around and, occasionally, one player is momentarily positioned at the wrong spot. “Who did that?”).
One approach would be to encapsulate the GameObject inside another object (e.g., DomainObject) and prevent direct access/manipulation of the transform. Callers would have to use something like SetPosition(Vector3 pos) instead.
Callbacks/events for a transform change would be another solution.
I see the “hasChanged” property on Transform, but that isn’t really what I’m looking for.
Maybe its just me but i dont think its very clear what you’re asking for.
You can wrap your calls to a higher level transform accessor, or use something like hasChanged.
That’s virtually impossible unless you record every time a transform change is made and run through the log when you see a problem or understand the problem conditions enough to setup a debugger to log what happened when the conditions are met.
Seems to me this would boil down to isolating the issue in a more narrow debugging scene scenario, removing non-critical systems and clutter with a focus on doing specific things to see if it replicates the issue.
When debugging a problem related to an object’s transform, it’s critical to be able to identify the “culprit.” e.g., Set a conditional breakpoint on a specific object for a specific condition: Player A’s y coord > 200. Break. Stack trace. "Ah! That’s who’s doing it.
I know from working in other environments that having this capability means the difference between finding the problem in 2 minutes versus a “trial and error approach” requiring hours or days.
The obvious way of doing this is to hide the transform and force callers to use public accessors for setting position, orientation, etc.
My question is Does Unity has a “hook” for detecting changes to the transform. If it doesn’t, I’ll use the encapsulation approach.
If transform has a rigid body component, you can use their velocities, to check motion. Or check if object physics is not sleeping/ is awake. But otherwise, you probably need own transform changes caching and checking methods.
As I said in the original post, “hasChanged” isn’t what I’m looking for. It doesn’t tell you who made the change. For that, you need a callback of some sort that’s invoked when the change is made.
I asked our unity rep for a transform callback feature years ago. I doubt it’s ever going in. Unity’s extremely publicly manipulatable transform object is a problem for larger projects.
The suggestion to wrap all of your own transform access into a traceable method is fine, but limited. It looks weird and your unity devs will need to adjust from their natural “transform.position = …” into “customTransform.position = …” which is not always easy to do. Especially since there is no easy way to catch times when a direct transform access slips into the codebase. (Sure, you can write parsers and git/p4 rules, but who wants to have to do that?)
Another limitation of wrapping the transform is that it won’t help detect when third party libraries are the culprit, or when a lower level unity system such as an Animation or Physics is moving the object.
One idea I toyed with was to use Fody to intercept calls into the transform setter. This was promising. I think it would have been possible. However, it would not catch low-level unity transform changes (animation, physics), which occur outside of C#.
If you want to track changes your code makes, the correct layer to track it is also your own code. Your logic stops at transforms because that’s all you are interested in. But your reasoning could be extended to practically everything. And if it was a good idea for transforms, then it would be a good idea most everywhere. But it’s not. What you are asking for is considered a horrible practice in software design generally.
So taking your example, position wouldn’t be what you want to track anyways. What you really need are good abstractions. You should have exactly one component that actually updates the transform for one. One place that handles the how much over time and smooth translation. And then whatever movement logic you have broken up so you know here is where I handle why and how much to rotate, here is where I handle where to move, etc…
Then when something is not acting correctly, you can reason about it at a higher level and pinpoint the likely source of the bug easier.
What you’re describing is encapsulation. That was option #1 described in my original post. Yes, preferable, but unfortunately, as another responder mentioned, the Transform object in Unity is “extremely publicly manipulatable.” Given that everything has access to the transform, the next best thing would be getting notified if someone changes it.
Thus the question about callbacks.
Question would be, why you actually worry, that someone manipulates transform?
Seams like adding unnecessary work.
You can not prevent anyone, to modify anything on local device.
If someone want to change anything, then will use reflections, or even direct memory manipulations.
Hi, any updates? Did you find solution to know what has changed transform? Have almost the same problem : something unexpectedly changes my player position and i want to know what and when does that. I had an idea to place breakpoint on player transform, but i don’t know if it is possible without encapsulating the player object.
Do not count on this. What you guys are asking for is a performance nightmare and seriously flawed. You really don’t have a choice but learn how your code actually works and find the culprit.
I’ve been in the same boat and put together a tool that weaves IL into your assembly that essentially redirects all calls from transform.postion = (or rotation/scale) to Interceptor.Set(transform, newValue) - this is then directly controlled by you and you can get all info necessary. Like which component exactly is doing that change, with fill stack trace and other parameters.
That’s all done automatically with a user friendly GUI without modifying your source code.
Just went a bit forward with the concept and put together a tool that’ll allow you to add events to unity. Could be for transform.position / rotation, or something else like boxCollider.size
Jesus, this looks quite the advanced take on the problem. Will be definitely be giving this a try, thanks for the share.
Maybe this is something that could be ultimately be implemented Unity side as an option.
I understand that transforms changes themselves happen at very high frequency with quite a few Matrix operations not to mention the cascade effect because of the hierarchical/parenting nature of them but have been exploring lately ‘data binding’ approaches and this would help to not have to do too much of custom code and have a more generic adapter if the transform itself can be listened to.
A bit late here but a simple solution I use in Visual Studio is to add:
public Transform transform{get;set}
To the component class with the transform that I want to track, then right click on transform and select “Find all references”. (just be sure not to save that change).
This doesn’t work for all cases but is great for narrowing it down.
My issue turned out to be not having Auto Sync Transform enabled in Project settings, so that when I cloned an object and moved its position by setting transform.position, it didn’t update the Rigidbody along with it, so the collision system was upset and was moving the object around as if the transform move had never happened. Because it was happening through physics and not my code, I wasn’t able to find it by any sort of code analysis.
You should never use this option as it’s terrible for performance.
The bit you’re not understanding is that the Instantiate call allows you to specify thw position, rotation and parent. Specify it there and the physics componentry will be created at that position. Changing it after means you’ve just changed the Transform so you need to wait until the next simulation step.