Kinematic Body 2D movement and collision

Godot Version

3.5.3.stable.mono

Question

I have a KinematicBody2D for a character scene and I’m using MoveAndCollide for the movement. The character collides and stops, but it does not slide against the collisions with a diagonal input ( Top and Left, Top and Right… ). I tried using MoveAndSlide but the character didn’t even budge.

Is there a way to make the character slide against collision with diagonal input whilst still detecting the collision?

Hmm… it’s somewhat hard to help without seeing the code you’re currently using to move your KinematicBody2D.

If you haven’t already, take a look at the Godot Docs tutorial page for the KinematicBody2D.

2 Likes
using Godot;
using System;

public class PlayerMovement : KinematicBody2D
{
    [Signal]
    public delegate void Hit();

    [Export]
    public int moveSpeed = 400;

    private Vector2 playerSpeed = Vector2.Zero;
    private Vector2 screenSize;
    private KinematicCollision2D playerCollision;

    // Called when the node enters the scene tree for the first time.
    public override void _Ready()
    {
        screenSize = GetViewportRect().Size;
    }

    // Called every frame. 'delta' is the elapsed time since the previous frame.
    public override void _Process( float delta )
    {
        detectMovement( delta );
        detectCollision();
        // spriteRotation();
    }

    // Function called when resetting the player
    public void start(Vector2 pos) {
        Position = pos;
        Show();
        GetNode<CollisionShape2D>( "CollisionShape2D" ).Disabled = false;
    }

    // Detecting inputs and applying movement
    private void detectMovement( float delta ) {

        // Resetting player speed
        playerSpeed = Vector2.Zero;

        // Detecting inputs - DOWN ( + )
        if( Input.IsActionPressed( "input_down" ) ) playerSpeed.y += 1;

        // Detecting inputs - UP ( - )
        if( Input.IsActionPressed( "input_up" ) ) playerSpeed.y -= 1;
        
        // Detecting inputs - RIGHT ( + )
        if( Input.IsActionPressed( "input_right" ) ) playerSpeed.x += 1;

        // Detecting inputs - LEFT ( - )
        if( Input.IsActionPressed( "input_left" ) ) playerSpeed.x -= 1;

        // Assigning collision to variable ( will return null if does not have collision )
        playerCollision = MoveAndCollide( playerSpeed.Normalized() * moveSpeed * delta );

        // Preventing player from leaving the screen
        Position = new Vector2( Mathf.Clamp( Position.x, 0, screenSize.x ), Mathf.Clamp( Position.y, 0, screenSize.y ) );

    }

    // Detecting collision
    private void detectCollision() {

        if( playerCollision != null ) {
            EmitSignal( nameof( Hit ) );
        }

    }

    // Managing sprite animation and flip
    private void spriteRotation() {

        // Getting player sprite
        var animatedSprite = GetNode<AnimatedSprite>( "PlayerSprite" );

        // Playing or stopping animation based on player movement
        if( playerSpeed.Length() > 0 ) {
            animatedSprite.Play();
        } else {
            animatedSprite.Stop();
        }

        // Validating direction and what animation should be played and fliping the sprite
        if( playerSpeed.y != 0 ) {
            animatedSprite.Animation = "up";
            animatedSprite.FlipV = playerSpeed.y > 0;
        } 
        else if( playerSpeed.x != 0 ) {
            animatedSprite.Animation = "walk";
            animatedSprite.FlipV = false;
            animatedSprite.FlipH = playerSpeed.x < 0;
        }

    }

}

Okay. Have you tried using the code example(s) in the link I provided in my previous reply?

1 Like

Yes, I used the documentation as reference when writing the script.

Wow, okay.

Explain why the code examples didn’t work. What went wrong then?
As an example, why did code from the following snippet not work for you?

using Godot;
using System;

public class KBExample : KinematicBody2D
{
    private PackedScene _bullet = (PackedScene)GD.Load("res://Bullet.tscn");
    public int Speed = 200;
    private Vector2 _velocity = new Vector2();

    public void GetInput()
    {
        // add these actions in Project Settings -> Input Map
        _velocity = new Vector2();
        if (Input.IsActionPressed("backward"))
        {
            _velocity = new Vector2(-Speed/3, 0).Rotated(Rotation);
        }
        if (Input.IsActionPressed("forward"))
        {
            _velocity = new Vector2(Speed, 0).Rotated(Rotation);
        }
        if (Input.IsActionPressed("mouse_click"))
        {
            Shoot();
        }
    }

    public void Shoot()
    {
        // "Muzzle" is a Position2D placed at the barrel of the gun
        var b = (Bullet)_bullet.Instance();
        b.Start(GetNode<Node2D>("Muzzle").GlobalPosition, Rotation);
        GetParent().AddChild(b);
    }

    public override void _PhysicsProcess(float delta)
    {
        GetInput();
        var dir = GetGlobalMousePosition() - GlobalPosition;
        // Don't move if too close to the mouse pointer
        if (dir.Length() > 5)
        {
            Rotation = dir.Angle();
            _velocity = MoveAndSlide(_velocity);
        }
    }
}
1 Like

I don’t know.
Like I said in the post, MoveAndSlide does not move at all, the character moves 1 pixel and that’s it.

EDIT: never mind, the

MoveAndSlide( playerSpeed.Normalized() * moveSpeed * delta );

the problem was the delta, it made the movement EXTREMELY slow, changing to

MoveAndSlide( playerSpeed.Normalized() * moveSpeed );

fixed the issue.

1 Like

Nicely done!

As a sidenote to other people that stumble upon this topic:
Unlike MoveAndCollide(), MoveAndSlide() multiplies the input parameter by delta for you. The similarities between the two functions (in Godot v3) make users prone to assume that the parameter should be the same. In Godot v4, the MoveAndSlide() function operates a little differently (I assume to avoid this confusion).

1 Like