Can I force Unity to do things within one frame?

Hi guys, I’m not sure what the problem is, but here’s my dilemma:

I’m trying to instantiate an arrow in a target. Directly at the target. The arrow doesn’t move to the target, it has to be there, instantly, on the same frame as your click is received.

The target moves downwards, and in an infinite loop. If you shoot it when it’s ‘shootable’, you get an arrow in the target, otherwise you miss. So I have a trigger [the little magenta thingy in the attached pic] and if the target overlaps it, it’s ‘shootable’, it spawns an arrow at the target/at 0 0 0, and parents it to the target object.

So the problem is that this works only on low target speeds, if I have a fast moving target, the arrow is instantiated a bit too late, the faster the speed, the more imprecision/delay I get. I need this system to be 100% precise. Checking the triggering for and OK to shoot, instantiating the arrow at the target, and parenting it to the the target, this all has to be inside one frame, it can’t happen in 2 successive frames or the system doesn’t work [at high speeds].

Is it possible that the trigger needs 1 frame to check for collisions??
Any ideas?
Thanks!!

2553741--177658--problem.jpg

So you are detecting a hit with a ontriggerenter or something?
As in, when the player clicks to fire, if the target is currently inside the detection trigger we will count it as a hit and spawn the arrow.

As far as I know, physx needs a one frame delay to do its calculations for things like ontriggerxxx and there is unfortunately no way to change that.

An alternative would be to use a raycast to detect the hit point and then spawn the arrow there. Casts seem to work with no delays, including giving accurate results for moving colliders even if a physics update did not happen (the documents say they wont).

So when your player fires, just shoot a spherecast or something from where the player is to where you have your detection zone and if the cast returns a collider, then there was a hit.

1 Like

yes, I am using ontriggerenter.
so I can’t rely on triggers for anything precise? I was thinking of using triggers extensively… in my next project. I presume they’re very cheap. although I had problems with triggers before. raycasts are expensive though, not for this project, but used on a massive scale they’d be a problem.

so a bit unrelated… but how do you normally deal damage via a missile, if you can’t use triggers? I mean with a normal, high speed bolt, arrow, bullet… that travels thru space, you can totally go through the target and miss it [cuz it ‘touches’ the target between frames, so in fact it never actually touches the target].

anyway thx, I’ll try to raycast.

The 1 frame delay makes triggers and what not pretty useless for precision. I cant even use rigidbodies as a charactercontroller due to that 1 frame delay. I’ll run into a wall and go inside it for a frame.

As for performance, I didnt test it, but I feel the ontrigger would be more performance heavy than a raycast. Not only that, but I think ontrigger and oncollision produce garbage every time they are called, which can lead to nasty lag spikes in your game due to garbage collection.

What people do to detect collision between fast moving objects is either decrease your timestep, which just runs the physics more often and is less performant, or you use casts from your rockets previous position to its current.
You can use a Physics.SweepTest for rigidbodies, but sometimes a single raycast will do.

1 Like

Thanks HM! Yea I read a bit about this. But I had no idea triggers are so expensive!! I thought they’re the most basic stuff. And I read about raycasting being very expensive… maybe it’s a matter of how you use raycast? As in… not raycasting on every frame, but only on demand, like with a trigger… And is a raycast cheaper if you don’t get hit details?.. something like that… I’m a bit confused about these raycasts atm. : D

For my next project I’m looking for something cheap and reliable, maybe calculating the ballistics, if it can be done cheaply, and delivering damage at [time and] location. But if I have crossbow bolts… which are both fast and large/visible [unlike bullets], I do want them to fly, visibly, and enter targets. Even if the bolt flies beyond the first target, cuz of lack of proper collision, and hits a second target… haha, that might be insane… but I still want to deliver damage to the first target as well.

I wonder what other games use, cheap ways for large numbers of AIs.

Anyway, I used a Linecast between 2 points to check the target availability, it worked well. I didn’t need to shoot from the archer, it’s the game manager that does the linecast. Is linecast cheaper than raycast btw? :smile:

Cheers!

Well lets find out shall we ^^

Click for code

using System;
using UnityEngine;

public class TestCastPerformance : MonoBehaviour
{
    public int calculationIterations = 10000;
    public int loopsForAverage = 20;
   
    System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();

    void OnEnable()
    {
        DoTestCastPerformance("Raycast", () => Physics.Raycast(Vector3.zero, Vector3.down));
        DoTestCastPerformance("Linecast", () => Physics.Linecast(Vector3.zero, Vector3.down * 1000));

        Debug.Log("Average calculation time over " + loopsForAverage + " loops, " + calculationIterations + " iterations.");
    }

