Unity3D C# positioning of panel after instantiation - c#

I'm instantiating a GO that has a panel component (RectTransform) as child of a canvas existing in the scene:
_heroSelectUI = (GameObject)Instantiate (_heroSelectUIPrefab, GameObject.Find ("Canvas").GetComponent<Transform>());
When it's created it gets the following values:
"Left", "Top", "Right" and "Bottom" get some unwanted values (probably due to the existing canvas which seems to have identical values).
The panels prefabs values are 0, how to set them back to 0 after instantiation? I can't find the proper variables for the RectTransform.

According to the documentation
For a stretching Rect Transform, it can be simpler to set the position using the offsetMin and offsetMax properties. The offsetMin property specifies the corner of the lower left corner of the rect relative to the lower left anchor. The offsetMax property specifies the corner of the upper right corner of the rect relative to the upper right anchor.
Try to do as follow :
RectTransform rt = _heroSelectUI.GetComponent<RectTransform>();
rt.offsetMin = rt.offsetMax = Vector2.zero ;
Else, according to the documentation too, you can try to do it when setting the parent :
Prefabs of UI elements are instantiated as normal using the Instantiate method. When setting the parent of the instantiated UI element, it’s recommended to do it using the Transform.SetParent method with the worldPositionStays parameter set to false.
_heroSelectUI = (GameObject)Instantiate (_heroSelectUIPrefab, GameObject.Find ("Canvas").GetComponent<RectTransform>(), false);
OR :
_heroSelectUI = (GameObject)Instantiate (_heroSelectUIPrefab );
_heroSelectUI.GetComponent<Transform>().SetParent( GameObject.Find ("Canvas").GetComponent<RectTransform>(), false ) ;

Related

Unity how to set the radius of a sphere in global scale?

When the sphere is a child, it inherits the scale of its parent. The spheres always become the ellipsoids. Is there a way to set the radius of that sphere to be a certain number no matter what its parent's scales are?
If none of the ancestors are rotated, there's a simple solution:
Set the local scale to be the reciprocol of the parent's lossyScale. Putting it in LateUpdate will guarantee that its scale is set after any of its ancestors' Update methods change its scale. If the ancestors' scales change in any of their LateUpdates, you might want to look into Script Execution Order Settings to set the sphere's script to execute last.
For instance:
public void LateUpdate(){
Vector3 parentScale = transform.parent.lossyScale;
transform.localScale = new Vector3(1f/parentScale.x, 1f/parentScale.y,
1f/parentScale.z);
}

Reason for automatic creation of RectTransform

I'm creating 2 GameObjects.
One automatically gets a RectTransform without explicitely adding one, the other doesn't.
In this case, a RectTransform isn't added, but it can be accessed:
GameObject nCanvasGO = new GameObject("CanvasContainer");
Canvas nCanvas = nCanvasGO.AddComponent<Canvas>();
nCanvas.renderMode = RenderMode.WorldSpace;
nCanvasGO.AddComponent<CanvasScaler>();
nCanvasGO.AddComponent<GraphicRaycaster>();
RectTransform rtCanvasGO = nCanvasGO.GetComponent<RectTransform>(); //can be accessed, isn't null
This one does not have a RectTransform:
GameObject nAnimInfo = new GameObject("AnimInfo");
RectTransform rtAnimInfo = nAnimInfo.GetComponent<RectTransform>(); // is null
I would therefore like to ask if adding a Canvas component to a GameObject add a RectTransform or what else might be the reason here.
Thank you.
Yes, Adding a canvas to a Gameobject will automatically change the Transform to a RectTransform. This is because the rect transform is the 2D equivalent of Transform, with some additional functionality like anchoring.
From the Unity docs:
The Rect Transform component is the 2D layout counterpart of the Transform component. Where Transform represents a single point, Rect Transform represent a rectangle that a UI
element can be placed inside. If the parent of a Rect Transform is also a Rect Transform, the child Rect Transform can also specify how it should be positioned and sized relative to the parent rectangle.
The docs don't actually clearly state anywhere that a transform gets replaced by RectTransform automatically though...
Furthermore Canvas depends on RectTransform, and can thus not be used without having a RectTransform. You can see ths when you try to delete the rect transform from a canvas. It will pop up saying "Can't remove RectTransform because Canvas depends on it".
Any other UI component you add to a GameObject will also automatically add the Recttransform component (I.E image, text etc.). this has the same reason as Canvas, that they depend on RectTRansform.
You will also notice that any GameObject you make that is a child of a canvas will also have a RectTransform by default, so that it can anchor itself relative to the parents (canvas in this example) rect transform. You can delete the rectTransform from these Objects as long as none of its components depend on RectTransform. Though i don't see why this would be desired, as any child of a canvas should be some form of UI like an image or text. And should thus require the RectTransform.

