I have three objects at A,B,C in Unity. I want to transform their coordinates to a new coordinate system which has point A as the new origin (0, 0, 0), AB as the line along the new x-axis, and AC as the line along the new y-axis. How do I get the new coordinates of A, B, C in this new coordinate space?
I looked at transform.InverseTransformPoint(), but I'm not sure how to make it work here.
The simplier - non-maths - solution would be
Place the objects A,B,C under the same parent object
Translate and rotate the parent object instead of all objects individually => Let Unity do the maths for you
Optionally when done place them again where there where before - or if you want to be able to still change their localPosition and localRotation keep them under the rotated parent.
Otherwise use e.g. Transform.TransformPoint without parenting
I added both to an example manager
public class CoordinateManager : MonoBehaviour
{
[Header("References")]
public Transform objectA;
public Transform objectB;
public Transform objectC;
[Header("Coordinate-System Settings")]
public Vector3 A;
public Vector3 AB;
public Vector3 AC;
[ContextMenu("Apply New Coordinates")]
public void ApplyNewCoordinates()
{
// just for making sure this transform is reset
transform.position = Vector3.zero;
transform.rotation = Quaternion.identity;
transform.localScale = Vector3.one;
// Make this object parent of objectA,objectB and objectC keeping their current transforms
// For reverting it later store current parents
var parentA = objectA.parent;
var parentB = objectB.parent;
var parentC = objectC.parent;
objectA.SetParent(transform);
objectB.SetParent(transform);
objectC.SetParent(transform);
// place this object to the new pivot point A
// and rotate it to the correct axis
// so that its right (X) vector euqals AB
// and its up (Y) vector equals AC
// Unity will handle the rotation accordingly
transform.position = A;
transform.right = AB;
transform.up = AC;
// Optionally reset the objects to the old parents
objectA.SetParent(parentA);
objectB.SetParent(parentB);
objectC.SetParent(parentC);
}
// Use this method to place another object in the coordinate system of this object
// without any parenting
public void SetPosition(Transform obj, Vector3 relativePosition)
{
// sets the obj to relativePosition in the
// local coordinate system of this rotated and translated manager
obj.position = transform.TransformPoint(relativePosition);
// adjust the rotation
// Quaternions are added by multiplying them
// so first we want the changed coordinate system's rotation
// then add the rotation it had before
obj.rotation = transform.rotation * obj.rotation;
}
// Only for visualization of the pivot point A and the
// AB(red) and AC(green) axis in the SceneView
private void OnDrawGizmos()
{
Gizmos.color = Color.white;
Gizmos.DrawWireSphere(A, 0.1f);
Gizmos.color = Color.red;
Gizmos.DrawLine(A, A + AB);
Gizmos.color = Color.green;
Gizmos.DrawLine(A, A + AC);
}
}
Related
Newbie here. I want to Instantiate a GameObject to a specific position on the Parent. I want to place it at the top of the parent. Can I position it immediately when I instantiate it or do I need to use the transform.position? In either case I don't know how to do it. I also need to figure out how to rotate the child on the parent if you are feeling generous with your time. Also, each child/copy or new instantiated object will scale with each new iteration. I'm trying to build a reverse fractal tree (the branches get bigger over time).
Just a warning - you might cringe at other parts of the code that probably could be written better.
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour
{
public Transform Cube;
public GameObject masterTree;
public int instanceCounter = 1;
public int numCubes = 30;
public float scalar = 1.4145f;
public float initialScale = 10f;
public float angle = 30f;
private Transform copy;
void Start()
{
}
private void Update()
{
if (instanceCounter <= numCubes)
{
if (instanceCounter == 1)
{
copy = Instantiate(Cube, new Vector3(0, 0, 0), Quaternion.identity);
copy.transform.localScale = new Vector3(1f, initialScale, 1f);
copy.name = "Copy" + instanceCounter;
copy.transform.parent = masterTree.transform;
instanceCounter++;
}
var copyParent = GameObject.Find("Copy" + (instanceCounter - 1));
Vector3 copyParentSize = copyParent.GetComponent<Renderer>().bounds.size;
Debug.Log("copyParentSizeY = " + copyParentSize.y);
copy = Instantiate(Cube, new Vector3(0, 0, 0), Quaternion.identity);
copy.transform.localScale = new Vector3(1f, initialScale, 1f);
initialScale = initialScale * scalar;
copy.name = "Copy" + instanceCounter;
//copy.transform.rotation *= Quaternion.Euler(angle, angle, 0);
copy.transform.parent = copyParent.transform;
instanceCounter++;
}
}
}
If I understands your needs right...
There is a Instantiate method with Parent parameter, so you can create new GO as a child of your parent
public static Object Instantiate(Object original, Transform parent);
If you want to have some kind of pivot, you can create empty GameObject in your target parent, shift it to right position and instantiate your GO as a child of that empty GO.
Also you can wrap your Cube in another empty GameObject (ex: position 0,0,0), so you can shift Cube up (0,5,0), but origin of whole GameObject remains same (0,0,0).
I understand Vector3 better now. It looks like I can add together different GameObject positions together in the same Vector3:
newPosition = new Vector3(0, copyParentSize.y + copyParentPosition.y + spacer, 0);
Now I move objects around based on the location of other objects.
Thank you for your help!
I've got two objects in World Space.
One is a cube has no parent.
The second is a triangle and it has a parent.
I change the cube's position and rotation.
And now I need to put cube at its first position but move triangle in the parent (local) in such position to fit the same position as if the cube won't be placed at the previous position.
Somwhere store the original position and rotation of cube
Vector3 origPosition = cube.transform.position;
Quaternion origRotation = cube.transform.rotation;
Get the offset values between cube and triangle
Vector3 posOffset = triangle.transform.position - cube.transform.position;
Quaternion rotOffset = Quaternion.Inverse(cube.transform.rotation) * triangle.transform.rotation;
(Re)Set cube and Triangle into place
cube.transform.position = origPosition;
cube.transform.rotation = origRotation;
triangle.transform.position = origPosition + posOffset;
triangle.transform.rotation = origRotation * rotOffset;
Example
public class CubeMover : MonoBehaviour
{
public Transform cube;
public Transform triangle;
private Vector3 origPosition;
private Quaternion origRotation;
// Start is called before the first frame update
private void Start()
{
origPosition = cube.transform.position;
origRotation = cube.transform.rotation;
}
[ContextMenu("Test")]
public void ResetCube()
{
Vector3 posOffset = triangle.transform.position - cube.transform.position;
Quaternion rotOffset = Quaternion.Inverse(cube.transform.rotation) * triangle.transform.rotation;
cube.transform.position = origPosition;
cube.transform.rotation = origRotation;
triangle.transform.position = origPosition + posOffset;
triangle.transform.rotation = origRotation * rotOffset;
}
}
(Had no Triangle so I used the Cylinder ... I hope that's fine for you ^^)
I want to get a vertex and store it in a variable. Then I want to keep updating the position, rotation and translation. Ik know I can just add up the position of the object to the position of the vertex to get the new vertex position. But I don't know how to do this with the scale and rotation. Does anyone know?
using UnityEngine;
using System.Collections;
public class FollowVertex : MonoBehaviour {
public Transform testSphere;
public Transform indicator;
void Update()
{
indicator.position = GetVertex();
}
Vector3 GetVertex()
{
Mesh TMesh = testSphere.GetComponent<MeshFilter>().mesh;
Vector3 Tvertex = TMesh.vertices[1];
return Tvertex + testSphere.position ; // + the rotation, scale and translation of "testSphere"
}
}
Thanks in advance!
Edit 1:
I think I can modify this RotateAround funtion for the rotation.
function RotateVertexAround(center: Vector3, axis: Vector3, angle: float){
var pos: Vector3 = transform.position;
var rot: Quaternion = Quaternion.AngleAxis(angle, axis); // get the desired rotation
var dir: Vector3 = pos - center; // find current direction relative to center
dir = rot * dir; // rotate the direction
transform.position = center + dir; // define new position
// rotate object to keep looking at the center:
var myRot: Quaternion = transform.rotation;
transform.rotation *= Quaternion.Inverse(myRot) * rot * myRot;
}
Now the Only thing I need to know is how to do the scale, can I do that with a matrix?
You should be able to do that via the TransformPoint() method, which transforms a vertex from local to world space.
i.e.
Vector3 GetVertex()
{
Mesh TMesh = testSphere.GetComponent<MeshFilter>().mesh;
Vector3 Tvertex = TMesh.vertices[1];
return testSphere.TransformPoint(Tvertex);
}
With the Unity 5.1 and Oculus SDK 0.6 and the new built-in Virtual Reality Supported check-box, I cannot seem to change the camera's position in VR.
Of course I can change the camera's Tranform Position component numbers (i.e. x and y and z) but when I run in play mode, it becomes obvious that Oculus' seeing camera has not moved.
How do I move/change the Oculus' perspective, to adjust how the Unity world is seen through the 2 lenses?
Add a parent GameObject to the camera and move that one, don't modify the VR camera.
Expanding on Krzysztof's answer, I usually use something like this (I attach this component to the VR Camera, give it an empty game object as a parent, then set that game object to be the vrCameraHolder):
[RequireComponent(typeof(Camera))]
public class VRPlayer : MonoBehaviour
{
// set to be the parent of this object
public Transform vrCameraHolder;
// simple utility functions
public static Quaternion LocalToWorldRotation(Transform t, Quaternion localRot)
{
return t.rotation * localRot;
}
public static Quaternion WorldToLocalRotation(Transform t, Quaternion rot)
{
return Quaternion.Inverse(t.rotation) * rot;
}
// Gets the body part position in world space
public Vector3 GetBodyPartPosition(UnityEngine.XR.XRNode bodyPart)
{
return vrCameraHolder.localToWorldMatrix.MultiplyPoint(UnityEngine.XR.InputTracking.GetLocalPosition(bodyPart));
}
// Gets the body part rotation in world space
public Quaternion GetBodyPartRotation(UnityEngine.XR.XRNode bodyPart)
{
return LocalToWorldRotation(vrCameraHolder, UnityEngine.XR.InputTracking.GetLocalRotation(bodyPart));
}
// moves entire player body so that this becomes true
public void SetBodyPartPosition(UnityEngine.XR.XRNode bodyPart, Vector3 pos)
{
Vector3 bodyPartPos = vrCameraHolder.localToWorldMatrix.MultiplyPoint(UnityEngine.XR.InputTracking.GetLocalPosition(bodyPart));
vrCameraHolder.position += pos - bodyPartPos;
}
// if you want to change rotation you should probably only use this to keep the headset tilted the same way (y axis is up, so 0 is forward along x axis, 180 is backwards along x axis, 90 and 270 are along z axis, etc.)
public void SetBodyPartYRotation(UnityEngine.XR.XRNode bodyPart, float yRot)
{
Vector3 curRot = GetBodyPartRotation(bodyPart).eulerAngles;
SetBodyPartRotation(bodyPart, new Vector3(curRot.x, yRot, curRot.z));
}
// rotates entire player body so that this becomes true (but doesn't change position)
public void SetBodyPartRotation(UnityEngine.XR.XRNode bodyPart, Quaternion rot)
{
Quaternion curWorldRot = GetBodyPartRotation(bodyPart);
Quaternion cancelOutWorldRot = Quaternion.Inverse(curWorldRot);
float angle1;
Vector3 axis1;
cancelOutWorldRot.ToAngleAxis(out angle1, out axis1);
Vector3 bodyPartPosition = GetBodyPartPosition(bodyPart);
vrCameraHolder.RotateAround(bodyPartPosition, axis1, angle1);
// now player rotation = not rotated
float angle2;
Vector3 axis2;
rot.ToAngleAxis(out angle2, out axis2);
vrCameraHolder.RotateAround(bodyPartPosition, axis2, angle2);
}
// rotates entire player body so that this becomes true (but doesn't change position)
public void SetBodyPartRotation(UnityEngine.XR.XRNode bodyPart, Vector3 eulerAngles)
{
SetBodyPartRotation(bodyPart, Quaternion.Euler(eulerAngles));
}
}
i want a gameObject to add instanciatetd objects prom a prefab as his chiled.
so i wrote this code:
public class TgtGenerator : MonoBehaviour {
public SpriteRenderer spriteTgt;
public Sprite[] targets;public SpriteRenderer spriteTgt;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
generateRandTgt ();
}
void generateRandTgt(){
SpriteRenderer tgt = GameObject.Instantiate (spriteTgt, getRandPosition (), Quaternion.identity)as SpriteRenderer;
tgt.sprite = randSprite ();
}
Sprite randSprite(){
var length = targets.Length-1;
Debug.Log (Random.Range(0,length));
return targets[Random.Range(0,length)];
}
Vector3 getRandPosition(){
float xScale = gameObject.transform.localScale.x / 2;
float yScale = gameObject.transform.localScale.y / 2;
float x = Random.Range(-xScale,xScale);
float y = Random.Range(-yScale,yScale);
float z = 0;
return new Vector3 (x, y, z);
}
the instantiated object is getting his size from the prefab like i wanted to.
but the problem is it instanciated at the root node of the hirarchy so they are not placed inside of the gameobject coordinate like i wanted to.
when i am adding this line of code just after the instaciate line :
tgt.transform.parent = gameObject.transform;
it generated as a child like i wanted to but it gets huge and upside down.
how should i manage this?
Try Using This To Force Child To Have It's Own Scale After Being a Child:
var localScale = child.transform.localScale; // store original localScale value
child.transform.parent = parent;
child.transform.localScale = localScale; // force set