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
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

How to position and scale a 3D gameobject to fit a canvas element?

Discussion in 'UGUI & TextMesh Pro' started by TxAg03, May 22, 2015.

  1. TxAg03

    TxAg03

    Joined:
    May 20, 2015
    Posts:
    21
    I have various 3D objects the user can select out of a list to see more details on. When an object is selected, I want to move it to the center of a preview window on the canvas and scale it so that it fills the preview window (similar to selecting a weapon in a FPS game and seeing the weapon rotate inside a preview window with a description below).

    Being new to Unity 5, I'm sure what I have come up with is probably not the best solution for locating the center point of the preview window:

    RectTransform previewRectT = previewWindow.GetComponent<RectTransform>();
    Vector3 previewBL = Camera.main.WorldToScreenPoint(previewRectT.position); // Bottom-Left
    float width = previewRectT.rect.width * canvas.scaleFactor; // Width of preview window
    float height = previewRectT.rect.height * canvas.scaleFactor; // Height of preview window
    Vector3 previewTR = new Vector3(previewBL.x + width, previewBL.y + height, previewBL.z); // Top-Right
    Vector3 previewCenter = Camera.main.ScreenToWorldPoint(new Vector3(previewBL.x + width / 2, previewBL.y + height / 2, previewBL.z));

    Is there a better way that does not require jumping back and forth between screen space and world space?

    Once the object has been moved to 'previewCenter', how can I best calculate the localScale so that it will fill the preview window? Since I want a uniform scaling, does this have to be calculated so the scaling in the z can be used to calculate the new 'previewCenter'?

    Thank you for your help.
     
  2. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    557
    Using ScreenToWorldPoint is probably the way to go. Something to make your life a little simpler though:

    Code (csharp):
    1. Vector3 previewCenter = Camera.main.ScreenToWorldPoint(previewRectT.rect.center)
    For scaling, you can use MeshRenderer.bounds to figure out your current in-world size, or Mesh.bounds to do the same without taking the object's current scale (and other transforms) into account. For example:

    Code (csharp):
    1. //select an item and instantiate + position as above
    2. Vector2 previewSizeWorld = new Vector2(previewRectT.rect.width * Camera.main.pixelWidth,
    3.                                        previewRectT.rect.height * Camera.main.pixelHeight);
    4. Mesh itemMesh = itemGameObject.GetComponent<MeshFilter>().mesh;
    5. float itemScale = previewSizeWorld.x / itemMesh.bounds.x; //calculate for a fit in width first
    6. itemScale = Mathf.Min(itemScale, previewSizeWorld.y / itemMesh.bounds.y); //also calculate for height, then choose the smallest
    7. itemGameObject.transform.lossyScale = new Vector3(itemScale, itemScale, itemScale);
    Note that this is untested, and more of a general idea than guaranteed-to-be-working code. I may have missed a conversion of local -> world or vice versa, and it doesn't take the item's pivot/ center into account.

    Regardless, I hope this helped a bit! Cheers!
     
  3. TxAg03

    TxAg03

    Joined:
    May 20, 2015
    Posts:
    21
    Thank you for the quick reply Senshi.

    Not sure why the way I listed above to find the center point of the preview window works over what you listed. When I try to set my gameobject to the center using myObject.transform.position = previewRectT.rect.center, it's no where to be seen, whereas my round-about way works somehow. I printed both centers to the debug window to find each different. I had to adjust the pivot to 0,0 for my way to work. I set the pivot back to 0.5,0.5 and tried your method again... the gameobject was placed at the origin.

    When calculating the previewSizeWorld, the number I get is extremely large ~(920000, 540000). I had tried using the MeshRenderer.Bounds.min & max and calculating the distance between them using Vector3.Distance. I then calculated the x and y (width and height) of the preview window. I tried, without success, to calculate a scale factor based off the distance and the min value of the width or height.

    Did I use the wrong component for bounds because I wasn't able to access an x or y value? I tried MeshFilter and MeshRenderer.
     
  4. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    557
    I've tried playing with this a bit, but I'm finding the same kind of unexpected numbers... Either (0,0,0) on off-center elements' rect.center, as well as its anchoredPosition and 3D counterpart, four identical corners on RectTranform.GetLocalCorners() or its World variant...Very strange.

    Sorry I couldn't be of more help! Please do post a solution once you find one though; it seems like very valuable information!
     
  5. TxAg03

    TxAg03

    Joined:
    May 20, 2015
    Posts:
    21
    Figured a way to position at the center of the preview window, but still working on an accurate scaling solution.

    The position returned from RectTransform.position is dependent upon the pivot point. For my animations to work properly, I use a pivot of 0,1 (bottom-right). Convert that position to Screen Point. Next get the width and height of the preview window using RectTransform.rect.width and height and multiple by the canvas scalingFactor. Since my pivot was set to 0,1, I added width/2 to my x and height/2 to my y of the position in Screen Point to get my center point.

    As for the scaling, I get the min value of width or height of the preview window, calculate the distance between bounds.min and bounds.max of that GameObject, then divide the distance of the preview window by the GameObject distance. This works great in a 2D solution, but because there's uniform scaling, the z scale tends to make the GameObject stick out the right side of the preview window. I've added a 'correction' factor, but the GameObject appears to not be in the center because of the perspective view. I'm still working on the offset required to make it appear center when scaled in the z-axis.

    I didn't post any code because I haven't cleaned it of all the previous trial and errors.
     
  6. Diesign

    Diesign

    Joined:
    May 15, 2016
    Posts:
    16
    Apologies for the necro-bump but did you ever figure this out @TxAg03?