Set Velocity at relative position.

I basically want to use

Rigidbody.AddForceAtPosition

But instead set the velocity of a Rigidbody at a given position.

I thought I could use the following code:
This line to first cancel the current velocity at Point

Body.AddForceAtPosition(-Body.GetRelativePointVelocity(Point), Body.transform.TransformPoint(Point), ForceMode.VelocityChange);

And this line to add the new velocity.

Body.AddForceAtPosition(newVelocity, Body.transform.TransformPoint(Point), ForceMode.VelocityChange);

This seemed simple enough but the first line is causing the Rigidbody to freak out, switching quickly between two positions every frame.
This happens no matter the orientation of the object or the position of Point.

No there’s no way to set the velocity of a given point. It also wouldn’t make any sense. The velocity at a given point is calculated on the fly and is the combination of linear and angular velocity. Applying an appropriate counter force at a point does stop the point temporarily. However the exact force needed depends on three factors:

  • linear velocity
  • angular velocity
  • inertia tensor (+rotation)
  • ( and of course the point )

That’s what the physics system has to take into account when applying collision forces. However velocity change for a single point makes not much sense.

Imagine you have a floating rigidbody that has no velocity / angular velocity. Say you want the local point (1,0,0) to get a velocity of (0,0,1). You could just add a linear velocity of (0,0,1) to the rigidbody and you get your desired velocity. However instead you could also apply only an angular velocity of (0,-1,0) and also get a velocity of (0,0,1) without having the rigidbody move at all. Of course in case of angular velocity the point velocity will change as the object rotates since the point velocity is the tangential velocity around the center of mass. Of course it’s also possible to add both, linear and angular velocity in any combination. There simply isn’t a general way how setting the velocity of a point should affect the velocity / angular velocity of a Rigidbody.

What Unity does when you use “AddForceAtPosition” is to do both, adding a linear velocity and a angular velocity. However it does not split the added force / impulse / velocity. It simply adds the velocity change directly to the linear velocity. In addition, depending on where the force is applied it adds angular velocity as well. Though the further out the point is from the center of mass, the more angular velocity is added. This generally makes sense, however Unity does not really preserve energy / momentum. It should split the applied momentum between linear and angular velocity which it doesn’t do.

GetRelativePointVelocity

Just another note on this method: **It doesn't work as it's implemented wrong, so don't use it**.

They made a mistake when implementing this method. While GetPointVelocity works as expected, they messed up local and world space velocities in GetRelativePointVelocity. Here’s a reference implementation of GetPointVelocity:

public static Vector3 GetVelocityAtPoint(this Rigidbody aRB, Vector3 aPoint)
{
    var linVel = aRB.velocity;
    var angVel = aRB.angularVelocity;
    var d = aPoint - aRB.worldCenterOfMass;
    return Vector3.Cross(angVel, d) + linVel;
}

This method does return the same value as “GetPointVelocity”. That is the current worldspace velocity of the given point. However “GetRelativePointVelocity” seems to calculate the tangential velocity in localspace but in the end they add the local space tangential velocity to the world space linear velocity which makes no sense. Their method usually returns a constant value which is impossible for a rotating and moving rigidbody. It’s not clear if they intended to return a local space velocity (in which case they have to convert the linear velocity into localspace) or if they wanted to return a worldspace velocity (in which case they need the tangential velocity in world space).

This would be the correct way to pass in a local vector and return a worldspace velocity vector:

public static Vector3 GetRelativePointVelocity2(this Rigidbody aRB, Vector3 aLocalPoint)
{
    var linVel = aRB.velocity;
    var angVel = aRB.angularVelocity;
    var d = aRB.transform.TransformPoint(aLocalPoint) - aRB.worldCenterOfMass;
    return Vector3.Cross(angVel, d) + linVel;
}

Unity’s physics system is just a rough approximation of real world physics. For example in Unity an object does not keep it’s proper angular momentum. So a tumbling motion like explained in this video is impossible in Unity as Unity will keep the angular velocity constant with no external force / torque applied.

There is no AddRelativeForceAtPosition, but you can translate the local position you have into world space before applying the force:

Why do you try something like this:

Vector3 worldForcePosition = transform.TransformPoint(yourLocalForcePosition);
 rigidbody.AddForceAtPosition(force, worldForcePosition);