0
\$\begingroup\$

I have a camera that rotates around an object with "Look At" .

I want the object to rotate to face the direction the camera is pointing (Camera.main.transform.forward) using AddTorque, but I can't understand how.

I tried to create a rotation in which the more the object's forward vector rotates towards the camera, the more it should slow down the rotation until it stops. But it only works in sections, when I use the rotation of the camera the object rotates and then stops, as if it detects only one part of the rotation, while the other is a blind spot.

What equation can I use to rotate the object well?

var currentR = rb.rotation.y;
var targetR = Camera.main.transform.rotation.y;
rb.AddTorque(transform.up * 1000f * (targetR - currentR));

@DMGregory♦ I put your script as it is in a separate new script on a new object, equipped with a rigidbody. I post the script how I entered it. At "targetOrientation" I gave the value of "Quaternion.LookRotation (Camera.main.transform.forward);" because I want the object to follow the direction of that camera. The result is that all the axes of the rigidbody are influenced by the rotation (if I lift the camera the object turns towards the ground, etc.), and above all that the rotation works even if I block the axes with Freeze Rotation, and with this script the object is no longer affected by external forces.

public class rotation : MonoBehaviour
{
    private Rigidbody rb;
    public Transform direction;
    // Start is called before the first frame update
    void Start()
    {
      rb = GetComponent<Rigidbody>();
    }

    // Update is called once per frame
    void Update()
    {

     Quaternion targetOrientation = Quaternion.LookRotation(Camera.main.transform.forward);       
    Quaternion rotationChange = targetOrientation * Quaternion.Inverse(rb.rotation);

    rotationChange.ToAngleAxis(out float angle, out Vector3 axis);
    if (angle > 180f)
        angle -= 360f;

    if (Mathf.Approximately(angle, 0)) {
        rb.angularVelocity = Vector3.zero;
        return;
    }

    angle *= Mathf.Deg2Rad;

    var targetAngularVelocity = axis * angle / Time.deltaTime;

    float catchUp = 1.0f;
    targetAngularVelocity *= catchUp;

    rb.AddTorque(targetAngularVelocity - rb.angularVelocity, ForceMode.VelocityChange);


     }
    }

I set a command [if (Input.GetKeyDown (KeyCode.T)) rb.AddTorque (transform.up * 5500f, ForceMode.Impulse);] to rotate the object with an impulse and this does not work when your rotation script is active. I also place a video where you see the object before activating the rotation script, and after it is active, to see how it does not react to the impulse with T, and how it rotates reacting on all rotation axes, while I want only that the rigidbody only rotates its Y-axis of rotation, in the direction of the camera (as a person does when turning in one direction). In the video The axes of rotation X and Z are on freeze (otherwise without fixed the rotation the object falls moving). I also put a video of the desired effect taken from a game so as to explain me better.

My video desired rotation

\$\endgroup\$
2
  • \$\begingroup\$ Note that transform.rotation is a Quaternion, so you should almost never try to access its x/y/z/w components one-by-one. The y component does not directly correspond to a yaw angle, which it looks like you're trying to use it as here. \$\endgroup\$
    – DMGregory
    Commented May 24, 2020 at 15:58
  • \$\begingroup\$ You completely skipped over the GetOrientationAwayFromCamera/TurretLookRotation portions of my answer that restrict the rotation to the vertical axis. If LookRotation was sufficient to get your desired behaviour, I would not have gone to the trouble of including those sections - so please implement the answer completely. \$\endgroup\$
    – DMGregory
    Commented May 25, 2020 at 13:15

1 Answer 1

2
\$\begingroup\$

If you want to orient an object to face away from the camera, the first step is to compute the orientation you want it to have. You can use a "Turret Look Rotation" convenience method like this one to form an orientation perpendicular to a particular surface, while facing toward a particular direction.

// If your ground has hills/ramps, you might want to update this with your ground normal
// from your ground check raycast/shapecast.
Vector3 _groundNormal = new Vector3(0, 1, 0);

