Setting to a World Scale Reference (lossyScale read-only work-around) A Constraint

Hi,

I wanted to share this solution and get some thoughts on how it might compare to other solutions. This is based on how I might work with a complete transform in a high-end 3D software package.

It assumes that destroying a simple GameObject would have essentially zero performance or garbage-collection issues, as everything I have read says it is extremely light.

C#: (I just like colors and PHP is the only option, right?)

    public void SetWorldScale(Transform xform, Transform sourceXform)
    {
        // Make a reference transform and set it to the source's lossyScale, which
        //   is Unity's estimate of "world scale". Since this new object doesn't
        //   have a parent (it is in world space) set it to carry the lossyScale.
        GameObject refGO = new GameObject();   // Used to destroy later.
        Transform refXform = refGO.transform;
        refXform.localScale = sourceXform.lossyScale;

        // Cast in to the local space for reference...
        //   Parent the reference transform to xform's parent so they are in the same
        //   space, now we have a local scale to use.
        refXform.parent = xform.parent;
        xform.localScale =  refXform.localScale;

        // Destroy the ref. Don't worry about Garbage Collection since this is a
        //   very simple object with no references - basically just a transform
        Destroy(refGO);
    }

This is part of a constraint component I wrote for PoolManager2, which we will be releasing soon. While this constraint is a minor, optional tool, I want it to be just as performance friendly as the rest of PoolManager.

In the real version:

  • Everything is cached (the transform, its parent and the target transform)
  • I’m thinking about trying to implement a ‘dirty’ state so it only runs when the target is updated, but unless there is a state I can grab from Unity, I’m thinking the check will be more intense then just running it. Thoughts?
  • Scale, Rotation and Position are each optional.
  • I will also have an option to only run it once, for situations where an update isn’t needed, only an ‘alignment’.
  • Runs via co-routine so it can be turned on and off completely (no update events used)

If this is as sleek as I expect it to be, I’ll post the complete source code for the community.

OK that version wasn’t as stable as I had hoped. I was able to break it by setting the parent of the object being set to a non-uniform scale (e.g. 0.9, 1.5, 2.3). I guess lossyScale got confused? Anyway…

This version seems to be stable even in a complex hierarchy. I’m not exactly sure why I need to add in a rotation match, but when working with scales I suppose it is logical (which is why I tried it to see if it helped.)

(I didn’t test this exact version, mine doesn’t need the first argument since I am using this.xform in my class. It should be right though…)

    /// <summary>
    /// Sets this transform's scale to equal the target in world space.
    /// </summary>
    /// <param name="sourceXform"></param>
    public void SetWorldScale(Transform xform, Transform sourceXform)
    {
        // Make a reference transform and set it to the source's lossyScale, which
        //   is Unity's estimate of "world scale". Since this new object doesn't
        //   have a parent (it is in world space) set it to carry the lossyScale.
        GameObject refGO = new GameObject();   // Used to destroy later.
        Transform refXform = refGO.transform;

        // Cast the reference transform in to the space of the source Xform using
        //   Parenting, then cast it back to set.
        refXform.parent = sourceXform;
        refXform.localRotation = Quaternion.identity;
        refXform.localScale = Vector3.one;

        // Parent the reference transform to this object so they are in the same
        //   space, now we have a local scale to use.
        refXform.parent = xform.parent;

        // Set the scale now that both Transforms are in the same space
        xform.localScale = refXform.localScale;

        //Debug.DebugBreak();
        // Destroy the ref. Don't worry about Garbage Collection since this is a
        //   very simple object with no references - basically just a transform
        Destroy(refGO);
    }

A Disclaimer and a Warning:
With a hierarchy of scaled and rotated objects it works but obviously geometry wouldn’t match (my two cubes looked totally different, but their colliders matched almost perfectly). There was a slight difference, but very little. I sincerely hope nobody is actually working this way. Scaling is bad in most cases. I really only made it this complex for testing. I have a UI element which has to be scaled down (uniformly) during run-time but that is only one object and for a specific need. Nothing in our game is scaled at the asset level.

I just want to say again how disappointed I am that Unity doesn’t have the ability to get and set a Transform (struct-like). Softimage XSI worked this way and it was so easy to manipulate things even if you don’t have any idea how to do matrix math. I could get a transform, manipulate anything, usually through simple methods, and then set it back. This lead to a much simpler workflow, code, learning curve, etc.