How could I "Slow down time", without affecting the player's movement?

Godot Version

4.3-beta-1

Question

So I ran into an issue. I’m trying to make it so the player can dash, and while they’re holding the dash button time slows down.

The problem is, that I’m using physics (velocity and move_and_slide()) to move the player in this platformer, and so if I say ‘Engine.time_scale = 0.125’, the whole movement system is messed up. You don’t jump as far, as high, etc.

How else could I implement a time slow, without messing up the physics calculations and the amount the player would move normally?

Here’s my player code :

class_name Player extends CharacterBody2D

@onready var right_cast: ShapeCast2D = $RightCast
@onready var left_cast: ShapeCast2D = $LeftCast
@onready var jump_buffer_timer: Timer = $Timers/JumpBufferTimer

@export_category("Physics")
@export var gravity: float = 6.5

@export_category("Movement")
@export var move_speed: float = 250.0
var horizontal_movement: int = 0
var is_going_left: bool
var is_idle: bool = false

@export_category("Jumping")
@export var jump_force: float = 1000.0
## Expressed in milliseconds
@export var jump_buffer_time: float = 100.0
var want_to_jump: bool = false

@export_category("Dashing")
@export var dash_force: float = 1500.0
var is_dash_held: bool


func _ready() -> void:
  jump_buffer_timer.wait_time = jump_buffer_time / 1000


func _process(delta: float) -> void:
  if not is_on_floor():
    velocity.y += pow(gravity, 2)

  is_dash_held = Input.is_action_pressed('dash')

  if Input.is_action_just_pressed('dash'):
    slow_time(true)
  if Input.is_action_just_released('dash'):
    slow_time(false)

  if Input.is_action_just_pressed('jump') and !is_on_floor():
    jump_buffer_timer.start()

  if Input.is_action_just_pressed('jump') and is_on_floor():
    _jump()

  if not jump_buffer_timer.is_stopped() and is_on_floor():
    _jump()

  if not is_on_floor():
    if is_going_left:
      horizontal_movement = -1
    else:
      horizontal_movement = 1
  else:
    horizontal_movement = 0

  _handle_direction_switch()


func _physics_process(delta: float) -> void:
  velocity.x = horizontal_movement * move_speed
  move_and_slide()


func _handle_direction_switch() -> void:
  if left_cast.is_colliding():
    is_going_left = false
  if right_cast.is_colliding():
    is_going_left = true


func _jump() -> void:
    velocity.y = 0
    velocity.y -= jump_force

func _stop_jump_buffer_time() -> void:
  jump_buffer_timer.stop()
  jump_buffer_timer.wait_time = jump_buffer_time / 1000


func _on_jump_buffer_timer_timeout() -> void:
  _stop_jump_buffer_time()

func slow_time(toggle: bool):
  if toggle == true:
    Engine.time_scale = 0.25
  else:
    Engine.time_scale = 1

You can set the characters node process to always, so it isn’t affected.if that doesn’t work, there are probably similar settings.I can’t test of process mode only works for pause

1 Like

Sadly these do nothing at all, some of them just simple disable all movement

This from 2021

“This was discussed on Godot’s RocketChat. The consensus is that this change is not necessary, and therefore this proposal is not desired. You can scale time yourself in the script, and other nodes such as particles or animation already support time scaling.
For physics time scaling won’t work anyway because the solver will break. So, closing.”

Can you link the discussion, maybe I’ll be able to find something actually useful there?

Add a universal time_scale property to Node · Issue #2507 · godotengine/godot-proposals (github.com)

Quite silly to say that as time scaling is useful in many games and instances. There’s a different way to implement this functionality without the side effects you mentioned. Time can be scaled right now, but not per node, meaning slowing down or stopping time for a specific node or set of nodes is impossible. Unless I want to reimplement a lot of the pre-existing engine nodes.

Something like this might work:

Engine.time_scale = 0.25
velocity /= Engine.time_scale # <- Counter the slowdown
move_and_slide()
velocity *= Engine.time_scale # <- Not sure if needed