How to move Rigidbody2D on Player a certain amount at a certain speed, for a dash move and knockback?

Hi everyone.

Im currently having two problems for, I think, the same or similar reason.
I’m working on a top-down game to learn Unity and C#. I have a PlayerController in which I’m using the Rigidbody2D’s velocity to move the player.

I would like to implement a dash move, allowing the player to move a certain amount in any direction at a fixed speed, as well as a knockback when the player is hit, that should behave in a similar way: knocking back the player a certain amount, at a certain speed. I do not understand at all how to do this. Below is the code of my PlayerController, with both of these things sort of working, but not correctly. I’m pasting the whole code, I guess a lot of it is irrelevant to the problem, but I’m leaving it in in case it isn’t.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour {

	//Speed Variables
	public float speed;
	public float walkSpeed;
	public float runSpeed;

	//Animation Variables
	private Animator anim;
	private bool PlayerMoving;
	public Vector2 lastMove;
	public float DirectionX;
	public float DirectionY;

	//Knockback Variables
	public float knockback;
	public float knockbackLength;
	public float knockbackCount;
	private Vector2 knockbackDirection;

	//Dash Variables
	[SerializeField] private float dashSpeed;
	public float dashCount;
	[SerializeField] private float startDashCount;
	private Vector2 dashOGpos;

	private Rigidbody2D rb2d;

	private Vector2 movement;

	[SerializeField] private float timeBetweenDamage;
	[SerializeField] private float startTimeBetweenDamage;

	void Start ()
	{
		rb2d = GetComponent<Rigidbody2D>();
		anim = GetComponent<Animator> ();
		knockbackCount = 0;
	}

	void FixedUpdate()
	{
		PlayerMoving = false;

		float moveHorizontal = Input.GetAxisRaw ("Horizontal");
		float moveVertical = Input.GetAxisRaw ("Vertical");

		if(knockbackCount <=0)
		{
		//Movement via Rigibody veloctiy:
		movement = new Vector2(moveHorizontal, moveVertical);
		rb2d.velocity = movement * speed * Time.deltaTime;
		} else{
			Debug.Log("Should knock back");
			//transform.Translate(knockbackDirection * knockback * Time.deltaTime);
			//rb2d.AddForce(knockbackDirection * knockback * Time.deltaTime, ForceMode2D.Impulse);
			rb2d.velocity = knockbackDirection * knockback * Time.deltaTime;

			knockbackCount -= Time.deltaTime;}

The part above handles the player’s general movement as well as the Knockback. It sort of works, but the main problem I have with it, it only does when I set the float knockback to an exorbitantly high. If I set it to 1000 for example, the player gets knocked back a way too big distance, but at a speed that’s too slow. If I set it to like 4000 the speed is better, but the distance of course is even bigger. What I want is movement at a short distance, at high speed. The Vector2 knockbackDirection is being set in a function further below, this works as intended. As you can see I’m currently doing it by manipulating the Rigidbody2D’s velocity, however I tried it as well with AddForce or even manipulating the object’s transform directly (which isn’t a good idea I guess).

//Running via Spacebar, or 0
		if (Input.GetKey ("space"))
		{
			speed = runSpeed;	
		} else
			{
				speed = walkSpeed;
			}

This little part above changes my character’s speed by pressing space, in order to run. This works fine.

//DASH
		if(dashCount <= 0f)
			{
				if(Input.GetKey("x"))
				{
					Debug.Log("ShouldDash");
					dashCount = startDashCount;
					rb2d.velocity = movement * dashSpeed * Time.deltaTime;
				}
			} else
				dashCount -= Time.deltaTime;

Now here’s the dash part above that I’m having trouble with. Again, like the knockback, this kind of works, but not nearly as well as I like it too. Again, I have to set dashSpeed to a similarly high amount like with the knockback. If I set it to 1000 the player dashes a tiny distance but way too fast. If I increase it to 5000, the distance is closer to what I want, but of course, it’s way too fast.

//Animation NEW
		if (Input.GetAxisRaw ("Horizontal") > 0.01f || Input.GetAxisRaw ("Horizontal") < -0.01f)
		{
			PlayerMoving = true;
			lastMove = new Vector2 (Input.GetAxisRaw ("Horizontal"), 0f);
		}
		if (Input.GetAxisRaw ("Vertical") > 0.01f || Input.GetAxisRaw ("Vertical") < -0.01f)
		{
			PlayerMoving = true;
			lastMove = new Vector2 (0f, Input.GetAxisRaw ("Vertical"));
		}
				anim.SetFloat("MoveX", Input.GetAxisRaw("Horizontal"));
				anim.SetFloat ("MoveY", Input.GetAxisRaw ("Vertical"));
				anim.SetBool ("PlayerMoving", PlayerMoving);
				anim.SetFloat ("LastMoveX", lastMove.x);
				anim.SetFloat ("LastMoveY", lastMove.y);
	}

	void Update()
	{
		if(timeBetweenDamage >= 0)
			timeBetweenDamage -= Time.deltaTime;
	}
	void LateUpdate ()
	{
			DirectionX = Input.GetAxisRaw("Horizontal");
			DirectionY = Input.GetAxisRaw("Vertical");
	}

	void OnTriggerEnter2D (Collider2D other) //Detect Collisons with Enemies etc.
	{
		Debug.Log("Got hit");
		if(timeBetweenDamage <= 0)
		{
			if(other.gameObject.CompareTag("Enemy"))
			{
				this.GetComponent<PlayerHealthManager>().HurtPlayer(other.GetComponent<HurtPlayer>().damageToGive);
				this.knockbackCount = this.knockbackLength;
				other.GetComponent<Transform>();

				knockbackDirection = (transform.position - other.transform.position).normalized;

				Debug.Log("knockbackDir:" + knockbackDirection);

				timeBetweenDamage = startTimeBetweenDamage; 
				
			}
		}
	}
}

