Search Unity

Smooth Movement Test Application (2D, WebGL)

Discussion in 'Physics' started by ZuBsPaCe, Aug 25, 2019.

  1. ZuBsPaCe

    ZuBsPaCe

    Joined:
    Oct 14, 2014
    Posts:
    4
    Did you ever ask yourself, what would happen, if you would move a rigidbody in Update? If yes, look no further, because I published a small WebGL application where you're able to play around with different rigidbody movement types in 2D.



    The Smooth Movement Test Application lets you add different lanes, where you can set and compare following settings:
    • Update vs FixedUpdate
    • Kinematic vs Dynamic rigidbody
    • Rigidbody interpolation
    • Physic Rate
    • V-Sync
    • And more!
    Here's a short overview:



    You can run the application here. More information can be found in the blog entry. The source code is also available on github.

    Hope it's useful to someone!
     
    lightbug14 and alexeu like this.
  2. JanOtt

    JanOtt

    Joined:
    Apr 23, 2019
    Posts:
    28
    That's a very cool application, nice work! The only thing I'd change if I were you is the spacing between the lanes - if you choose FixedUpdate(), there's no real visual separation between them anymore. Alternatively, you could also look into assigning colors to the different lanes, to add some contrast.

    Oh, by the way. I don't know if you've mentioned this in your blog post, but to get a true demonstration of the differences between movement types, users really need to download and run the Windows build - WebGL can add very noticeable stutter, depending on what browser they're using and if the browser is up-to-date.

    For example, when I run your application in Firefox, the stuttering is so severe that it defeats the purpose of the demonstration. Chrome works better, but still isn't anywhere close to the Windows build.

    Just something to keep in mind. Keep up the good work!
     
    ZuBsPaCe likes this.
  3. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,966
    I admire the effort you've put in to make this a useful demonstration of the features.

    One thing I saw that I'd like to correct if I may:
    This is incorrect and/or misleading. Setting transform.position does nothing apart from setting transform.position. It doesn't modify the body at all until the simulation is run or if you call Physics2D.SyncTransforms explicitly (avoid!) or if you've got Physics2D.AutoSyncTransforms on (avoid!) and call any query or property in which case sync-transform is first done. With that option off, the Transform is what you specified but the body won't be in that pose.

    Internally when a Transform is changed, the physics systems (or any more internal system) doesn't get any kind of callback to know that happens. This is why we perform a transform-sync prior to executing the simulation.

    For ultimate smooth motion with physics sacrificing some performance and reduced determinism, updating the physics simulation per-frame is better using Physics2D.Simulate. This'll be a lot easier when the following lands too: https://twitter.com/melvmay/status/1164591679535497219?s=20

    Can you explain what you mean by this? Are you referring to the fact that interpolation is interpolating from the previous pose to the current pose therefore it's behind where the current pose is? In that case you could use extrapolation which uses forward-velocity based estimates.

    Again, wanted to add that it's very cool that you took the time and effort to produce something like this to help others. Ping me if you need any more info or help with it either here or message me directly.
     
    Last edited: Aug 26, 2019
    alexeu and ZuBsPaCe like this.
  4. ZuBsPaCe

    ZuBsPaCe

    Joined:
    Oct 14, 2014
    Posts:
    4
    @JanOtt

    Thank you so much for your feedback! I gave the lane settings a bit more room and put some space in-between them. Looks a lot better now!

    And you're right about the WebGL performance. I added a hint to the release section.


    @MelvMay

    Thank you for pointing that out. It's obvious that you're correct, when you increase the velocity and at the same time reduce the physic steps per second:



    As you can see, the red rigidbody lies behind the sprite at transform.position. I will check out Physics2D.Simulate() and will probably integrate it as an option into the application.

    Yeah, with interpolation lag I mean the sprite lagging behind the rigidbody for a physic frame. This was very obvious in a 2D platformer I made with fast movement and high maximum velocity. Think of something like Super Meat Boy. In my game, when the rigidbody hit some spikes, the player died and the 'dead sprite' was left behind as a platform the player could jump on in the next try. This lead to following issue:



    Here the player tried to jump down from the upper left onto the jump pad. The player died, because the physic engine correctly saw the rigidbody and the spike overlapping. But the player just thinks, that something is horribly wrong or that the controls are bad.

    (Another thing is input lag, because you need to wait for the next physic frame to process the input. I admit it's a small delay, but you can avoid it by handling everything in Update.)


    Possible fixes for interpolation lag I know:

    * One fix is simply setting transform.position in Update. A common proposal for 2D platform games is, that you should use a kinematic rigidbody to control the player. You gain full control of the player movement but need to handle collisions with the environment through raycasts. So you raycast in Update and set transform.position in Update. As a bonus you get the smoothest motion possible, which is very important for those kind of games.

    It fixes the problem above with the spikes, because you can properly determine an overlap of player and spike with some raycasts.

    Drawback: Needs more code. Interactions with dynamic rigidbodies are not fully correct. Interacting with other moving objects is complicated.

    * I'm not so sure about this, so please correct me: You don't touch transform.position. Instead you properly use rigidbody methods in *Update* and call Physic2D.Simulate() before the Update Cycle finishes, right? Sounds simple... You gain correct physic interactions with dynamic rigidbodies and don't need any interpolation at all.

    If you use a kinematic rigidbody you still need the raycasts. But the rigidbody and sprite positions are identical.

    Drawback: Performance. Because I love WebGL Releases I hate sacrificing performance...

    * I haven't had much success with extrapolation. It doesn't work with Rigidbody2D.MovePosition() I think. And the other movement types (setting velocity or AddForce) lead to unnecessary stuttering, which can be seen in the test application. Fast velocity changes, which occur alot in 2D platformers, are a no go in combination with extrapolation.

    * Maybe something else?... Would love to know ;)


    However, thank you for your valuable input. I will incorporate your views into my blog post.
     
    Last edited: Aug 26, 2019
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,966
    That's a good summary but I wanted to reemphasize the problem with
    Again, when you do this it does not update the rigidbody.position! If you have Physics2D.AutoSyncTransforms on (off by default) then a transform sync happens anytime you try to read some state from a Rigidbody or Collider so it might look like it updates it but it doesn't, you pay the cost anytime you read state again and again. What happens is that any body/collider registers with the transform system that it needs to know about updates. We can ask at anytime if there are any relevant changes for these components to transforms. This is transform-sync as we are returned which GameObjects and we process updates appropriately. It's pretty fast if there are zero changes so doing transform-sync prior to any read operation isn't bad but if you are making lots transform changes then...

    Of course with this off (again that's by default) then you'll see the body/collider isn't updated. You should always have this setting off, it's only there for legacy behaviour.

    Note that explicitly setting the position of a body results in all its contacts being destroyed. They'll get recreated during the next simulation step.

    If your game requires the physics sync'd to the framerate and interpolation discrepancies is important then I'd say seriously consider running the physics per-frame. In the twitter post above where you can select per-frame and it'll automatically be executed then, it also knows that interpolation is not required and will ignore any interpolation option you set on a Rigidbody2D.

    Well MovePosition doesn't modify the velocity of the body so extrpolation has nothing to extrapolate.

    I'm confused, these are not "movement types". Giving a body velocity directly or indirectly via a force doesn't in itself cause stuttering so the statement is very misleadings to be honest. Running in the editor causes stuttering, stuff running on the local machine causes stuttering etc. In no way does a constant fixed-update of Transform position causes suttering in itself. For instance, setting a Transform position to move at a constantly velocity each frame is no different than doing that on a rigidbody. Same goes if you did both of those during a fixed-update.

    The thing to watch out for and I want to emphasize this isn't the physics system (2D or 3D) is that fixed-update can be called multiple times per-frame which will mean you get multiple physics steps as well as other things that run in the fixed-update. This normally only happens though if the game-time is lagging, perhaps due to poor frame-rate etc.

    Also, the default fixed-update time of 50hz is bonkers. I am not sure who came up with that originally but it's nonsense. It should be set to 60hz which would mean less likelyhood of the fixed-update time-base being out-of-sync with the game-time i.e. fixed=update on a 1:1.

    Sorry for the inconcise rambling above, kind of typed it as I thought of it; hopefully you can pick out useful stuff from it.

    Feel free to ping me if you need anything specific.


     
    Last edited: Aug 27, 2019
    ZuBsPaCe likes this.
  6. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,966
    I missed this part. Basically yes, do everything that you previously did during fixed-update in update. Also note that you can run the simulation before "Update" is called or after "Update" is called by simply calling the Physics2D.Simulate in a monobehaviour that's had its execution order set early or late. This can be handy depending on whether you want to set up stuff then simulate or simulate and then look at results and respond for next frame. Running the simulation early can be good if you're only setting velocities, adding forces etc. If you're setting positions directly (ugh!) then you'll probably want to run the simulation after but before rendering.
     
    ZuBsPaCe likes this.
  7. ZuBsPaCe

    ZuBsPaCe

    Joined:
    Oct 14, 2014
    Posts:
    4
    @MelvMay

    That's a bit odd, because Unity could calculate the velocity from the movement. But ok, I can live with that!


    Yeah, taken out of context this sounds misleading. I was referring to extrapolated rigidbodies moved by setting velocity or AddForce(). If you take a rigidbody, enable extrapolation, keep applying velocity or AddForce() and move it into a bunch of other dynamic rigidbodies, you can observe the sprite moving erratically forward and backward, just a tiny bit, compared to an interpolated rigidbody with exactly the same movement.

    Maybe stuttering is the wrong word for that. (Jitter? Sprite jitter?) It's easy to create that situation in my test app and the effect is more visible if you reduce the physic steps per second.

    But this is expected! A side effect of extrapolation, which cannot foresee a sudden velocity change. And I admit, I'm nitpicking here. The effect is probably not visible to a normal eye with a standard physic rate on a desktop build.


    Awesome! That's something I wondered too. Thanks for saying that out loud!


    Alright, I will sure play around with Physics2D.Simulate()


    Thank you for you all that in-depth information. You're awesome.
     
  8. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,966
    It's not odd at all. If all it did was set the velocity to move to that position in one time frame then it'd still have that velocity and not "move to that position" but carry on past that position. For large positional jumps, this velocity can get very high indeed.

    MovePosition needs to be superior to just setting the position because that can cause collision tunnelling because it just instantly teleports to that position. MovePosition temporarily sets the linear velocity required to move to the specified position over a single simulation step. It does this by storing any existing linear velocity and linear drag and assigning the internal velocity. When the simulation runs it uses this internal linear velocity to move to the position and this means it moves through the interviening space and collides with everything along the way. When the simulation step has finished it reassigns the stored velocity.

    If you call MovePosition but the body already has a non-zero linear velocity, this will be restored when the simulation step has finished. Also, if you read the linear velocity it'll be the stored linear velocity; the calculated one for the MovePosition is completely hidden from you.

    Kinematic bodies will always get to the specified position. Dynamic bodies won't necessarily get there if they collide. Collisions along the way work for both body-types.

    You can obviously implement your own MovePosition which is just (distance vector / time-step) and you'll need to save/restore any linear drag. We implement this for you so you don't have to deal with all that nonsense.
    Yes, that's expected as you say. The way I like to explain it to anyone is that interpolation is based upon the past facts but extrapolation is based upon a guess of the future. :)

    You're most welcome. I'd be interested to see what you think of running the simulation per-frame; hopefully you'll never want to use anything else after trying it. ;)
     
    ZuBsPaCe likes this.
  9. JanOtt

    JanOtt

    Joined:
    Apr 23, 2019
    Posts:
    28
    Great conversation so far! I definitely learned a few new things about rigidbody physics along the way.

    There's just one thing I was wondering about:
    Maybe I missed something here and you're both talking about 2D Physics exclusively, but when using the regular 3D physics, moving a kinematic rigidbody with MovePosition() will definitely result in smooth movement (if rigidbody interpolation/extrapolation is activated).

    In addition to that, the kinematic rigidbody will also return a velocity (when calling rigidbody.velocity).

    I haven't had time to check yet, but does the 2D physics system handle this differently?

    Also, @ZuBsPaCe:

    I just skimmed over your blog post again, but what prevented you from just setting the rigidbody's velocity directly in your game (LifeCycle)? That approach should have solved all the interpolation problems you experienced, as far as I can tell.
     
    Last edited: Aug 27, 2019
    ZuBsPaCe likes this.
  10. ZuBsPaCe

    ZuBsPaCe

    Joined:
    Oct 14, 2014
    Posts:
    4
    @JanOtt

    Yes, we'v been talking solely about Rigidbody2D.

    But your input is Interesting. I haven't considered 3D rigidbodies, yet. Therefore I made a small sample application to figure out the differences and this is what I came up with. (I can provide the source if you'd need it.)


    (small correction after I've posted: 1) and 4) will not affect the velocity. Instead the velocity before calling MovePosition() will be returned)


    I can only guess that those differences originate from PhysX and Box2D, but maybe there are other reasons?


    You are right. Interpolation and extrapolation works correctly with Rigidbody2D moved by setting the velocity. After reading the most recent post from MelvMay, the set-velocity approach seems to be a better match to move a character around compared to MovePosition().

    If I'm not completely mistaken, MovePosition() makes sense to use, if...

    a) ...you really want to make sure the character moves to the target position within one physic step,
    b) ...you want to re-position the character without forces affecting it (e.g. drag),
    c) ...you want to re-position the character without affecting the current velocity.

    One scenario where MovePosition() makes sense: You need to spawn a kinematic rigidbody at a spawn point, occupied by another kinematic rigidbody. Now you can move away the offender with MovePosition.

    Another scenario for I can come up with, where MovePosition() could be useful, would be some kind of "Charge-Attack", which happens instantly and where the player needs to collide with everything in-between.


    Regarding my blog post: Sooner or later I will include the knowledge in this thread over there. One reason I wrote that blog post was, that I was unhappy with rigidbody interpolation and extrapolation. Each physic step, the rigidbody and sprite position differ for a short while, which I find bad in fast games where movement accuracy is of high importance. But the Physic2D.Simulate() approach, brought up by MelvMay, resolves this issue, because that way you are able to tie the internal physic update to the normal Update cycle.
     
    Last edited: Aug 28, 2019
  11. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,966
    When I implemented MovePosition it made complete sense to not just have it modify the velocity otherwise it's a MoveToPositionThenContinueOnInThatDirectionUnlessISayStop. This just isn't something that made sense and indeed, when initially it was implemented there was a bug where the velocity wasn't restored and we had a lot of bug reports for it which makes sense.

    In the end, if you're doing step-wise moves from position to position but may not want to continually move to a new position then MovePosition is perfect. If you want to modify the velocity permanently of your body to do the same then that's trivial to do yourself as I mentioned above with velocity = (direction vector / time-step) and choose whether you want linear drag or not as this can affect it. In the end, no method can do everything for everyone but as long as what it's actually doing is available to do yourself in a simple function then it's all good.

    Personally I think all of the above conversation is just highlighting all the horrible hoops a developer has to jump through to fight the problems of running physics at a fixed time-step as opposed to a variable time-step. Input, visual interpolation, queries etc.

    I do think that a lot of games and devs should just use per-frame physics updates and completely ignore the fixed-update stuff. In many cases it's simply not relevant or in the very least, my way to sell it would be that when you start learning this stuff, the fixed-update stuff is a confusing distraction. Like learning to drive while the instructor explains details of the internal combustion engine.

    My hope is that in the future we'll see more per-frame physics stuff in video tutorials. Should be easier for beginners now that it's a simple option with no scripts required.
     
    Last edited: Aug 28, 2019
    JanOtt and ZuBsPaCe like this.
  12. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,966
    Just to note, as I mentioned above somewhere in the wall-O-text so easily lost, Box2D doesn't have any concept of this. It's purely a construct of Unity code and is there as a convenience function to something you could easily do yourself in scripts, say an extension method etc.
     
    ZuBsPaCe likes this.
  13. lightbug14

    lightbug14

    Joined:
    Feb 3, 2018
    Posts:
    61
    Nice thread, very useful information.

    @ZuBsPaCe really cool little app, it would be cool to control the camera zoom.

    Hi @MelvMay I have a question for you about MovePosition (since you're the man and this is a related thread). In my scene i have some platforms and some characters (both kinematics), i update every platform first, then the characters, for this I'm using an external script (a manager if you want). I noticed that, for both platforms and characters:
    1 - Setting the position directly using Rigidbody.position works perfectly. No "character lag" (-> the character is moving perfectly with the platform (in sync), this was measured so i'm quite certain it works as it should).
    2 - Setting the position using MovePosition with interpolation turned off results in "character lag" (maybe by one fixedupdate cycle?).

    From 1 to 2 only one line changed (rb.position = pos ... to ... rb.MovePosition( pos ) )

    What is going on here? I know that interpolation requires one FixedUpdate cycle to work, but since interpolation is turned off, Why is this "character lag" still messing around? Should case 2 produce the same result as case 1? Probably i'm doing something wrong or missed something.

    By the way I'm using 2018.3.2.
    Thanks in advance.
     
  14. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,966
    It's just a matter of when stuff happens, something which you don't mention above.

    So you say when you set the Rigidbody position directly you don't get any "lag". I've never liked this term as it's vague. I can only presume you're doing this per-frame and not per-fixed update (which by default might happen less frequently). It should be obvious then that this means the RB position is always (per-frame) at the position you specify so in your terms, there's no "lag". The downside is that as soon as you issue this command, all contacts are destroyed, you can cause collision tunneling and it isn't compatible with interpolation (it actually cancels interpolation until the next physics update). Depending on the bodies/colliders/effect-you-want these may or may not be issues.

    When you use MovePosition it just tells the physics system to calculate a move to that position for when the physics system updates. It's only actioned when the simulation step occurs. Again, I can only presume you're doing this per-frame which means you'll (potentially) call it several times before the physics actually updates and the last one called will be used when the physics (fixed-update) step occurs.

    In the end, the physics is running at a fixed frequency that isn't in sync with the frame-rate which could be much higher or lower. You need to understand if a function has immediate affect (like setting a position of a RB) or only gets actioned when the physics system steps (like MovePosition on a RB).

    Interpolation is just a function that doesn't affect physics, it's only job is to update the Transform only (nothing in physics is changed, especially not the RB) from the old RB position to the current one. It's a pure visual feature and has no impact on physics calculations. It's there simple because by default, physics does not run in-sync with the visuals.

    You can ignore all of this and never use interpolation again if you simply perform a manual simulation step per-frame using the frame-delta. If the physics in your game is happy with this variable update rate on physics then it makes everything simpler as the physics is always in-sync with the visuals.

    In the future you'll be able to just switch to per-frame physics updates in the settings without any code changes: https://twitter.com/melvmay/status/1164591679535497219?s=20
     
    lightbug14 likes this.
  15. lightbug14

    lightbug14

    Joined:
    Feb 3, 2018
    Posts:
    61
    Hi, thanks for answering. Sorry i didn't specified more about my situation, I was moving every kinematic object in my scene using rb.position (or rb.MovePosition) inside a single FixedUpdate (no transform.position/Translate whatsoever).

    I noticed that after doing two simple prints, and before and one after (*facepalm). So basically was a character-platform sync problem, since the platform position/rotation was updated after the simulation.

    Finally I solved my problem! :) (while writing this post) ... this is how i did it (in case anyone is having the same issue). My fixed update cycle do the following:
    1 - Saves all the initial positions/rotations of every body (for later use).
    2 - Update every kinematic body to their target positions/rotations (just like case 1 from my previous post)
    3 - Restore their positions/rotations (from 1) and perform a MovePosition/Rotation passing the targetPositions/Rotations (from 2).
    4 - Simulate Physics (autoSimulation or manually).

    Maybe it is a little convoluted , but it works perfectly now.


    Thanks for the help and all the useful information!