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.

Question "What is the best way to move a kinematic rigidbody 3D trigger manually?" and related questions.

Discussion in 'Physics' started by BasicallyGames, Aug 31, 2023.

  1. BasicallyGames

    BasicallyGames

    Joined:
    Aug 31, 2018
    Posts:
    91
    I'm working on an entities system for my game (3D, first-person, currently using Unity 2020.3) that pretty much all moving objects will use. Essentially I want it so the player, NPCs, items and other free-moving objects all behave consistently and respond to certain game elements the same way (For example, a conveyor belt will push all entities on top of it in the direction it is going). I also want this system to be as physically simple as possible. No gravity, no friction, no FixedUpdate, all I need is for entities to not pass through static colliders and for entities to be able to trigger each other and static triggers.

    Initially I set this system up by giving each entity a CharacterController and using that to move them. While that worked pretty much exactly how I wanted it to, I quickly discovered that CharacterControllers are way too resource intensive. Currently I'm planning on scrapping the CharacterController to just make my own simple collision system with stuff like the Physics Overlap and ComputePenetration functions.

    The thing is... I'm not sure what the best way to move my entities is. I've been doing a lot of research and I'm getting mixed messages.

    Each entity has two colliders. One is meant for collision with static colliders and will be used to prevent the entity from passing through them. The other is a trigger, meant to activate other triggers. The triggers are usually bigger than the standard colliders. It's my understanding that it's best to have a rigidbody attached to any moving colliders. Since I don't want to use physics to move the entities, I just made them all kinematic. My initial instinct is to, after calculating where they should move to each frame, just change transform.position. Initially I encountered a pretty big performance hit from this, but I fixed that by disabling Physics.autoSyncTransforms (Not sure why this was on to begin with). Now changing transform.position directly in Update seems to work fine, but I want to make sure I fully understand what's going on behind the scenes before moving forward with this method.

    First, I have seen a lot of people on these forums saying that rigidbodies should never be moved with transform.position, but if the rigidbody is kinematic then how else are you supposed to move it? Instead of transform.position, I tried using rigidbody.position which, according to the docs, "is faster than updating the position using Transform.position, as [that] will cause all attached Colliders to recalculate their positions relative to the Rigidbody", but is that still true with Physics.autoSyncTransforms turned off? This didn't even work however, as the movement became extremely choppy and unstable (I'm talking "triggers hitting other triggers on the other side of the map every other frame" unstable). If I am to use rigidbody.position, do I need to make sure it is only changed in FixedUpdate? Or should it just not be used in this way at all? Would this work if I turned Physics.autoSimulation off and manually simulated at the same rate as Update?

    I also tried using rigidbody.MovePosition, but obviously that would need to be in FixedUpdate, which I don't really want to use since my entire game up to this point has avoided it and I get the feeling using it now would introduce a ton of issues.

    I also tried changing both transform.position and rigidbody.position in Update, and while that works properly I don't really know if there's actually any difference from just changing transform.position. Changing transform.position and rigidbody.position in the same frame seems like it would completely avoid any performance benefits of just using the latter.

    I've read that it's not really a big problem in modern Unity to move colliders without a rigidbody, but I can't remove the rigidbody from the entities because without it they don't trigger each other. That's where a lot of the uncertainty comes from, the fact that I have to have a rigidbody attached for desired trigger behavior, but not necessarily use it for anything else.

    One other thing I've considered is not having any colliders on the entities and using Physics functions for both collision and triggers, but I don't see any way to manually call OnTriggerEnter/Stay/Exit and I'd rather not have to create a custom interface and update all my triggers to use it in order to do this. Is a custom trigger system a reasonable option?

    At the end of the day I realize the correct solution is just whatever works best, but I want to make sure I'm not missing some simple solution or Unity quirk that will make for better performance than just changing transform.position. I also just want to make sure I have a correct understanding of what's going on under the hood, because again, I've seen some mixed information about moving kinematic rigidbodies, both from users on these forums and the Unity docs. Any insight is appreciated!
     
  2. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,440
    Here are some some thoughts on specific parts of your post.

    Since Unity 5 (2017) this is no longer a requirement. However, at least one of the triggers must be attached to a Rigidbody in order to launch the OnTrigger events.

    Rigidbody.MovePosition from FixedUpdate. This ensures that the physics simulation is performed correctly, specifically about collisions and friction. Using any other method (Transform.position, Rigidbody.position) just "teleports" the Rigidbody to the new position.

    However, if you're not using physics friction and only want the collision detection, then just "teleporting" the rigidbody might still work for you (I haven't worked in this scenario so I couldn't tell for sure).

    You may always make the OnTrigger methods public and call them externally. Then they could be called from both Unity and your code. This could allow you to develop, experiment and compare both approaches without having to implement special interfaces.

    The Physics class contains some useful methods you might use to detect and handle collisions:

    https://docs.unity3d.com/ScriptReference/Physics.html

    These methods include Physics.*Cast, Physics.Overlap*, Physics.Check* for querying the scene in different ways with primitives. Physics.ComputePenetration uses two arbitrary colliders and returns the collision data.

    The colliders also have specific methods for queries, for example: BoxCollider.Raycast, MeshCollider.ClosestPoint.

    If using Rigidbodies, then the Rigidbody.SweepTest may also be used to predict collisions.

    All query methods may be used to detect triggers as well.

    Regardless using FixedUpdate or not, by default the actual physics simulation and the OnTrigger calls take place at the fixed rate specified in Project Settings > Time (0.02 / 50 Hz by default). This means that, for example, if your game runs at 120 Hz and you move your objects in Update, then the OnTrigger calls won't happen until the next FixedUpdate cycle, which happens at 50 Hz.

    This also has performance implications. If you're moving your objects at 120 Hz but physics detection happens at 50 Hz, then there will be a lot pointless object moving cycles each second, as physics will use only the most recent position in each fixed update.

    Disabling automatic physics simulation (Physics.simulationMode = Script) and calling physics update manually (Physics.Simulate), or configuring physics to run at Update rate, might help with this. However, at high Update rates it might be a much higher load than using the default 50 Hz.
     
    BasicallyGames likes this.
  3. BasicallyGames

    BasicallyGames

    Joined:
    Aug 31, 2018
    Posts:
    91
    Thank you very much for the info and advice! I ended up creating my own collision system where a physics manager updates all entity positions at the end of each frame and then runs the physics simulation to update Trigger/Collider events (Fun side effect of this method: I was able to create a system which allows entities to call OnTrigger/ColliderExit functions when disabling them in a single frame by creating a custom SetActive function for entities by first moving them to an inactive layer, running the physics simulation, and then disabling the disabled entity game objects immediately afterwards).

    The entities have their positions updated by first checking for overlapping colliders and getting pushed out via ComputePenetration if necessary. Then movement is checked using a SphereCast and some simple trig until all that frame's velocity has been used up. Entities are all moved by just updating the transform position. Even if that's not "correct" it works for my needs and doesn't have any noticeable performance impact.

    The system isn't perfect (Some collision is janky) but it's good enough for non-player entities. For the player I just stuck with using a CharacterController since just having one doesn't have a significant performance impact.
     
    Edy likes this.