- Home /
Having a lot of trouble with 2d movement
So I have been scouring the internet to figure out how to make a good 2d player controller that will work with a tilemap built level, and i have found two main ways to handle movement. They are both commented out under void playermove and are the top lines of code. Way one is what ive seen most people use, but when I impliment it many problems occur. Firstly it has sort of bug, regardless of weather or not its handled in fixedupdate or not, it looks like the player is teleporting back very slightly while moving every frame. And secondly it will require all tiles making my level have a frictionless material on them, but then my "dash" mechanic wont work becuase it will get stuck. And the second way has very smooth movement, but when you run against walls you kind of freak out and can somewhat stick to them. There HAS to be some way to make sense of this chaos, and I would really appriciate any help you could provide.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.Sprites;
public class playerController : MonoBehaviour {
//MOVEMENT
[Tooltip("The maximum horizontal movement speed in units per second.")]
public float speed = 8; // movenment speed for Luca
[Tooltip("The maximum vertical jump speed in units per second.")]
public float jumpspd = 12; // jumo height for Luca
private float moveInput;
private Rigidbody2D rb;
public Transform groundCheck;
public float checkRadius;
public LayerMask whatIsGround;
//ANIMATION
public Animator anim;
bool canDash = true;
private bool grounded;
bool isRunning;
public SpriteRenderer ren;
//UNUSED AS OF NOW
public static float LucaHP;
public static float LucaSP;
public GameObject hitbox;
public static float damage;
//TODO: MOVE ONTO NEW SCRIPT
// Use this for initialization
void Start()
{
anim = gameObject.GetComponent<Animator>();
anim.SetBool("isRunning", false);
anim.SetBool("dashing", false);
canDash = true;
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
HandleFlip();
HandleJump();
HandleDash();
playermove();
}
private void FixedUpdate()
{
}
void playermove()
{
grounded = Physics2D.OverlapCircle(groundCheck.position, checkRadius, whatIsGround);
// WAY 1
//float axixX = Input.GetAxis("Horizontal");
//rb.velocity = new Vector2(axixX * speed, rb.velocity.y);
//WAY 2
// float axixX = Input.GetAxis("Horizontal");
//transform.Translate(new Vector2(axixX, 0) * Time.deltaTime * speed);
if (Input.GetButton("Horizontal"))
{
isRunning = true;
}
else
{
isRunning = false;
}
anim.SetBool("isRunning", isRunning);
}
void HandleJump()
{
if (Input.GetKeyDown(KeyCode.Space))
{
if (grounded == true)
{
GetComponent<Rigidbody2D>().velocity = new Vector2(GetComponent<Rigidbody2D>().velocity.x, jumpspd);
grounded = false;
}
}
}
void HandleDash()
{
if (Input.GetKeyDown(KeyCode.LeftShift))
{
StartCoroutine(dash());
}
}
void HandleFlip()
{
if (Input.GetKeyDown(KeyCode.A))
{
ren.flipX = true;
}
if (Input.GetKeyDown(KeyCode.D))
{
ren.flipX = false;
}
}
IEnumerator dash()
{
if (canDash == true)
{
canDash = false;
anim.SetBool("dashing", true);
if (ren.flipX == true)
{
GetComponent<Rigidbody2D>().velocity = new Vector2(-jumpspd / 2, GetComponent<Rigidbody2D>().velocity.y);
}
else
{
GetComponent<Rigidbody2D>().velocity = new Vector2(jumpspd / 2, GetComponent<Rigidbody2D>().velocity.y);
}
yield return new WaitForSeconds(0.5f);
anim.SetBool("dashing", false);
canDash = true;
}
}
void Flip()
{
ren.flipX = !ren.flipX;
}
}
Answer by Stratos-Mak · Mar 05, 2018 at 10:09 AM
Can't really test it right now, but from the looks of it, I would suggest that you don't modify the velocities of your player directly, use forces instead and let the physics simulation calculate the velocities.
To move horizontally, you apply a force like this:
void playerMove() {
// Get the direction as an integer, implying that the default direction of your player is right
int direction = ren.flipx ? -1 : 1;
// Vector2.right is the (1,0,0) vector, multiplying it with the direction,
// we get the force direction we want (left or right)
// Multiplying it with the speed, we get the magnitude we want
Vector2 force = Vector2.right * direction * speed;
// Now, we should apply the force only if the player's speed is less than we want
// maxSpeed should be a public float member variable set to a value you want
if(rb.velocity.x < maxSpeed)
rb.AddForce(force); // You can also tamper with the 2nd parameter, the force type
}
Of course, this should be run from the FixedUpdate()
method, as it's part of the physics simulation.
Same logic applies to the jump mechanic (apply an Impulse force with Vector2.up). That's the general idea. (Not sure about how you'd do the dash mechanic this way, hence I omitted it for now).
Notice that I use rb instead of GetComponent<RigidBody2D>()
. You already have the rigidbody2d cached on the member variable "rb" at the Start()
method. GetComponent is a heavy method to be called each frame on Update, or each physics cycle on FixedUpdate, because it searches for the component everytime. The good practice is to cache what you need in member variables (as you do with "rb") and use that to access the components.
Okay so I tested it and for some reason, when you tap a direction you character picks up speed gradually but then gets uncontrollably fast and flies of screen...
Hey thanks for the help, I really appriciate it. I actually went back to some older code and it seems to work, there is just one major glitch and I will ask another question about it.
Your answer
Follow this Question
Related Questions
The sphere goes Slow 1 Answer
Why is my character so slow? 1 Answer
Executing multiple actions one after another Unity 1 Answer
The name 'Joystick' does not denote a valid type ('not found') 2 Answers