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

Resolved Jittery rotation while moving gameobject

Discussion in '2D' started by DaniKroes, Apr 10, 2022.

  1. DaniKroes

    DaniKroes

    Joined:
    Jul 26, 2021
    Posts:
    12
    Hi everyone!

    I have an issue with jittery movement and I have searched the internet thin and tried countless solutions, but none have worked. :confused:

    Essentially, I am moving a 2D Enemy GameObject towards my player, which involves moving and rotating at the same time.

    At the start it is smooth, but when my player shoots the Enemy, causing it to fly backwards because of the RigidBody2D physics, it starts jittering when it rotates back towards my player.

    Also, when my enemy tries to rotate back towards my player after getting hit, it struggles to aim/rotate directly at my player. It's just kind of struggling to rotate the last 20 degrees while jittering.

    I have tried EVERY combination of using velocity and AddForce for movement, and using FromToRotation, RotateTowards, Lerp, and Slerp for rotation.

    I have tried using both Update, LateUpdate, and FixedUpdate for either or both moving and rotating.

    I have tried setting the GameObjects Interpolation to None, Interpolate and Extrapolate.

    Nothing works... :(

    Here is a video showing the issue:


    Here is the script I have on my Enemy gameobject:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class EnemyBehaviour : MonoBehaviour
    6. {
    7.     Rigidbody2D _rb;
    8.     [SerializeField] GameObject _player;
    9.  
    10.     float _moveSpeed = 2f;
    11.     Vector2 _currentVelocity;
    12.     Vector2 _targetVelocity;
    13.     Vector2 _moveDirection;
    14.     Quaternion _targetRotation;
    15.     bool _disableEnemy = false;
    16.  
    17.     void Start()
    18.     {
    19.         _rb = GetComponent<Rigidbody2D>();
    20.     }
    21.  
    22.     void Update()
    23.     {
    24.    
    25.     }
    26.  
    27.     void FixedUpdate()
    28.     {
    29.         if (!_disableEnemy)
    30.         {
    31.             RotateEnemy();
    32.             MoveEnemy();
    33.         }
    34.     }
    35.  
    36.     void MoveEnemy()
    37.     {
    38.         _moveDirection = _player.transform.position - transform.position;
    39.         _moveDirection.Normalize();
    40.  
    41.         // Prevent redundacy
    42.         _currentVelocity = _rb.velocity;
    43.         _targetVelocity = _moveDirection * _moveSpeed;
    44.  
    45.         if (_currentVelocity != _targetVelocity)
    46.         {
    47.             _rb.velocity = _moveDirection * _moveSpeed;
    48.         }
    49.     }
    50.  
    51.     void RotateEnemy()
    52.     {
    53.         // We can't use FromToRotation() since it is instantanious and not smooth
    54.         /*_targetRotation = Quaternion.LookRotation(Vector3.forward, _moveDirection);
    55.         if (transform.rotation != _targetRotation)
    56.         {
    57.             transform.rotation = Quaternion.RotateTowards(transform.rotation, _targetRotation, 200 * Time.deltaTime);
    58.         }*/
    59.  
    60.         _targetRotation = Quaternion.LookRotation(Vector3.forward, _moveDirection);
    61.         if (transform.rotation != _targetRotation)
    62.         {
    63.             transform.rotation = Quaternion.Slerp(transform.rotation, _targetRotation, 20 * Time.deltaTime);
    64.         }
    65.  
    66.     }
    67.  
    68.     private void OnCollisionEnter2D(Collision2D collision)
    69.     {
    70.         if (collision.gameObject.tag == "Bullet")
    71.         {
    72.             StartCoroutine(Damaged());
    73.         }
    74.     }
    75.  
    76.     private IEnumerator Damaged()
    77.     {
    78.         _disableEnemy = true;
    79.  
    80.         yield return new WaitForSeconds(0.5f);
    81.  
    82.         _disableEnemy = false;
    83.     }
    84. }
    85.  
     
    Last edited: Apr 10, 2022
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    Unfortunately, you're bypassing physics completely and instantly setting the Transform.rotation so it doesn't matter what you do with physics, you're bypassing it. The whole point of a Rigidbody2D is to do physics work and write its pose to the Transform which, for 2D, is XY position and Z rotation. You're just stomping over rotation, then when the physics simulation runs, it sees that you've changed the Transform and has to read it and update the body.

    Rotation is no different than linear motion of position yet you're treating it differently.

    The Rigidbody2D has angularVelocity for rotation and other methods that work with interpolation such as MoveRotation.

    In short, if you're modifying the Transform, you're fighting the physics system. Isn't this why you're using velocity for position though?
     
    DaniKroes likes this.
  3. karliss_coldwild

    karliss_coldwild

    Joined:
    Oct 1, 2020
    Posts:
    530
    As for what you can actually do, first you should decide what behavior do you actually want. Do you want the enemy to behave like a physics object and fly backwards when hit by bullet or do you want to calculate the movement and rotation yourself? If you want both things are a bit more tricky.

    If the physics is only used for collision detection with bullet and flying back when hit is not required, you can probably switch the enemy to kinematic body type and keep doing the movement and rotation calculations manually.

    If you want to keep full physics you need do all the movment and rotation by adding force and torque. You mentioned that you tried that but you probably didn't fully understand the problem or implemented it badly. Depending on how well you want it will require some knowledge of math, physics and motion control algorithms (commonly used in robotics). In some cases it's possible to cheat and get away with simpler algorithms by setting high friction, and adding big force in the preferred movement direction. But that may have undesirable results of less dynamic behavior when external forces get applied to the enemey, or stuff suddenly flying around due to the enemy pushing stuff around with high force. Third potential failure mode for this approach when friction is low and the control algorithm is bad it can result in slippery movement.

    Different approach which is somewhere between the two methods above is treating enemy body as kinematic while the enemy walks around and temporary switching to dynamic mode when the enemy is hit. The idea is similar to how some games treat ragdolls where humanoids are controlled with scripted animations by default, but after getting hit game switches to ragdoll mode controlled by physics engine. Only in your case enemy is probably represented by single rididbody instead of ragdoll consisting of multiple rigid bodies connected with joints. While the enemy is in ragdoll mode you shouldn't try to move or rotate him. It depends on your game design choices when to switch back from ragdoll mode: it could be done after fixed time, when the movment speed falls bell
     
    Last edited: Apr 11, 2022
    DaniKroes likes this.
  4. DaniKroes

    DaniKroes

    Joined:
    Jul 26, 2021
    Posts:
    12
    Got an answer on StackOverflow :)

    I moved my RotateEnemy() into Update, which I had tried before and solves the issue, but makes the enemy movement choppy.

    Then I changed the movement from a velocity to a MoveTowards type, which fixed the choppyness, but only until it got hit by a bullet from the player.

    Then I added a Drag on the RigidBody2D component, since the "infinite knockback" was "fighting against" the MoveTowards.

    And everything is smooth and working now. :)
     
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    This is incorrect btw. Changing the Transform does NOT change the Rigidbody2D rotation, it only changes the Transform. Only when the simulation runs does the body then instantly change position which will cause overlaps.

    All you're doing here is masking the problem by constantly stomping over it. If you're happy with that then that's up to you however note, you're just hiding the problem.

    You should rotate using MoveRotation which is compatible with interpolation. If you're modifying the Transform, you're doing it wrong.
     
  6. DaniKroes

    DaniKroes

    Joined:
    Jul 26, 2021
    Posts:
    12
    I did also change the rotation from Slerp() to using RotateTowards(). Is that a valid way to solve the issue rather than using MoveRotation() ?
     
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    As I said, "If you're modifying the Transform, you're doing it wrong."

    If you want a Rigidbody2D to move then you're going to have to use its API and not the Transform. :)
     
  8. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    What I don't understand is that for linear movement, you're using "Rigidbody2D.velocity" (not the Transform) so that's okay but then you're incorrectly using the Transform to do the same for rotation.

    To be clear, the Rigidbody2D writes its position/rotation to the transform. Use the Rigidbody2D API to move/rotate.
     
  9. DaniKroes

    DaniKroes

    Joined:
    Jul 26, 2021
    Posts:
    12
    Ah I think I understand now...

    You are saying that I have to choose to either use RigidBody2D OR transform for both moving and rotating. Since mixing the two is considered bad.

    So either:
    _rb.velocity and_rb.MoveRotation
    OR using:
    transform.position = Vector2.MoveTowards() and transform.rotation = Quaternion.RotateTowards().

    ?
     
  10. DaniKroes

    DaniKroes

    Joined:
    Jul 26, 2021
    Posts:
    12
    I should point out that currently my MoveEnemy() and RotateEnemy() looks like this after the StackOverflow solution, which I'm aware is me changing the transform directly rather than the RigidBody2D movement method.

    Code (CSharp):
    1.  
    2.     void MoveEnemy()
    3.     {
    4.         transform.position = Vector2.MoveTowards(transform.position, _player.transform.position, _moveSpeed * Time.deltaTime);
    5.     }
    6.  
    7.     void RotateEnemy()
    8.     {
    9.         _moveDirection = _player.transform.position - transform.position;
    10.         _moveDirection.Normalize();
    11.  
    12.         _targetRotation = Quaternion.LookRotation(Vector3.forward, _moveDirection);
    13.         if (transform.rotation != _targetRotation)
    14.         {
    15.             transform.rotation = Quaternion.RotateTowards(transform.rotation, _targetRotation, 200 * Time.deltaTime);
    16.         }
    17.     }
     
  11. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    There's should be no need to interpret what I said. If you're using 2D physics components then don't modify the Transform. If it moves in physics, in any way then move the Rigidbody2D via its API.

    There are many ways to move a Rigidbody2D such as adding force/torque, directlying modifying the velocity/angular velocity and using MovePosition/MoveRotation. Never the Transform.
     
  12. DaniKroes

    DaniKroes

    Joined:
    Jul 26, 2021
    Posts:
    12
    I'm really just trying to learn here... There is no need to be snarky about me trying to put things into perspective in a way I can easier understand.

    You could just have said:
    "If you use a RigidBody component on your gameobject, then you should use RigidBody related methods to move your gameobject. So as long as there is a RigidBody2D attached to your gameobject, use methods like MovePosition/MoveRotation. Only use transform related methods if there is no RigidBody at all."

    You should really learn get down on the same level as the person you are trying to explain it to, and not sound condescending when I'm clearly trying to understand and better my understanding.

    Either way...

    I appreciate the help, and I have learned something new. I'll close this topic off as "resolved".
     
  13. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    I'm sorry you feel like that but I was certainly not being snarky! Why would you interpret what I said as that? I was simply saying that I'm being very explicit and have repeated what I said (in several ways) so there's no need for interpretation when I say don't modify the Transform. That was all.

    I have taken the time and effort to make it clear several times because I want you to understand.
     
  14. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    And I'm really trying to help you despite you thinking the opposite, apparently. I have no idea why you have taken offence, I really don't.
     
  15. DaniKroes

    DaniKroes

    Joined:
    Jul 26, 2021
    Posts:
    12
    You are clearly very experienced in Unity seeing you are a Administrator, and I wanted to "fully understand" rather than "just get by", so that I do things correctly in my future endeavors in Unity.

    The reason for the friction was that I was in need for it to be explained in layman's terms, and because I felt it wasn't given to me, I tried explaining it that way back to you, to get confirmation that what I had understood was correct. Just so we avoided a continuous "back and forward".

    So when I get a "There's should be no need to interpret what I said." as the first phrase in the next reply, I took it as you feeling offended that "I had to dumb things down, since I should understand your explanation". Your explanation probably is technically perfect, and any other programmer might fully understand it, but I clearly still needed it "dumbed down further" hehe.

    Drama is never a good thing, and I hate that there had to be friction. You told me you didn't intend to be "snarky" with the "There's should be no need to interpret what I said.", so I'll take your word for it. Although I still believe that had a teacher said that to a student, then the surrounding people would look cross eyed at the teacher. But then again, this is a forum post and not a classroom, so maybe I'm just misinterpreting what the relationship is between people in here.

    I'm sorry that you felt I was unappreciative of your help, and it had to end in discomfort. It TRULY was very helpful in me better understanding the usage of methods that affect the transform vs the RigidBody. :) And thanks for the private message. You are right, unfortunately conversations in text and emotes can often lead to miscommunication.
     
    Last edited: Apr 13, 2022
    MelvMay likes this.
  16. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    Absolutely and it happens a lot.

    To answer your query about the relationships here; the forums do require a minimum level of technical knowledge and are not a good place for dumbing everything down therefore there should be no “layman”. Using Unity terminology and names for things is expected. You are expected to understand the very basics by first following tutorials such as those on Unity Learn first although we do have a Getting Started forum. Any free support offered by Unity employees here is out of their free time and is always because they want to help, particularly in their areas. In your case, you’re getting free help from the person who wrote the physics system you’re using and I’m happy to give that help.

    I helped you, in the end, this is a win! :)