How to get children's world position in a GridLayoutGroup in Unity?

I have a scroll view, in its content object there's a GridLayoutGroup component(the content object has its own position and scales instead of (0,0,0) or (1,1,1) ), and under it there're several images. During runtime I want to take out an image and set its parent to other UI object, but I want the image remain its position on screen.
However I tried all the following ways, all ended up with wrong position on screen(the image moved sometimes even off screen).
1 Use SetParent method on the image object(tried both True or false as second parameter):
imageObject.transform.SetParent(otherObj, True);
2 Use SetParent method in the 1st way, then give a position to the image manually, but id does not appear at the mouse position(this code works fine for other objects that are not in a layoutgroup):
imageObject.transform.SetParent(otherObj, True);
var p = Camera.main.ScreenToWorldPoint(Input.mousePosition);
imageObject.transform.position = new Vector3(p.x, p.y, 0);
3 Set the position to its original value after calling SetParent.
var p = imageObject.transform.position;
imageObject.transform.SetParent(otherObj, True);
imageObject.transform.position = new Vector3(p.x, p.y, 0);
4 don't use any code but during runtime in the editor, manually drag the image out to targeted parent object. Still its position is changed.
5 don't use any code but during runtime in the editor, manually uncheck the GridLayoutGroup to disable it. Still the image position is changed.
Why the 3rd way is not working? I think probably the transform.position of the image object is not used so the value is not used so the value is not where the position is on screen, or something happened in the end of the frame so my reseting of the position is useless.
More info: my canvas render mode is set to "Screen Space-Camera". But even changed it to overlay, it remained the same result. Canvas scaler mode is "Scale with Screen Size", screen match mode is "Expand".
So what should I do to take out the image out of the GridLayoutGroup but let it stays where it is on screen? Thanks!
After several tests, I found out that the position after you call SetParent(objParent, True); is right; but due to something related with GridLayoutGroup, the position is changed in the time after this and before the end of the frame. So take a record of that position and do whatever you need in other frames.
Example:
in main thread:
imageObject.transform.SetParent(otherObj, True);
originalPosition = imageObject.transform.position;
imageObject.SetObjectActive(false); // If not do this, the image might flicker at it's position before put it into the GridLayoutGroup. My guess is that it gets rendered before the AdjustTransInTheEndOfFrame method is executed.
StartCoroutine(AdjustTransInTheEndOfFrame(imageObject));
private IEnumerator AdjustTransInTheEndOfFrame(GameObject obj)
{
yield return new WaitForEndOfFrame();
obj.transform.position = originalPosition;
obj.SetObjectActive(true);
}
Ok, I've made test project. Didn't solve your problem completely, but I got parent changing working for GridLayoutGroup located at 0,0,0 with:
Use RectTransform.anchoredPosition to get relative position in
GridLayoutGroup.
Change parent
Make sure anchors of images remain the same (grid layout can change anchor of image).
Wait for end of frame after changing parent to set new anchor.
Set new anchoredPosition for your RectTransform.
If as you said your GridLayoutGroup has scale different from 1,1,1
then divide/multiply coordinates accordingly (divide by 2 for 0.5,
0.5, 0.5)
(If your Grid in not located in (0,0,0) you may also need to make
some alignments)
I use this function:
public Vector2 GetChildLocalPosition(RectTransform rectTransformGrid, RectTransform rectTransformChild)
{
var localPositionGrid = (Vector2)rectTransformGrid.localPosition;
var sizeDeltaGrid = rectTransformGrid.sizeDelta;
var deltaGridFromCenterToLeftTop = new Vector2(-0.5f * sizeDeltaGrid.x, 0.5f * sizeDeltaGrid.y);
var anchoredPositionChild = rectTransformChild.anchoredPosition;
var childPosition = localPositionGrid + anchoredPositionChild + deltaGridFromCenterToLeftTop;
return childPosition;
}
If you need, please update the GridLayoutGroup as given below:
public void UpdateGrid(LayoutGroup gridLayoutGroup)
{
gridLayoutGroup.CalculateLayoutInputHorizontal();
gridLayoutGroup.CalculateLayoutInputVertical();
gridLayoutGroup.SetLayoutHorizontal();
gridLayoutGroup.SetLayoutVertical();
}

How to move an gameobject in Unity properly?

