I have some bugs with conflicting key input (W+S , W+D, W+A) after letting go server keeps applying velocity . I tried with remote events but , it worked of course but that’s bad practise i think, too many requests will be sent. Ideas?
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local HoverbikeConfig = require(ReplicatedStorage:WaitForChild("HoverbikeConfig"))
local HoverbikeLights = require(script.Parent:WaitForChild("HoverbikeLights"))
local remoteEvents = Instance.new("Folder")
remoteEvents.Name = "HoverbikeEvents"
remoteEvents.Parent = ReplicatedStorage
local mountBikeEvent = Instance.new("RemoteEvent")
mountBikeEvent.Name = "MountBike"
mountBikeEvent.Parent = remoteEvents
local dismountBikeEvent = Instance.new("RemoteEvent")
dismountBikeEvent.Name = "DismountBike"
dismountBikeEvent.Parent = remoteEvents
local updateControlsEvent = Instance.new("RemoteEvent")
updateControlsEvent.Name = "UpdateControls"
updateControlsEvent.Parent = remoteEvents
local hoverbike = workspace:WaitForChild("Hoverbike")
local vehicleSeat = hoverbike:WaitForChild("VehicleSeat") or hoverbike:WaitForChild("Seat")
local proximityPrompt = vehicleSeat:FindFirstChild("ProximityPrompt") or hoverbike:FindFirstChild("ProximityPrompt")
if not proximityPrompt then
warn("ProximityPrompt not found! Place it either under Hoverbike or under VehicleSeat")
return
end
local hoverSound = vehicleSeat:FindFirstChild("HoverSound") or hoverbike:FindFirstChild("HoverSound")
if not hoverSound then
warn("HoverSound not found! Create a Sound object named 'HoverSound' under VehicleSeat or Hoverbike and set its SoundId in Studio")
else
hoverSound.Looped = true
if hoverSound.SoundId == "" then
warn("HoverSound found but no SoundId set! Set the SoundId in Studio Properties panel")
end
end
local bikeData = {
rider = nil,
isActive = false,
boostCooldown = 0,
targetHeight = 0,
groundHeight = 0,
hoverStarted = false,
currentSpeed = 0,
isMoving = false,
landingMode = false,
isAscending = false,
isDescending = false,
isRotating = false,
lastRotationState = false,
lastStabilizationUpdate = 0,
lastGroundCheck = 0,
bodyVelocity = nil,
bodyPosition = nil,
bodyAngularVelocity = nil,
bodyGyro = nil,
originalOrientation = nil,
idleBobOffset = 0,
isIdle = false
}
local lastControlUpdate = {}
local CONTROL_UPDATE_COOLDOWN = 0.05
local function findGroundHeight(position)
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
raycastParams.FilterDescendantsInstances = {hoverbike}
local rayOrigin = position
local rayDirection = Vector3.new(0, -HoverbikeConfig.raycastDistance, 0)
local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
if raycastResult then
return raycastResult.Position.Y
else
return position.Y - HoverbikeConfig.raycastDistance
end
end
local function setupHoverbike()
if not vehicleSeat then
warn("Hoverbike missing VehicleSeat or Seat")
return false
end
local initialPosition = vehicleSeat.CFrame.Position
bikeData.groundHeight = findGroundHeight(initialPosition)
bikeData.originalOrientation = vehicleSeat.CFrame
local bodyVelocity = Instance.new("BodyVelocity")
bodyVelocity.MaxForce = Vector3.new(10000, 10000, 10000)
bodyVelocity.Velocity = Vector3.new(0, 0, 0)
bodyVelocity.Parent = vehicleSeat
local bodyPosition = Instance.new("BodyPosition")
bodyPosition.MaxForce = Vector3.new(0, 0, 0)
bodyPosition.Position = initialPosition
bodyPosition.D = 3000
bodyPosition.P = 50000
bodyPosition.Parent = vehicleSeat
local bodyAngularVelocity = Instance.new("BodyAngularVelocity")
bodyAngularVelocity.MaxTorque = Vector3.new(50000, 50000, 50000)
bodyAngularVelocity.AngularVelocity = Vector3.new(0, 0, 0)
bodyAngularVelocity.Parent = vehicleSeat
local bodyGyro = Instance.new("BodyGyro")
bodyGyro.MaxTorque = Vector3.new(40000, 0, 40000)
bodyGyro.P = 3000
bodyGyro.D = 500
bodyGyro.CFrame = bikeData.originalOrientation
bodyGyro.Parent = vehicleSeat
bikeData.bodyVelocity = bodyVelocity
bikeData.bodyPosition = bodyPosition
bikeData.bodyAngularVelocity = bodyAngularVelocity
bikeData.bodyGyro = bodyGyro
if vehicleSeat.ClassName == "VehicleSeat" then
vehicleSeat.MaxSpeed = 0
vehicleSeat.Torque = 0
vehicleSeat.TurnSpeed = 0
vehicleSeat.Disabled = false
end
vehicleSeat.Anchored = false
vehicleSeat.CanCollide = true
return true
end
local function canPlayerControlBike(player)
if bikeData.rider ~= player then return false end
if not vehicleSeat.Occupant or vehicleSeat.Occupant ~= player.Character.Humanoid then
return false
end
return true
end
local function mountBike(player)
local character = player.Character
if not character then return end
local humanoid = character:FindFirstChild("Humanoid")
if not humanoid then return end
if vehicleSeat.Occupant and vehicleSeat.Occupant.Parent ~= character then
return
end
if not vehicleSeat.Occupant then
vehicleSeat:Sit(humanoid)
task.wait(0.2)
end
if vehicleSeat.Occupant == humanoid then
bikeData.rider = player
bikeData.isActive = false
proximityPrompt.Enabled = false
if hoverSound then
hoverSound:Play()
end
HoverbikeLights.CancelDelayedShutdown()
HoverbikeLights.TurnOnAllEffects()
local currentPosition = vehicleSeat.CFrame.Position
bikeData.groundHeight = findGroundHeight(currentPosition)
bikeData.targetHeight = math.max(bikeData.groundHeight + HoverbikeConfig.minHoverHeight, currentPosition.Y + HoverbikeConfig.hoverHeight)
bikeData.bodyPosition.MaxForce = Vector3.new(0, 100000, 0)
bikeData.bodyPosition.Position = Vector3.new(currentPosition.X, bikeData.targetHeight, currentPosition.Z)
local _, currentYaw, _ = vehicleSeat.CFrame:ToEulerAnglesYXZ()
bikeData.bodyGyro.CFrame = CFrame.new(vehicleSeat.Position) * CFrame.Angles(0, currentYaw, 0)
bikeData.bodyVelocity.Velocity = Vector3.new(0, 20, 0)
task.delay(1, function()
if bikeData.bodyVelocity then
bikeData.bodyVelocity.Velocity = Vector3.new(0, 0, 0)
end
end)
bikeData.isActive = true
bikeData.hoverStarted = true
bikeData.isIdle = true
end
end
local function dismountBike(player)
if vehicleSeat.Occupant then
vehicleSeat.Occupant.Jump = true
end
proximityPrompt.Enabled = true
if hoverSound then
hoverSound:Stop()
end
HoverbikeLights.TurnOffAllEffectsWithSmartDelay(vehicleSeat, bikeData.groundHeight)
bikeData.rider = nil
bikeData.isActive = false
bikeData.isMoving = false
bikeData.isIdle = false
bikeData.isAscending = false
bikeData.isDescending = false
bikeData.isRotating = false
bikeData.currentSpeed = 0
bikeData.landingMode = true
local currentPosition = vehicleSeat.CFrame.Position
bikeData.groundHeight = findGroundHeight(currentPosition)
if bikeData.bodyVelocity then
bikeData.bodyVelocity.Velocity = Vector3.new(0, 0, 0)
end
if bikeData.bodyAngularVelocity then
bikeData.bodyAngularVelocity.AngularVelocity = Vector3.new(0, 0, 0)
end
end
local function updateBikeControls(player, controls)
if not canPlayerControlBike(player) then return end
local now = tick()
if lastControlUpdate[player] and now - lastControlUpdate[player] < CONTROL_UPDATE_COOLDOWN then
return
end
lastControlUpdate[player] = now
if not bikeData.isActive then return end
local moveVector = Vector3.new(controls.X or 0, 0, controls.Z or 0)
local boost = controls.boost or false
bikeData.isAscending = controls.ascending or false
bikeData.isDescending = controls.descending or false
local wasRotating = bikeData.isRotating
if controls.X and controls.X > 0 then
bikeData.bodyAngularVelocity.AngularVelocity = Vector3.new(0, HoverbikeConfig.rotationSpeed, 0)
bikeData.bodyAngularVelocity.MaxTorque = Vector3.new(0, 100000, 0)
bikeData.isRotating = true
elseif controls.X and controls.X < 0 then
bikeData.bodyAngularVelocity.AngularVelocity = Vector3.new(0, -HoverbikeConfig.rotationSpeed, 0)
bikeData.bodyAngularVelocity.MaxTorque = Vector3.new(0, 100000, 0)
bikeData.isRotating = true
else
bikeData.bodyAngularVelocity.AngularVelocity = Vector3.new(0, 0, 0)
bikeData.bodyAngularVelocity.MaxTorque = Vector3.new(50000, 50000, 50000)
bikeData.isRotating = false
end
bikeData.lastRotationState = wasRotating
local hasInput = moveVector.Magnitude > 0 or bikeData.isAscending or bikeData.isDescending or bikeData.isRotating
bikeData.isIdle = not hasInput
local currentBoostMultiplier = 1
if boost and bikeData.boostCooldown <= 0 then
currentBoostMultiplier = HoverbikeConfig.boostMultiplier
bikeData.boostCooldown = HoverbikeConfig.boostCooldown
end
bikeData.currentSpeed = moveVector.Z * HoverbikeConfig.maxSpeed * currentBoostMultiplier
bikeData.isMoving = moveVector.Z ~= 0
end
local function updatePhysics(deltaTime)
if bikeData.boostCooldown > 0 then
bikeData.boostCooldown = math.max(0, bikeData.boostCooldown - deltaTime)
end
if bikeData.landingMode then
local currentY = vehicleSeat.Position.Y
local groundY = bikeData.groundHeight
if currentY > groundY + 1 then
bikeData.targetHeight = bikeData.targetHeight - (10 * deltaTime)
bikeData.bodyPosition.Position = Vector3.new(vehicleSeat.Position.X, bikeData.targetHeight, vehicleSeat.Position.Z)
else
bikeData.bodyPosition.MaxForce = Vector3.new(0, 0, 0)
bikeData.bodyGyro.MaxTorque = Vector3.new(0, 0, 0)
bikeData.landingMode = false
bikeData.hoverStarted = false
end
end
if bikeData.isActive and bikeData.hoverStarted then
local currentTime = tick()
if (currentTime - bikeData.lastGroundCheck) >= HoverbikeConfig.groundCheckRate then
bikeData.lastGroundCheck = currentTime
bikeData.groundHeight = findGroundHeight(vehicleSeat.Position)
end
if (currentTime - bikeData.lastStabilizationUpdate) >= HoverbikeConfig.stabilizationUpdateRate or bikeData.isRotating ~= bikeData.lastRotationState then
bikeData.lastStabilizationUpdate = currentTime
if not bikeData.isRotating then
local currentCFrame = vehicleSeat.CFrame
local _, currentYaw, _ = currentCFrame:ToEulerAnglesYXZ()
bikeData.bodyGyro.CFrame = CFrame.new(vehicleSeat.Position) * CFrame.Angles(0, currentYaw, 0)
bikeData.bodyGyro.MaxTorque = Vector3.new(40000, 0, 40000)
bikeData.bodyGyro.P = 3000
bikeData.bodyGyro.D = 500
else
bikeData.bodyGyro.MaxTorque = Vector3.new(0, 0, 0)
end
end
if bikeData.isAscending then
bikeData.targetHeight = bikeData.targetHeight + (HoverbikeConfig.ascendSpeed * deltaTime)
elseif bikeData.isDescending then
local minHeight = bikeData.groundHeight + HoverbikeConfig.minHoverHeight
bikeData.targetHeight = math.max(minHeight, bikeData.targetHeight - (HoverbikeConfig.descendSpeed * deltaTime))
end
end
if bikeData.hoverStarted then
local bobOffset = 0
if bikeData.isIdle then
bobOffset = math.sin(tick() * HoverbikeConfig.idleBobSpeed) * HoverbikeConfig.idleBobAmplitude
else
bobOffset = math.sin(tick() * 3) * 0.3
end
bikeData.bodyPosition.Position = Vector3.new(vehicleSeat.Position.X, bikeData.targetHeight + bobOffset, vehicleSeat.Position.Z)
bikeData.bodyPosition.MaxForce = Vector3.new(0, 100000, 0)
end
if bikeData.isActive then
local heightDiff = bikeData.targetHeight - vehicleSeat.Position.Y
local verticalVelocity = math.clamp(heightDiff * 5, -30, 30)
if bikeData.isMoving then
local bikeCFrame = vehicleSeat.CFrame
if math.abs(bikeData.currentSpeed) > 100 then
local moveDistance = (bikeData.currentSpeed * deltaTime)
local newPosition = vehicleSeat.Position + (bikeCFrame.LookVector * moveDistance)
bikeData.bodyPosition.MaxForce = Vector3.new(100000, 100000, 100000)
bikeData.bodyPosition.Position = Vector3.new(newPosition.X, bikeData.targetHeight, newPosition.Z)
bikeData.bodyPosition.D = 10000
bikeData.bodyPosition.P = 100000
bikeData.bodyVelocity.MaxForce = Vector3.new(0, 0, 0)
else
local desiredVelocity = bikeCFrame.LookVector * (bikeData.currentSpeed or 0)
bikeData.bodyVelocity.Velocity = Vector3.new(desiredVelocity.X, verticalVelocity, desiredVelocity.Z)
bikeData.bodyVelocity.MaxForce = Vector3.new(50000, 10000, 50000)
bikeData.bodyPosition.MaxForce = Vector3.new(0, 100000, 0)
end
else
bikeData.bodyVelocity.Velocity = Vector3.new(0, verticalVelocity, 0)
bikeData.bodyVelocity.MaxForce = Vector3.new(10000, 10000, 10000)
bikeData.bodyPosition.MaxForce = Vector3.new(0, 100000, 0)
end
end
end
vehicleSeat:GetPropertyChangedSignal("Occupant"):Connect(function()
local occupant = vehicleSeat.Occupant
if occupant then
local character = occupant.Parent
local player = Players:GetPlayerFromCharacter(character)
if player and bikeData.rider ~= player then
mountBike(player)
end
else
if bikeData.rider then
dismountBike(bikeData.rider)
end
end
end)
mountBikeEvent.OnServerEvent:Connect(function(player)
local character = player.Character
if character and character.PrimaryPart then
local bikePosition = hoverbike.PrimaryPart and hoverbike.PrimaryPart.Position or vehicleSeat.Position
local distance = (character.PrimaryPart.Position - bikePosition).Magnitude
if distance > 10 then return end
end
mountBike(player)
end)
dismountBikeEvent.OnServerEvent:Connect(function(player)
dismountBike(player)
end)
updateControlsEvent.OnServerEvent:Connect(function(player, controls)
if typeof(controls) ~= "table" then return end
updateBikeControls(player, controls)
end)
RunService.Heartbeat:Connect(updatePhysics)
task.spawn(function()
while true do
task.wait(5)
if bikeData.rider then
local rider = bikeData.rider
if not rider.Parent or not rider.Character or
not vehicleSeat.Occupant or
vehicleSeat.Occupant.Parent ~= rider.Character then
dismountBike(rider)
end
end
end
end)
Players.PlayerRemoving:Connect(function(player)
if bikeData.rider == player then
dismountBike(player)
end
lastControlUpdate[player] = nil
end)
setupHoverbike()
local lightsInitialized = HoverbikeLights.Initialize(hoverbike)
if not lightsInitialized then
warn("Hoverbike lights system failed to initialize - effects may not work")
end