Search code examples
c#unity-game-engine2dcollision-detectiongame-physics

How can I create a consistent dash speed in Unity 2D?


I have implemented a Dash Function, into the playerMovement script of my Unity2D top-down game. The dash works pretty much as intended with one exception. When the Player object collides with an object, by traveling in the objects direction, and then dashes. The dash becomes extremely slow. I suspect this is because the object blocks the movement of the Player object causing its velocity to equal 0. This is all of the relevant code to my Player's movement.

public class playerMovement : MonoBehaviour
{
    private Rigidbody2D playerRB;
    private float dashForce = 75f;

    public float playerSpeed = 175f;

    void Start()
    {
        playerRB = GetComponent<Rigidbody2D>(); // Grab rigidbody of object using this script.
        position = transform.position; // Get position of this object.
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space)) dash();
    }

    void FixedUpdate()
    {
        playerInput = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
        playerInput.Normalize();
        playerRB.AddForce(playerInput * playerSpeed); // Player movement
    }

    void dash()
    {
        // Disable collisions with enemies and enemy bullets during the dash
        gameObject.layer = LayerMask.NameToLayer("dashIgnore");

        StartCoroutine(enableCollisionsAfterDelay(0.2f)); // Adjust the delay as needed for the dash duration
        Vector2 dashDirection = playerRB.velocity.normalized; // Store the current normalized velocity as the dash direction

        dashTrail.emitting = true; // enabling trail
        playerRB.AddForce(dashDirection * dashForce, ForceMode2D.Impulse);
    }

    IEnumerator enableCollisionsAfterDelay(float delay)
    {
        yield return new WaitForSeconds(delay);

        // Enable collisions with enemies and enemy bullets after the dash
        gameObject.layer = LayerMask.NameToLayer("Player");

        dashTrail.emitting = false; // disabling trail
    }
}

Some of the solutions I have tried were to use Rigidbody2D.velocity for the dash function instead of the RigidBody2D.AddForce method, however, this produced the same bug with a less satisfying dash movement so I reverted to my previous code. I also considered trying to calculate the difference in velocity from when the player was moving and still, to try and have a consistent dash speed regardless of the players movement speed. My attempts at this produced some unfavorable results so I scrapped the idea, although I think this would be the best solution to my problem.

Any help, ideas, or recommendations are appreciated! Thanks!

**Edit: After more Debugging I've deduced the problem is caused by the Rigidbody.velocity equalling zero when the dash is used. This causes the dash to no longer function.


Solution

  • I am glad to say I was able to solve this one relatively easily. After some debugging, I found that Unity collision detection continues to update the player's position several times after the initial collision. Since my movement relies on the player's motion I believe the dash force was working in the opposite direction of my player's movement force, thereby causing a slowing effect.

    My solution to this was quite simple. I started by creating two new variables, lastVector and threshold.

    private Rigidbody2D playerRB;
    private float dashForce = 75f;
    
    public float playerSpeed = 175f;
    
    private Vector2 lastVector;
    private float threshold = 0.1f;
    
    void Start()
    {
        playerRB = GetComponent<Rigidbody2D>(); // Grab rigidbody of object using this script.
        lastVector = playerRB.velocity;
        position = transform.position; // Get position of this object.
    }
    

    The idea behind this was to save the player's last velocity and ignore any changes in velocity with a magnitude greater than or equal to the threshold. The way I did this was by adding a new line to my FixedUpdate(). Note: Moved player input code to Update() as recommended by @ComaneanuMugurel.

    void FixedUpdate()
    {
        if ((playerRB.velocity - lastVector).magnitude >= threshold) lastVector = playerRB.velocity;
        playerRB.AddForce(playerInput * playerSpeed); // Apply player movement forces
    }
    

    Finally, after storing the player's previous velocity, I just replaced the variable within my dash() function. This way it only uses the player's last vector.

    void dash()
    {
        // Disable collisions with enemies and enemy bullets during the dash
        gameObject.layer = LayerMask.NameToLayer("dashIgnore");
    
        StartCoroutine(enableCollisionsAfterDelay(0.2f)); // Adjust the delay as needed for the dash duration
        Vector2 dashDirection = lastVector.normalized; // Store the current normalized velocity as the dash direction
    
        dashTrail.emitting = true; // enabling trail
        playerRB.AddForce(dashDirection * dashForce, ForceMode2D.Impulse);
    }
    

    If anybody has any questions about my solution feel free to ask!