this is the 3D character controller for third person player game in godot, I'm trying to make it so that the player can run either by pressing SHIFT or by double tapping the movement keys
below the script I'm using with comments, SHIFT seems to work but but double tapping doesn't, no matter how much I increase the timing viability
I tried this script and increasing the timer for double tap, modified the engine's maximum FPS thinking it had something to do with that, but it didn't fix the problem.
extends KinematicBody
export var walk_speed = 5.0 # Speed of the character's movement when walking
export var run_speed = 10.0 # Speed of the character's movement when running
export var sprint_speed = 20.0 # Speed of the character's movement when sprinting
export var gravity = 9.8 # Gravity affecting the character's vertical movement
export var jump_power = 5.0 # Power of the character's jump
export var run_key = "run" # Key to use for running
export var weight = 1
var velocity = Vector3.ZERO # Velocity vector of the character's movement
var is_running = false # Whether the character is currently running
var is_double_tapping = false # Whether the player is double-tapping to sprint
var double_tap_time = 0.5 # Time frame for double-tapping (in seconds)
var double_tap_timer = 1 # Timer for tracking double-tap time
var jumping = false # Whether the player is currently jumping
func _physics_process(delta):
# Get the input from the player
var move_dir = Vector3.ZERO
var cam_transform = get_node("Camroot/Camera").global_transform
var forward = -cam_transform.basis.z
var right = cam_transform.basis.x
move_dir = Input.get_action_strength("move_forward") * forward - Input.get_action_strength("move_back") * forward + Input.get_action_strength("move_right") * right - Input.get_action_strength("move_left") * right
move_dir = move_dir.normalized()
# Update the velocity based on the input and speed, taking into account the character's orientation and whether they are running
var target_speed = walk_speed
if is_running:
target_speed = run_speed
if is_double_tapping:
target_speed = sprint_speed
velocity.x = move_dir.x * target_speed
velocity.z = move_dir.z * target_speed
# Apply gravity only if the character is not on the ground or on a slope
if not is_on_floor() or abs(floor(get_floor_normal().dot(Vector3.UP)) - 1) > 0.001:
velocity.y -= gravity *weight* delta
# Jump only if the character is on the ground
if is_on_floor():
if Input.is_action_just_pressed("jump"):
jumping = true
# Update the velocity based on whether the character is jumping
if jumping:
velocity.y = jump_power
jumping = false
# Check if the run key has been pressed or released, and update the running state accordingly
if Input.is_action_just_pressed(run_key):
is_running = true
if not is_double_tapping:
is_double_tapping = true
double_tap_timer = double_tap_time
else:
is_double_tapping = false
target_speed = sprint_speed
elif Input.is_action_just_released(run_key):
is_running = false
# Check if the player is double-tapping to sprint
if is_double_tapping:
double_tap_timer -= delta
if double_tap_timer < 0.0:
is_double_tapping = false
# Move the character
velocity = move_and_slide(velocity, Vector3.UP)
I suppose this is the mechanism you want to trigger:
var target_speed = walk_speed
if is_running:
target_speed = run_speed
if is_double_tapping:
target_speed = sprint_speed
So let us see how is_double_tapping
becomes true
. That would be here:
if Input.is_action_just_pressed(run_key):
is_running = true
if not is_double_tapping:
is_double_tapping = true
double_tap_timer = double_tap_time
else:
is_double_tapping = false
target_speed = sprint_speed
elif Input.is_action_just_released(run_key):
is_running = false
So, to is_double_tapping
to become true
, the player first needs to press the run_key
action.
That is not what you said you want. You said either:
I'm trying to make it so that the player can run either by pressing SHIFT or by double tapping the movement keys
But you have one mechanism gating the other. In fact, you have defined three speeds.
Also, you seem to interpret is_double_tapping
in two different ways. Sometimes it means that that the time to double tap is active, sometimes it seems to mean the character should be going faster because the player double tapped. And these can't work together, because when the time to double tap ends (and is_double_tapping
becomes false
) you want the character to go fast if the player did double tap (with the change of speed happening when is_double_tapping
is true
).
And please notice that you are not checking if the player did double tap at all. Where is the code that checks if the player pressed the directions within the given time? It isn't there.
So, let us rethink this.
You need a timer solution. I'll keep the one you have. On the first input you start it. So you can check if the next input is within the appropriate time.
Thus, you should set double_tap_timer
when the player pressed the direction…
That is, you should set double_tap_timer
when move_dir
became different from zero. Which is something you are not checking.
I'll pseudo-code it first:
move_dir
is not zero but was zero before:
is_double_tapping
and the direction is roughly the same as the old one:
is_double_tapping
:
is_double_tapping = true
.Right, I need to have some old_move_dir
:
move_dir
is not zero and old_move_dir
is zero:
is_double_tapping
and move_dir.dot(old_move_dir) > 0.0
:
is_double_tapping
:
is_double_tapping = true
old_move_dir = move_dir
Having three speeds will throw a wrench on it. So you have two reasons to increase speed: The player double tapped, or the player used the run_key
action. My intuition would be to keep track of these as bool variables, and depending on them decide target_speed
.
But when do you set them to false
? For the run_key
action that is easy: it ends when the player released the run_key
action. But there is no clear end for the double tap. Surely you want to set it to false
when the player stopped. That is when move_dir
is zero.
Ok, let us add that:
is_fast_because_run_key = Input.is_action_pressed(run_key)
move_dir
is zero:
is_fast_because_double_tap = false
old_move_dir
is zero.
is_double_tapping
and move_dir.dot(old_move_dir) > 0.0
:
is_fast_because_double_tap = true
is_double_tapping
.
is_double_tapping = true
.I'll write that as code now:
is_fast_because_run_key = Input.is_action_pressed(run_key)
if move_dir.is_equal_approx(Vector3.ZERO):
is_fast_because_double_tap = false
else:
if old_move_dir.is_equal_approx(Vector3.ZERO):
if is_double_tapping and move_dir.dot(old_move_dir) > 0.0:
is_fast_because_double_tap = true
if not is_double_tapping:
is_double_tapping = true
double_tap_timer = double_tap_time
old_move_dir = move_dir
Then you can pick target_speed
like this:
if is_fast_because_run_key and is_fast_because_double_tap:
target_speed = sprint_speed
elif is_fast_because_run_key or is_fast_because_double_tap:
target_speed = run_speed
else:
target_speed = walk_speed
You can get rid of the part where you used to check run_key
.
And, of course, you still need this code:
# Check if the player is double-tapping to sprint
if is_double_tapping:
double_tap_timer -= delta
if double_tap_timer < 0.0:
is_double_tapping = false