2d Platfomrer Help

Hiya all. I’m relatively new to Unity and C#. I have made some progress using add force to get basic movement. I dont know how to incorporate a max speed or a deceleration speed. Here is the code I have so far. does anyone have any suggestions on how I can achieve these functions? Any help would be appreciated

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

public class PlayerMovement : MonoBehaviour
{
    private Rigidbody2D rb;
    [SerializeField] private float moveSpeed = 7;
    [SerializeField] private float accel = 1;
   
    [SerializeField] private float decel = 1;
    private float dirX = 0f;

    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    //Put non physics based movement in here
    void Update()
    {
        dirX = Input.GetAxis("Horizontal");
       

    }

    //Put physics based movement in here
    private void FixedUpdate()
    {
        if(dirX > 0f)
        {
            rb.AddForce(new Vector2(moveSpeed * accel, 0));
        }
        else if(dirX < -0f)
        {
            rb.AddForce(new Vector2(-moveSpeed * accel, 0));
        }
        else
        {
            rb.AddForce(new Vector2(0, 0));
        }
    }
}

If you move by adding force then you need to compute a drag that limits you, or else tone down the force as you reach the desired speed.

A better way is to set the velocity of the Rigidbody directly.

You can set it immediately, eg, no acceleration, or you can tween the velocity up from a current value to the desired value based on input. This is a handy and extremely-common pattern in gamedev:

Smoothing movement between any two particular values:

You have currentQuantity and desiredQuantity.

  • only set desiredQuantity
  • the code always moves currentQuantity towards desiredQuantity
  • read currentQuantity for the smoothed value

Works for floats, Vectors, Colors, Quaternions, anything continuous or lerp-able.

The code: SmoothMovement.cs · GitHub

So where you have things like “desiredQuanity” and currentQuanity", are those values I would set for my moveSpeed and accel? (or should I be setting a maxMoveSpeed?) I’m just trying to work out how to implement it. Also, when it comes to adding drag, would I apply that in the deceleration phase? or does it need to be constantly added?

Yes!

Based on input, set the desiredQuantity (perhaps private Vector2 desiredVelocity; ?) to what you want.

Then each fixed update, read it back from the Rigidbody2D, do a moveTowards on it, and write it back.

void FixedUpdate()
{
  Vector2 currentVelocity = rb.velocity;

  currentVelocity = Vector2.MoveTowards( currentVelocity, desiredVelocity, acceleration * Time.deltaTime);

  rb.velocity = currentVelocity;
}

Choose a value for acceleration that is acceptable. Start with 1 or 5 or 10 for instance.

So, i’ve made adjustments to the code based on the info you have given me. This is what I currently have:

public class PlayerMovement : MonoBehaviour
{
    [Header ("Components")]
    private Rigidbody2D rb;

    [Header ("Movement Variables")]
    [SerializeField] private float moveSpeed = 7f;
    [SerializeField] private float maxMoveSpeed = 10f;
    [SerializeField] private float accel = 1f;

    private float dirX = 0f;
   
    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        dirX = Input.GetAxis("Horizontal");

    }

    private void FixedUpdate()
    {
        Vector2 currentVelocity = rb.velocity;

        currentVelocity = Vector2.MoveTowards( currentVelocity, maxMoveSpeed, accel * Time.deltaTime);

        rb.velocity = currentVelocity;
    }
}

But now I’m getting an error and I don’t really understand what it means or how to solve it:
Assets\PlayerMovement.cs(41,65): error CS1503: Argument 2: cannot convert from ‘float’ to ‘UnityEngine.Vector2’

Any ideas whats causing it? I Know its complaining about a problem on this line:
currentVelocity = Vector2.MoveTowards( currentVelocity, maxMoveSpeed, accel * Time.deltaTime);
but not sure what

Yes, go back carefully and look at what I typed and how it diverges from what you typed.

Don’t concern yourself with names. Ask yourself what each object is: float, Vector2, etc.

More broadly, a summary of your issue:

Some help to fix “Cannot implicitly convert type ‘Xxxxx’ into ‘Yyyyy’:”

http://plbm.com/?p=263

OK, I just copy and pasted exactly what you sent me and I’m still getting the exact same error

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

public class PlayerMovement : MonoBehaviour
{
    [Header ("Components")]
    private Rigidbody2D rb;

    [Header ("Layer masks")]
    [SerializeField] private LayerMask groundLayer;
    [SerializeField] private LayerMask wallLayer;
    [SerializeField] private LayerMask cornerCorrectLayer;

    [Header ("Movement Variables")]
    [SerializeField] private float moveSpeed = 7f;
    [SerializeField] private float desiredVelocity = 10f;
    [SerializeField] private float acceleration = 1f;
    [SerializeField] private float groundLinearDrag = 5f;
    private float dirX = 0f;
   
    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    // Update is called once per frame
    //Put non physics based movement here
    void Update()
    {
        dirX = Input.GetAxis("Horizontal");

    }

    //Put physics based movement in here
    private void FixedUpdate()
    {
        Vector2 currentVelocity = rb.velocity;

        currentVelocity = Vector2.MoveTowards( currentVelocity, desiredVelocity, acceleration * Time.deltaTime);

        rb.velocity = currentVelocity;
    }
}

Error: Assets\PlayerMovement.cs(41,65): error CS1503: Argument 2: cannot convert from ‘float’ to ‘UnityEngine.Vector2’

