Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question Pick up objects like in Amnesia - problem with smooth object rotation on bumping onto things

Discussion in 'Physics' started by mentor93, Oct 9, 2021.

  1. mentor93

    mentor93

    Joined:
    Mar 6, 2015
    Posts:
    7
    I'm making Amnesia-like mechanics and currently I'm working on picking objects up. The behaviour I'm after is as follows: when I pick a book (for example) up, I want this book to be able to bump onto things that are on the scene, but the book should always try to return to the direction towards camera it had when it was picked up. After some trial and error I managed to code it, but the movement is very jittery.

    My code:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. [RequireComponent(typeof(Rigidbody))]
    6. public class Grabbable : Interactable
    7. {
    8.     private Rigidbody rigidbody;
    9.  
    10.     private bool grabbed = false;
    11.  
    12.     private Vector3 targetDirection;
    13.     private Quaternion originalRotation;
    14.  
    15.     private Player player;
    16.  
    17.     [SerializeField] private float sensitivity = 0.05f;
    18.  
    19.     void Awake()
    20.     {
    21.         rigidbody = GetComponent<Rigidbody>();
    22.     }
    23.  
    24.     void FixedUpdate()
    25.     {
    26.         if (grabbed)
    27.         {
    28.             Ray playerAim = player.PlayerCam.ScreenPointToRay(new Vector3(Screen.width / 2.0f, Screen.height / 2.0f));
    29.  
    30.             Vector3 nextPos = player.PlayerCam.transform.position + playerAim.direction.normalized * player.GrabbedObjectDistance;
    31.             Vector3 currentPos = transform.position;
    32.  
    33.             rigidbody.velocity = (nextPos - currentPos) * 1000.0f * Time.fixedDeltaTime;
    34.  
    35.             Vector3 currentDirection = (player.PlayerCam.transform.position - transform.position);
    36.             transform.rotation = Quaternion.Lerp(transform.rotation,
    37.                 Quaternion.LookRotation(currentDirection) * Quaternion.Inverse(Quaternion.LookRotation(targetDirection)) * originalRotation,
    38.                 sensitivity * Time.fixedDeltaTime);
    39.         }
    40.     }
    41.  
    42.     public override void OnInteract(Player playerObject)
    43.     {
    44.         grabbed = true;
    45.         player = playerObject;
    46.         rigidbody.useGravity = false;
    47.         rigidbody.detectCollisions = true;
    48.         targetDirection = (player.PlayerCam.transform.position - transform.position);
    49.         originalRotation = transform.rotation;
    50.     }
    51.  
    52.     public override void OnRelease()
    53.     {
    54.         grabbed = false;
    55.         rigidbody.useGravity = true;
    56.     }
    57. }
    As for rigidbody that's on the object I'm picking up, there are no constraints, it is not set to kinematic, no kind of interpolation. Mass set to 50, both drags to 0, use gravity checked and collision type set to continuous dynamic.

    The code above actually works almost as intended, but when an object bumps onto something, the movement is very jittery. Original Frictional Games titles (SOMA and first Amnesia game are the ones I'm comparing my mechanics to) have very smooth movement.

    I think that I should rather calculate angular velocity for rotating object, but I'm at a loss on how to calculate it.

    GIF of how this mechanic currently looks:
     
    Last edited: Oct 9, 2021
  2. razzraziel

    razzraziel

    Joined:
    Sep 13, 2018
    Posts:
    362
  3. mentor93

    mentor93

    Joined:
    Mar 6, 2015
    Posts:
    7
    Thanks! I managed to get it working using AddForce() and AddTorque(). I searched Internet for how to calculate torque and I don't understand all the math yet, but it works much better now. Almost like in Amnesia or SOMA, though the held object can still jitter a bit when you drag it across the table, but in the mentioned games objects also jitter a bit now and then, so I think it's all right.

    For anyone wondering, here's the code:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. [RequireComponent(typeof(Rigidbody))]
    6. public class Grabbable : Interactable
    7. {
    8.     private Rigidbody rigidbody;
    9.  
    10.     private bool grabbed = false;
    11.  
    12.     private Vector3 targetDirection;
    13.     private Quaternion originalRotation;
    14.  
    15.     private Player player;
    16.  
    17.     [SerializeField] private float positionForce = 1000.0f;
    18.     [SerializeField] private float angularForce = 50.0f;
    19.  
    20.     // Start is called before the first frame update
    21.     void Awake()
    22.     {
    23.         rigidbody = GetComponent<Rigidbody>();
    24.     }
    25.  
    26.     // Update is called once per frame
    27.     void FixedUpdate()
    28.     {
    29.         if (grabbed)
    30.         {
    31.             rigidbody.velocity = Vector3.zero;
    32.             rigidbody.angularVelocity = Vector3.zero;
    33.             Ray playerAim = player.PlayerCam.ScreenPointToRay(new Vector3(Screen.width / 2.0f, Screen.height / 2.0f));
    34.  
    35.             Vector3 nextPos = player.PlayerCam.transform.position + playerAim.direction.normalized * player.GrabbedObjectDistance;
    36.             Vector3 currentPos = transform.position;
    37.  
    38.             rigidbody.AddForce((nextPos - currentPos) * positionForce * Time.fixedDeltaTime, ForceMode.VelocityChange);
    39.  
    40.             Vector3 currentDirection = (player.PlayerCam.transform.position - transform.position).normalized;
    41.             Quaternion target = Quaternion.LookRotation(currentDirection) * Quaternion.Inverse(Quaternion.LookRotation(targetDirection)) * originalRotation;
    42.             rigidbody.AddTorque(ComputeTorque(target) * angularForce * Time.fixedDeltaTime);
    43.         }
    44.     }
    45.  
    46.     public override void OnInteract(Player playerObject)
    47.     {
    48.         grabbed = true;
    49.         player = playerObject;
    50.         rigidbody.useGravity = false;
    51.         rigidbody.detectCollisions = true;
    52.         targetDirection = (player.PlayerCam.transform.position - transform.position).normalized;
    53.         originalRotation = transform.rotation;
    54.     }
    55.  
    56.     public override void OnRelease()
    57.     {
    58.         grabbed = false;
    59.         rigidbody.useGravity = true;
    60.         rigidbody.isKinematic = false;
    61.         rigidbody.velocity = Vector3.zero;
    62.         rigidbody.angularVelocity = Vector3.zero;
    63.     }
    64.  
    65.     Vector3 ComputeTorque(Quaternion desiredRotation)
    66.     {
    67.         Quaternion q = desiredRotation * Quaternion.Inverse(transform.rotation);
    68.  
    69.         Vector3 x;
    70.         float xMag;
    71.         q.ToAngleAxis(out xMag, out x);
    72.         x.Normalize();
    73.         Vector3 w = x * xMag * Mathf.Deg2Rad / Time.fixedDeltaTime;
    74.         w -= rigidbody.angularVelocity;
    75.         Vector3 wl = transform.InverseTransformDirection(w);
    76.         Vector3 Tl;
    77.         Vector3 wll = wl;
    78.         wll = rigidbody.inertiaTensorRotation * wll;
    79.         wll.Scale(rigidbody.inertiaTensor);
    80.         Tl = Quaternion.Inverse(rigidbody.inertiaTensorRotation) * wll;
    81.         Vector3 T = transform.TransformDirection(Tl);
    82.         return T;
    83.     }
    84. }