[solved] Quaternion Math : Rotate Sphere so that it's (clicked) child faces the fixed main cam ?

Hi all,

could someone please give me a hand with quaternion math please ? Since I have 3 objects particpating, I just don’t get the math behind it.

Here is the thing :

  1. I’m building a menu, so my Camera.main is FIXED

  2. I have a parentObject with SphereCollider and several childs. This parentobject can be rotated around with OnMouseDrag(), and by doing so, the childs connected to this parent object follow it’s rotation. (it’s a globe, and on the globe 7 Markers) Parent Object has the script “GlobeController”

  3. The childs have childs themselve (if it matters) and they have a box collider to recognize a mouse click on them. Each Child has a script “MarkerController”… All childs are made the same from a prefab, they just have different and fixed positions on the sphere.

So the hierarchy looks like this:

Parent (with spherecollider and SphereController-Script)
— Globe model
— Marker (with controller script, has BoxCollider)
--------child of marker
--------child of marker

Now, technically everything is working except the math. When I click the marker, the MarkerControllerscript recognizes which marker I clicked, and passes that info over into the SphereControllerScript).

It lands there fine, and the method to rotate the sphere get’s executed and the sphere rotates, just not correctly.
This is what I tried (please don’t hurt me, I’m a coder beginner :wink: Also, I need to apply a speed towards the rotation, it shouldn’t happen instantly.

public void RotateSphereToSector(GameObject sectorClicked)
{
Quaternion currentGlobeRotation = transform.rotation;
Quaternion selectedSectorRotation = sectorClicked.transform.rotation;

transform.rotation = selectedSectorRotation * Camera.main.transform.rotation;
}

BTW, I later will give in the quaternion directly into the method, not the whole gameobject… This was just for testing. Most likely I will have to do something with Quaternion.Euler(x,y,z), but I just don’t know the synthax for what is required here.

Big thank you in advance for helping. :slight_smile:

Construct a forward vector between the child object and the camera and feed that to Quaternion.LookRotation().

Thank you… After playing around, and with the help of some other tipps I managed to do something that works for me.

Feel free to post your solution for future readers. :slight_smile:

Yeah, I also thought so :slight_smile: So, here it is, but please note that I’m still a beginner and I can’t gurantee that this is code is bulletproof, but it seem to work fine :slight_smile:

Script on the parent (needs sphere collider, set to trigger) to work.

public class MarsGlobe_Rotator : MonoBehaviour {

    public float rotSpeed = 160;   //dragging speed
    public float autoRotSpeed = 3;   //auto move spped

    private bool moveGlobe = false;
    private Quaternion rotGlobe;

    private void Update()
    {
    Quaternion currentGlobeRotation = transform.rotation;
 
    //with the moveGlobe variable the auto movement get enabled and disabled
    //also if another movement is initiated (dragging or clicking a different marker) the variable get's disabled again.
    if (moveGlobe && (currentGlobeRotation != rotGlobe))
    {
    transform.rotation = Quaternion.Lerp(transform.rotation, rotGlobe, autoRotSpeed*Time.deltaTime);  //moves the globe automatically
    }
    else if (transform.rotation == rotGlobe)
    {
    moveGlobe = false;
    }
    else
    {
    //nothing to expect here
    }

    }
    //**********end of Update()***************


    void OnMouseDrag()   //player dragging the globe
    {
    moveGlobe = false;
    float rotX = Input.GetAxis("Mouse X") * rotSpeed * Mathf.Deg2Rad;
    float rotY = Input.GetAxis("Mouse Y") * rotSpeed * Mathf.Deg2Rad;

    transform.Rotate(Vector3.up, -rotX, Space.World);
    transform.Rotate(Vector3.right, rotY, Space.World);
    }


    public void CalcNewSphereQuaternion(GameObject sectorClicked)  //calculating the quaternion for the auto movement
    {
    moveGlobe = false;

    Vector3 sphereSpacePoint = transform.InverseTransformPoint(sectorClicked.transform.position);
    Vector3 sphereSpacetoCamera = transform.InverseTransformDirection((Camera.main.transform.position - transform.position).normalized);
    rotGlobe = (transform.rotation * Quaternion.FromToRotation(sphereSpacePoint, sphereSpacetoCamera));

    moveGlobe = true;

    }

}

This is the second script (only a part of it) that is placed on one of the markers. Marker needs box collider set to trigger.

    private void OnMouseDown()
    {
    if (Input.GetMouseButtonDown(0) && !mapPointerHidden)
    {
    //Store the gameObject you clicked
    GameObject sectorClicked = gameObject;
    marsGlobe_Rotator.CalcNewSphereQuaternion(sectorClicked);   //call method in sphere script.

Note : I’m transfering an entire gameObject here to the method in the other script. I think transfering only a quaternion will be enough…

I hope this explanation can help someone looking for something similar.

Cheers
Michael

Cool. Glad it’s working for you.

Note that since you’re using information from the game object in the method you call, you’re actually better off with your current setup. Just the reference is copied, whereas if you sent a Quaternion, it’d be more bytes. :wink:

1 Like

Really ? That’s good to know… I thought in my version a complete object data (copy) would be carried over. Will keep that in mind for the future :slight_smile:

A reference variable is : 4 bytes (32-bit), 8 bytes (64-bit). :slight_smile:

A value type copied is the size of the entire value, so a Quaternion would be 4x4 (16) bytes.

That’s useful Info. Thank you for that :slight_smile: