According to the Unity docs for Transform.lossyScale, this attribute is "The global scale of the object". From what I can tell, this isn't actually an estimate of the scaling aligned with the World's coordinate system, but instead it is aligned to something else - possibly the top-most ancestor of the object in the Scene hierarchy, but it's not very clear.
To see what I mean, create a Grand Parent, Parent and Child object hierarchy in a new scene, and have them all print out their transform.lossyScale
vectors in Update()
. Set the scaling on the Grant Parent to something like x=10, y=1, z=1. Now run the project and rotate the Grant Parent object within the scene and you'll note that none of the lossy scale vectors actually change when the Grand Parent object rotates. But rotate just the Parent and you'll see that both it and the Child lossy scale vectors do change. It's as if they are both expressed in the Grand Parent's local frame of reference. But why both? Why isn't the Child's lossy scale expressed in the Parent's local frame of reference instead? Perhaps it's the highest object in the hierarchy that is used as the final frame of reference?
None of this seems to suggest that the lossy scale vector is in any way aligned with the World coordinate system.
In terms of what I'm actually trying to achieve - I have a script that creates a CapsuleCollider along the local X axis of a GameObject. I want the collider to be sized such that its overall height ("height" + 2 * "radius") is determined by some parameter of the GameObject ("width"):
public class MyCollider : MonoBehaviour
{
[SerializeField] private float radius = 0.5f;
[SerializeField] private float width = 1.0f;
private CapsuleCollider _collider;
private void Start()
{
_collider = gameObject.AddComponent<CapsuleCollider>();
_collider.radius = radius;
//_collider.height = width + 2 * radius * transform.lossyScale.x; // works when aligned with parent's X axis
//_collider.height = width + 2 * radius * transform.lossyScale.z; // works when aligned with parent's Z axis
_collider.direction = 0; // X-axis
}
I want the final CapsuleCollider size to be determined by any scaling on the parent (and perhaps its parents, although a single level will do for now). I've managed to get to the point where my scaling works correctly when the object with this script is in the same frame of reference as the parent object (no rotation!) - see first commented line, using the x component. If I rotate the object 90 degrees to align with the parent's Z axis, I can get away with using the z component. But I can't find a general solution for an arbitrary rotation of my MyCollider object.
I've tried converting lossyScale to local coordinates with transform.InverseTransformDirection(transform.lossyScale)
but because lossyScale isn't really in World coordinates, this doesn't work.
So how can I get an estimate or otherwise of final scaling in my object's local reference frame? Any suggestions for a fix or alternate approach?
I am aware that Transform.lossyScale
is really only a guess at the final scaling, due to skew caused by rotation. However in my case I'm constrained to a single Parent and therefore only its rotation and the object's own rotation.
I'm also aware that the CapsuleCollider's actual radius can be different to the value specified, and that non-uniform scaling of the collider on an axis will actually do some kind of max(abs()) of the other two axes,