(Or, “Why public setters that I don’t control are costing me money”)
I need a good way to track the setting of transforms.
I’ve got a large project that has many different scripts all looking at Transform’s getters, and lots of them using the setters. This has spiraled out into a nightmare. Many of the most confusing, and time-consuming bugs throughout the project have been tracking down the reason why an object is being repositioned, or rotated in unexpected ways at unexpected times.
It is especially egregious during the first few scene-initialization frames, when the world is being built and edited according to the game state. Several different things are operating on the same object at roughly the same time, without a well defined order. The positions of objects are being set multiple times within the same frame by various outside sources. I need to know who is doing it.
What I would very much enjoy having is a way to produce a stack trace for each call to the transform.position setter, or transform.rotation setter. Then I could parse the log files to determine which systems are operating out of order, and fix them. (Note: I can’t easily place my own logs and breakpoints at this point, since the project has 2292 usages of transform.position, plus an unknown number of third-party scripts compiled into dlls).
So, I am wondering how other developers keep their teams from stomping on each other’s transform.positions. I would enjoy suggestions on how other projects debug the collisions between transform changes.
One thought that I had for future projects is to run all transform changes through a custom SafeTransform class that just wraps the MonoBehaviour transform members. Changing a transform directly will be forbidden by convention. Anyone who does so will be summarily humiliated. Then I have the power to add logs and breakpoints to debug out which systems are moving objects unexpectedly.
I’m sorry. I moved your transform. I will stop doing it.
I tend to run with a convention that only one component gets to move the transform, and everything else has to go through that component. Works most of the time. Unless I get lazy. Then it doesn’t work.
Their are other conventions that work too. Some of the bigger studios only use MonoBehaviour for hooks, and have all of the game logic implemented in C# classes. This eliminates the tendency to just grab .transform or .gameObject whenever you feel like it. And it also means that there is a natural debug point for anything that touches transform or gameObject.
The other convention I’ve used is that transform.position is never set directly (except on initialisation). Its always set relative to its existing value. This means that several scripts can be operating on the same transform without interfering with one another.
If you’re using Visual Studio, mark the property with [ObsoleteAttribute] and Visual Studio will spray non-breaking warnings everywhere the property is referenced. Once you’ve cleaned up the references you don’t want, you can remove the attribute.
Mark a GameObject’s transform property with an attribute? Sure, I guess you just decompile the class…seems like an awful lot of work to do the equivalent of a Find.
What I would do if Transform positions got to be a problem is to implement an interface class that you route your transform position changes through. Then, it’s easy to log anything going through that class. Anything that’s not going through it is easy to search for. It’d be easiest to do this as an extension method…like setting up extension methods for GetPosition and SetPosition.
There is a property on both Component (ie MonoBehaviour) and on GameObject. There is no promise which one was used, or that marking one will fix the other.
In either case you have to decompile to get access to the property. Its a pretty extreme route.
Unless there is some C# magic I’m unaware of to mark a property with an attribute without having access to the property.
This is probably not a great way to do it, but if I was desperate… you could make an extension method on Transform like:
public static PositionWrapper positionTempHack(this Transform tr) {
Debug.Log("Stack trace and whatever other info you need.");
return new PositionWrapper(tr);
}
where PositionWrapper is something like:
public class PositionWrapper {
private Transform transform;
public Vector3 Position { set { transform.position = value; } }
public PositionWrapper(Transform tr) { transform = tr; }
}
Then do a global Find and Replace on "transform.position = " to "transform.positionTempHack().Position = ".
Some of the verbosity of the wrapper there because you can’t make an “extension property”, just extension methods, and this makes it easier to Find and Replace. Then after you track down what’s wrong, reverse the Find and Replace. And for God’s sake don’t accidentally check in the code with the hack still in place or everyone will think you’re terrible.
I spend SO much professional energy trying to keep my own lazy out of my codebase.
ALSO, regards the OP, anytime you have lots of stray events fired hither and yon from inside Unity, which already treats all your disparate objects as a randomly-ordered collection receiving their Update() events, you probably want to rethink using events to decouple.
Like @Kiwasi said, I try to have only one controller moving any particular thing, and from the complexity of what you describe, you probably want a master controller that sequences their updates so you can control order.
One way is to replace the Update() function with MyUpdate() and then explicitly call them in the order you want from a central controller that can fire them in a reliable order, both relative to each other and relative to other types of objects.
For instance I might have all my enemies throw themselves into a table that gets iterated first, then any enemy that creates a bullet fired at the player will register the bullet script in a second table, that is processed separately and after enemies.
The main advantage to this approach is for debugging and logging. You can develop a clear and repeatable notion of how the flow of your program is going.
That’s true, you could do a find and replace on just ".position = " in that case, and try to make sure you’re only modifying transforms. VS might even have a way to filter it to just transforms. But yeah… like I said, it’s a really messy way of doing it, but I don’t think there’s any easy way to hook into the setter of Transform.position without hacking the Unity dll’s.
I know this is an old post, but I was running into a situation similar to the OP. Something, somewhere was changing my object’s positions and they were warping into new locations - seemingly at random.
The extensions approach to intercept the setting/getting of the transform position really helped because it allowed me to find the cause. Bug killed.
I know that Unity has changed the whole concept of how gameobjects work, so maybe this is a bit of a moot point now, but being able to override transforms and vectors would have really helped.
I know a very dumb but effective solution to this.
Simply remove the gameObject from the hierarchy at runtime.
Scripts that access transform will throw a null ref exception.
And which one of those methods was causing the problem? A call stack only tells you so much you need to be able to step through code and catch the position change when it happens. Hence the need for the extension.
With source control there is a broad class of problems (particularly when you are working in an unfamiliar Unity project) where this approach is absolutely the best and fastest way to getting the intel you need to fix it.