OnTriggerStay performance bottleneck

Hi, so I’ve experienced some serious framerate drops lately and figured I’d check profiler to see what’s up.
Apparently it’s all because of OnTriggerStay (I use a lot of them, mostly for AI to spot different objects or other NPCs) and checking object tags.


Why would something so simple as getting object’s tag cause any performance impact at all?
As you can see on the pic, 17 ms (tag thing) is only half of 35 ms (OnTriggerStay), does that mean the other half is “internal” cost of OnTriggerStay function, that can’t be reduced?

You should use CompareTag() instead of tag == “”

The old method is slower and generates garbage

    void OnTriggerStay(Collider other)
    {
        if (other.tag == "Player")
        {
        }
    }

Instead use

    void OnTriggerStay(Collider other)
    {
        if (other.CompareTag("Player"))
        {
        }
    }
7 Likes

I don’t think there’s any way to call 55k methods a frame and get a decent framerate. You should read this blog post which details things that are adjacent to this.

@Pengocat thanks, I’ll try that.
@Baste I understand but it’s not like I’m controlling how often OnTriggerStay is called, it’s a Unity thing. And I do need the “Stay” one, not “Enter”.

@Pengocat ok thanks a lot, literally over 10 times less ms now, lag’s all gone.

1 Like

You might want to consider using other techniques than OnTriggerStay, or not even using triggers period.

How many AI agents do you have? That’s a LOT of calls.

Also, why do you ‘need’ it to be OnTriggerStay?

Lets consider how I usually do things… first off I don’t use Triggers, but I’ll put it into the context of triggers anyways for context sake.

…

OnTriggerEnter - the entering object gets placed into an ‘observed list’. We may only add it if it meets certain criteria (like a specific tag type)
OnTriggerExit - the exiting object gets removed from the ‘observed list’.
OnSomeUpdate - I have a generic update that may run ever 0.2 seconds, or even more (AI doesn’t have to be calculated constantly). The observed list is usually sorted in some manner so I can easily find what is the closest, or the most important thing in the observed list. And with this update I do any actual update.

This way we’re not running OnTriggerStay for EVERY trigger I happen to be intersecting.

6 Likes

I considered lists but aren’t they also slow, when you constantly Add/Remove from/to them?

Not if you use the correct kind of list.

And it depends on relativeness.

Can it be slow to remove from a list/set? Sure. Can it be MORE slow to have OnTriggerStay called for EVERY trigger in your vacinity leading to 55k calls to it? Yes.

Personaly though… I usually use a HashSet or something which as a O(1) speed for adding/removing.

1 Like

Good point, I might switch to lists / hashsets if things get too bad.

You still have some garbage allocation going on coming mainly from the Coroutine it seems. Perhaps instead of starting a new Coroutine you should leave it running until it is no longer needed?

The coroutine thing is NPC’s action, like swinging a sword or using a bow, it’s only running if there’s someone in sight.
But yeah I see what you mean, I could totally make it repeat instead of re-starting every time. I’ll fix it in a $50 DLC.

I would second @lordofduct 's idea. If you’re dealing with a lot of objects inside a trigger, adding/removing them to a list when they enter/exit is usually much better, however, it really all depends on what your trying to do exactly.

In your case it might be best if you just added all possible AI targets to a single global list and then iterated over all of them to see if any are in range of a particular agent. This might sound slow, but it could actually be much faster than just having triggers on every AI agent in the scene. Once again it all really depends on what’s most efficient for your particular situation.

3 Likes

I switched to hashsets and now all runs smooth. Profiler graph doesn’t go through the roof no more.