- Home /
Rotating a parent object so that child is at the closest possible position to another object
Hi there,
I have spent the last 4 hours trying to resolve this however I cannot seem to be able to get this to work, so I thought that I would raise this on the forum.
Basically I am working on a random dungeon generator. in this generator we have prefab rooms blocks. each prefab room block have an empty gameobject which is a child of the room prefab, this empty node is used to denominate the fact that a door will be there. (see screen shot a)
Now I am generating rooms positions from -200 to 200 in the x and z axis and am trying to force the rooms prefabs to assign themselves a rotation so that the child object will be at the shortest possible distance to the nominated centre (in this case (0,0,0). (i.e the child points towards the centre)
(note that the objects are prefabs and have not been initializes at this point)
Now I was experimenting with the concept that since I know the absolute positions of the three vectors then I can derive the relative angle between the parent -> child and the parent -> centre. (See screenshot B green label)
However it seems to produce 3 rotation outcome. 1) it produces the correct angle and orientation 2) it produces the 180 - angle to what I need. 3) or it produces a negative angle to what I need.
So this shows that the formula is working. However I am starting to think that there might be an alot easier method for this that I have missed.
If anyone's got any advice or suggestion to resolve this then that would be most appreciated!
****************Code***************
note:
position = the parent's real world position
selectedExit.transform.position = is the local position of the exit point that needs to be orientated.
new vector3(0,0,0) is the world centre that I want all pieces to orientate towards.
var rotate = AngleSigned(position + selectedExit.transform.position, new Vector3(0, 0, 0), position);
var rotation = new Vector3(0,rotate,0);
var room = new InstantiatedRoom(RandomRoomPrefab, position, rotation);
//go and create a new object we will use it to decide if we are really going to create it as a gameobject later on.
public static float AngleSigned(Vector3 v1, Vector3 v2, Vector3 n)
{
double a;
double b;
double c;
c= (v1 - v2).magnitude;
b = (v2 - n).magnitude;
a = (v1 - n).magnitude;
double temp1;
double top = (((a*a)+ (b*b))-(c*c));
double bottom = (2 * a*b );
double temp = (Math.Acos(top/bottom))*Mathf.Rad2Deg;
return (float)temp;
}
reky, post comments AS CO$$anonymous$$$$anonymous$$ENTS, not answers
Answer by robertbu · Apr 22, 2014 at 03:58 AM
Let me start by saying, that IMHO, the best solution would be to put the door in the front (positive 'z' side) of every prefab. Then you can just do:
transform.LookAt(Vector3.zero);
And then everything is done. But that may not work for your situation, so my next suggestion is to do the rotation this way:
Vector3 direction = selectedExit.transform.position - position;
rotation = Quaternion.FromToRotation(direction, -position);
Note the use of '-position' in the FromToRotation() works because you are using Vector3.zero as the target position. Also I'm assuming the prefab has (0,0,0) rotation. If the prefab has a rotation, then you will need to do:
rotation = Quaternion.FromToRotation(direction, -position) * prefabRotation;
Hi robertbu, thanks for that, all my exits are due north (in Z) so that solution would work with the prefabs that I have at present however it doesn't give me the flexiblity to have multiple exits pieces. (where the exits are chosen at random)
Now, just tried your solution and it is basically producing the same 3 types (negative degree, degree, and 180-degree)
So that suggest that the formula works however it is not respecting clockwise and anti clockwise angle movement.
Can you think of a simple way of deriving which of the three types of output to use? (the only way that I can think of is to rotate the parent by each of the three in turn and then see which one yields the shortest distance) but that seems excessive!
You comments indicate that I'm missing some fundamental information about your problem. If you have multiple child objects, the one that will be closest to the origin when rotated will be the one with the greatest distance from the pivot of the prefab to the child object. As for your "3 types (negative degree, degree, and 180-degree)" I'm not sure where you are getting this measurement. If this is from eulerAngles, then there are multiple eulerAngle representations for any given physical rotation. But I get a sense that is not what you are talking about.
Since this appears to be 2D rotation around the 'Y' axis, you can treat this as a 2D problem and use $$anonymous$$athf.Atan2().
float angleToCenter = $$anonymous$$athf.Atan2(position.z, position.x);
Vector3 direction = selectedExit.transform.position - position;
float angleOfDoor = $$anonymous$$athf.Atan2(direction.z, direction.x);
Now you have two angles and do whatever calculations you want. You can use Quaternion.AngleAxis() to do a relative rotation between the two angles. And $$anonymous$$athf.DeltaAngle() will allow you to calculate the shortest angle between the two angles.
Hi Roberto,
Sorry I should have been more clear, basically my idea is that each dungeon room is a prefab and each room can have more than one exit point.
Now I wanted the prefab to be automatically assigned a position in the dungeon and then one of the exit points to be no$$anonymous$$ated and for that exit point to be alligned towards the origin of the dungeon.
Fortunately I took your formula's and made a few tweaks (so that the parent position is set to the zero origin and offset the orientated position to negative parent position.
Then ran the code and it has worked.
CODE float angleToCenter = $$anonymous$$athf.Rad2Deg $$anonymous$$athf.Atan2(orientateTo.z, orientateTo.x); float angleOfDoor = $$anonymous$$athf.Rad2Deg $$anonymous$$athf.Atan2(exit.z, exit.x); float correctedAngle = angleOfDoor-angleToCenter ;
var quanternionOffset= Quaternion.Euler(new Vector3(0, correctedAngle, 0));
var room = new InstantiatedRoom(RandomRoomPrefab, position, new Vector3(0, correctedAngle, 0));
var go = Instantiate(room.RoomGameObject, new Vector3(x, 0, z), quanternionOffset) as GameObject;
room.RoomGameObject = go; go.transform.parent = transform;
dungeonRoomList.Add(room);
As you can see from screenshot D https://www.dropbox.com/s/ewxi1n01xdwxp9q/d.png
All the rooms are now orientating themselves towards my no$$anonymous$$ated point (in this case the origins)
and for screenshot E, as you can see I have cloned one of my exit points and set it as a default (so that my random exit node will priorities the selection of it over any other node and it has correctly orientated that exit point so that it points to the centre. (it does look strange as I purposesly moved it's translation to a place off it's own prefab tile.
https://www.dropbox.com/s/821uj4y2e5nwldf/e.png
Now I will write abit of code in order to round the angle to the nearest 90 degree and construct a collision detection mechcanism and I am all set to go!
Thanks for your help on this one!
Rich