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

    De-Panther

    Joined:
    Dec 27, 2009
    Posts:
    564
    Hey,

    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
    https://docs.unity3d.com/Documentation/ScriptReference/Transform.RotateAround.html
    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
    A=Vector3(9,8,7)

    New pivot position(relative to object's parent)
    B=Vector3(4,5,6)

    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):
    A-B=C
    Vector3(9,8,7)-Vector3(4,5,6)=Vector3(5,3,1)

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

    Final Position:
    RS*C+B=FP

    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)
    Vector3(16.5,12.5,8.5)

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

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

    MaRTiDoRe

    Joined:
    Jul 4, 2011
    Posts:
    41
    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

    gregroberts

    Joined:
    Nov 21, 2014
    Posts:
    25
    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

    trul

    Joined:
    Nov 15, 2013
    Posts:
    9
    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

    KyleBlumreisinger

    Joined:
    Mar 6, 2017
    Posts:
    2
    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;
    5.  
    6.     Vector3 C = A - B; // diff from object pivot to desired pivot/origin
    7.  
    8.     float RS = newScale.x / target.transform.localScale.x; // relative scale factor
    9.  
    10.     // calc final position post-scale
    11.     Vector3 FP = B + C * RS;
    12.  
    13.     // finally, actually perform the scale/translation
    14.     target.transform.localScale = newScale;
    15.     target.transform.localPosition = FP;
    16. }
    Usage:
    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

    De-Panther

    Joined:
    Dec 27, 2009
    Posts:
    564
    @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

    stenfeio

    Joined:
    Jan 18, 2015
    Posts:
    22
    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__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,137
    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 Vector3.one
    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;
    18.  
    19.     // scale
    20.     var finalScale = target.transform.localScale;
    21.     finalScale.Scale(scaleFactor);
    22.     target.transform.localScale = finalScale;
    23. }
    24.  
    25. /// <summary>
    26. /// Scales the target around an arbitrary pivot.
    27. /// This is absolute scaling, meaning using for example a scale factor of
    28. /// Vector3.one 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;
    45.  
    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.