I think I made the code below work with MoveAndSlideWithSnap
, but I am not positive. The trick is to check if the jump has been pressed, and the use MoveAndSlide
, otherwise using MoveAndSlideWithSnap
so the player stays grounded. That said, I'm not sure if the code will work is or is compatible, so I would make a backup of your script/project before testing.
using Godot;
using System;
public class PlayerControlScript : KinematicBody
{
private Vector3 gravity = Vector3.Down * 30f;
private float speed;
[Export] public float crawlSpeed = 3f;
private bool isCrawling = false;
[Export] public float walkSpeed = 7f;
[Export] public float sprintSpeed = 21f;
[Export] public float accel = 3f;
private Vector3 velocity;
[Export] public float jumpSpeed = 14f;
private bool canJump = false;
// new
private bool isJumping = false;
// not strictly required, but this is
// used to give a little time before snapping
// again so if the Y velocity isn't instantly taking the
// player off the ground it doesn't not allow the player to jump
private float isJumpingTimer = 0.0f;
private const IS_JUMPING_TIME = 0.2f;
private Spatial camPivotY;
private Spatial camPivotX;
private Camera camera;
[Export] public float mouseSens = 0.3f;
[Export] public float minAngle = 50f;
[Export] public float maxAngle = 50f;
private Spatial playerMesh;
public override void _Ready()
{
camPivotY = GetNode<Spatial>("../Player/CameraPivotY");
camPivotX = GetNode<Spatial>("../Player/CameraPivotY/CameraPivotX");
camera = GetNode<Camera>("../Player/CameraPivotY/CameraPivotX/Camera");
playerMesh = GetNode<Spatial>("../Player/PlayerMesh");
Input.SetMouseMode(Input.MouseMode.Captured);
speed = walkSpeed;
}
public void _GetInput()
{
bool isMoving = false;
float verticalVel = velocity.y;
velocity = new Vector3();
if (Input.IsActionPressed("Forward"))
{
velocity -= camera.GlobalTransform.basis.z * speed;
isMoving = true;
}
else if (Input.IsActionPressed("Backward"))
{
velocity += camera.GlobalTransform.basis.z * speed;
isMoving = true;
}
if (Input.IsActionPressed("Left"))
{
velocity -= camera.GlobalTransform.basis.x * speed;
isMoving = true;
}
else if (Input.IsActionPressed("Right"))
{
velocity += camera.GlobalTransform.basis.x * speed;
isMoving = true;
}
velocity.y = verticalVel;
//Which way does the MeshRenderer of the Player need to face
if (isMoving)
{
float angle = Mathf.Atan2(-velocity.x, -velocity.z);
Vector3 playerRot = playerMesh.Rotation;
playerRot.y = angle;
playerMesh.Rotation = Vector3.Up * playerRot;
}
if (Input.IsActionPressed("Sprint") && !isCrawling)
{
speed = sprintSpeed;
}
else if (Input.IsActionPressed("Crawl"))
{
speed = crawlSpeed;
isCrawling = true;
}
else
{
speed = walkSpeed;
isCrawling = false;
}
canJump = false;
if (Input.IsActionJustPressed("Jump") && !isCrawling)
{
canJump = true;
}
if (Input.IsActionJustPressed("ui_cancel"))
{
Input.SetMouseMode(Input.MouseMode.Visible);
}
}
public override void _PhysicsProcess(float delta)
{
velocity.y += gravity.y * delta;
_GetInput();
// New code below
if (isJumping == true)
{
velocity = MoveAndSlide(velocity, Vector3.Up, true);
}
else
{
velocity = MoveAndSlideWithSnap(velocity, GetFloorNormal(), Vector3.Up, true);
}
if (isJumpingTimer > 0)
{
isJumpingTimer -= delta
}
if (IsOnFloor())
{
if (isJumpingTimer <= 0)
{
isJumping = false
}
if (canJump)
{
velocity.y = jumpSpeed;
isJumping = true;
isJumpingTimer = IS_JUMPING_TIME;
}
}
}
}
Another solution instead of using the boolean that might work is making the snap vector zero when you are jumping, at least according to this StackExchange post.
Hopefully that will work! If not, then what I would recommend doing is check if the player just left the ground. If they did, I would push their velocity downwards with a set force and/or cancel or dampen out the upward velocity. Then you could check to see if they are still in the air, and if they are, then let the player fall like normal. That's how I would try to approach it if MoveAndSlideWithSnap
doesn't work, as I think that would work for preventing the player from jumping into the air on ramps.