Velocity working differently on different computers

I have the following script for an projectile like a Minecraft Ender Pearl that teleports you where you throw it. I’ve ran it on a windows desktop and it flies normally, then on my more powerful linux laptop it falls like it’s made from lead.

Windows:

Linux:

And this is the script for the projectile:

extends CharacterBody3D

var speed : float = 0.5
var launchstrength : float = 20
var dotp : bool = false
var setposition:Vector3
var gravity_scale : float = 0
var lerpPushSize : bool = false

var push_field : Node3D = preload("res://scenes/items/push_field.tscn").instantiate()

@onready var model: Node3D = $blockbench_export2
@onready var push_sphere : Area3D = $PushSphere
@onready var ground_detection: Area3D = $'ground detection'
@onready var kill_timer: Timer = $KillTimer


func _ready() -> void:
	var velo : float = gamemanager.player.velocity.length() + 30
	velocity = gamemanager.player.camera.global_transform.basis.z.normalized() *-1 * velo
	gamemanager.is_tp_on_screen = true

func grav() -> void:
	velocity.y += -gravity_scale

func _process(_delta: float) -> void:
	gravity_scale = move_toward(gravity_scale, 7.0, 0.05)
	if global_position.distance_to(gamemanager.player.global_position) > 150:
		gamemanager.is_tp_on_screen = false
		queue_free()
	if dotp==true:
		position=setposition
		if gamemanager.player.camera.global_rotation.y<7:
			if gamemanager.player.global_position.distance_to(global_position)>2:
				gamemanager.player.global_position= lerp(gamemanager.player.global_position, global_position+Vector3(0,0.5,0),0.5)
			else:
				teleport()
		else:
			if gamemanager.player.global_position.distance_to(global_position)>2:
				gamemanager.player.global_position= lerp(gamemanager.player.global_position, global_position,0.5)
			else:
				teleport()
	else:
		setposition=position
	grav()
	move_and_slide()
func teleport() -> void:
	gamemanager.player.air_time+=30
	dotp = false
	if gamemanager.player.has_ender_pearl_upgrade:
		add_sibling(push_field)
		push_field.global_position = global_position
		for i : Variant in push_sphere.get_overlapping_bodies():
			if i is CharacterBody3D:
				kb(i)
				if i is enemy:
					var enemy_thing : enemy = i
					enemy_thing.fall_distance -= 6
			elif i is RigidBody3D:
				if i != null:
					var kbVec : Vector3 = (i.position - position).normalized()
					i.linear_velocity.y += 15
					i.linear_velocity += kbVec * 20
	model.hide()
	ground_detection.monitoring = false
	kill_timer.start()
	gamemanager.player.hurt_player(3, self, 0.4, 0.3)
func kb(i : Variant) -> void:
	await  get_tree().create_timer(0.1).timeout
	if i != null:
		var kbVec : Vector3 = (i.position - position).normalized()
		i.velocity.y += 25
		i.velocity += kbVec * 60
		if i is enemy && i.fall_distance < 0:
			i.die()
func _on_ground_detection_body_entered(_body: Node3D) -> void:
	dotp=true


func _on_kill_timer_timeout() -> void:
	gamemanager.player.camera_shake_entries.erase(self)
	gamemanager.is_tp_on_screen = false
	call_deferred("queue_free")

You’re using _process() for the integration of the projectile’s state. You should be using _physics_process() instead.

There’s a long explanation to be made as to why you should be using one over the other. However, there’s already resources on Godot Docs explaining this:

The gist of it is that due to the way integration works in general, the result will be different depending on the update rate of the system. When you use _process(), the framerate fluctuates and varies due to the specs of the PC (like you’re seeing). This is not the case for _physics_process() which maintains and runs a constant update rate for physics and the nodes implementing it.


I hope that helps.