So, I'm fairly new to unity and coding. I'm currently coding a top down 2d game and wanted to add a dash mechanic. Couldn't find a tutorial that really suited me, so I decided to code it myself. And well.. it didn't go too well for such an easy mechanic. So here I come asking for help to improve the shitcode I have produced. I feel like there's a much easier way to do it, but my lack of experience doesn't allow me to notice it. The code works just as I want it to now, but I'm looking to simplify my code with the same output.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
//run variables
Rigidbody2D rb;
float horizontal, vertical;
public float runSpeed;
//dash variables
public float dashSpeed;
public float startDashTime;
public float currentDashTimer;
public int dashDir;
bool isDashing;
bool shiftReplacement;
float shiftHolder;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
CheckIfShifted();
}
void FixedUpdate()
{
Dash();
}
public void CheckIfShifted()
{
if (Input.GetKeyDown(KeyCode.LeftShift))
{
shiftReplacement = true;
shiftHolder = 0.08f;
}
else if(shiftHolder <= 0)
{
shiftReplacement = false;
}
shiftHolder -= Time.deltaTime;
}
public void Dash()
{
//check ín which direction player should dash
if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
{
dashDir = 1;
}
else if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
{
dashDir = 2;
}
else if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow))
{
dashDir = 3;
}
else if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow))
{
dashDir = 4;
}
//check if player has pressed dash button and initiate dash
if (shiftReplacement)
{
isDashing = true; //initiate dash
currentDashTimer = startDashTime;
rb.velocity = Vector2.zero;
}
//check if dash was initiated
if (isDashing == true)
{
if (dashDir == 1)
{
rb.velocity = transform.right * dashSpeed; //dash properties
}
else if (dashDir == 2)
{
rb.velocity = transform.right * -1 * dashSpeed; //dash properties
}
else if (dashDir == 3)
{
rb.velocity = transform.up * dashSpeed; //dash properties
}
else if (dashDir == 4)
{
rb.velocity = transform.up * dashSpeed * -1; //dash properties
}
Debug.Log("bich Im dashin'");
currentDashTimer -= Time.deltaTime;
}
if (currentDashTimer <= 0)
{
isDashing = false;
}
}
I've run into a variety of problems. For example like shift input not registering on time for the fixedupdate() to pick it up and or not registering at all. Or when I put everything inside the update() function my player didn't move. Any advice?
CodePudding user response:
Why use an
int
and not simply directly store the desired vector in aprivate Vector2 dashDir;
and then
if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow)) { dashDir = transform.right; } else if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow)) { dashDir = -transform.right; } ...
etc and later only use
if(isDashing) { rb.velocity = dashDir * dashSpeed; }
Also by configuring your axis you could probably use
var dashDir = Vector2.ClampMagnitude(new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")), 1f);
instead of all the repeated Input checks.
Per default the
Horizontal
axis refers to either the left/right keys or a/d and theVertical
refers to either up/down keys or w/s.
So you could actually simply do
Vector2 dashDir;
private void Update()
{
if (Input.GetKeyDown(KeyCode.LeftShift))
{
shiftReplacement = true;
}
// the resetting of this flag is handled by the Dash itself see below
}
private void FixedUpdate()
{
Dash();
}
private void Dash()
{
// ignore the input if you already are dashing
if (!isDashing)
{
if(shiftReplacement)
{
isDashing = true; //initiate dash
currentDashTimer = startDashTime;
rb.velocity = Vector2.zero;
// according to your Input settings this can already cover both arrow keys and WASD, by default that is the case
var input = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
// you want to limit the vector to a maximum magnitude of 1 so diagonal movement is not faster than movement on a single axis
dashDir = Vector2.ClampMagnitude(input, 1f);
}
}
else
{
rb.velocity = dashDir * dashSpeed;
// in general avoid logging too often
//Debug.Log("bich Im dashin'");
currentDashTimer -= Time.deltaTime;
if (currentDashTimer <= 0)
{
isDashing = false;
}
}
// you probably want to handle this only exactly once
// => instead of resetting this time based in Update rather do it right after eventually handling it here
shiftReplacement = false;
}
I would btw then also change your Shift
input and rather use a Coroutine using WaitForFixedUpdate
like this
bool isDashing;
private void Update()
{
// Ignore input when already dashing
if (!isDashing && Input.GetKeyDown(KeyCode.LeftShift))
{
// Get the dash direction ONCE
var input = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
var direction = Vector2.ClampMagnitude(input, 1f);
// start a coroutine for dashing into this direction
// => direction can't be changed until dashing has finished
StartCoroutine(DashRoutine(direction));
}
}
private void DashRoutine(Vector2 direction)
{
// just in case if we already are dashing do nothing
if(isDashing) yield break;
// set dashing flag so no other concurrent routine can be started
isDashing = true;
// This is handy way for writing a while loop with a timer
// basically this is executed every fixeUpdate for startDashTime seconds
for(var timePassed = 0f; timePassed < startDashTime; timePassed = Time.deltaTime)
{
// wait for the next FixedUpdate call
yield return new WaitForFixedUpdate();
rb.velocity = direction * dashSpeed;
}
// finally release the flag so the next dashing can be triggered
isDashing = false;
}
this gets rid of all your timer and direction fields entirely and bundles the behavior and variables together. Also this way you don't all the time get the FixedUpdate
and Dash
all even though most of time it isn't used anyway. The Coroutine only gets invoked when actually needed.
CodePudding user response:
I fixed a couple of complier errors unity gave me and this is what I have now. But the code still doesn't make the player dash. What's wrong with the code? Here's the code as it is now @derHugo:
public class AltDashScript : MonoBehaviour
{
Rigidbody2D rb;
Vector2 dashDir;
public float dashSpeed;
bool isDashing;
public float startDashTime;
void Start()
{
rb = GetComponent<Rigidbody2D>();
isDashing = false;
}
private void Update()
{
// Ignore input when already dashing
if (!isDashing && Input.GetKeyDown(KeyCode.LeftShift))
{
// Get the dash direction ONCE
var input = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
var direction = Vector2.ClampMagnitude(input, 1f);
// start a coroutine for dashing into this direction
// => direction can't be changed until dashing has finished
StartCoroutine(DashRoutine(direction));
}
}
private IEnumerator DashRoutine(Vector2 direction)
{
// just in case if we already are dashing do nothing
if (isDashing) yield break;
// set dashing flag so no other concurrent routine can be started
isDashing = true;
// This is handy way for writing a while loop with a timer
// basically this is executed every fixeUpdate for startDashTime seconds
for (var timePassed = 0f; timePassed < startDashTime; timePassed = Time.deltaTime)
{
// wait for the next FixedUpdate call
yield return new WaitForFixedUpdate();
rb.velocity = direction * dashSpeed;
}
Debug.Log("I should dash");
// finally release the flag so the next dashing can be triggered
isDashing = false;
}
}
Thanks again for your help.