I say this only because this code is not yours: this code is horrific. I would not use any code from this source. Some of the problems:
Comparing against / adding offsets to Time.time
. Because this is a single-precision float storing the total number of seconds the game has been running, it grows in magnitude and loses precision over time. If you're making a game that can be suspended and resumed, it might accumulate more than 9 hours of continuous play across multiple sessions, and start showing millisecond-sized errors in your double-tap timings.
Calling a variable a distance
but using it as an Impulse
. If your code advertises to the developer tuning it that putting a "5" here means it will dash 5 units, it should do the math to ensure that the dash goes exactly 5 units of world space distance. Not apply an impulse of 5 Newton-seconds. (Do you know how far an impulse of 5 Newton-seconds will push a character? I sure don't, and it's not the designer's job to do that conversion!)
Using hard-coded key codes (KeyCode.A
) instead of logical input actions (like "Dash Left"). This will make it harder if you ever want to port to a platform that does not use a keyboard (like gamepad controls or touch controls on mobile), or if you want to support input remapping options the player can choose. (Which I'd recommend, if you plan to sell your games commercially. Input remapping is important to player comfort and accessibility)
Magic numbers that aren't set as named constants or exposed as tuning parameters. If you ever wanted to change the double-tap window for example, you'd have to change the 0.3
in multiple places. Missing one would give you inconsistent double-tap timings going in one direction versus the other, while doing a global find & replace might alter 0.3
s that had nothing to do with double-tap.
Be very careful copying code you don't understand from YouTube. Just because someone has a YouTube channel does not mean they're a good programmer, or that what they're showing you is a good way to do things.
So, how would we do this if we wanted to not just make something quick to throw up on YouTube, but actually build a game we could ship cross-platform down the line if we wanted to?
Rather than rolling your own code for timing a double-tap, let's use what Unity already has built into the new Input System package. This will be a bit of an adjustment at first, but it will make your life much easier if you need to support alternate control mappings in future, so it's worth learning how to use it.
Follow the setup instructions here to install and enable the Input System in your project. Note that this will break anything relying on the legacy Input Manager (Input.GetButton
/Key
/Axis
...), so you may have a few bits of code to refactor, but better to do that early in a project when you don't have much code than at the end when changing anything is that much harder.
In your assets folder, right-click and select Create -> Input Actions, and set up an Input Actions Asset something like this:

Note that I've made two logical actions for Dash Left and Dash Right. Each one is set up as a Button-type action, and listens to three bindings: keyboard arrow keys, WASD keys, and the D-Pad of a gamepad. Already the game is supporting multiple input types out of the box, with no extra code on our part. And, these are also easier to remap if you want to add that feature in future. Each of these bindings is configured to fire on "Multi-Tap" using the project default settings - so you have one place where you can tune every double-tap timing at once, uniformly.
You can add more input actions to the list, or even add alternative mappings for menus or different modes (like if your character can switch between forms or vehicles that need their own distinct controls).
We can add a script that works with the Input System like this (there are other methods too, but this is a convenient one):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
[RequireComponent(typeof(Rigidbody2D))]
public class Dash : MonoBehaviour
{
public float dashDuration = 0.2f;
public float dashDistance = 1.0f;
public float cooldownDuration = 1.0f;
Coroutine _dashInProgress;
// Auto-populate our Rigidbody reference.
[SerializeField, HideInInspector]
Rigidbody2D _rb;
void OnValidate() {
if (_rb == null) _rb = GetComponent<Rigidbody2D>();
}
public void OnDashLeft(InputAction.CallbackContext context) {
if (context.performed) TryDash(-1);
}
public void OnDashRight(InputAction.CallbackContext context) {
if (context.performed) TryDash(1);
}
/// <summary>
/// Attempt to perform a dash move.
/// </summary>
/// <param name="direction">1 to dash right, -1 to dash left.</param>
/// <returns>true if dash is performed, false if dash is on cooldown.</returns>
public bool TryDash(int direction) {
// Don't start a new dash if we're currently dashing, or on cooldown.
if (_dashInProgress == null) return false;
// Otherwise, start a new dash, and remember that it's in progress.
_dashInProgress = StartCoroutine(PerformDash(direction));
// Report success to whoever called this.
return true;
}
IEnumerator PerformDash(int direction) {
float targetVelocity = direction * dashDistance / dashDuration;
// Because this was in your source script - unclear to me why you need
// to change gravity scale, but I assume you have good reason.
float cachedGravity = _rb.gravityScale;
_rb.gravityScale = 1f;
// Stop any fall or rise in progress and dash horizontally.
_rb.velocity = new Vector2(targetVelocity, 0);
// Every physics step, set our horizontal velocity to
// achieve the target distance over the dash duration.
for (float t = 0; t < dashDuration; t += Time.deltaTime) {
yield return new WaitForFixedUpdate();
// Allow the character to start to fall during a dash,
// by preserving the y component of the velocity.
var v = _rb.velocity;
v.x = targetVelocity;
_rb.velocity = v;
}
// Put gravity back the way we found it.
_rb.gravityScale = cachedGravity;
// Prevent a new dash from starting until we're past our cooldown.
yield return new WaitForSeconds(cooldownDuration);
// Clear out the dash in progress, so a new one can start.
_dashInProgress = null;
}
}
Now add a Player Input component to your character object, and set its Actions to use the Input Actions Asset and Gameplay mapping we just created.
Set its Behaviour to "Invoke Unity Events", and wire up the Dash Left / Dash Right gameplay actions to invoke our DashLeft()
/DashRight()
methods, respectively.

And there we have it. Dash on double-tap, without having to manage button mappings and press intervals in our own code. This style is much more robust to future changes than the YouTube example, and as a bonus, its distance
variable actually measures a distance in worldspace units!
isDashing
to true/ false in your Coroutine but never check against the value. Where are you settinglastKeyCode
? – Zibelas Feb 13 '22 at 12:49