I’ve decided to go back to the AddForce method I was using previously for the movement. I was able to apply drag in a way that seems to be working very close to how I want it to work, and just needs a few tweaks. I seem to be having trouble with Jumping now.
Here is what I have so far:

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

public class PlayerMovement : MonoBehaviour
{
    [Header ("Components")]
    private Rigidbody2D rb;
    private CapsuleCollider2D capColl;

    [Header ("Layer masks")]
    [SerializeField] private LayerMask groundLayer;
    [SerializeField] private LayerMask wallLayer;
    [SerializeField] private LayerMask cornerCorrectLayer;

    [Header ("Movement Variables")]
    [SerializeField] private float moveSpeed = 7f;
    [SerializeField] private float maxMoveSpeed = 10f;
    [SerializeField] private float movementAcceleration = 1f;
    [SerializeField] private float groundLinearDrag = 5f;
    private float dirX = 0f;
    private float dirY = 0f;
    private bool changeDirection => (rb.velocity.x > 0f && dirX < 0f) || (rb.velocity.x < 0f && dirX > 0f);
   
    [Header ("Jump Variables")]
    [SerializeField] private float jumpForce = 10f;

   
    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        capColl = GetComponent<CapsuleCollider2D>();
    }

    // Update is called once per frame
    //Put non physics based movement here
    void Update()
    {
        dirX = Input.GetAxisRaw("Horizontal");
        dirY = Input.GetAxisRaw("Vertical");

    }

    //Put physics based movement in here
    private void FixedUpdate()
    {
        //Directional movement
        if(dirX > 0f)
        {
            rb.AddForce(new Vector2(moveSpeed * movementAcceleration, 0));
        }
        else if(dirX < -0f)
        {
            rb.AddForce(new Vector2(-moveSpeed * movementAcceleration, 0));
        }
        else
        {
            rb.AddForce(new Vector2(0, 0));
        }
        if(Input.GetButtonDown("Jump"))
        {
            Jump();
        }

    }

    private void ApplyGroundLinearDrag()
    {
        if (Mathf.Abs(dirX) < 0.4f || changeDirection)
            {
                rb.drag = groundLinearDrag;
            }
            else
            {
                rb.drag = 0f;
            }
    }

    //Jump logic
    private void Jump()
    {
        if (isGrounded())
        {
            rb.AddForce(new Vector2(rb.velocity.x, jumpForce));
        }
    }

    private bool isGrounded()
    {
        RaycastHit2D raycastHit = Physics2D.BoxCast(capColl.bounds.center, capColl.bounds.size, 0, Vector2.down, 0.1f, groundLayer);
        return raycastHit.collider != null;
    }
}

Am I doing something wrong with how I’m getting the jump to work? because when I press jump in-game, it doesn’t seem to be doing anything

I take that back. It looks like groundLinearDrag isn’t actually doing anything and its th linear drag of th rigidbody thats doing the work. Is there a way I can get the ground linear drag to work properly?

As I said here:

In your case you have made desiredVelocity a float and then tried to use that to MoveTowards with a Vector2

This is just apples and oranges and will not work, which is what all of the links above point out.

Let’s review where #7 above stands.

You made desiredVelocity a float variable in your class and set it to 10. Do you perhaps mean to call that PlayerWalkSpeed or something else? That’s what it feels like.

You made dirX be the input from the player. That’s fine.

Now on this problematic line:

(which will not work because desiredVelocity is a float, NOT a Vector2)

Instead, to fix your code in #7 above:

use dirX (player input) and what you call desiredVelocity (probably better called PlayerWalkSpeed since it appears to be preset to 10), and create a new Vector2 to use for your MoveTowards:

Vector2 tempVector = new Vector2( dirX * desiredVelocity, 0);
// and now use this tempVector for our computations
currentVelocity = Vector2.MoveTowards(
                  currentVelocity, tempVector, acceleration * Time.deltaTime);

As I pointed out multiple times above, you don’t just try combinations. You reason about the data, the error, what you are trying to achieve, and you engineer a solution.

First, I’m sorry for talking back to you earlier, I’m just frustrated because it feels like nothing I do works.
I am genuinely more confused. I think I might need a fresh start on this project.
If I’m using dirx as an input for directional movement on the x axis, that’s ok. I think some of the confusion is coming from what terminology we are using.
Is my “moveSpeed” supposed to be what you are calling “currentVelocity”?
Is my “maxMoveSpeed” what you are referring to as “desiredVelocity”?
I know you’re saying to not get hung up on terminology, But that’s what makes sense in my head. Is there something wrong with the terminology I’m using and how C# reads what terms I’m using?
I think I need to start from a new script.
This is what I now have:

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

public class PlayerMovement2 : MonoBehaviour
{
    [Header("Components")]
    private Rigidbody2D rb;
    private CapsuleCollider2D capColl;

    [Header("Layer Masks")]
    [SerializeField] private LayerMask ground;
    [SerializeField] private LayerMask wall;

    [Header("Movement Variables")]
    [SerializeField] private float moveSpeed = 10f;
    [SerializeField] private float maxMoveSpeed = 20f;
   

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        capColl = GetComponent<CapsuleCollider2D>();
    }

    void Update()
    {
        dirX = Input.GetAxisRaw("Horizontal");
    }

    private void FixedUpdate()
    {
       
    }
}

Can we please go through this 1 step at a time so I know what went wrong?

I only just realized I forgot to make a private float for dirX, now remedied