Above the rest of the code. The last function, that checks for collisions with enemies also sets the knockbackDirection Vector2 used for the knockback, as well as resets the knockbackCount.

I apologize for the kind of messy code, I’m a total beginner and I’m trying to make things work by watching tutorials, trial and error and googling my way around.

I guess what I need for both of those things is a way to move my player a certain amount, at a certain speed and I have no idea hot to do it. I’ve experimented with Rigidbody2D.MovePosition and/or Vector2.MoveTowards with no success.

Does anybody have an idea how to go about this? I’d really appreciate it any tip whatsoever.

I much tidier way to do the run thing would be:

speed = Input.GetKey(KeyCode.Space) ? runSpeed : walkSpeed;

As for the being pushed back, I guess you could have something along the lines of:

float knockbackamount = transform.position.z - 10;
if (transform.position.z != knockbackamount)
{
transform.translate(0,0,-speed);
}

mind you this is just off the top of my head and I haven’t tested it, so you would have to tinker around.
if it does work then a similar principle would work for dash too.

@Knightsurfer Thanks a lot for the tips!

I meanwhile figured out a way to do what I wanted. There was a logical error, I wasn’t manipulating the velocity at the right place. For the dash, I now set the velocity inside another if statement checking if the dashCount is over 0, so the speed change is valid for the whole time dashCount is counting down. That allows me to tweak the distance/speed by setting dashSpeed and startDashCount;

//DASH
		if(dashCount <= 0f ) 
			{ 
				if(Input.GetKeyDown("x"))
				{   dashCount = startDashCount; //set dash counter when pressing button

					Debug.Log("ShouldDash");	
				}				
			} else
				dashCount -= Time.deltaTime; //count town dash counter after dash

		if(dashCount > 0f) // while dash counter is over zero move at dash speed
			{
				rb2d.velocity = movement * dashSpeed * Time.deltaTime;	
			}

For the knockback, I did it in a similar way, but I put it in a separate function to call on collisions (where the timer variable knockBackCount is being set as well).

	void knockBack()
	{
			Debug.Log("Knockback Direction: " + knockbackDirection);
			Debug.Log("Should knock back");
						if(knockbackCount > 0){
			rb2d.velocity = knockbackDirection * knockback * Time.deltaTime;
			}
	}

Both things are working exactly like I wanted now. I quite like how it feels with setting the velocity directly, but I guess now that it works I will also experiment with AddForce.