Moving Dynamic Rigidbodies (2D)

Hi,

I am stuck with a problem: I need to move my character from point A to point B; I’m using a dynamic rigidbody for this character. Now, since I can’t use the function rigidbody.MovePosition, for it’s specifically designed for kinematic rigidbodies, nor I want my character to swtich type of rigidbody, I was wondering how I can accomplish this.

Other info: point A is the transform.position of the character, while point B is another object in the scene. I also think a good solution would be to use a coroutine, something like this I found on the web:

public IEnumerator SmoothMovement(Vector3 end)
    {
        float sqrRemainingDistance = (transform.position - end).sqrMagnitude;

        while (sqrRemainingDistance > float.Epsilon) // While that distance is greater than a very small amount (Epsilon, almost zero):
        {
            Vector3 newPosition = Vector3.MoveTowards(rb.position, end, inverseMoveTime * Time.deltaTime); // Find a new position proportionally closer to the end, based on the moveTime

            // Call MovePosition on attached Rigidbody2D and move it to the calculated position.
            rb.MovePosition(newPosition);

            sqrRemainingDistance = (transform.position - end).sqrMagnitude; // Recalculate the remaining distance after moving.

            yield return null; // Return and loop until sqrRemainingDistance is close enough to zero to end the function
        }

        rb.MovePosition(end);
    }

The only problem is that in this code, it is used the rigidbody.MovePosition() function, which I’d avoid for the aforementioned reasons.

I then would need something else in its place (I suppose using AddForce(), which is the main way to move dynamic rigidbodies), but I can’t figure out how to set it up properly: do I need to add force every frame within the corutine, and how can I direct this force so that it moves my character towards the B point?. Can you help me? I’m stuck on this since a week or even more!

Thanks in advance,

Jader

An object’s velocity is stored in the rigidbody’s “.velocity” property as a Vector3. This is how much movement will be applied in each axis each fixedUpdate.

You can take this velocity value and assign a new Vector3 to it.

I suggest storing the velocity in a new Vector3 variable, adding the new movement to it as a Vector3, and then assigning this modified Vector3 back to the .velocity property on the rigidbody.

If you just assign the movement to the velocity value without first adding the current velocity, you’ll be ignoring any momentum and other forces that have already acted on the object. That may be what you want - but I think that would be the same effect as using MovePosition.

Remember that, either way, after you assign this value, the rigidbody may still modify it if any additional force affects it afterward.

Hi, thanks for the tips.

Actually what I want is to make my 2d character (this is why I use Vector2 instead of Vector3) moving from its position to a specific point (in the game, it’s when the player finds a cover and presses the button to make the toon getting behind the cover). And actually yes, I do not want other forces to interfere with this movement, since it will be a very short movement and I’ll already take care that it can be done only in specific circumstances.

I’ve found a way to achieve this with a kinematic rigidbody using the function rigidbody2d.MovePosition(targetposition) within a coroutine:

        [SerializeField] Transform destination;
    [SerializeField] float moveTime = 0.1f;

    private Rigidbody2D rb;
    private float inverseMoveTime;

    private void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        inverseMoveTime = 1f / moveTime;
        Move(destination.position.x, destination.position.y);
    }

    public void Move(float xDir, float yDir)
    {
        Vector2 start = rb.velocity;
        Vector2 end = start + new Vector2(xDir, yDir);
        StartCoroutine(SmoothMovement(end));
    }

    public IEnumerator SmoothMovement(Vector3 end)
    {
        float sqrRemainingDistance = (transform.position - end).sqrMagnitude;

        while (sqrRemainingDistance > float.Epsilon) // While that distance is greater than a very small amount (Epsilon, almost zero):
        {
            Vector3 newPosition = Vector3.MoveTowards(rb.position, end, inverseMoveTime * Time.deltaTime); // Find a new position proportionally closer to the end, based on the moveTime

            rb.MovePosition(new Vector3(newPosition.x, newPosition.y));

            sqrRemainingDistance = (transform.position - end).sqrMagnitude; // Recalculate the remaining distance after moving.

            yield return null; // Return and loop until sqrRemainingDistance is close enough to zero to end the function
        }

        rb.MovePosition(end);
    }

