2D swimming

Hi! I am working on a 2d platformer game and I have an underwater level. Currently, my movement is not really based on acceleration, rather it is based on moving my position. This works but it does not have the feeling of moving through the water like I imagine acceleration-based movement would have. I have attached my script for underwater movement below. Can someone help me convert this script into an acceleration-based movement to feel like I’m underwater?

//Player Stuff

public float moveSpeed = 5f;
public Rigidbody2D rb;
Vector2 movement;
Vector2 mousePos;
public Camera cam;
private bool m_FacingRight = true;

public Animator animator;
// Update is called once per frame
void Update()
{
    //input
    movement.x = Input.GetAxisRaw("Horizontal");
    movement.y = Input.GetAxisRaw("Vertical");

    mousePos = cam.ScreenToWorldPoint(Input.mousePosition);
}

void FixedUpdate()
{
    //movement
    rb.MovePosition(rb.position + movement * moveSpeed * Time.fixedDeltaTime);
    animator.SetFloat("Speed", Mathf.Abs(movement.x + movement.y));

    //Makes the player face the right way
     if (movement.x > 0 && !m_FacingRight)
     {
        // animator.SetBool("TurningLeft", true);
        Flip();
     }

     else if (movement.x < 0 && m_FacingRight)
     {

         Flip();
     }

}

//Code to flip player
void Flip()
{
    m_FacingRight = !m_FacingRight;

    transform.Rotate(0f, 180f, 0f);
}

Interesting question… And worth of two different approaches.

The first - and simpler - approach is not to use rb.position but rb.AddForce. Create a vector2 with value of, say (2,0) and then code: rb.AddForce(force, ForceMode2D.Impulse);

That will add a one-off force but water is so viscous that it slows you down quickly. Therefore set the Drag in your rigid body to something like 1.5 and that will slow you down quickly.

The second approach - and one that is more accurate - is not to apply a single force but a continuous force for as long as your arms are propelling you forward. An Impulse gives you maximum speed immediately. However, when you swim, you are slower at the start of your stroke than you are at the end of it. You are constantly accelerating and decelerating.

This is harder to code and would probably involve using a coroutine to accelerate for a fixed period. It would also potentially mean you would have to synchronise the acceleration with your animation. Not impossible but it needs coordination.

Depending on how good your coding is, you could also try to use Animation Events. One at the start of the arm movement to turn the force on and one at the end of the propelling arm movement to turn the force off.

To use this more accurate method, you’d use ForceMode2D.Force, not ForceMode.Impulse and you’d do that every FixedUpdate for the duration of the propelling motion.

I’m afraid it’s quite hard to demonstrate all this in a simple Answer so there may be some more work for you, unless you’re happy with the first approach, using Impulse.

Good luck!

@SurreyMuso thank you so much for your help! I’m not too good at programming, but I think I can wrap my head around this. I will try it out and post here again. Thank you so much again!

im also having the same issue with getting my swimming to feel like swimming but i cant seem to get the add force to work properly for me,Hey im having the same issue but i cant get the new vector or the add force to work any advice?

The first thing to remember is that you shouldn’t use standard input mechanics. So, when running, you can hold down the WASD keys and you run continuously. Swimming is individual strokes and only when you press a key down so I’ve assigned the Space key to it in the example below. You should be able to adapt this but remember to set the drag in your RigidBody2D to 1.5 or possibly higher to slow you down. Change the force amount in the inspector if you want a stronger stroke.

using UnityEngine;

public class Swimming : MonoBehaviour
{
    [SerializeField] Vector2 force;
    Rigidbody2D rb;

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

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            rb.AddForce(force, ForceMode2D.Impulse);
        }
    }
}