In my game, I have barrier that activates once you walk through it.
This is achieved using a standard BoxCollider2D trigger.
At low speeds, this seems to work fine, but once the player moves too fast, they simply move past the trigger, and the OnTriggerEnter2D event is never fired.
On lower framerates, the issue gets increasingly worse. At 20 FPS or lower, the trigger does not work more than half of the time.
The video below shows this happening. Note how it works fine when I slide through the barrier, but does not trigger as soon as I touch the spring which sends me past the trigger:
twxsy5
For the player, I’m using a Rigidbody2D with isKinematic set to true. I’m calling MovePosition to move the player, since the documentation implies that using MovePosition should prevent this very thing from happening:
“Because this feature allows a rigidbody to be moved rapidly to the specified position through the world, any colliders attached to the rigidbody will react as expected i.e. they will produce collisions and/or triggers.”
I should note that my game is not using FixedUpdate() for physics. Rather, I simulate physics at the end of every Update().
What I’ve tried:
Calling Physics2D.simulate multiple times every frame
Slicing the player position into 4 and moving the player to each sub-position, hoping that one sub-position will touch the trigger. This doesn’t work unless you also simulate physics at each sub position, which is computationally expensive and doesn’t actually solve the problem at low enough framerates.
Using FixedUpdate to move the player with a very low Fixed Timestep. This also kind of fixes the problem, but I would have to rewrite my entire game to use FixedUpdate instead of Update for this, and it still doesn’t fix the problem at low enough framerates.
How can I make the player actually activate trigger the triggers they pass instead of just phasing through them?
Is there any way I can simply trigger all triggers between two positions?
I’ve been considering some raycast shenanigans to fix this where I would raycast from the origin to the new position, activating triggers along the way, but I’m not sure this is a good solution either, and it would likely require BoxCasting as well as refactoring all triggers in my game to expose the OnTriggerEnter2D method to be public through i.e. an interface.
Well, that really sucks. Perhaps I should change this specific trigger to use OnCollisionEnter2D instead then.
How would I use movement code to detect the trigger? I usually mark my OnTriggerEnter2D methods at private, so I can’t really access them from the movement class. What do you suggest?
I’m assuming that, before moving the (kinematic) character, you are performing a bunch of queries (raycasts, boxcasts, etc) in order to detect obstacles. If that’s the case, your character should be able to detect anything between current position and target position, and that includes triggers (if Physics2D.queriesHitTriggers is enabled).
This should only notify the character that a trigger has been detected.
You could, just make sure to configure those interactions using the collision matrix. OnCollisionXXX requires these “fake triggers” to be regular colliders now. Because of this, It is important to use different layers, one for kinematic characters/npcs and one for fake triggers. Once this is set up, you can use OnCollisionEnter without problems.
Who knows, perhaps there is a better way to implement this.
This is true, but I do not know how to implement this without having to refactor every single trigger in my game so far.
I can detect triggers with raycasts, sure, but I’m not sure how I would call the trigger’s method from outside considering I always make my OnTriggerXXX methods private - and I would have to also only call it once, too, until you exit and reenter it.
Would I somehow have to use SendMessage for this? And how would I go about ensuring it only happens once each time you enter the trigger?