Kinematic 2D Rigidbody movement: Rigidbody2D.MovePosition vs Rigidbody2D.position problem
I'm trying to use 2D physics and character controller solution from this Unity live training (parts 3--6).
Basic idea is that script, which describes any moving object's behaviour is inherited from PhysicsObject script, that handles gravity and collisions.
However, this solution uses Rigidbody2D.position in FixedUpdate to move the object, which causes visible lag whenever there are moving objects other than the player. Basically, when player stands still, everything else moves smoothly, but when player moves, only player moves smoothly, everything else moves jerky.
After running a few simple tests (moving two objects at the same time using different methods) I found that only two solutions give smooth results: -moving objects WITHOUT rigidbody through transform.position in Update -moving object with Kinematic Rigidbody2D with Interpolation set to Interpolate via Rigidbody2D.Moveposition in FixedUpdate.
So I'm trying to change the script according to second variant. However, when I replace line
rb2d.position = rb2d.position + offset;
with
rb2d.MovePosition(rb2d.position + offset);
Character and objects stop moving on horizontal axis. Character still responds to input (animation plays correctly), and he can jump, but he doesn't change position.
But! If I replace offset
with any new Vector2()
the objects glide along x axis without any problems.
I checked through Debug.Log if offset
variable is being calculated correctly, and it is, however, nothing changes.
The MovePosition line is placed inside the method Movement(), which, in turn, is inside FixedUpdate().
The question is, why isn't RigidBody2D.MovePosition() working with calculated variables?
The FixedUpdate and Movement methods:
void FixedUpdate ()
{
targetVelocity = Vector2.zero;
ComputeVelocity(); //velocity input
velocity = velocity + gravityModifier * Physics2D.gravity * Time.deltaTime; // gravity
velocity.x = targetVelocity.x;
grounded = false;
Vector2 deltaPosition = velocity * Time.deltaTime;
Vector2 moveAlongGround = new Vector2(groundNormal.y, -groundNormal.x);
Vector2 move = moveAlongGround * deltaPosition.x;
Movement(move, false);
move = Vector2.up * deltaPosition.y;
Movement(move, true);
}
protected void Movement (Vector2 move, bool yMovement)
{
float distance = move.magnitude;
if (distance > minMoveDistance)
{
int count = rb2d.Cast(move, contactFilter, hitBuffer, distance + shellRadius);
hitBufferList.Clear();
for (int i = 0; i < count; i++) {
hitBufferList.Add(hitBuffer[i]);
}
for (int i = 0; i < hitBufferList.Count; i++)
{
//------------HANDLING SLOPES-------------------
Vector2 currentNormal = hitBufferList[i].normal;
if (currentNormal.y > minGroundNormalY)
{
grounded = true;
if (yMovement)
{
groundNormal = currentNormal;
currentNormal.x = 0;
}
}
float projection = Vector2.Dot(velocity, currentNormal);
if (projection < 0)
{
velocity = velocity - projection * currentNormal;
}
//----------------------
float modifiedDistance = hitBufferList[i].distance - shellRadius;
distance = modifiedDistance < distance ? modifiedDistance : distance;
}
}
Vector2 offset = move.normalized * distance;
//rb2d.position = rb2d.position + offset; //This line works with jagged movement
rb2d.MovePosition(rb2d.position + offset); //This line doesn't work at all
}
Answer by Krackd · Apr 22, 2018 at 12:42 AM
I got the exact same issue. The MovePosition method is used to register a desired position. The actual position will be computed next frames using interpolation. So we need to call MovePosition only once.
MovePosition is called twice in this code. Meaning that the first call (x movement) is overriden with the second call (y movement). So you need to modify a little to first compute the desired position, then call MovePosition at the end of fixed update.
public class PhysicsObject : MonoBehaviour
{
// ...
private Vector2 desiredPos;
// ...
void FixedUpdate()
{
// ...
desiredPos = rb2d.position;
Movement(move, false);
move = Vector2.up * deltaPosition.y;
Movement(move, true);
rb2d.MovePosition(desiredPos);
}
void Movement(Vector2 move, bool yMovement)
{
// ...
Vector2 offset = move.normalized * distance;
desiredPos = desiredPos + offset;
}
}