I've been trying to make a hoverboard with a hover height based on terrain/surfaces

I’ve been trying to make a hoverboard with bodyposition that is 4 studs off from the ground but anything I do it won’t go 4 studs off from the terrain/surface.

I also want it to match the angle the hoverboard is on or the surface it’s on, so the hoverboard will be the same orientation/angle as the terrain, or wedge for example. So basically I’m trying to have hoverboard physics, or a hoverboard suspension. I don’t know what I’m doing wrong

basically the problem is it’s too close to the ground, won’t match the same angle/orientation as the surface and won’t stay 4 studs off the ground.

here’s the script:

local model = script.Parent
local seat = model.VehicleSeat
local base = model.Base

local engine = base.Engine
local bg = base.BodyGyro
local bv = base.BodyVelocity

local velocityDecay = 0.8
local currentSpeed = 0
local maxSpeed = 50
local maxReverse = 12
local acceleration = 1.2

local steerRoll = 0.5
local steerAmount = 0.05
local yawVelocity = 0

local pitchTilt = 0
local targetPitch = 0
local pitchLerpSpeed = 0.05
local rollTilt = 0
local targetRoll = 0
local rollLerpSpeed = 0.05

local rot = {yaw = base.Orientation.Y, pitch = 0, roll = 0}

local playingSound = false
local enginePitch = 6
local forwardEnginePitch = 12
local backwardEnginePitch = 8

local anim = nil

local hoverHeight = 3
local suspensionStrength = 50
local suspensionDamping = 0.8

local function lerp(a, b, t)
	return a + (b - a) * t
end

local function calcRot(yaw, pitch, roll)
	return CFrame.Angles(pitch, 0, 0) * CFrame.Angles(0, yaw, 0) * CFrame.Angles(0, 0, roll)
end

local function added(child)
	if child.ClassName == "Weld" then
		local human = child.Part1 and child.Part1.Parent:FindFirstChild("Humanoid")
		if human and seat:FindFirstChild("Animation") then
			anim = human:LoadAnimation(seat.Animation)
			anim:Play()
		end
	end
end

local function removed(child)
	if anim then
		anim:Stop()
		anim:Destroy()
		anim = nil
	end
end

seat.ChildAdded:Connect(added)
seat.ChildRemoved:Connect(removed)

while true do
	task.wait()

	if seat.Occupant and not playingSound then
		playingSound = true
		engine:Play()
	elseif not seat.Occupant and playingSound then
		playingSound = false
		engine:Stop()
	end

	if seat.Throttle == 1 then
		engine.Pitch = math.clamp(engine.Pitch + 1, enginePitch, forwardEnginePitch)
		currentSpeed = math.min(currentSpeed + acceleration, maxSpeed)
	elseif seat.Throttle == -1 then
		engine.Pitch = math.clamp(engine.Pitch + 1, enginePitch, backwardEnginePitch)
		currentSpeed = math.min(currentSpeed + acceleration, maxSpeed)
	else
		currentSpeed = bv.Velocity.Magnitude
		engine.Pitch = lerp(engine.Pitch, enginePitch, 0.1)
	end

	if seat.Steer == 1 then
		yawVelocity = -seat.Steer * steerAmount
		targetRoll = -steerRoll
	elseif seat.Steer == -1 then
		yawVelocity = -seat.Steer * steerAmount
		targetRoll = steerRoll
	else
		yawVelocity = 0
		targetRoll = 0
	end

	local origin = base.Position
	local rayDir = Vector3.new(0, -hoverHeight-5, 0)
	local ray = Ray.new(origin, rayDir)
	local hit, hitPos, normal = workspace:FindPartOnRay(ray, model)

	local desiredVel = Vector3.new()
	if hit then
		local distance = (origin - hitPos).Magnitude
		local offset = hoverHeight - distance

		desiredVel = Vector3.new(0, offset * suspensionStrength, 0)
	end


	if seat.Throttle ~= 0 then
		local moveDir = base.CFrame.LookVector * (seat.Throttle == 1 and currentSpeed or -maxReverse)
		desiredVel = desiredVel + moveDir
	end


	bv.Velocity = bv.Velocity * suspensionDamping + desiredVel * (1 - suspensionDamping)


	rot.yaw = rot.yaw + yawVelocity
	rot.roll = lerp(rot.roll, targetRoll, rollLerpSpeed)
	pitchTilt = lerp(pitchTilt, targetPitch, pitchLerpSpeed)

	bg.CFrame = calcRot(rot.yaw, pitchTilt, rot.roll)
end

i think the approach looks correct

can you show a video of what is happening

and try setting the position to exactly the target position without any velocity to see if its the target position or the velocity thats being handled wrong

and to align with the ground you just align with the normal i think

basically I want it to stay 4 studs off from the ground when I climb terrain for example it’s too close to the ground. It won’t just stay 4 studs off from the ground when climbing a slope for example, or terrain like for example if I climbed terrain to get to a flat surface it’s too close to the ground but when I go back to the ground the bodyposition keeps it 4 studs from the ground but not when I climb any terrain, surface.

I also want it to match the orientation/angle the hoverboards surface it’s on. What I mean by that let’s say you’re climbing a slope, or terrain the hoverboard will be the same angle as the terrain/surface it’s on so it looks more realistic as if you’re actually climbing it and not just straight 24/7.