The problem is that this code gives strange results when the rigidbody is set to dynamic, which is the case of the character of my videogame, and it’s confirmed by the Unity’s documentation, which says that MovePosition() works only with kinematic rigidbodies. So, as you were suggesting, I’ve tried to add a Vector2 (instead of a Vector3, since I’m in a 2D environement) each loop of the coroutine, with this code:

[SerializeField] Transform destination;
    [SerializeField] float moveTime;

    private Rigidbody2D rb;
    private float inverseMoveTime;

    private void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        inverseMoveTime = 1f / moveTime;
        Move(destination.position.x, destination.position.y);
    }

    public void Move(float xDir, float yDir)
    {
        Vector2 startVelocity = rb.velocity;
        Vector2 end = startVelocity + new Vector2(xDir, yDir);
        StartCoroutine(SmoothMovement(end));
    }

    public IEnumerator SmoothMovement(Vector3 end)
    {
        float sqrRemainingDistance = (transform.position - end).sqrMagnitude;

        while (sqrRemainingDistance > float.Epsilon) // While that distance is greater than a very small amount (Epsilon, almost zero):
        {
            Vector2 newPosition = Vector2.MoveTowards(rb.position, end, inverseMoveTime * Time.deltaTime); // Find a new position proportionally closer to the end, based on the moveTime

            rb.AddForce(new Vector3(newPosition.x, newPosition.y), ForceMode2D.Force);

            sqrRemainingDistance = (transform.position - end).sqrMagnitude; // Recalculate the remaining distance after moving.

            yield return null; // Return and loop until sqrRemainingDistance is close enough to zero to end the function
        }

        rb.MovePosition(end);
    }

which is very similar to the first one, changing the way the character moves within the coroutine. The thing is that results are pretty weird :sweat_smile:

  1. The character moves exactly AWAY from the target destination
  2. Adding each frame a force makes the character literally flying away each frame faster (I think I can easily solve this)

Basically my problem now is: how can I calculate the Vector2 to use withing the function AddForce(Vector2, ForceMode2D.Force), so that it simply leads my character towards the destination and then the character stops when the destination is reached? Moreover, it should reach the destination at a constant speed, instead of keeping accelerating (but as I was saying above, I think I can solve this easily).

Thanks again for the patience and for the next answer :slight_smile:

Since you’re okay with losing susceptibility to external forces, you can switch it to kinematic when doing this programmed movement, set velocity to Vector3.zero, and then switch it back to non-kinematic after arriving (approximately close) to the destination.

Thanks for the answer, I actually also thought about this but I’ve read somewhere that switching between the two kind of rigidbodies is very computational intensive, can you confirm it?

I’ll try it out anyways :slight_smile: and report here the results!

Best,

Jader

Nope, I have no idea.

Does it matter if it is?

Well, for my game specifically I don’t think it’s very important the resources usage, so I think I’ll follow your suggestion: I’ll switch to kinematic rigidbody for that function. I was anyways interested in how I could calculate the velocity (better: the vector for the rigidbody’s velocity) to apply each frame for my object to reach its destination. This way I could not switch between the two rigidbodies.

Thanks!

Jader

I’m not 100% certain, but I think I mathed it out one time that to achieve a speed of 1 meter (Unity unit) per second, you need to apply 50 force per mass on the rigidbody.

I’m not certain how long it would take drag to lower this - I’m guessing that drag reduces your speed at 1 per second by modifying your velocity directly.

To keep a constant speed, you’d either have to reduce drag or be constantly applying a force. There is something called a constant force, but I have not worked with it.

None of this matters if you edit velocity directly. If you modify the velocity, you eliminate all forces currently acting on an object and set your own velocity without any force involved. So to do this would just be like moving something with a transform. (target.transform.position - transform.position).normalized * speedPerSecond

The main difference is you don’t need to use Time.deltaTime to compensate for frame rate. The rigidbody will do that for you based on the physics settings.

Thanks for the kind reply, now everything is clearer :slight_smile:

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms