I am having a weird issue with Quaternion.FromToRotation

So I am starting on a new project where I am building a random dungeon out of room prefabs. The first thing that happens is I instantiate a game object called “Start Room”. This room has 4 exits as empty child game objects set in the locations that represent exits out of the room. Once the "Start Room"has been placed at 0,0,0 it spawns a new clone of a room prefab for each of its exits.

The new prefab that the "Start Room"has called has a single entrance and can have up to 3 exits as empty child game objects. The new prefab is then positioned at the "Start Room"exit and then Quaternion.FromToRotation is used to rotate the entrance of the newly spawned prefab to face the "Start Room"exit. Each Entrance and Exit child game object face to where their z axis is facing outward from the prefab so the entrance rotates on the y axis so that its z axis faces the z axis of the exit(if that makes sense)

This works fine for the most part but every so often one or two of the newly spawned prefabs are positioned correctly but the rotation is flipped 180 degrees on the z axis and I am not really sure why.

This is the script used to call the “Start Room” as well as position and rotate the newly spawned rooms:

 using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class RoomPooler : MonoBehaviour
{
    public GameObject startRoomPrefab;
    public GameObject roomPrefab;
    public static RoomPooler pooler;

    void Awake()
    {
        pooler = this;
    }

    void Start()
    {
        SpawnStartRoom();
    }

    void SpawnStartRoom()
    {
        GameObject startRoom = Instantiate(startRoomPrefab, new Vector3(0, 0, 0), Quaternion.identity) as GameObject;
        startRoom.GetComponent<StartRoomScript>().AssignExits();
    }

    public void SpawnAdditionalRooms(GameObject exit)
    {
        GameObject newRoom = Instantiate(roomPrefab);
        newRoom.GetComponent<Room>().AssignEntrance();
        AlignEntranceToExit(newRoom, newRoom.GetComponent<Room>().entrance, exit.transform.parent.gameObject, exit);
    }

    void AlignEntranceToExit(GameObject newRoom, GameObject entrance, GameObject exitParent, GameObject exitChild)
    {
        Vector3 v1 = newRoom.transform.position - entrance.transform.position;
        Vector3 v2 = exitChild.transform.position - exitParent.transform.position;
        newRoom.transform.position = exitChild.transform.position + v2.normalized * v1.magnitude;
        newRoom.transform.rotation = Quaternion.FromToRotation(v1, v2) * newRoom.transform.rotation;
    }

The “Start Room” uses this script to call a new room for each exit:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class StartRoomScript : MonoBehaviour
{
    public List<GameObject> doors;

    public void AssignExits()
    {
        for (int i = 0; i < doors.Count; i++)
        {
            RoomPooler.pooler.SpawnAdditionalRooms(doors*);*

}
}
}
The Rooms use this script to assign a random entrance from its list of doors. For now it also attaches another script that just draws a line in OnDrawGizmos for each of the exits x,y and z. Also until I can figure out the issue the Room script does not call for an addition room.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Room : MonoBehaviour
{
public List doors;
public GameObject entrance;

public void AssignEntrance()
{

int pickRandomDoorForEntrance = Random.Range(0, doors.Count);
entrance = doors[pickRandomDoorForEntrance];
entrance.name = “Entrance”;
doors.Remove(entrance);
for (int i = 0; i < doors.Count; i++)
{
doors*.gameObject.AddComponent();*
doors*.name = "Exit # " + i;*
}

}

}
I have attached a screen capture of what happens. The first is when everything aligns correctly but the second is where the rooms are misaligned because it has been rotated on the z axis as well as the y axis.
I could understand if it happens all the time but this does not. Sometimes it works correctly without an issue but sometimes it does not. These are also the only scripts in the project so there is no other scripting interfering.
Anyone have any thoughts?
[86504-correct-rotation.png|86504]
[86505-getting-there.png*|86505]*
*
*

FromToRotation is designed to create a rotation which will transform a vector into another vector, however vectors just represent “directions”, not full orientations. As a result, there’s some ambiguity about how to do this. If you’re facing forward, and someone tells you to turn around, you could turn either left or right, and both would be correct. Your “heading” would be the same either way, but the rotations you apply are totally different. The same is true in 3 dimensions. Your room could flip over the X axis completely, and the FromToRotation is just as valid as if it spun in place on the Y axis.

You could replace the call to FromToRotation with a call to Quaternion.LookRotation, which takes an up vector as a parameter, or you could apply a second rotation to correct for the potential 180° rotation…

newRoom.transform.rotation *= Quaternion.FromToRotation(v1,v2);
newRoom.transform.rotation *= Quaternion.FromToRotation(newRoom.transform.up, exitChild.transform.up);

So after playing around with this project for a few hours this morning I figured out a solution to correct the issue that I was having by using transform.RotateAround as the fix.

Here is the original script :

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 public class RoomPooler : MonoBehaviour
 {
     public GameObject startRoomPrefab;
     public GameObject roomPrefab;
     public static RoomPooler pooler;
 
     void Awake()
     {
         pooler = this;
     }
 
     void Start()
     {
         SpawnStartRoom();
     }
 
     void SpawnStartRoom()
     {
         GameObject startRoom = Instantiate(startRoomPrefab, new Vector3(0, 0, 0), Quaternion.identity) as GameObject;
         startRoom.GetComponent<StartRoomScript>().AssignExits();
     }
 
     public void SpawnAdditionalRooms(GameObject exit)
     {
         GameObject newRoom = Instantiate(roomPrefab);
         newRoom.GetComponent<Room>().AssignEntrance();
         AlignEntranceToExit(newRoom, newRoom.GetComponent<Room>().entrance, exit.transform.parent.gameObject, exit);
     }
 
     void AlignEntranceToExit(GameObject newRoom, GameObject entrance, GameObject exitParent, GameObject exitChild)
     {
         Vector3 v1 = newRoom.transform.position - entrance.transform.position;
         Vector3 v2 = exitChild.transform.position - exitParent.transform.position;
         newRoom.transform.position = exitChild.transform.position + v2.normalized * v1.magnitude;
         newRoom.transform.rotation = Quaternion.FromToRotation(v1, v2) * newRoom.transform.rotation;
     }

This is the new script with the changes I made. Note importing System so I could have access to Single:

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
public class RoomPooler : MonoBehaviour
{
    public GameObject startRoomPrefab;
    public GameObject roomPrefab;
    public static RoomPooler pooler;

    void Awake()
    {
        pooler = this;
    }

    void Start()
    {
        SpawnStartRoom();
    }

    void SpawnStartRoom()
    {
        GameObject startRoom = Instantiate(startRoomPrefab) as GameObject;
        startRoom.GetComponent<StartRoomScript>().AssignExits();
    }

    public void SpawnAdditionalRooms(GameObject exit)
    {
        GameObject newRoom = Instantiate(roomPrefab);
        newRoom.GetComponent<Room>().AssignEntrance();
        GameObject entranceGO = newRoom.GetComponent<Room>().entrance;
        Vector3 VectorToMatch = -exit.transform.forward;
        Single correctRotation = MatchRotation(VectorToMatch) - MatchRotation(entranceGO.transform.position);
        newRoom.transform.RotateAround(entranceGO.transform.position, Vector3.up, correctRotation);
        Vector3 correctPosition = exit.transform.position - entranceGO.transform.position;
        newRoom.transform.position += correctPosition;
    }
    

    private static float MatchRotation(Vector3 vector)
    {
        return Vector3.Angle(Vector3.forward, vector) * Mathf.Sign(vector.x);
    }
}

@andrewgotow I want to think you for your assistance with this. It was because of your help I was pointed in the right direction.