Move and collide seems to collide with old positional data

Godot Version

v4.2.2

Question

I’m working on a fighting game with a fixed update loop to maintain the 60fps of the genre. What I’m having trouble getting to work smoothly is push boxes between fighters as MoveAndCollide seems to use outdated positional data when calculating collisions if I move the CPU prior to the Player.

Essentially what I’m doing is calling ‘MoveAndCollide’ with the ‘testOnly’ set to ‘true’ and if I detect the CPU I then move it by the same velocity I intend to move the player to push it backwards. I then call ‘MoveAndCollide’ on the player which should be able to move into the space created by the now moved CPU.

The problem is when I do move the player after the collision handling the player only moves forward the distance expected when actually colliding with the CPU meaning that 2nd ‘MoveAndCollide’ seems to be operating off old positional data from the CPU somehow. The end result is the CPU being pushed and the player moving forward but it’s very jittery looking as it’s basically moving a little, pushing, moving a little, pushing, etc.

Is there something wrong with how I’m trying to go about this pushing mechanic and if so what?

Both Player and CPU bodies are CharacterBody2d types if that’s helpful and this code is executed in the _Process function.

Code:

    public override void Execute(Fighter fighter, double delta)
    {
        var floatDelta = (float)delta;

        var moveVelocity = new Vector2(1f * (fighter.FighterMovement.ForwardWalkSpeed * 120f), fighter.Velocity.Y); // Calculate movement for player
        fighter.Velocity = moveVelocity;

        var collision = fighter.MoveAndCollide(moveVelocity * floatDelta, testOnly: true); // Checking if a collision will happen
        if (collision != null)
        {
            var cpu = collision.GetCollider() as Fighter;
            if (cpu != null)
            {
                var cpuGlobalPos = cpu.GlobalPosition;
                cpu.Velocity = moveVelocity;
                var cpuCol = cpu.MoveAndCollide(moveVelocity * floatDelta); // Move CPU away from player if collision happens
                GD.Print($"{cpu.Name} Position: " + (cpu.GlobalPosition.X - cpuGlobalPos.X));
            }
        }

        var playerGlobalPos = fighter.GlobalPosition;
        collision = fighter.MoveAndCollide(moveVelocity * floatDelta); // This seems to still think CPU is in its previous position and still collides
        GD.Print($"{fighter.Name} Position: " + (fighter.GlobalPosition.X - playerGlobalPos.X));
    }

Log output:

Player Position: 8 // Walking
Player Position: 8  // Walking
Player Position: 8  // Walking
Cpu Position: 8 // Collided & push CPU
Player Position: 1.390625 // Walking
Player Position: 8  // Walking
Cpu Position: 8.065552  // Collided & push CPU
Player Position: 0.00079345703 // Walking

The positions of collision objects are updated once per frame in a batch for performance reasons, but you can update the position early with force_update_transform().
Unfortunately this method doesn’t seem to work with CharacterBody2D or AnimatableBody2D, which I have already reported.

Thanks for pointing me to that GitHub issue, is there any good work arounds for this problem when it comes to CharacterBody2d?

Using StaticBody2D and writing the movement code on a slightly lower level should work. My main project doesn’t even use collision objects, so I haven’t thought about it too much.

Just in case anyone else stumbles onto the same problem as me. I literally just changed CharacterBody2D to StaticBody2D then added a public Vector2 called Velocity to the base class as StaticBody2D does not have that by default. I then called force_update_transform() and everything worked without a problem. Didn’t need to rework or change anything else.

    public override void Execute(Fighter fighter, double delta)
    {
        var floatDelta = (float)delta;

        var moveVelocity = new Vector2(1f * (fighter.FighterMovement.ForwardWalkSpeed * 120f), fighter.Velocity.Y);
        fighter.Velocity = moveVelocity;

        var collision = fighter.MoveAndCollide(moveVelocity * floatDelta, testOnly: true)
        if (collision != null)
        {
            var cpu = collision.GetCollider() as Fighter;
            if (cpu != null)
            {
                var cpuGlobalPos = cpu.GlobalPosition;
                cpu.Velocity = moveVelocity;
                var cpuCol = cpu.MoveAndCollide(moveVelocity * floatDelta);
                GD.Print($"{cpu.Name} Position: " + (cpu.GlobalPosition.X - cpuGlobalPos.X));

                cpu.ForceUpdateTransform(); // Ensure you call this after converting your CharacterBody2D to StaticBody2D and adding the Velocity property
            }
        }

        var playerGlobalPos = fighter.GlobalPosition;
        collision = fighter.MoveAndCollide(moveVelocity * floatDelta);
        GD.Print($"{fighter.Name} Position: " + (fighter.GlobalPosition.X - playerGlobalPos.X));
    }

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