    void DoTestCastPerformance(string label, Action action)
    {
        double averageTime = 0f;

        for(int i = 0; i < loopsForAverage; i++)
        {
            stopWatch.Reset();
            stopWatch.Start();

            for(int j = 0; j < calculationIterations; j++)
            {
                action();
            }

            stopWatch.Stop();
            averageTime += stopWatch.Elapsed.TotalMilliseconds;
        }

        averageTime /= loopsForAverage;

        Debug.Log(label + " = " + averageTime + " Milliseconds.");
    }
}

Using that, I got these results.

Raycast no hit = 3.854795
Linecast no hit = 5.30248

Raycast with hit = 8.156305
Linecast with hit = 9.33258

Distance of object hit didnt seem to really make a difference from very close to 1000 away.
I am also not using the out hitInfo, but I did test that and while that does decrease performance a tiny bit, I think that is mainly having to do with just the fact that we have another parameter, and nothing to really do with the physics side of things.

Testing OnCollision / OnTrigger would be a bit trickier. One way would be to just have a bunch of objects in the scene with OnTriggerStay so it is constantly being called, and then look at the unity profiler to see the performance.

Now just in case you didnt get this speech already, it is important for you to not get so caught up in this performance stuff unless you are actually in a position where you are needing the performance.

Since I am pretty inexperienced, I cant really give you an answer on your question on massive amounts of AI shooting and what not, so good luck on that =).

1 Like

Awesome!! Thanks for testing!! : D
So wait, you calculated the average performance for one raycast? Just 1 raycast takes 8ish ms? O__O Or am I completely misunderstanding… I’ll try to test too and decipher this tomorrow : >

Hehe, not one raycast, but 10,000.
You can see in the code its in a for loop set to run 10,000 times.

There can be many factors that would possibly increase the time such as how many colliders are near the raycast. I had hardly any near the raycast when doing the test.

OK, I got it, so the Raycast shoots an infinite ray that collides with all objects in its path. And the Linecast is only faster if you significantly reduce the distance/number of objects collided with. But Raycast scales incredibly well, from 3.5ms at no hit to 5-6ms for 1 cube, to 7-8ms for 100+ cubes. I tested with 1000 cubes and got the same 7-8ms.

This is surprisingly fast, I wonder why some AIs using Raycast are slowing down performance. Maybe it’s how they use it? If I manage to only Raycast on demand, instead of constantly/every frame, it should be really cheap.

The good thing about triggers tough, philosophically speaking… is that you get a volume… : P and they are only computing if triggered. Now a brute force triggers test would be good… : D

Thanks again Monk which is Hidden! : ]

1 Like

You should have a look at this

all kinds of physics methods.

Raycast dont need to be an infinite length either.
You can also kind of get a volume check with Physics.CheckBox, Physics.OverlapBox, Physics.CheckSphere, etc…
They dont return the point where the intersection happened, but do let you know if a collider is touching. Its basically OnTrigger, just with simple shapes.

1 Like

Interesting… but a CheckSphere needs to be called every frame, if I’m understanding this right.
It also checks the object you’re using as caster. I can’t set the caster to Ignore Raycast since it needs to be visible to other Raycasts.
OverlapSphere has similar issues, and it also ‘returns an array with all colliders touching or inside the sphere’.
So both cases are expensive and problematic.

The good thing about triggers is that they’re only computing if triggered. And I do need that trigger enter and exit functionality. Like a soldier being able to hear you when you are nearby, vs not hear anything after you’ve exited the trigger. With hundreds of soldiers you can get a lot of triggering… still, I presume, much cheaper to just get entry and exit events. As opposed to arrays calculated every frame, for every soldier.

Yes it does need to be called every frame. One thing Id assume is that even though ontrigger/oncollider does not get called unless a collider is touching, surely in the backend there are calculations going on every fixed update to check if a collider is touching. It doesnt just magically know, although its probably more optimized and such, but dont just think that doing a checksphere every frame is bad compared to ontrigger unless if you actually tested it (which I have not).
OnTrigger doesnt seem to cause garbage (OnCollision does) and if the collider does not have a rigidbody on it, then that collider is reliant on a rigidbody in the scene to touch it. So maybe it is better to use trigger for performance. I wouldnt say they are only computed when triggered, but since it could just rely on another objects rigidbody, then it might be better then having a bunch of CheckSpheres all over.

As for ignoring the casters collider, I also ran into that issue. The fix was I created a physics layer (or just use the ignore raycast layer) in the inspector that was dedicated for temporarily placing my casters collider before doing my cast and then when I am done with the cast, I put my collider back into its original layer.
Since I had a need for doing this a lot, I created a wrapper to the normal cast methods that automatically does this layer swap. This does lower performance since more is being done per cast, but I found it handy.

I also will just use the NonAlloc version of overlapsphere and such to avoid garbage collection.

Thx for sharing all this, I’m sure I’ll find a way to test this stuff soonish.

1 Like