0
\$\begingroup\$

I'm using the following script to move a spray bottle around a head.

It should looke like in a barber shop.

The code works fine: I have assigned the script to the spray bottle, and I have set the head as the target.

One thing that is wrong is that the spray bottle needs to have a -90 yaw rotation in order to face the head.

I have tried the wildest things to include these -90 degrees, all produced weird results.

Could anybody state how to include this -90 Y rotation into the script?

Thank you very much!

using UnityEngine;
using System.Collections;

[AddComponentMenu("Camera-Control/Mouse Orbit with zoom")]
public class MouseOrbitImproved : MonoBehaviour {

public Transform target;
public float distance = 5.0f;
public float xSpeed = 120.0f;
public float ySpeed = 120.0f;

public float yMinLimit = -20f;
public float yMaxLimit = 80f;

public float distanceMin = .5f;
public float distanceMax = 15f;

private Rigidbody rigidbody;

float x = 0.0f;
float y = 0.0f;

// Use this for initialization
void Start () 
{
    Vector3 angles = transform.eulerAngles;
    x = angles.y;
    y = angles.x;

    rigidbody = GetComponent<Rigidbody>();

    // Make the rigid body not change rotation
    if (rigidbody != null)
    {
        rigidbody.freezeRotation = true;
    }
}

void LateUpdate () 
{
    if (target) 
    {
        x += Input.GetAxis("Mouse X") * xSpeed * distance * 0.02f;
        y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;

        y = ClampAngle(y, yMinLimit, yMaxLimit);

        Quaternion rotation = Quaternion.Euler(y, x, 0);

        distance = Mathf.Clamp(distance - Input.GetAxis("Mouse ScrollWheel")*5, distanceMin, distanceMax);

        RaycastHit hit;
        if (Physics.Linecast (target.position, transform.position, out hit)) 
        {
            distance -=  hit.distance;
        }
        Vector3 negDistance = new Vector3(0.0f, 0.0f, -distance);
        Vector3 position = rotation * negDistance + target.position;

        transform.rotation = rotation;
        transform.position = position;
    }
}

public static float ClampAngle(float angle, float min, float max)
{
    if (angle < -360F)
        angle += 360F;
    if (angle > 360F)
        angle -= 360F;
    return Mathf.Clamp(angle, min, max);
}
}
\$\endgroup\$
2
\$\begingroup\$

Here are four ways to solve this problem...

First:

In the general case, the easiest way to adjust the rotation of something in Unity is to wrap it.

  1. Create a new empty GameObject called "parent"

  2. Take the visual object you want to rotate and make it a child of this parent.

  3. Take whatever animation/script/etc had been manipulating the visual object directly, and make it act on the parent object instead.

Now your visual object's orientation (and translation offset & scale) is decoupled from whatever the script is assigning, so you can rotate it relative to its parent to achieve whatever outcome you want. You can even do this in the scene view with the rotation gizmos, to visually match the look you want, if you have trouble visualizing it from the raw code/numbers.


Second, specifically when your script is assigning:

transform.rotation = rotation;

You can add an extra "shim" rotation to bias the result by any amount you like. eg...

transform.rotation = rotation * Quaternion.Euler(0, -90, 0);

This applies a -90 degree Y rotation about the local yaw axis before whatever the rotation want to do to the object. To apply this after / around the world Y axis, just flip the order of the multiplication.


Third, because you're calculating your rotation via:

Quaternion rotation = Quaternion.Euler(y, x, 0);

You can just offset this rotation directly:

Quaternion rotation = Quaternion.Euler(y, x - 90, 0);

Note however that since you're calculating position based on rotation, this will rotate your position by 90 degrees too - so you might want to adjust this by changing how you build Vector3 negDistance = new Vector3(0.0f, 0.0f, -distance) or use one of the solutions above that leaves position unaffected.


Fourth, your script as-written already has a solution to this problem built-in:

void Start () 
{
    Vector3 angles = transform.eulerAngles;
    x = angles.y;
    y = angles.x;
    //...

This says "whichever way we're oriented when the scene loads / when I spawn into the scene, use that orientation as the starting/neutral point for any future motion".

So, just turn you object 90 degrees in your scene before you enter play mode (or rotate it 90 degrees when you spawn it with Instantate()) and the script will pick up that starting rotation automatically.

Similar to point 3, this will mean tweaking the way you assign the position so it acts at 90 degrees to the rotation.

\$\endgroup\$
  • \$\begingroup\$ Your answers are always such amazing lessons. Thank you so much!! \$\endgroup\$ – tmighty May 23 at 2:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.