Need help with Timing my weapon firing.

Ok i have a task that checks if the left mouse button is down and then it would fire a laser if it was. But i need to make it not shoot 50 lasers a second. SO i need to time it somehow so that it only shoots one every second (or whatever the weapon’s fire rate is) The intervals in the manual looked ok but i have no clue how to use them in my case. Does anyone know of something that could help me? :blush:

When someone shoots, set a variable canShoot to False. Then use taskMgr.doMethodLater to invoke a method a few moments later that sets it to True again.
Initially set canShoot to True and make sure the shooting code only shoots when it is True.

Alternatively, for an auto-fire weapon, you could catch your appropriate input events for trigger-pull and trigger-release (likely key and key-up events) and use them to loop and halt a custom Sequence(Func(fireWeaponMethod), Wait(fireDelay)). The advantage to this approach is that it’s init-and-forget, with nothing on your end spinning actively in the background. The disadvantage is that, if the interval picks up from the start every time, players can spam the fire key to fire faster than the usual delay, but if it picks up from wherever it halted every time, you may have a delay before the first shot (this is remediable, but you need to build some extra timers/manual-resets in, likely involving doMethodLater anyway).

I made something like this for my megaman game, where a variable number of shots are needed. It’s a lot like what LKN is talking about. What makes this code beautiful is that I assembled the firing sequence like a piece of music <3… (haha)

The function returns a sequence of Trues (x) and Falses (o) that can be cycled through every frame the mouse is pressed. It delays between shots, and an extra delay between rounds. That way, you can’t spam the keys like LKN mentioned.

ENG is the number of shots per round and invRPD is the number of rounds per frame. In this game, if the player has an upgrade to max his energy out at 8, there is no reload time between rounds. ATK and RNG aren’t used here. For ENG=3 and RPD=1 the function will return:

x o o o x o o o x o o o o o o
^round                  ^extra delay
def busterMelody(ATK,ENG,RNG,RPD):
    note=[True]
    measure=[True]
    notelen=1
    measurelen=1
    invRPD=5-RPD    #MAXRPD=4
    
#    CREATE NOTE
    if invRPD>0:
        for i in range(1,invRPD):
            note.append(False)
            
    notelen=len(note)
    measurelen=ENG*notelen-1

#    CREATE MEASURE
    j=0
    for i in range(0,measurelen):
        j+=1
        if j>=notelen:j=0
        measure.append(note[j])
        
#    ENERGY APPEND
    if ENG<8:   #MAXENG=8
        for i in range(0,int(ENG/(RPD))):
            measure.append(False)
            
        measure.append(False)
        
    return measure

print busterMelody(1,6,1,1)

The key spamming has nothing to do with the structure of the sequence, but rather how you regulate where and when the sequence starts when a key is pressed. If you just started at the first True every time the key was pressed (with no pro-rsoft-style regulation of when firing was valid), you could still spam the firing, and if you just .loop/.pause the sequence, you may still pick up e.g. in the middle of the trailing False stream, creating a delay (while the remining Falses are processed) before the first shot actually fires. I’m assuming in your approach you don’t allow the sequence to be cancelled early or re-started when it’s already in progress, in which case, yes it is a slick way to mock up a rapid/burst-fire system without doing a bunch of manual taskMgr triggering.

I was talking about your second method that only advances through the sequence when the mouse is pressed (no resetting until the end of the sequence is reached).

Yeah, so the point is that you need a function that doesn’t reset the sequence counter to 0 when you let go of the trigger, but leaves it at the last position in the sequence. This is how a real gun would work. I don’t know how you want your laser gun to work. Lasers don’t work at all in the earth’s atmosphere, because the beam ionizes the air causing the laser path to be disrupted. Make it a space game if you want realism.

Huh. That’s an interesting strategy, then- leave the counter running in the background regardless, and only react to it when the trigger is held. It still opens you up to firing lag if you happen to pull the trigger, release, and pull again well after the normal firing delay but still inadvertently in the middle of a False string. The ultimate solution is a combination of both approaches- don’t let the firing sequence restart immediately on release, but set a timer to reset it after the normal firing delay so the next shot is guaranteed to fire immediately… but then remove that timer and return to auto-regulated firing rate if the trigger is pulled again before the timer is out.

That why we use timing to set our fire, one timer for the gun and another timer to reset it during down time or none use. As for a counter, you can use task.time for this.

In my last game, the behavior I wanted was that if you held down the fire key, you would get a repeating fire rate up to a certain number of shots. But I also wanted to reward the player for blistering their thumb by smashing the fire button repeatedly. This allowed them to override the auto-fire timer and spam as many bullets as was allowed by their currently selected weapon.

Different weapon types allowed more/less onscreen bullets.

I implemented it as a level-triggered event bound to the fire keyboard. Every tick of the game engine logic (1/30th a second), it would be checked. I used a single counter that amounted to pauses between allowed event triggers, in a period of 1/30th seconds.

If the fire button was held down, it would check to see if this counter was zero. If so, it would count the number of bullets onscreen and add it to this counter. Then, it would check to see if the counter was below a certain threshold for each type of weapon. If so, engage the fire event. If autofire mode was engaged, it would always set the counter to the minimum of the first weapon’s threshold.

If the fire button wasn’t held down, it would simply reset this counter to zero.

So, it was possible to override the autofire minimum by whacking the keyboard rapidly, but it would stop allowing bullets to be spawned when the threshold value was reached. If you just held the fire key down, the timer would not be reset so you would always get the minimum autofiring time between shots.

Really simple, but worked like a charm. Note that one possibly undesirable side effect of this behavior is that if there is some world boundary for which shots are clipped, the player can fire faster when against that boundary.

I don’t see why you couldn’t implement this logic in Panda, minus the fixed-frame timing.

Which brings up an interesting but relevant glitch in the real MegaMan Legends (or at least Legends 2): when you move forward, the firing timer is reset (likely to sync with animation or support not-firable-while-moving weapons), meaning you can spam shots by holding fire but spamming the up button. Whichever route you choose, be sure to keep track of what regulates the firing when :wink: