- Home /
(Rapid fire) How to accurately wait for a very short amount of time?
I'm trying to script a rapid-firing gun. I use a CoRoutine to handle one shot, and then use WaitForSeconds(delayBetweenShots) before I can fire again. However, with a gun that shoots 16 rounds per seconds, that's a delay of only 0.0625 seconds, far too short for WaitForSeconds. I wrote a simple test script and indeed noticed that WaitForSeconds is inaccurate for short timespans because it only checks if the value has been reached after the last frame has finished.
Is there a (better) way to have (reasonably) time-accurate rapid-fire?
Answer by Bunny83 · Feb 17, 2016 at 06:42 PM
You can use a CustomFixedUpdate routine which basically does what coolraiman has in his answer. My CustomFixedUpdate class allows you to add as many fixed update method you like with any interval you like. 10000 calls per second is also possible. It uses the same approach as Unity's FixedUpdate method, just in a variable / customizable way.
However this approach has a little problem when you're firing actual physics objects at a high rate (greater than framerate) as those created during the same frame will be spawned at the same position. As long as the bullet can't collide with other bullets this shouldn't be a problem. So using a bullet layer that doesn't collide with itself should fix the problem.
All you have to do is calling "Update" on the CustomFixedUpdate object every frame while you want to fire.
Thanks for the tip and link! Looks like it's waht I need :]
All answers were helpful in their own right, at least for understanding the problem, but I can only accept one and I choose yours because it delivered a completed solution and it only took me about 30 $$anonymous$$utes to adapt it to my own needs. Thanks again.
Answer by Eno-Khaon · Feb 17, 2016 at 05:38 AM
I think this depends on exactly what you're looking to do. If you look at it on the basis that your framerate is your primary limitation, then there are certainly numerous ways to try and make it work.
Regardless of your approach, you're still going to wind up at the mercy of Unity's main Update thread, but options to get around it could include:
1) Counting up deltaTime and generating your bullets based on how much of a backlog you've accumulated. This can be further modified by placing each bullet's initial location (as applicable) relative to how far off from its own personal timing it's become (i.e. if 0.0627 second(s) has passed, start the bullet at a position simulating 0.0002 second(s) travel time). Additional tests would probably be necessary in case framerate would result in bullets starting beyond a nearby target.
2) In the case of multiplayer intent, tell the server when the first shot is fired and it can follow the procedure of "1)" itself.
3) For the timing itself, an alternative may be able to be managed using System.DateTime in an alternate thread, but then you may run into timing desynchronization issues if a poor framerate causes Time.time to stall and fall behind real time. This approach would likely be able to get the most accurate timing, without need to depend on Unity's performance, but it would still be limited on when information can be retrieved again by the main game thread.
Anyway, that's my input on it -- I hope there are better solutions possible, but if so, I haven't thought of 'em yet!
Thanks, that some food for thought. I really wonder how the hundreds of games, indie and AAA, do it with their machine guns and whatnot.
Well, just as a weird, anecdotal point of reference, I know that Team Fortress 2 handles their machine gun fire in a special manner.
Both for the sake of network latency and pseudo-simplicity, rather than firing a huge number of shots per second, theirs ins$$anonymous$$d fires at 1/4 that rate and fires a shotgun blast of 4 pellets at a time. Granted, that one is also instant trajectory, so there's no need to worry about bullet position upon creation in that case.
Answer by coolraiman · Feb 17, 2016 at 02:34 PM
if you have to fire more than one bullt per frame wich is your limitation, you can get around it
by example
float firingSpeed = 0.4f;
float shooted = 0;
float firingStartTime;
bool shooting = false;
void startShooting()
{
shooting = true;
shooted = 0;
firingStartTime = Time.time;
}
void stopShooting()
{
shooting = false;
}
void Update()
{
if(shooting)
{ //calculate how many bullets to shoot this frame. this should work, i made this on the fly
float temp = ((Time.time - firingStartTime)/firingSpeed) - shooted
for(float i = 0; i < temp; i++)
{
shooted++
//your code to create bullets
}
}
}
because of the nature of floats and divisions, you may have a small margin of error in the number of bullet shooted; You can use decimal if you are good with it on performance side;
Thanks. This looks interesting. I will see if I can adapt it to my needs.
Answer by HellsPlumber · Feb 17, 2016 at 05:15 AM
You'll want to take advantage of Time.deltaTime. I'd try something like this:
public float timeSinceLastShot = 0f;
public float timerBetweenShots = 0f;
void Update(){
timeSinceLastShot+=1*Time.deltaTime;//Update counter
if(timeSinceLastShot=>timeBetweenShots){//If it's been long enough to shoot again
shoot();//Fire the weapon
timeSinceLastShot=0f;//Reset the timer
}
Time.deltaTime makes things framerate dependent, so they're more reliable. There's more info on Time.deltaTime over here: http://docs.unity3d.com/ScriptReference/Time-deltaTime.html
Hope this helps!
Unfortunately, this would run into the same issue described in the initial question.
As an example, let's say the game is running at 10 frames per second. $$anonymous$$eanwhile, the gun fires 25 bullets per second. That means you get a frame every 0.1 second and the gun would attempt to fire a bullet every 0.04 second.
The problem arises, in this case, when you simply reset the "timeSinceLastShot" to zero. After 0.1 second, you should have fired 2.5 bullets. Ins$$anonymous$$d, the only check made is whether it's been long enough to fire one more shot, then resetting the counter to zero. As a result of this, you can only fire a single bullet per frame, limiting your fire rate under these conditions to 10 bullets per second rather than an intended 25.
A simple, immediate counter for this is to reduce the value rather than setting it to zero:
timeSinceLastShot -= timeBetweenShots;
In this case, additional bullets can be piled up into a single "while()" statement to ensure that no bullets fail to be generated when the fire rate exceeds the framerate.
But then logistical questions arise:
♦ Do all the bullets fire from the player each frame, or should they all fire from where they logically would at the ti$$anonymous$$g between frames when they should have been fired?
♦ If the bullets aren't instant trajectory, are they also instantiated part-way into their flight path?
♦ If the bullets do start forward, are they being instantiated beyond a target they would have otherwise hit?
Solving these problems can be done with a series of position interpolations and Raycasts, but that still winds up being limited to framerate and Unity's main thread. Every call of Update() will only occur as rapidly as everything can be handled at a time, without regard to the possibility of more complex and elaborate ti$$anonymous$$g.