Search Unity

More transform callbacks

Discussion in 'UGUI & TextMesh Pro' started by Dirk-Gregorius, Aug 21, 2014.

  1. Dirk-Gregorius

    Dirk-Gregorius

    Joined:
    Feb 6, 2014
    Posts:
    6
    I happily noticed that you added onTransformParentChanged() to MonoBehaviour. In my opinion this doesn't go far enough and Unity would also need an onTransformChanged() callback. This kind of callback is necessary to keep proxies in synchronization with the game object. E.g. collision proxies, rigid bodies or update the navigation mesh to apply dynamic changes .

    I am actually surprised that is not possible till now since it seems to be a basic feature of any game engine.

    Thanks,
    -Dirk
     
    rahulk1991, EyePD and Cross22 like this.
  2. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    So you want OnPositionChanged(), OnRotationChanged(), OnScaleChanged(), OnTransformChanged()?? Where does it end? How about we do that for everything and have OnVelcityChanged(), OnAngularVelocityChanged(), OnMassChanged(), OnColliderSizeChanged(), OnDragChanged().
     
  3. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    557
    _Daniel_ beat me to it. I agree it could lead to some nice event-driven/ change-driven code, but it would have to be far more generic than onTransformChanged() to be useful. Something like generic property binding could be really swell though. (i.e.: transform.position.bind(positionCallback)).
     
  4. Dirk-Gregorius

    Dirk-Gregorius

    Joined:
    Feb 6, 2014
    Posts:
    6
    Daniel, say e.g. you have a custom collision system that maintains proxies in some acceleration structure. A classic way to implement this is to flag the proxy as 'dirty' when the position changes. Then whenever you perform a ray cast or volume query the dirty proxies are moved to the update location prior to performing the query. A lot of game systems like character movers or weapon hit detection rely on the proxies being at the correct location.

    Of course I could just iterate over all shapes and test if its transform has been updated (e.g. using Transform.hasChanged()). I think we both agree that this becomes very unreasonable once the number of objects grow over some threshold.

    We could of course move the responsibility to the user and whenever he moves the game object he is also required to move the corresponding proxies. I guess that we agree that this is highly impractical and error prone.

    From my experience it is pretty usual in a game engine to receive a callback whenever the state of the hierarchy changes since it is such a central part of the engine. So I think an OnTransformChanged() callback passing an event type (e.g. position, rotation, scale) makes a lot of sense and from my experience is actually pretty common in game engines.

    HTH,
    -Dirk.
     
    rvinowise and XGT08 like this.
  5. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    Who's changing the position? Have the object that is changing the position also call your other proxy system to notify it of a position change.
     
  6. Dirk-Gregorius

    Dirk-Gregorius

    Joined:
    Feb 6, 2014
    Posts:
    6
    I actually mentioned this in my post. This is unpractical and error prone in my opinion. It actually creates unnecessary couplings between the proxy system and *every* other component in the game object that alters the position.
     
    rahulk1991, Havokki and Act0r like this.
  7. holyfuzz

    holyfuzz

    Joined:
    Nov 16, 2010
    Posts:
    21
    Dirk is absolutely right here. Having events for when the position/rotation/scale changes (and potentially other components as well such as colliders and rigidbodies) would be invaluable. It would be much more performant than manually checking transform.hasChanged every frame (which, for example, is a major source of performance issues in NGUI), it would reduce code coupling, and it would still work when we do not have access to the code that modifies the transform (such as with closed-source 3rd-party libraries, or even Unity's animation system). It's been a longstanding problem that Unity support callbacks like these.
     
  8. XGT08

    XGT08

    Joined:
    Aug 1, 2013
    Posts:
    1,905
    I know this is an old thread, but seriously, it would be great to be able to write something like this:
    Code (CSharp):
    1. gameObject.transform.Changed += OnTransformChanged;
    Where OnTransformChanged could look something like this:
    Code (CSharp):
    1. void OnTransformChanged(TransformChannel transformChannel)
    2. {
    3.    // Do stuff
    4. }
    TransformChannel could be defined as:
    Code (CSharp):
    1. public enum TransformChannel
    2. {
    3.      Position,
    4.      Rotation,
    5.      Scale
    6. }
    Even without any parameters and it would still be awesome.

    I am working on an editor extension in which I want to perform raycasts, overlaps etc without the Physics API. One aspect of this is that I need to know when the transform of an object has changed. Looping through all objects in the scene and checking the hasChanged property of its transform is plain silly.

    Same goes for when an object is created or destroyed. Since game objects are the core entity in Unity, it seems absurd to not have any way of knowing when one is created in the scene or destroyed. You have to attach a script to your game objects to detect this and its annoying.
     
  9. dvr7

    dvr7

    Joined:
    Apr 24, 2016
    Posts:
    34
  10. Jinja

    Jinja

    Joined:
    Nov 28, 2012
    Posts:
    10
    Just a note to those listening to this thread, hasChanged does not strictly detect changes, it is just set to true when transform properties are set. e.g. transform.position = Vector3.zero; will set hasChanged to true even if position is already Vector3.zero. So if you need to detect true property changes, you'll need to perform your own comparisons.
    https://docs.unity3d.com/ScriptReference/Transform-hasChanged.html

    Code (CSharp):
    1.     public void Update()
    2.     {
    3.         if(transform.hasChanged)
    4.         {
    5.             transform.hasChanged=false;
    6.             Vector3 currPos = (mTrackLocal?transform.localPosition:transform.position);
    7.             if(mPrevPos!=currPos)
    8.             {
    9.                 Debug.LogFormat("<b>POS CHANGE</b> {0}'s pos={1}->{2}, frame={3}", FullGOName(transform), mPrevPos, currPos, Time.frameCount);
    10.                 mPrevPos = currPos;
    11.             }
    12.         }
    13.     }
    14.  
     
  11. SweatyChair

    SweatyChair

    Joined:
    Feb 15, 2016
    Posts:
    140
    Voted up for the feedback...

    I have a number of components in many partial classes, attached at character controller. In some point it moved unexpectedly and it's really hard to find which script exactly do it...
     
  12. EyePD

    EyePD

    Joined:
    Feb 26, 2016
    Posts:
    63
    Since the feedback site is no more I'm just going to say "+1"!
     
  13. ChrisHandzlik

    ChrisHandzlik

    Joined:
    Dec 2, 2016
    Posts:
    205
    Last edited: Nov 15, 2021
  14. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    It's been nine years, and we still *need* this method. Nine years!

    My use-case today: RectTransform is missing this callback, but has ALL the others (did the size change? did the parent change? did the children change?). This means that UI's CANNOT efficiently detect changes (this is, I believe, one of the reasons why Unity GUI is so sloooooow in editor when your GUI gets sufficiently complex: no-one can write any GUI code that fires "only" if something changed)
     
    ga5p0d3 and dvr7 like this.
  15. wethings

    wethings

    Joined:
    Aug 26, 2018
    Posts:
    28
  16. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    FYI to anyone else reading this: I found a clever workaround for any cases involving SceneView (which is almost all the ones you end up needing).

    For anyone to touch a transform in SceneView, by definition they will also have that transform selected in at least on Inspector (unless they closed all Inspector tabs, but that's very very rare - you can't use most of the Editor any more if you do that!). So: create a custom editor for any MB whose transform you need to track, and in OnInspectorGUI check on every update whether or not the transform is different to last time. This method is - by definition - called every time the user moves in sceneview, so you get your callback.

    Unity could easily have implemented this themselves, at any time in the last 10 odd years.
     
  17. Lesnikus5

    Lesnikus5

    Joined:
    May 20, 2016
    Posts:
    131
    Indeed, it is very strange that there is still no such necessary function.

    Such a workaround does not suit me, because I have to track the position of objects in the game. I want to implement CullingGroup for dynamic objects (including rigidbody). To update the positions of the spheres, I could use a callback when changing the transforms of the objects, then this would not affect the performance as much as manually scanning each object in the list and checking its transform.hasChanged.

    I wrote another request to the developers. Speak out there to demonstrate to developers that people still have an interest in adding this feature: https://forum.unity.com/threads/need-a-callback-when-changing-transform.890854/
     
  18. ChrisHandzlik

    ChrisHandzlik

    Joined:
    Dec 2, 2016
    Posts:
    205
    I've put together a tool that could help, it allows you to add events directly to Unity dlls, so you can write code like
    Code (CSharp):
    1. transform.SetPositionExecuting += (sender, e) => <your handler code>
    You could quite easily customise which events are added it should still work if you want to add one for rectTransform.size, eg
    Code (CSharp):
    1. rectTransform.SetSizeExecuting += (sender, e) => <your handler code>
    Tool adds events directly to Unity dlls so there's no code rewriting that happens on your assemblies.

    Got some troubles with adding a link, just click on 'G' at begining of this like or look for Unity.MissingUnityEvents on github

    Hope that helps.
     
    Last edited: May 17, 2020
    a436t4ataf likes this.
  19. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Sounds great for individual projects! Doesn't work for me, as most of my code is shared with other people - there's no way I can demand everyone hacks their DLL's to make my code work :). But for your own game, it sounds great.
     
  20. d2clon

    d2clon

    Joined:
    Aug 19, 2017
    Posts:
    19
    I need a `onTrasnformChanged` to recalculate variables that are used to draw the gizmos/handles
     
  21. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Try filing it as a bug report.

    You will probably get denied, and told you have to report it in the forums.

    In the forums no-one will answer - but if they do, they'll tell you to file a bug report.

    (this has happened to me and others, it may be Unity's way of dealing with bugs they don't want to admit to :)).

    But it's worth a try!