Place object rotated relative to another object?
Hello, I am trying to make a procedural world generator but making the connections seems a bit difficult.
When connecting everything in a straight line everything works because that's easy, I use this:
to.transform.position = from.transform.position + from_offsets.position_offsets[taken_idx] - to_offsets.position_offsets[0];
to.transform.eulerAngles = from.transform.eulerAngles + from_offsets.rotation_offsets[taken_idx] - to_offsets.rotation_offsets[0];
But this doesn't account for the rotation, so something like this is impossible:
How can I place Object B relative to Object A when I have two offsets which both consist of a position + rotation offset like this:
public Vector3[] position_offsets;
public Vector3[] rotation_offsets;
You can see this in the screenshots.
So to sum up:
I have Object A with it's transform (Position, Rotation), connection point(Offset and Rotation), and Object B with it's connection point (Offset and Rotation) and I need to place B relative to A like in the screenshots. How to do that programatically (C#)?
My current code produces this (inclusive what I want):
And this is the full code if needed (probably not):
public class ConnectionOffsets : MonoBehaviour
{
// 0 shall be entry point (exit in case of 180 degree turn), 1 shall be forward / exit point (entry in case of 180 degree turn), 2 left, 3 right
public Vector3[] position_offsets;
public Vector3[] rotation_offsets;
}
bool ConnectFromFirstAvailable(GameObject from, GameObject to, int first_available = 0)
{
if(from == null || to == null)
{
return false;
}
ConnectionOffsets from_offsets = from.GetComponent<ConnectionOffsets>();
if(from_offsets == null)
{
return false;
}
int taken_idx = first_available;
for (int i = first_available; i < from_offsets.taken.Length; ++i)
{
if(!from_offsets.taken[i])
{
taken_idx = i;
break;
}
}
if (taken_idx < from_offsets.taken.Length && from_offsets.taken[taken_idx])
{
return false;
}
ConnectionOffsets to_offsets = to.GetComponent<ConnectionOffsets>();
if(to_offsets == null || to_offsets.taken[0] != null)
{
return false;
}
to_offsets.taken[0] = from;
from_offsets.taken[taken_idx] = to;
Vector3 total_relative_pos = from_offsets.position_offsets[taken_idx] - to_offsets.position_offsets[0];
Vector3 total_relative_rot = from_offsets.rotation_offsets[taken_idx] - to_offsets.rotation_offsets[0];
to.transform.position = from.transform.position + total_relative_pos;
to.transform.eulerAngles = from.transform.eulerAngles + total_relative_rot;
/*to.transform.Translate(total_relative_pos, from.transform);*/
return true;
}
Edit: I have made progress that allows me to place the objects I need under a certain rotation:
private Vector2 RotatePointAroundPoint(float center_x, float center_y, float point_x, float point_y, float angle)
{
return RotatePointAroundPoint(new Vector2(center_x, center_y), new Vector2(point_x, point_y), angle);
}
private Vector2 RotatePointAroundPoint(Vector2 center, Vector2 point, float angle)
{
float x1 = point.x - center.x;
float y1 = point.y - center.y;
angle *= Mathf.Deg2Rad;
float x2 = x1 * Mathf.Cos(angle) - y1 * Mathf.Sin(angle);
float y2 = x1 * Mathf.Sin(angle) + y1 * Mathf.Cos(angle);
point.x = x2 + center.x;
point.y = y2 + center.y;
return point;
}
private Vector2 RotatePointAroundPoint(float point_x, float point_y, float angle)
{
return RotatePointAroundPoint(new Vector2(point_x, point_y), angle);
}
private Vector2 RotatePointAroundPoint(Vector2 point, float angle)
{
float x1 = point.x;
float y1 = point.y;
angle *= Mathf.Deg2Rad;
float x2 = x1 * Mathf.Cos(angle) - y1 * Mathf.Sin(angle);
float y2 = x1 * Mathf.Sin(angle) + y1 * Mathf.Cos(angle);
point.x = x2;
point.y = y2;
return point;
}
bool ConnectFromFirstAvailable(GameObject from, GameObject to, int first_available = 0)
{
if(from == null || to == null)
{
return false;
}
ConnectionOffsets from_offsets = from.GetComponent<ConnectionOffsets>();
if(from_offsets == null)
{
return false;
}
int taken_idx = first_available;
for (int i = first_available; i < from_offsets.taken.Length; ++i)
{
if(!from_offsets.taken[i])
{
taken_idx = i;
break;
}
}
if (taken_idx < from_offsets.taken.Length && from_offsets.taken[taken_idx])
{
return false;
}
ConnectionOffsets to_offsets = to.GetComponent<ConnectionOffsets>();
if(to_offsets == null || to_offsets.taken[0] != null)
{
return false;
}
to_offsets.taken[0] = from;
from_offsets.taken[taken_idx] = to;
Vector3 to_position_offsets = to_offsets.position_offsets[0];
Vector3 to_rotation_offsets = to_offsets.rotation_offsets[0];
Vector3 from_position_offsets = from_offsets.position_offsets[taken_idx];
Vector3 from_rotation_offsets = from_offsets.rotation_offsets[taken_idx];
Vector2 correction;
// correct position
correction = RotatePointAroundPoint(to_position_offsets.z, to_position_offsets.y, from_rotation_offsets.x);
to_position_offsets.z = correction.x;
to_position_offsets.y = correction.y;
correction = RotatePointAroundPoint(to_position_offsets.z, to_position_offsets.x, from_rotation_offsets.y);
to_position_offsets.z = correction.x;
to_position_offsets.x = correction.y;
correction = RotatePointAroundPoint(to_position_offsets.x, to_position_offsets.y, from_rotation_offsets.z);
to_position_offsets.x = correction.x;
to_position_offsets.y = correction.y;
// correct angles
correction = RotatePointAroundPoint(from.transform.eulerAngles.z, from.transform.eulerAngles.y, to_rotation_offsets.x);
to_rotation_offsets.z = correction.x;
to_rotation_offsets.y = correction.y;
correction = RotatePointAroundPoint(from.transform.eulerAngles.z, from.transform.eulerAngles.x, to_rotation_offsets.y);
to_rotation_offsets.z = correction.x;
to_rotation_offsets.x = correction.y;
correction = RotatePointAroundPoint(from.transform.eulerAngles.x, from.transform.eulerAngles.y, to_rotation_offsets.z);
to_rotation_offsets.x = correction.x;
to_rotation_offsets.y = correction.y;
// apply transforms
Vector3 total_relative_pos = from_position_offsets - to_position_offsets;
Vector3 total_relative_rot = from.transform.eulerAngles; // + from_rotation_offsets - to_rotation_offsets;
to.transform.rotation = Quaternion.Euler(to_rotation_offsets);
to.transform.Rotate(Vector3.right, from_rotation_offsets.z);
to.transform.Rotate(Vector3.up, from_rotation_offsets.y);
to.transform.Rotate(Vector3.forward, from_rotation_offsets.x);
to.transform.position = from.transform.TransformPoint(total_relative_pos);
return true;
}
But this only works for either one nonzero x y or z rotation, if multiple rotations have rotations then it doesn't work
Edit2:
Alright the solution seems to be:
private Vector2 RotatePointAroundPoint(float point_x, float point_y, float angle)
{
return RotatePointAroundPoint(new Vector2(point_x, point_y), angle);
}
private Vector2 RotatePointAroundPoint(Vector2 point, float angle)
{
float x1 = point.x;
float y1 = point.y;
angle *= Mathf.Deg2Rad;
float x2 = x1 * Mathf.Cos(angle) - y1 * Mathf.Sin(angle);
float y2 = x1 * Mathf.Sin(angle) + y1 * Mathf.Cos(angle);
point.x = x2;
point.y = y2;
return point;
}
bool ConnectFromFirstAvailable(GameObject from, GameObject to, int first_available = 0)
{
if(from == null || to == null)
{
return false;
}
ConnectionOffsets from_offsets = from.GetComponent<ConnectionOffsets>();
if(from_offsets == null)
{
return false;
}
int taken_idx = first_available;
for (int i = first_available; i < from_offsets.taken.Length; ++i)
{
if(!from_offsets.taken[i])
{
taken_idx = i;
break;
}
}
if (taken_idx < from_offsets.taken.Length && from_offsets.taken[taken_idx])
{
return false;
}
ConnectionOffsets to_offsets = to.GetComponent<ConnectionOffsets>();
if(to_offsets == null || to_offsets.taken[0] != null)
{
return false;
}
to_offsets.taken[0] = from;
from_offsets.taken[taken_idx] = to;
Vector3 to_position_offsets = to_offsets.position_offsets[0];
Vector3 to_rotation_offsets = to_offsets.rotation_offsets[0];
Vector3 from_position_offsets = from_offsets.position_offsets[taken_idx];
Vector3 from_rotation_offsets = from_offsets.rotation_offsets[taken_idx];
Vector2 correction;
// correct position
correction = RotatePointAroundPoint(to_position_offsets.z, to_position_offsets.y, from_rotation_offsets.x);
to_position_offsets.z = correction.x;
to_position_offsets.y = correction.y;
correction = RotatePointAroundPoint(to_position_offsets.z, to_position_offsets.x, from_rotation_offsets.y);
to_position_offsets.z = correction.x;
to_position_offsets.x = correction.y;
correction = RotatePointAroundPoint(to_position_offsets.x, to_position_offsets.y, from_rotation_offsets.z);
to_position_offsets.x = correction.x;
to_position_offsets.y = correction.y;
// apply transforms (and correct rotation)
to.transform.rotation = Quaternion.Euler(from.transform.eulerAngles);
to.transform.Rotate(from_rotation_offsets, Space.Self);
to.transform.Rotate(to_rotation_offsets, Space.Self);
to.transform.position = from.transform.TransformPoint(from_position_offsets - to_position_offsets);
return true;
}