[TIP] Restricting Manual Fast Firing

Hey everyone.

As you know, the aim of this lecture is to spawn projectiles based on a firing speed (and some other things).
The method in the lecture (using InvokeRepeating) is great and simple. However, it gives the player the ability to bypass the shooting speed, and potentially shoot really really fast by spamming the space-bar.

Now, it might not be elegant, but I’ve found a solution:

  1. We set up our variables, much like in the lecture, with our projectile prefab, firing rate and projectile speed:
	public float fireRate;
	public float projectileSpeed;
	public GameObject projectilePrefab;
	
	private float nextFire;

Notice however, that we also add a private float called nextFire. This will make sense later.

  1. In our update method, we check to see if the player is holding down the space key. For this, we use Input.GetKey
	void Update () {
		if (Input.GetKey(KeyCode.Space)) {
			FireProjectile();
		}
	}

If the play is holding down space, we call the method FireProjectile.

  1. In our FireProjectile method, we will now do our calculations. Ignore the if statement for now, and imagine we just run the code inside of it.
	void FireProjectile() {
		if (nextFire <= Time.realtimeSinceStartup) {
			float nextFire = Time.realtimeSinceStartup + fireRate;
			GameObject projectile = Instantiate(projectilePrefab, this.transform.position, Quaternion.identity) as GameObject;
			projectile.rigidbody2D.velocity = new Vector2(0f, projectileSpeed);
		}
	}

When the method is called, the first thing we do is set the nextFire float to Time.realTimeSinceStartup. Time.realTimeSinceStartup is simply a float representing the amount of seconds since the game started. When we set nextFire to the real time plus our fire rate, we get the time at which the player is allowed to fire his/hers next projectile.
If we now consider the if statement again, it checks to see whether enough time has elapsed since the last projectile was fired. If not, we wont fire another one. If enough time has elapsed, we will fire a new one.

I hope this made sense, and was somehow useful. If there’s some part of this you don’t understand, please do not hesitate replying to this thread. I will answer all the question I can.

Best of luck, and keep on making awesome games!

-FP

1 Like

I noticed the same issue while going through this section of the course. My solution was a little different. Mine looked like this:

	void Update () {
	lastfire += Time.deltaTime;
	if(Input.GetKeyDown(KeyCode.Space)){
		if(lastfire >= firerate){
			InvokeRepeating("Fire", 0.00001f, firerate);
			lastfire = 0f;
		}
	}	

There is still an issue that if the player goes from holding the fire button to pressing back to fire you can either get stuck not firing or fire off two shots faster than the firerate.

Interesting. Much the same, but a lot more compact.

For some reason I don’ really like the InvokeRepeating way of doing things.
My background is Garry’s Mod lua, where using “timers” can be a unstable and unreliable thing, so maybe I’m just tainted.

Thanks for sharing! :smiley:

Harry game making!
-FP

@Fillipuster i tried your method and didn’t have any success. But i am a new coder so i could have done something incorrect but it seems pretty simple.

@PlutoniumDragon I used your method and experience the problem you spoke of. If you hit “space” quickly twice, and on the second hit, hold it down, it doesn’t fire automatically.

In my mind this has something to do with “GetKeyDown” Because when you press space down quickly twice, obviously “lastfire” will not be greater than the fire rate because of how quickly it was pressed. So on the second press it has already decided that it’s not going to fire.

I’m not sure what to do, I’d appreciate anybody voicing their opinion. But my theory is either changing GetKeyDown, into GetKey, or adding and “else” section.

When i replace GetKeyDown with GetKey, it actually works, IE, when you rapid fire space bar it doesn’t glitch out when you press it twice quickly, however if you continue to hold it multiple streams of bullets come out indefinately.

I am trying to think up an else function to prevent this but my novice skills are causing my brain to collapse on itself. It’s like its on the tip of my tongue but i don’t have the skills to write the code. Such is life for the novice coder.

It would be awesome if one of the experts could give their opinion. @Joao_Dalvi or @ben ?

1 Like

Okay so after some fiddling around I managed to get a working solution, albeit probably more of a “hack” than a fix. And I would still love to hear from someone else in regards to the proper way of doing it, but basically what i did was I created a new boolean that would apply itself as false inside of the if statement once space was pressed. Since you’re still holding space, changing the boolean doesn’t effect the current action, which is repeating fire. So by changing the boolean to true inside of the same method, it stops multiple streams of bullets coming out of the “GetKey” command.

Declaration/Initialization

public float lastfire;
private bool fireRapid = false;

Code block

void Update ()
{
	lastfire += Time.deltaTime; 
	//Example, if your fire rate is 5 seconds, and lastfire is less than 5 seconds
	//it won't fire unless space has not been pressed for 5 seconds.
	//so if your fire rate is 0.5 seconds, and you press space twice very quickly,
    //within (0.2 seconds) using GetKeyDown, 
	//the second press will not register because 
    //variable lastfire still is not greater than firingRate
	
	if (Input.GetKey (KeyCode.Space)) { 
    //I changed GetKeyDown to GetKey, but starting recieving multiple streams
    //of bullets by holding space
		if (lastfire >= firingRate && fireRapid == false) { 
    //this boolean is set to false by default 
    //which means by holding space you will rapid fire
			InvokeRepeating ("Fire", 0.000001f, firingRate);
			lastfire = 0f;
			fireRapid = true; 
    //once space is pressed and held, boolean is changed to true,
    //preventing multiple streams of bullets
		}
	}
	if (Input.GetKeyUp (KeyCode.Space)) {
		CancelInvoke("Fire");
		fireRapid = false; 
    //once space is released, boolean is changed to false again
    //to allow the holding of space.
	}
1 Like

Hello Wicked, nice solution over there, I’m not sure if it would solve the problem of the player rapidly pressing the fire button though, since the same event that cancels the invoke is the event that sets the flag to false.

I don’t like using InvokeRepeating for things like this (I don’t like using it at all to be honest) , I would probably just use the update loop and a timer to control the fire rate. Or a Coroutine (but it could cause some other problems).

I would use something like this:

Void Update()
{
lastfire += time.deltatime;
if (input.getkey(keycode.space) && lasfire>=firingrate)
{
Fire();
lastfire = 0;
}
}

Sorry for the messy code, I’m on my mobile right now and the auto complete don’t help much :s

Actually I would do something a bit different to make it smooth, this way the interval between every shot won’t be always the same since it is frame dependant and not time dependant. You shouldn’t be able to see it in very high frame rates but would surely see it in low performance situations (on the actual web build for example). I would probably use the fixed update to handle the fire() call and time counting, and I would make it check for the boolean “isShooting” too (turned on and off depending on the player input and within the update loop since input methods are problematic in fixedupdate).

Privacy & Terms