Quaternion GetOrientionAwayFromCamera() {
    // To face in the direction of the camera axis, use this.
    Vector3 approximateForward = Camera.main.transform.forward;

    // To face away from the camera object, use this. It only makes a difference if your character
    // can be off-center in the frame, facing leftward when they're on the left side of the screen.
    // Vector3 approximateForward = transform.position - Camera.main.transform.position;

    return TurretLookRotation(approximateForward, _groundNormal);
}

Now the easiest way to get a rigidbody to face that new orientation is with MoveRotation:

var targetOrientation = GetOrientationAwayFromCamera();
rigidbody.MoveRotation(targetOrientation);

But if you'd prefer to compute the torque to reach this orientation yourself, it's:

// Compute the change in orientation we need to impart.
Quaternion rotationChange = targetOrientation * Quaternion.Inverse(rigidbody.rotation);

// Convert to an angle-axis representation, with angle in range -180...180
rotationChange.ToAngleAxis(out float angle, out Vector3 axis);
if (angle > 180f)
    angle -= 360f;

// If we're already facing the right way, just stop.
// This avoids problems with the infinite axes ToAngleAxis gives us in this case.
if (Mathf.Approximately(angle, 0)) {
    rigidbody.angularVelocity = Vector3.zero;
    return;
}

// If you need to, you can enforce a cap here on the maximum rotation you'll
// allow in a single step, to prevent overly jerky movement from upsetting your sim.
// angle = Mathf.Clamp(angle, -90f, 90f);

// Convert to radians.
angle *= Mathf.Deg2Rad;

// Compute an angular velocity that will bring us to the target orientation
// in a single time step.
var targetAngularVelocity = axis * angle / Time.deltaTime;

// You can reduce this parameter to smooth the movement over multiple time steps,
// to help reduce the effect of sudden jerks.
float catchUp = 1.0f;
targetAngularVelocity *= catchUp;

// Apply a torque to finish the job.
rigidbody.AddTorque(targetAngularVelocity - rigidbody.angularVelocity, ForceMode.VelocityChange);

This will try to rotate the entire difference in a single physics timestep, but for very large turns or short timesteps you might run up against the maximum angular velocity limit, forcing the rotation to spread out over multiple ticks. If you observe this happening, you can raise the limit under Project Settings -> Physics.

\$\endgroup\$
7
  • \$\begingroup\$ My error is on targetOrientation. I put a "public transfrom (called" direction ") on the Camera.main and indicated Quaternion targetOrientation = direction.rotation; The result is an 'error "rigidbody.torque assign attempt for' Ethan 'is not valid. Input torque is {NaN, NaN, NaN}. UnityEngine.Rigidbody: AddTorque (Vector3, ForceMode) " I tried to indicate targetOrientation = Quaternion.LookRotation (direction.forward) but it gives me the same error. \$\endgroup\$ Commented May 24, 2020 at 17:59
  • \$\begingroup\$ Ah. When your orientation is already facing the direction you want, AngleAxis gives back an infinite axis. Checking if the angle is zero and taking an early-out fixes this. I've edited the question to show that. \$\endgroup\$
    – DMGregory
    Commented May 24, 2020 at 21:56
  • \$\begingroup\$ The error has disappeared, now that I can use it I see that unfortunately the rotation goes beyond what I need. If I rotate the camera also rotates the object, but in all directions, while I just want the object to rotate horizontally (Y axis) in relation to the horizontal rotation of the camera (that's why even if done wrong I used the Y axes as a unit measurement and transform.up as vector in my script) \$\endgroup\$ Commented May 24, 2020 at 22:46
  • \$\begingroup\$ What does not convince me is that there is a forced rotation. I blocked (freeze) the rotation of the object on all axes but the object kept rotating relative to the camera. This makes me think that physics will have problems when external forces intervene on the object, while I wanted to use AddTorque precisely because I already use AddForce to move the object and AddTorque I need to do rotations also taking physics into consideration. \$\endgroup\$ Commented May 24, 2020 at 22:46
  • \$\begingroup\$ The effect I want is to simulate what is usually seen in third-person games, with an object that moves and rotates and is sensitive to physics. They also recommended a PID controller for this but I have never used one and I would not know how do. In any case, thank you very much for the answers and patience, I will try to edit your script tomorrow, you have gained the best answer however it goes. \$\endgroup\$ Commented May 24, 2020 at 22:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .