Search Unity

CreateShapes being called hundreds of times every frame... why?

Discussion in '2D' started by Pattrigue, Oct 6, 2019.

  1. Pattrigue

    Pattrigue

    Joined:
    Nov 24, 2018
    Posts:
    11
    In all my enemy scripts, I noticed a severe lag spike causing each of them to spend more than 1 ms on calculations every frame. The culprit turns out to be Raycasting, which calls SyncTranformChanges and CreateShapes/DestroyShapes. The latter is sometimes called over 100 times, which causes a massive slowdown.

    Now this is where it gets funky.

    My Player uses the same Controller2D.Move() method for movement, and the SyncTranformChanges call for the player never calls CreateShapes/DestroyShapes:

    Enemy:


    Player:


    Same script, same .Move() method, same scene, same setup (BoxCollider2D + Rigidbody2D).
    What in the world is going on? Has anyone else ever experienced this?
     
  2. coidevoid

    coidevoid

    Joined:
    Oct 26, 2017
    Posts:
    46
    Maybe you unintentionally checked the "static" option?
     
  3. Pattrigue

    Pattrigue

    Joined:
    Nov 24, 2018
    Posts:
    11
    What "static" option? Where?
     
  4. coidevoid

    coidevoid

    Joined:
    Oct 26, 2017
    Posts:
    46
  5. Pattrigue

    Pattrigue

    Joined:
    Nov 24, 2018
    Posts:
    11
    upload_2019-10-14_11-33-16.png
    No, I did not.
    Interestingly, I found that upon booting up the project again, the results are different. It now barely uses any resources like last time I checked...

    What the heck Unity
     
  6. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,966
    The "static" option has nothing whatsoever to do with 2D physics.

    You should never change a transform that has a physics object on it. SyncTransformChanges happens when you do that. Also, SyncTransformChanges is automatically called when you have AutoSyncTransforms checked (this is off by default on new projects). This causes any user-changed transforms to ensure physics objects are updated. The collider will be recreated if it's static (nothing to do with the above option) or if you're updating the Transform that the collider is on and not the one which its rigidbody is on.

    So:
    • Don't change Transform on a GO with Rigidbody2D or Collider2D on it.
    • Don't have AutoSyncTransforms on (it's only there for legacy behaviour)
     
    Pattrigue likes this.
  7. Pattrigue

    Pattrigue

    Joined:
    Nov 24, 2018
    Posts:
    11
    I had no idea this was a thing! Thank you so much! I turned it off and it seems to have solved it:
    upload_2019-10-14_16-57-33.png

    I do have a question about this, though. My player has both a RigidBody2D and a Collider2D on them because I need triggers to work with the player. I use a custom raycast system to detect collisions and set the player's transform.Position manually based on calculated velocity instead of using the built-in Unity physics.

    This goes against what you just said. What do you propose I do instead?
     
  8. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,966
    This really sounds like you've followed some of the bad advice (or at least bad descritions) on the forums. Why does having a rigidbody2d/colliders allow you to use triggers? Not having a RB means the collider is static, adding one means you can select whether it's static, kinematic or dynamic. I suspect it's a case of seeing the description, "you must have at least a RB on one collider for collision/trigger to work" which is a confusing way of describing the body types that can interact so if two colliders don't have a RB then they're both static which don't interact and shouldn't because nobody should be crazy enough to move static colliders. ;) Not saying you are, just that the descriptions I see floating around don't adequately describe why.

    I see "physics" described as many things, I guess you're referring to movement here; you're obviously using built-in physics i.e. rigidbodies, colliders, queries etc.

    When you add a Rigidbody it acts as a proxy to the Transform so go through it directly. If you want to work with the physics systemt then ask the body to move to a position via Rigidbody2D.MovePosition. This will move through the space from the old to new position and is therefore compatible with the collision-detection system and will correctly interpolate bodies (if selected). You can set Rigidbody2D.position which is still far better then do this via setting the Transform but it teleports the body to the new position instantly so is working against the collision-detection system and interpolation won't work.

    Note that the fact that you're seeing shape recreation isn't simply a consequence of setting the Transform; the shape will only be recreated if absolute needed so you're actually doing this on a static collider, doing this on a GameObject that has a Collider2D on it but no Rigidbody2D (static collider) or are changing the scale of the transform. Nothing else will cause a recreation as you're seeing.

    Anyway, hope that helps. Feel free to ask any more questions.
     
    Pattrigue likes this.
  9. Pattrigue

    Pattrigue

    Joined:
    Nov 24, 2018
    Posts:
    11
    Thanks for the reply, getting a completely accurate description of these components is really useful because people seem to always understand them differently.
    I'm not. I'm detecting collisions and calculating velocity myself using raycasts and math, not the built-in physics. The only built-in physics I'm using for this is Collider2Ds for the raycast hits.

    I would like to keep it this way, because it works really well for me in my game to not use Unity's physics for movement. I followed your advice on using Rigidbody2D.position or MovePosition, but this causes my camera to get really jittery:


    Previously I assumed setting transform.position on an object with a Kinematic Rigidbody2D was fine, meaning I currently update the player's position in Update() and the camera's position in LateUpdate(). The camera uses lerping, so to me the jitter seems to be caused by the player updating after the camera, since it now uses Rigidbody2D.position instead of transform.position.

    What would be the best approach to do here if I want to "merge" my "custom collisions/physics" with Unity's built-in triggers and Rigidbody2Ds?
     
  10. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,966
    The "built-in physics" as you put it isn't just collision-response which is what you seem to be deferring to it as. Rigidbodies, colliders, joints, queries ARE built-in physics. My point was I see all the time users saying "not using physics" to mean almost anything. It seems in your definition this means not using collision detection/response only but you are using "physics"; rigidbodies, colliders and queries.

    Anyway, moving on. MovePosition moves when the simulation is run which by default is during the fixed-update. I assume you know this already but that might not be the case? The body/transform will only update then. If the body has interpolation on then the transform will be interpolated per-frame.

    I'm not sure why you're using triggers either. If you don't want bodies/colliders to collide then set then set the body-type to be kinematic which is what you're doing here, kinematic motion. Kinematic/Kinematic and Kinematic/Static don't contact by default anyway.


    You are also free to get the physics system to update per-frame too by using Physics2D.Simulate and passing in the frame time-delta. This way physics is always in-sync and seems to be the best way for you to work.

    In summary:
    - Using "Physics2D.Simulate(Time.deltaTime)" per-frame to simulate (make sure "Physics2D.AutoSimulate" is off)
    - Use Rigidbody2D.MovePosition to move bodies,.
    - Use Rigidbody2D with Kinematic body-type but you can continue to use triggers if you want to.
    - If you use the default FixedUpdate simulate then set the Rigidbodi2D to use interpolate and READ the Transform-position per-frame (it's updated per-frame).
     
    Last edited: Oct 15, 2019 at 9:26 PM
    Pattrigue likes this.
  11. Pattrigue

    Pattrigue

    Joined:
    Nov 24, 2018
    Posts:
    11
    Just to be clear, I'm not using triggers for collisions, I'm using them for, well, triggers. Let's say the player enters an area and the music has to change, this would be an instance where I'd use a trigger to detect when the player has entered said area. Isn't that perfectly fine?

    I was aware the physics update in FixedUpdate, but I didn't know you could disable Auto Simulation and tell Unity when to do it instead, that's amazing! Thank you so much for making these things much clearer.

    I updated the code to call Physics2D.AutoSimulate in Update() with Time.deltaTime. Also I use Rigidbody2D.position now instead of transform.position. DIsabling AutoSimulatePhysics and manually calling Physics2D.AutoSimulate() in Update() solved the camera jitter as well. This has been bothering me for a long time because, as you said, the definitions thrown around online aren't very clear. As far as I understand from your comments, this is the right way to do it in my case, yes?

    Also, as far as I understand, a rule of thumb is that any object that is moved through code should be moved using a Rigidbody instead of transform.position?
     
    Last edited: Oct 16, 2019 at 10:17 AM
  12. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,966
    Yes, the way I try to describe it is that a RigidBody(2D) is your proxy to the Transform. By adding it you are asking it to take control of the Transform and as part of that contract you promise not to modify the Transform yourself and undermine what it's trying to do.

    Note that I do no recommend that you set the position of a Rigidbody2D directly either because when you do that you're teleporting the object from position to position. Always use Rigidbody2D.MovePosition. This is certainly the case if you're using the built-in col-det however if you are sure that this has no effects on your own col-det queries then it's fine.

    Note that for later versions the AutoSimulation (bool) has been replaced by an enum that let's you select between fixed-update, per-frame or script (manual) from within the editor so you don't need to add code to run per-frame either: https://twitter.com/melvmay/status/1164591679535497219?s=20
     
    Pattrigue likes this.
  13. Jichaels

    Jichaels

    Joined:
    Dec 27, 2018
    Posts:
    92
    Just trying to understand, but why would you want to do it per-frame ? Isn't FixedUpdate supposed to be better because it is framerate independant ?

    I'd really like to know where/why one should go fixed or per-frame for a game !

    Thanks a lot
     
  14. Pattrigue

    Pattrigue

    Joined:
    Nov 24, 2018
    Posts:
    11
    This is great stuff, thanks for elaborating. Just to make sure I understood everything correctly, any object that contains a Collider should only be moved via a Rigidbody, while it's perfectly fine to use transform.position to move an object without Colliders, correct? (I.e. the camera doesn't have colliders, so setting its transform.position is fine).

    ALSO: When should I call Physics2D.Update()? After all Rigidbody.MovePosition() calls have been made or?
     
    Last edited: Oct 16, 2019 at 4:27 PM
  15. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,966
    It'll still be frame-rate independent (to some degree) as you pass in the Time frame-delta. Running physics using a variable time-step is perfectly fine depending on how fine-grained and consistent you want the simulation. For shoot-em-ups and many other game-types it matters not. Mostly it'll reduce determinism (not that it's fully deterministic anyway) so for instance if your game requires knowing where a stack of boxes falls exactly then this isn't for you but a lot of games, especially when you start, don't need a fixed-interval physics and all the nonsense that goes along with it i.e. having a separation of physics update and frame rendering. Most of the time you'll just know if your project needs it or not but you can just try it and see.

    Yes but more accurately, colliders are attached to a Rigidbody2D and only the Rigidbody2D should move. Moving a Rigidbody2D should only ever be done by communicating with it directly and never via a transform. If you break these rules then you'll either pay a penalty in performance or certain features won't work such as Interpolation.

    I presume you mean Physics2D.Simulate and not Physics2D.Update? In the future you'll be able to do this automatically but for now, put it in a MonobBehaviour that has its execution order set to late so that it simulates after you've performed all your physics actions such as MovePosition. That said, it really depends on what you're doing. You might want to perform a simulation early (set the Monbehaviour execution order to be early) so you have all the physics results, perform your queries and set moves for the next frame. Only you can decided what you want there.
     
    Pattrigue likes this.
  16. Pattrigue

    Pattrigue

    Joined:
    Nov 24, 2018
    Posts:
    11
    Alright, I've been updating my game to work this exact way and it all works really well. My LeaflyAINEW script from the first post that took over 1 ms to calculate per frame is now at 0.06 ms, for example. Thank you so much. I might return later if I have more questions (if that's okay) but so far this is all I needed to know. I really appreciate the explanations!
     
    MelvMay likes this.
  17. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    1,966
    Ask away. One thing to note is that if you're running physics per-frame then you should turn-off Rigidbody2D interpolation as it's just wasted processing. The reason why we can't just ignore that setting is that we have no way to know if you're manually simulating per-frame or per-fixed-update. As I mentioned above, in the future, we have a setting that allows you to explicitly select per-frame updates. When this is selected then interpolation/extrapolation is automatically turned off. For now though, best to select all your Rigidbody2D and ensure they're off to save you a few cycles.

    Good luck.
     
    Pattrigue likes this.