I want to move an Text object, and the part of code is as follows.
GameObject.transform.position = new Vector3(-210, -200, 0);
When I execute and check the posX of GameObject in Unity, its value becomes -1170(in 1920x1080), -1653.566(16:9). But posY can work properly. I've set the reference convolution to 1920x1080, and I think it may it have something to do with the resolution settings. Is there any thing wrong? Thanks.
If you are talking about unity ui text you should do it like this.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UITestSO : MonoBehaviour
{
public Text textObject;
void Start ()
{
//Position relative to parent transform
textObject.rectTransform.localPosition = new Vector3 (-210, -200, 0);
//Position in world space
textObject.rectTransform.position = new Vector3 (-210, -200, 0);
}
}
All the UI objects(text, image etc.) are parented by canvas object in unity. Canvas behaves differently based on it's screen space setting as follows -
Screen Space - Overlay : If the screen is resized or changes resolution, the Canvas will automatically change size to match this.
Screen Space - Camera : If the screen is resized, changes resolution, or the camera frustum changes, the Canvas will automatically change size to match as well.
Screen Space - World : The Canvas will behave as any other object in the scene. The size of the Canvas can be set manually using its Rect Transform.
The default setting is Screen Space - Overlay Which is the reason you are getting different position values for your text object on different resolutions.
The unity UI elements uses RectTransform. From unity docs
The Rect Transform component is the 2D layout counterpart of the
Transform component. Where Transform represents a single point, Rect
Transform represent a rectangle that a UI element can be placed
inside. If the parent of a Rect Transform is also a Rect Transform,
the child Rect Transform can also specify how it should be positioned
and sized relative to the parent rectangle.
So, to set position of UI elements use RectTransform's anchoredPosition variable, which sets the position of the pivot of this RectTransform relative to the anchor reference point.
textObject.rectTransform.anchoredPosition = new Vector3 (-10, -10, 0);
Reference to rect transform script API.
In Unity, the transform values you see in the inspector are relative to the gameobject's parent. However, when you try to set value for a gameobject's position (by assigning transform.position = ...), you are dealing with position relative to world's center (i.e Vector(0,0,0)). This holds true for whether you are dealing with 3d or 2d.
So, if the parent object is at Vector(0,0,0), world and local positions would be same. There isn't anything wrong with the resolution. You need to set values relative to your parent.
This is how you set values for objects.
anObject.transform.localPosition = new Vector3 (X, Y, Z);
Although there is nothing stopping you from using the same for 2d workflow, RectTransforms are used over simple Transform.

Gameobject instantiated in canvas shows up behind other canvas items

In my game, if it is the first time the player has ever played the game then I have an image that is displayed. I instantiate it in the canvas like so:
if (PlayerPrefs.GetInt("First Time", 1) == 1)
{
introEnabled = true;
//pause all activity
pause.pauseOrResume();
intro = Instantiate (Resources.Load ("intro"), transform.position, Quaternion.identity) as GameObject;
Canvas canvas = GameObject.Find("Canvas").GetComponent<Canvas>();
intro.transform.SetParent(canvas.transform, false);
//robot = Instantiate (Resources.Load ("robot"), transform.position, Quaternion.identity) as GameObject;
PlayerPrefs.SetInt("First Time", 0);
PlayerPrefs.Save();
}
I can see in the inspector when the game is running that when the object is created, it does show up in the canvas. I just have no idea why it won't show up on top of all of the other objects in the canvas.
The canvas rendering mode is set to "Screen space - Overlay" and it is set to 0 in the sort order. The game object being instantiated also has a sort order of 0 and it is in the default layer.
Nothing I have tried has worked. The z values make no difference and even putting the canvas and the instantiated object in the Default layer and setting the canvas to a sort order of 2 and the instantiated object to a sort order of 1 (so it is rendered first) does not make a difference.
What you want is to use transform.SetAsLastSibling() . The CanvasRenderer renders the GameObjects in order according to their sibling index's (position in the hierarchy), from top to bottom.
However, after examining your code further I noticed something and I should say that if you are trying to instantiate the intro screen, I would highly recommend to instantiate it with it's own Canvas, instead of parenting it to the existing one. Something that significant should have it's own dedicated Canvas.
Update: The problem here is that you are instantiating GameObjects with a transform and Sprite renderer. Canvas UI gets rendered last, over all mesh layers, so if you want your newly instantiated GameObjects to participate, they will need to have RectTransform and Image components instead.
Instead of Default layer change its layer to UI.
You code seems fine, it might be problem related to the gameobject where this script is attached (because you are passing transform.position in Instantiate). it might be positioning the Instantiated game object off the canvas. Try like this once it might work.
intro = Instantiate (Resources.Load ("intro")) as GameObject;
and after setting its parent(very important), set it's position like this
intro.transform.localPosition = new vector3(0,0,0);

Categories

Resources