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
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

Smooth character animation when using root motion and AnimatePhysics

Discussion in 'Animation' started by FunkStallion, Nov 20, 2015.

  1. FunkStallion

    FunkStallion

    Joined:
    Nov 4, 2014
    Posts:
    22
    Hello,

    I'm having a problem achieving consistently smooth character animation/movement.

    Our characters are built with rigidbody's and capsule colliders (Not the Unity CharacterController) using mostly humanoid rigs and mecanim for animation and are viewed in 3rd person.
    Their movement is driven by root motion from their animations and since they have rigidbody's and interact with other rigidbody's their animator update is set to AnimatePhysics, which means they are driven by the fixed/physics update tick

    Vsync is on and in order to get smooth animation and movement, the fixed/physics update tick has to match the render update exactly. If these deviate from each other at all, the result is glitchy/stuttering animation/movement. If we maintain 60fps, all is well, but the character animation/movement is incredibly intolerant to frame rate drops.

    I've tried rigidbody interpolation/extrapolation but if anything that made things worse.

    For the most part, I'm explicitly setting the rigidbody position and rotation in OnAnimatorMove:

    rigidbody.rotation = animationController.rootRotation;
    rigidbody.position = animationController.rootPosition;

    I occasionally scale the animationController.deltaPosition for 'tweaking' the motion of certain animations, but that isn't the issue.

    But even if I use animationController.ApplyBuiltinRootMotion(), or even comment out OnAnimatorMove completely, the problem persists.

    Really scratching my head on this one so any thoughts would be appreciated.
    Is it a limitation of the path I've chosen for character animation/movement that it will only work if we maintain the target framerate, or am I doing something completely wrong.
    We're far into development of the title and are at the polish/optimization phase so hopefully I can get this fixed or at least improved without having to abandon the current setup for something less physics based.

    Thanks,

    Dave.
     
    howong likes this.
  2. Serepa

    Serepa

    Joined:
    May 15, 2015
    Posts:
    32
    Where do you update your camera? In Update()/LateUpdate()? If yes - just try to update it in FixedUpdate().

    Also I think the better way to move rigidbody is rigidbody.MovePosition() instead of rigidbody.position. As well as MoveRotation instead of just rotation. At least if you want that interpolation settings does make sense.
     
    Last edited: Nov 20, 2015
  3. FunkStallion

    FunkStallion

    Joined:
    Nov 4, 2014
    Posts:
    22
    Hi Serepa, thanks for replying.

    I've tried various different camera setups to test this, even having the camera stationary, all of which still exhibit the stuttering motion.

    Also, if I rely on the animator to set the character position instead of handling it myself in OnAnimatorMove, I still see the problem which rules out me setting the rb pos and rot as the problem.

    Thanks again,
    Dave.
     
  4. Serepa

    Serepa

    Joined:
    May 15, 2015
    Posts:
    32
    Which version of Unity do you use? Also try to setup physics materials for your colliders. I saw one topic where the guy said that just setting up the physics material for his floor collider solves his problems.
     
  5. FunkStallion

    FunkStallion

    Joined:
    Nov 4, 2014
    Posts:
    22
    Using 5.2.2p4 at the moment.
    I'm using physics materials on the characters and walls, I'll check into whether it is used on the floors too (been a while since I looked at that code).

    Thanks again for your help.
     
  6. Multithreaded_Games

    Multithreaded_Games

    Joined:
    Jul 6, 2015
    Posts:
    122
    @FunkStallion

    I had a lot of problems that sound very similar to what you're describing. I ended up resorting to some pretty wonky S*** to make all of this play nice (I have an added complication that my characters also have NavAgents attached to them as well) What I did was basically have kinematic and non-kinematic movement modes. Essentially, characters are always kinematic while moving throughout the world. I place a child trigger object on the player character and any other character that I will 'control'. This trigger is set to be slightly larger in radius than the actual 'collider' contained on the character itself. When this trigger is 'triggered' by moving into the vicinity of a collider/rigidbody, I then switch the physics mode on the actual character to non-kinematic. I keep the physics mode set to non-kinematic in OnTriggerStay as well. When OnTriggerExit is called, I set the character back to kinematic. While non-kinematic, the character will collide smoothly and properly with other colliders/rigidbodies.

    There are a few other complexities that will occur as a result of this: firstly, I have both FixedUpdate AND Update functions in code. If I am kinematic, I move in Update--if I am non-kinematic, I move in FixedUpdate. They both call a standard Move function. Again, inside this move function, if I am non-kinematic, I update the Rigidbody's velocity directly. If I am kinematic, I do transform.Translate based on the passed-in movement vector. Root motion can be used if desired, it doesn't matter, it'll work either way.

    OK, so there are a few other things as well: physics should be ran at the highest FPS you hope to achieve in the game--for us, this is 60, thus the fixed timestep should be set to 0.016. I hate to do this, but again, I could literally find no alternative. Animator update mode should be set to AnimatePhysics. Finally, camera position/rotations are done in LateUpdate.

    I know this sounds like a lot, but mainly because it's a movement scheme that literally handles everything that we need in our project--we wanted the character to adhere to the NavMesh as well as an added complication (our characters have NavMeshAgents attached to them, but they DO NOT use the pathfinding, they merely use it for its nice properties, such as keeping the character grounded, and keeping him out of certain areas)

    I would imagine our approach is actually far more efficient, but this is just conjecture. The less physics-based calculations you have to do the better (we are developing a 3d, semi open-world RPG).

    If anyone has any legitimate (I've heard a LOT of random (and wrong) advice, so please, only comment if you KNOW what you are talking about) questions or pointers to give me as to how this solution may be overkill, I'm all ears, but again, after nearly two months of working this out, this is the ONLY way everything works properly. Again, you can easily have an entirely physics-based movement scheme, but we also are fairly mature in our development and thus, we have many levels/areas already created. For that reason, it was very difficult to switch to a purely physics-based scheme, which is why we have this hybrid approach.

    Sorry if any of this is unclear--hopefully something in here is helpful to you.
     
    howong likes this.
  7. DerDicke

    DerDicke

    Joined:
    Jun 30, 2015
    Posts:
    291
    I'm working on a third person controller now for a month, using Unity 5.5. What I found out for me is:

    1.) Do all movement in Update() or FixedUpdate(), but never both. If you Update your Camera in Update() and your character moves in FixedUpdate() or vice versa, you'll always have stuttering; no matter how hard you try to match frame rates. Same is true for moving platforms. Their movement must be updated when character updates.

    2.) Tried a lot to avoid parenting the character to a platform. RigidBody.GetPointVelocity() works nice on horizontal moving platforms, but as soon as platform moves vertically by 3m/s or more, lots of gravity related issues kick in. I stayed with parenting and that works best.

    3.) Personally I've given up on moving the character by rigidBody in FixedUpdate(). I do it in Update() by using the transform. Same is true for any moving platform and camera.

    I'm using non-root-motion for most animations but some use root motion. I had stuttering here, because Animator seems to update with FixedUpdate() no matter what UpdateMode is set. Surprisingly updating the camera in FixedUpdate() if character.animator.applyRootMotion= true fixed this for me.

    I still have stuttering when player keeps running into a wall. Not nice but discardable.

    EDIT: just rediscovered this post. In the meantime Unity provides Physics.Simulate flag, which lets you do a Physics-Update() manually. This allows you to sync Physics with your rendering framerate perfectly. https://docs.unity3d.com/ScriptReference/Physics.Simulate.html

    Someone told me, UE is doing this by default. That is why they don't have these issues.

    If Physics performance is not an issue, I recommend writing a script doing Physics-Update manually every frame, as it removed all microstuttering and all issues with Physics vs Rendering synchronization completely for me. Just subdivide the frame time if it is too long, e.g if frametime > 1/50, make two steps, each with step= frametime/2, just to not miss any collisions. Ignore FixedUpdate() completely and only use Update() for everything.
    Good luck!
     
    Last edited: Mar 31, 2019
    frarf and dyupa like this.
  8. FunkStallion

    FunkStallion

    Joined:
    Nov 4, 2014
    Posts:
    22
    I've just gone back and looked over my solution to this problem. Should have replied here at the time I guess.
    My solution was to use the animator root motion to derive a velocity which I then applied to the character rigid bodies instead of explicitly setting the position/rotation (or using animationController.velocity), this seemed to smooth things out.

    virtual public void OnAnimatorMove()
    {
    // what is the animation telling us to do?
    Vector3 rootMotion = animationController.deltaPosition;

    // preserve current velocity for later
    Vector3 currentVelocity = rigidbody.velocity;

    // derive new velocity
    Vector3 animatorVelocity = rootMotion / Time.deltaTime;

    // preserve effect of gravity
    animatorVelocity.y = currentVelocity.y;

    // apply our derived velocity
    rigidbody.velocity = animatorVelocity;

    // just apply angular velocity as is
    Vector3 animatorAngularVelocity = animationController.angularVelocity;
    rigidbody.angularVelocity = animatorAngularVelocity
    }
     
    skeeter144, RogDolos, Leonid and 3 others like this.