- Home /
Efficient AutoFire Burst Script Help
I am attempting to make a script where an enemy Ai is just stationary and he he fires 10 shots every X seconds. I have tried a few ways and have one working, but it seems a bit taxing on the iPhone.
The following code, that I thought relevant shows what works so far, but performance-wise, its not so great. It starts with the StationaryGunnerMode:
void StationaryGunnerMode() {
if (!amIDead)
//Gunner();
InvokeRepeating("Gunner", 2, .1f);
}
void Gunner () {
if (_stationaryGunnerCurrentAmmo == 0 ){
StartCoroutine("GunnerReload");
}
if (Time.time - stationaryGunnerFireRate > nextFireTime)
nextFireTime = Time.time - Time.deltaTime;
while( nextFireTime < Time.time && _stationaryGunnerCurrentAmmo != 0) {
StartCoroutine(ShootWeapon());
nextFireTime += stationaryGunnerFireRate;
_stationaryGunnerCurrentAmmo -= 1;
}
}
IEnumerator GunnerReload() {
yield return new WaitForSeconds(3f);
_stationaryGunnerCurrentAmmo = stationaryGunnerAmmo;
}
IEnumerator ShootWeapon () {
//shoots weapon routine
}
If anyone has any thoughts for an enemy AI to fire a ten round volley every few seconds in an efficient manner, your help would be appreciated. If you need more info to assist, please let me know.
Any thoughts?
Thanks
Answer by Fattie · Jul 08, 2012 at 07:20 AM
Don't use coroutines and yield at all, unless you are way way advanced. Just use invoke.
A simply approach is to just order up the next bullet, after the end of the previous bullet.
invoke shootOne 0.1
shootOne...
if no ammo, reload
actually fire the gun
invoke shootOne 0.1
reload...
actually reload the gun
let's say that takes about 2 seconds
invoke shootOne 2.05
make sense? It's just a case of K.I.S.S.
Caveat: your "gunner" code is so incredibly advanced, I just do not understand it in the slightest. What are you trying to do in there, some sort of state machine that tracks shooting or something? It's beyond me, sorry. But you can do continuous gunfire like the above!
PERFORMANCE: you mention performance. It's likely completely unrelated to the issues mentioned here.
Are you repeatedly instantiating a bullet, perhaps? What you do is just make a stash of 100 of them offscreen, and use them cyclincally, usually.
Hope it helps in some way
in general for any beginners reading (not you, voncarp);
For any beginners reading: it's a great pity Unity mention coroutines and yield in the doc. There is no reason at all to use them other than for very advanced situations. Basic everyday video game programming is just long lists of "Invoke" commands. The "Invoke" command are built, technically, using Fancy Stuffs like coroutines and yield. This is precisely why they added "Invoke" ... it's the simple, everyday way to do stuff like this.
For example, routine for a three shot burst ..
_fire() Invoke("_fire", 0.1); Invoke("_fire", 0.2);
ten shot burst ... type it out like that or use a loop.
Starting sequence for a race game..
// let's say it is now 5 seconds before the race starts
Invoke("setALlCarsFirstGear", 3.25);
Invoke("blinkOneSecondLights", 4.0);
Invoke("releaseBrakes", 4.0);
Invoke("beginCrowdRoaringStuff", 3.25);
Invoke("launchAICars", 1.055);
Invoke("beginThreeSecondCountdownAudio", 2);
Invoke("startRaceTimer", 5.0);
..and so on (you'd have dozens of those, to start a race in a real race game)
Yes, I follow somewhat. $$anonymous$$.I.S.S. is exactly what I was looking for. The "gunner" code is the delay between shots based on the invoke repeating. The code is a bit sloppy, but toying around with different approaches, this one works, its just a bit heavy in the profiler, yet it was less heavy than the other approaches I tried. I am not instantiating, just using a ray cast.
I am trying to avoid FS$$anonymous$$ or Update() unless it brings down the ms in the profiler in comparison to this approach.
I haven't tried just Invoke() yet. Let me see if that improves performance and we'll thumb up your answer. Thanks.
I hope it helps in some way. I would definitely never, ever touch "yield" or coroutines for something like this. Just use Invoke.
$$anonymous$$gest, DO NOT do something where you "repetitively" check for a time. Simply command the next thing you want to happen using Invoke.
In other words: any time you have code along the line of "is time passed greater than / less than something or other" then, that code should almost certainly go away, it's not the way to go.
If you have INCREDIBLY subtle needs regarding interacting timers ... very simply, use a state variable to "lock" stuff. (along the lines, if PlayerCannotShootNowDueToPenalty ...)
again i would strongly suggest, NEVER, EVER "test on time" - if you have code like that, something is wrong.
Answer by captaincrunch80 · Jul 08, 2012 at 10:32 AM
I would not use InvokeRepeating() here use the Update() Method instead for your timing.
I would suggest a simple state machine. Using a timer to switch to Fireing-state and executing of the whole stuff (counting shots, start and time animations and sounds) in Update().
Update then switches to waiting or reloading or whatever state you implement after the 10 shots.
You gotta see the Gunner as an agent with a state machine and very simple KI (It should not shoot when not in range, should it?).
You could invoke just something like...
Invoke("fireShot", 1);
Invoke("fireShot", 2);
.
.
.
Invoke("fireShot", 10);
but then you have no control. What if the player hides behind a wall? What if the player gets out of range. What if the gunner dies?
Sure there is CancelInvoke(), but for the most situations that will bring a lot pain, handling the state with invokes and cancel.
About
"Basic everyday video game programming is just long lists of "Invoke" commands."
I heavily doubt that. And I may quote Game Engine Architecture, p. 9 for the classification of Videogames as
"soft real-time interactive agent-based computer simulation"
Games are much, in some simple cases like a sequence even a list of invokes, but NOT JUST a list of invokes.
KISS does not only mean "Keep it simple, Stupid!" too. It also means "Keep it simple and straightforward" and some other interpretations depending on your situation. An that would (IMAO) mean make a small simple agent with a state machine, because a gunner with it's own little brain and states is straightforward and a good abstraction for a "living" game object.
No, I do not agree with that. I never read something like that before and I believe the Unity guys know what they are doing with their engine.
Check out i.e. the AngryBots Demo.
There are only a few files (7) where FixedUpdate is used. In these files there are rigidBody operations like add Force etc. applied. - That is what I would expect from a physics aligned function callback.
In the rest 30+ files, like AI, $$anonymous$$ovement, flying bullets, walking spiders etc. they use Update(). - And why not? There is not much overhead. The Invoke thing has a scheduler overhead too and Update is called every frame in every $$anonymous$$onoBehaviour if you implement it or not.
In the full project there is not even one Invoke call btw.!
AngryBots runs on my old Galaxy SG1 like a charm, so it can not be that broken...
No I wasn't exacly.
The only reason to use FixedUpdate would be for rigid body operations. Depending on your settings, FixedUpdate will be sometimes called twice a rendered frame if the framerate has droped below 50(default). And if it is above, it is don't care anyways.
What could be very performant is disabling a $$anonymous$$onoBehaviour until a condition is met. Like the player enters the trigger => enable behaviour with fire, reloading, AI etc. performed in Update.
Of course, one would not check every frame i.e. if the player is in range. That should happen via callbacks from the physics system or in some cases maybe even a cyclic check with a Invoke callback etc.
And maybe I was thinking a step too far with rotating the gunner-guy smoothly and so on, so the Update() solution would be overpowered that is true.
$$anonymous$$aybe that was our misunderstanding.
Thanks for the help. I was just looking if there was a more efficient way to shoot x bullets/times every y seconds, regardless of distance/sight. Essentially, for this scenario: yes; blind/$$anonymous$$dless AI.
Thanks again for your persistence. An invoke repeating of ten invokes, for this situation, did seem to increase performance in the profiler.