Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Scale Around Point - similar to Rotate Around

Discussion in 'Scripting' started by De-Panther, Mar 7, 2014.

  1. De-Panther


    Dec 27, 2009

    So I had a problem, and I solved it, and because I saw lots of questions about it, but without a solution, here is how I solved it:

    Unity has Transform.RotateAround
    Rotates the transform about axis passing through point in world coordinates by angle degrees.
    But it doesn't have ScaleAround

    In order to scale around a point in the world, you'll need this formula:

    GameObject Local Position

    New pivot position(relative to object's parent)

    Old scale Vector3(2,2,2)
    New scale Vector3(5,5,5)

    Find Object position relative to pivot(It's similar to put the game object as a child of a pivot object, and then get it's localPosition):

    Relative Scale(we need to check how much did the object scaled):
    RS=New Scale / Old Scale
    2.5=5 / 2

    Final Position:

    Vector3.Scale(Vector3(2.5,2.5,2.5), Vector3(5,3,1)) + Vector3(4,5,6)
    Vector3(12.5,7.5,2.5) + Vector3(4,5,6)=Vector3(16.5,12.5,8.5)

    (Relative Scale * Object Position Relative to Pivot) + Pivot Position

    Good luck :)
    Afroman and amonitzer like this.
  2. MaRTiDoRe


    Jul 4, 2011
    I don't understand where do you get:

    Old scale Vector3(2,2,2)
    New scale Vector3(5,5,5)

    Where do you get those values from and why do you have two scale values?

    The rest is crystal clear.
    Last edited: Jul 3, 2014
  3. gregroberts


    Nov 21, 2014
    I've translated De-Panther's pseudocode into working code. Enjoy:

    var targetGO : GameObject;
    var pivotGO : Transform;

    function Start () {
    var A : Vector3 = targetGO.transform.position;
    var B : Vector3 = pivotGO.transform.position;

    var RS : float = 0.2; // relataive scale factor
    var startScale = targetGO.transform.localScale;
    var endScale = targetGO.transform.localScale *RS;

    var C : Vector3 = A-B; // diff from object pivot to desired pivot/origin

    // calc final position post-scale
    var FP : Vector3 = (C*RS)+B;

    // finally, actually perform the scale/translation
    targetGO.transform.localScale = endScale;
    targetGO.transform.position = FP;
  4. trul


    Nov 15, 2013
    I've created static method to scale target around the pivot:

    Code (CSharp):
    1. public static void ScaleAround(Transform target, Transform pivot, Vector3 scale) {
    2.         Transform pivotParent = pivot.parent;
    3.         Vector3 pivotPos = pivot.position;
    4.         pivot.parent = target;      
    5.         target.localScale = scale;
    6.         target.position += pivotPos - pivot.position;
    7.         pivot.parent = pivotParent;
    8.     }
  5. KyleBlumreisinger


    Mar 6, 2017
    So trul's code with parenting wasn't quite what I personally was looking for and I couldn't get it to work, but gregroberts's example code also didn't work for me out of the box, so I eventually made this:
    I've translated gregroberts's mostly-working (I'm assuming Java) code translation of De-Panther's pseudocode into fully functioning and tested C#:

    Code (CSharp):
    1. public void ScaleAround(GameObject target, Vector3 pivot, Vector3 newScale)
    2. {
    3.     Vector3 A = target.transform.localPosition;
    4.     Vector3 B = pivot;
    6.     Vector3 C = A - B; // diff from object pivot to desired pivot/origin
    8.     float RS = newScale.x / target.transform.localScale.x; // relative scale factor
    10.     // calc final position post-scale
    11.     Vector3 FP = B + C * RS;
    13.     // finally, actually perform the scale/translation
    14.     target.transform.localScale = newScale;
    15.     target.transform.localPosition = FP;
    16. }
    Code (CSharp):
    1. ScaleAround(targetGameObject, scaleCenterPivot, desiredFinalScale);
    Alternatively, if you would rather use a Transform / GameObject's position as the pivot, you can do:
    Code (CSharp):
    1. ScaleAround(targetGameObject, pivotGameObject.transform.position, desiredFinalScale);
    Just remember: It is extremely important that the pivot point be in the target's PARENT'S space. So if your target object has no parent, your pivot is in world space. If your target is a child of SomeGameObject, you pivot point must be in SomeGameObject's space. This can be accomplished with
    Code (CSharp):
    1. targetGameObject.transform.parent.InverseTransformPoint(pivot);
    if your pivot is in world space.

    Hope this helps someone who needed it as much as I did, thanks to everyone who contributed here, and happy coding!
  6. De-Panther


    Dec 27, 2009
    @KyleBlumreisinger , I'm happy that it helped someone after so many years :D

    I think that the "target" type on ScaleAround should be Transform.

    And maybe we can add a bool that ask if pivot is in parent space or world space.

    Also, now that I think about it, it works only if the scaling was unified.
    So iguess it shold be:
    Vector3 RS= new Vector3(newScale.x / target.transform.localScale.x, newScale.y / target.transform.localScale.y, newScale.z / target.transform.localScale.z);
    Vector3 FP = B + C.Scale(RS);

    But I'll have to test it.
    Diablo404 likes this.
  7. stenfeio


    Jan 18, 2015
    This was excellent. It's easy to forget that final positions need to also be handled as we've come so much to rely on parenting...
    Last edited: Sep 3, 2018
  8. _geo__


    Feb 26, 2014
    I know it's an older post but just for completeness and to help others who might come across this.
    So here are some versions of this method with some write-up on how they work.
    I took the liberty to add @De-Panther s suggestions.

    Code (CSharp):
    1. /// <summary>
    2. /// Scales the target around an arbitrary point by scaleFactor.
    3. /// This is relative scaling, meaning using  scale Factor of
    4. /// will not change anything and new Vector3(0.5f,0.5f,0.5f) will reduce
    5. /// the object size by half.
    6. /// The pivot is assumed to be the position in the space of the target.
    7. /// Scaling is applied to localScale of target.
    8. /// </summary>
    9. /// <param name="target">The object to scale.</param>
    10. /// <param name="pivot">The point to scale around in space of target.</param>
    11. /// <param name="scaleFactor">The factor with which the current localScale of the target will be multiplied with.</param>
    12. public static void ScaleAroundRelative(GameObject target, Vector3 pivot, Vector3 scaleFactor)
    13. {
    14.     // pivot
    15.     var pivotDelta = target.transform.localPosition - pivot;
    16.     pivotDelta.Scale(scaleFactor);
    17.     target.transform.localPosition = pivot + pivotDelta;
    19.     // scale
    20.     var finalScale = target.transform.localScale;
    21.     finalScale.Scale(scaleFactor);
    22.     target.transform.localScale = finalScale;
    23. }
    25. /// <summary>
    26. /// Scales the target around an arbitrary pivot.
    27. /// This is absolute scaling, meaning using for example a scale factor of
    28. /// will set the localScale of target to x=1, y=1 and z=1.
    29. /// The pivot is assumed to be the position in the space of the target.
    30. /// Scaling is applied to localScale of target.
    31. /// </summary>
    32. /// <param name="target">The object to scale.</param>
    33. /// <param name="pivot">The point to scale around in the space of target.</param>
    34. /// <param name="scaleFactor">The new localScale the target object will have after scaling.</param>
    35. public static void ScaleAround(GameObject target, Vector3 pivot, Vector3 newScale)
    36. {
    37.     // pivot
    38.     Vector3 pivotDelta = target.transform.localPosition - pivot; // diff from object pivot to desired pivot/origin
    39.     Vector3 scaleFactor = new Vector3(
    40.         newScale.x / target.transform.localScale.x,
    41.         newScale.y / target.transform.localScale.y,
    42.         newScale.z / target.transform.localScale.z );
    43.     pivotDelta.Scale(scaleFactor);
    44.     target.transform.localPosition = pivot + pivotDelta;
    46.     //scale
    47.     target.transform.localScale = newScale;
    48. }
    I'd argue that "ScaleAroundRelative()" is the way most people would assume the ScaleAround() methods from above work. Unitys own RotateAround() also works relative (rotate by) instead of absolute (rotate to angle).

    Hope it's useful.
    Last edited: Jul 23, 2020
    LeviLeuthold, doompr, NotaNaN and 9 others like this.