I've a question about how to get the transform created by the addition of child objects to a parent. It's easier to see what I mean by looking at the picture that follows.
Tranform
. I understand that the world position of the parent is effectively at 0 but how do I get the displayed one?
Best Regards
Some info, first. That location is where the Center of that GameObject is. The alternative to having the handles there is at the Pivot, which will put the handles at the same location as Transform.position. You can toggle between those two handle display modes in the Unity Editor.
So, you want to find the location of the Center handles. That depends on if the parent has a Renderer or not.
In your situation, where the parent object has no Renderer, you have to go into the children of the gameObject and average their transform positions:
Vector3 sumVector = new Vector3(0f,0f,0f);
foreach (Transform child in parentObject.transform)
{
sumVector += child.position;
}
Vector3 groupCenter = sumVector / parentObject.transform.childCount;
If the situation were a little different and the parent gameObject had a Renderer, then it is much easier:
Vector3 groupCenter = parentObject.getComponent<Renderer>().bounds.center;
I met the same problem. Thanks for the answer of #Ruzihm.
I want to make the center, but don't know whether the object has 'Renderer', so I write recursive function like this to get a center pos of the object.
Vector3 getCenter(Transform obj)
{
Vector3 center = new Vector3();
if (obj.GetComponent<Renderer>() != null)
{
center = obj.GetComponent<Renderer>().bounds.center;
}
else
{
foreach (Transform subObj in obj)
{
center += getCenter(subObj);
}
center /= obj.childCount;
}
return center;
}
Related
I am generating many cubes during runtime and applying some photos to them (it's a type of VR photo browser). Anyway, I am trying to encapsulate them all in a larger cube using the smallest possible bounding box of all the little cubes. This is what I have so far.
After generating all the cubes, the first thing I do is center them all on their parent so the user can position and rotate them relative to the center.
Transform[] children = GetComponentsInChildren<Transform>();
Vector3 newPosition = Vector3.zero;
foreach (var child in children)
{
newPosition += child.position;
child.parent = null;
}
newPosition /= children.Length;
gameObject.transform.position = newPosition;
foreach (var child in children)
{
child.parent = gameObject.transform;
}
Then I calculate the bounds of all the cubes which are children of the parent.
Renderer[] meshes = GetComponentsInChildren<Renderer>();
Bounds bounds = new Bounds(transform.position, Vector3.zero);
foreach (Renderer mesh in meshes)
{
bounds.Encapsulate(mesh.bounds);
}
Bounds maximumScrollBounds = bounds;
Then I create the new larger (red/transparent) cube which will encapsulate all the smaller ones and use the bounds I previously calculated for it's scale.
GameObject boundingBox = GameObject.CreatePrimitive(PrimitiveType.Cube);
Destroy(boundingBox.GetComponent<Collider>());
boundingBox.GetComponent<Renderer>().material = Resources.Load("Materials/BasicTransparent") as Material;
boundingBox.GetComponent<Renderer>().material.color = new Color32(255, 0, 0, 130);
boundingBox.name = "BoundingBox";
boundingBox.transform.localScale = new Vector3(maximumScrollBounds.size.x, maximumScrollBounds.size.y, maximumScrollBounds.size.z);
boundingBox.transform.localPosition = maximumScrollBounds.center;
boundingBox.transform.SetParent(transform);
But the problem is the encapsulating cube is always slightly too big and I cannot figure out why.
I need it to basically only reach the very edges of the smaller cubes inside of it.
Things I would try:
1.- I believe Bounds.Encapsulate retrieves the center in world space. So I think that:
boundingBox.transform.localPosition = maximumScrollBounds.center;
Needs to be subtituted by:
boundingBox.transform.position = maximumScrollBounds.center;
2.- Its the same, but just to give ideas to try with local/global space, I would also try:
boundingBox.transform.localPosition = boundingBox.transform.InverseTransformPoint(maximumScrollBounds.center);
On the other hand I would give a look and check all the faces of the bounding box to check if the over extension of the volume is simmetrical. Also draw the center, to try to narrow the problem and check if its more in the extents or just in the center.
As far as I can guess it is a misplaced center of the obtained bounding box, but for that to be, the exceeding volume in one side should be lacking in the other, so if that is not the problem, that could be an interesting update to move on towards the solution.
I am creating an inventory system, in which you can drag and drop items into slots. My problem is that when I drop my item into the slot, it doesn't align correctly.
I've tried using transform.localposition to set it correctly, but it changes absolutely nothing, they are still misaligned.
public void OnDrop(PointerEventData eventData)
{
//If there's nothing already in this slot
if(!item)
{
DragNDropController.itemBeingDragged.transform.SetParent(transform);
DragNDropController.itemBeingDragged.transform.position = this.transform.position;
}
}
Here is the result : https://i.imgur.com/iosqVAT.png
Note how the X/Y positions are -12.5/12.5. If I change it to 0/0 in the Rect Transform, then everything is aligned perfectly, but I can't seem to do it using code.
You are probably trying to set the localposition of the tranform, instead you should get the RectTransform and set its localposition.
Code:
RectTransform myRectTransform = GetComponent<RectTransform>();
myRectTransform.localPosition += Vector3.zero;
myRectTransform.anchoredPosition += Vector3.zero;
When you set a transform as a child of another.
The child position no longer representing its own position,Let's just say it is the sum of all its parent positions + its local position.
So you made it a child of a transform with position = 5,5,5.
but then you sat its position to be same as its parent which is 5,5,5.
Its total position is now 10,10,10.
You need to set its localPosition to 0,0,0, so its position is its parent position and wherever the slot goes, goes the item.
So to position it correctly at the center of the slot your code should be:
DragNDropController.itemBeingDragged.transform.SetParent(transform);
DragNDropController.itemBeingDragged.transform.localPosition = Vector3.zero;
I have an enemy that has children in it; the enemy also has a death animation. Within the death animation (using the animator), I have scaled the enemy to an appropriate size. However, the children within the enemy is also being scaled down even though I have a animation on the child where I have sized it, I also added anchor positions on this child. Is there a way I can scale down the enemy but also keep the size of the child, p.s. the child is a UI text object. Thank you!
The easiest way to solve your problem that I see is to introduce another GameObject higher in the hierarchy.
This GameObject would be the parent of your enemy object and your Text object which currently is a child of the enemy.
This way you can scale the enemy independently of the Text.
Maybe (hopefully) there are better solutions but you could use a component on the child objects that always keeps the original scale inverting relative changes in the parents scale
public class FreezeScale : MonoBehaviour
{
private Vector3 originalScale;
private Vector3 parentOriginalScale;
private void Awake()
{
// afaik RectTransform inherits from Transform
// so this should also work for UI objects.
originalScale = transform.localScale;
parentOriginalScale = transform.parent.localScale;
}
private void LateUpdate()
{
var currentParentScale = Transform.parent.localScale;
// Get the relative difference to the original scale
var diffX = currentParentScale.x / parentOriginalScale.x;
var diffY = currentParentScale.y / parentOriginalScale.y;
var diffZ = currentParentScale.z / parentOriginalScale.z;
// This inverts the scale differences
var diffVector = new Vector3 (1/diffX, 1/diffY, 1/diffZ);
// Apply the inverted differences to the original scale
transform.localScale = originalScale * diffVector;
}
}
Not tested since hacked in on my mobile phone but I hope you get the idea ;)
set the child's scale to world space not local space.
local space is the default, but it will go off of the scale of the parent so when the enemy shrinks so will the text.
alternatively you could set both objects to be children of an empty object, then just scale your enemy down and the text should stay the same size since its using the scale of the empty parent, which isn't changing size either.
see here:
public static Vector3 GetWorldScale(Transform transform)
{
Vector3 worldScale = transform.localScale;
Transform parent = transform.parent;
while (parent != null)
{
worldScale = Vector3.Scale(worldScale,parent.localScale);
parent = parent.parent;
}
return worldScale;
}
just a work around though, your meant to use this:
yourtransform.LocalScale=Transform.localToWorldMatrix
but it gives me issues... the above method works well though.
transform.scale=GetWorldScale(transform);
edit: lets be clear, the easiest thing to do would be to unpraent the objext before shrinking the parent. this will separate the scales.
I want to create 2d game.
Structure of objects
[]
"View" present a square where everything happens.
"Initial" This is the same square with a mask
"SquareList" is empty panel.
Why am I doing this?
I created a script where when I click on the screen, a new square is added to the SquareList. SquareList is the parent. I created a script to determine the position of the cursor.
createPos = new Vector2 (Input.mousePosition.x, Input.mousePosition.y);
Next, I want to set for new object position = cursor position.
Cursor.position equivalent = (562,1134 / 242.6486)
But the new object get position (94931.29 / 103365.6 / -17280)
But if I set position of new object to = new Vector(0,0);
Then everything is fine
What is the problem?
Input.mousePosition returns position in pixel-coordinate. You have to use Camera.main.ScreenToWorldPoint to convert it to world position then assign that to the object. If 3D Object, add an offset to it before converting with ScreenToWorldPoint.
public GameObject prefab;
public float offset = 10f;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 clickPos;
clickPos = Input.mousePosition;
clickPos.z = offset;
clickPos = Camera.main.ScreenToWorldPoint(clickPos);
GameObject obj = Instantiate(prefab, clickPos, Quaternion.identity);
}
}
Hello nameless brother/sister! I believe you are seeing this issue because your game object's coordinates are affected by its parent's coordinates. You may need to use localPosition to find more relative coordinates.
More info: https://docs.unity3d.com/ScriptReference/Transform-localPosition.html
this is the 3d model i wanted to connect another model like this to its silver connectors on top side and also another model to right side(so do help me to snap it)I want to know how to snap two 3D objects together in runtime. i.e during "play" the user must be able to dragup, down, left, right to snap one object with the other object .for example like "lego", ie. one 3D object should snap to another 3D object. How would I achieve this?
This is the code I use for dragging:
using System.Collections;
using UnityEngine;
public class drag : MonoBehaviour {
Vector3 dist;
float posX;
float PosY;
void OnMouseDown()
{
dist = Camera.main.WorldToScreenPoint(transform.position);
posX = Input.mousePosition.x - dist.x;
PosY = Input.mousePosition.y - dist.y;
}
void OnMouseDrag()
{
Vector3 curPos = new Vector3(Input.mousePosition.x - posX, Input.mousePosition.y - PosY, dist.z);
Vector3 worldPos = Camera.main.ScreenToWorldPoint(curPos);
transform.position = worldPos;
}
}
There are many many ways to accomplish this. Here is the first method I came up with. If we break down what might comprise a snap, we can muster up a script to attach to each snappable child:
1) Assign tag "parentblock" to the object you are dragging around.
2) Attach a trigger collider to both the parent object and the snappable child object.
3) When the the dragged object enters the collision area, snap it to the parent.
4) Store the offset from the parent to maintain its position once snapped.
bool snapped = false;
GameObject snapparent; // the gameobject this transform will be snapped to
Vector3 offset; // the offset of this object's position from the parent
Update()
{
if (snapped == true)
{
//retain this objects position in relation to the parent
transform.position = parent.transform.position + offset;
}
}
void OnTriggerEnter(Collider col)
{
if (col.tag == "parentblock")
{
snapped = true;
snapparent = col.gameObject;
offset = transform.position - snapparent.transform.position; //store relation to parent
}
}
Keep in mind this will only snap child objects to 1 master parent object that you are dragging around. It should go without saying you may need to tweak this for it to perform specifically to your project like you want, but hopefully this gets you in the right direction. (FYI I have not tested this so am not guaranteeing it will work right out of the gate)