I’ve recently been working on a skateboard and have come across a problem. My skateboard floats in the air. I found out that the best way to fix this is by properly orienting the skateboard to whatever it is currently on. The skateboard looks like this when going up a hill:
It doesn’t seem like an issue, but because of the way the skateboard actually moves, it goes in the direction of the skateboard.CFrame.LookVector and since the skateboard is pointing a little above the area it’s supposed to be looking, it goes in that direction, and doesn’t actually go down the slope. Instead it goes into the air and stays there until it stops moving, at which time, it falls down.
Server Script Code
local tool = script.Parent
local start = tool:WaitForChild("Start")
local move = tool:WaitForChild("Move")
local stop = tool:WaitForChild("Stop")
local skateboard = game.ReplicatedStorage:WaitForChild("Skateboard")
local sk
local bv
local Loop
local leftLoop
local leftBv
local rightLoop
local rightBv
local backLoop
local backBv
local cruise = tool:WaitForChild("CruiseLoop")
local boardStop = tool:WaitForChild("BoardStop")
function onServerInvoke(player)
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local hrp = character:WaitForChild("HumanoidRootPart")
sk = skateboard:Clone()
sk.Parent = workspace
sk.Position = hrp.Position
sk.Orientation = hrp.Orientation
character:MoveTo(sk.Position)
local weld = Instance.new("Weld")
local hrp = character:WaitForChild("HumanoidRootPart")
weld.Part0 = sk
weld.Part1 = hrp
weld.C0 = sk.CFrame:Inverse() - Vector3.new(0,1,0)
weld.C1 = (hrp.CFrame * CFrame.new(Vector3.new(-0.4,0,0)) * CFrame.Angles(0,math.rad(85),0)):Inverse()
weld.Parent = hrp
weld.Name = "SkWeld"
humanoid.JumpPower = 35
character:FindFirstChild("Animate").Disabled = true
humanoid.WalkSpeed = 0
return sk
end
function stopFunction(player)
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local hrp = character:WaitForChild("HumanoidRootPart")
local weld = hrp:FindFirstChild("SkWeld")
if weld then weld:Destroy() end
humanoid.JumpPower = 50
humanoid.WalkSpeed = 16
character:FindFirstChild("Animate").Disabled = false
sk:Destroy()
end
local Xunit = Vector3.new(1, 0, 0)
local Yunit = Vector3.new(0, 1, 0)
local Zunit = Vector3.new(0, 0, 1)
function moveFunction(player, eventType)
if eventType == "Start" then
cruise:Play()
bv = Instance.new('BodyVelocity')
bv.Name = "Movement"
bv.MaxForce= Vector3.new(math.huge,math.huge,math.huge)
bv.P = math.huge
bv.Parent = sk
Loop = game:GetService("RunService").Heartbeat:Connect(function()
bv.Velocity = -(sk.CFrame.LookVector * 60)
--bv.Velocity = player.Character.HumanoidRootPart.CFrame.rightVector * -10
end)
elseif eventType == "Left" then
leftBv = Instance.new('BodyAngularVelocity')
leftBv.Name = "LeftMovement"
leftBv.MaxTorque = Vector3.new(math.huge,math.huge,math.huge)
leftBv.Parent = sk
leftLoop = game:GetService("RunService").Heartbeat:Connect(function()
leftBv.AngularVelocity = Vector3.new(0,2,0)
end)
elseif eventType == "Right" then
rightBv = Instance.new('BodyAngularVelocity')
rightBv.Name = "RightMovement"
rightBv.MaxTorque = Vector3.new(math.huge,math.huge,math.huge)
rightBv.Parent = sk
rightLoop = game:GetService("RunService").Heartbeat:Connect(function()
rightBv.AngularVelocity = Vector3.new(0,-2,0)
end)
elseif eventType == "Back" then
backBv = Instance.new('BodyVelocity')
backBv.Name = "BackMovement"
backBv.MaxForce= Vector3.new(math.huge,math.huge,math.huge)
backBv.P = math.huge
backBv.Parent = sk
backLoop = game:GetService("RunService").Heartbeat:Connect(function()
backBv.Velocity = (sk.CFrame.LookVector * 10)
end)
elseif eventType == "Stop" then
cruise:Stop()
boardStop:Play()
Loop:Disconnect()
if leftLoop and leftBv then
leftLoop:Disconnect()
leftBv:Destroy()
end
if rightLoop and rightBv then
rightLoop:Disconnect()
rightBv:Destroy()
end
if backBv and backLoop then
backLoop:Disconnect()
backBv:Destroy()
end
bv:Destroy()
elseif eventType == "StopLeft" then
if leftLoop and leftBv then
leftLoop:Disconnect()
leftBv:Destroy()
end
elseif eventType == "StopRight" then
if rightLoop and rightBv then
rightLoop:Disconnect()
rightBv:Destroy()
end
elseif eventType == "StopBack" then
if backBv and backLoop then
backLoop:Disconnect()
backBv:Destroy()
end
end
end
start.OnServerInvoke = onServerInvoke
stop.OnServerEvent:Connect(stopFunction)
move.OnServerEvent:Connect(moveFunction)
local function getRotationBetween(u, v, axis)
local dot = u:Dot(v)
if (dot > 0.99999) then
-- situation 1
return CFrame.new()
elseif (dot < -0.99999) then
-- situation 2
return CFrame.fromAxisAngle(axis, math.pi)
end
-- situation 3
return CFrame.fromAxisAngle(u:Cross(v), math.acos(dot))
end
while true do
if sk then
local rayResult = workspace:Raycast(sk.Position,-10*sk.CFrame.UpVector)
if rayResult then
local normal = rayResult.Normal
local cf = getRotationBetween(sk.CFrame.UpVector, normal, Vector3.new(1,0,0))
end
end
wait()
end
Local Script Code
local tool = script.Parent
local skateboard = game.ReplicatedStorage:WaitForChild("Skateboard")
local player = game.Players.LocalPlayer
local usi = game:GetService("UserInputService")
local camera = game.Workspace.Camera
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local coastingPose = tool:WaitForChild("CoastingPose")
local start = tool:WaitForChild("Start")
local stop = tool:WaitForChild("Stop")
local hrp1 = character:WaitForChild("HumanoidRootPart")
local ogOrient = hrp1.CFrame.upVector
local move = tool:WaitForChild("Move")
local leftTurn = tool:WaitForChild("LeftTurn")
local rightTurn = tool:WaitForChild("RightTurn")
local boardKick = tool:WaitForChild("BoardKick")
local equipped = false
local moving = false
local placed = false
local sk
local animator = humanoid:WaitForChild("Animator")
local cp = animator:LoadAnimation(coastingPose)
local bk = animator:LoadAnimation(boardKick)
local rt = animator:LoadAnimation(rightTurn)
local lt = animator:LoadAnimation(leftTurn)
lt.Looped = true
rt.Looped = true
cp.Priority = Enum.AnimationPriority.Action
bk.Priority = Enum.AnimationPriority.Action
rt.Priority = Enum.AnimationPriority.Action
lt.Priority = Enum.AnimationPriority.Action
tool.Equipped:Connect(function()
equipped = true
sk = start:InvokeServer()
wait(0.1)
local rn = humanoid:WaitForChild("Animator"):GetPlayingAnimationTracks()
for i, v in ipairs(rn) do
v:Stop()
end
cp:Play()
end)
tool.Unequipped:Connect(function()
equipped = false
placed = false
if sk then
stop:FireServer()
cp:Stop()
bk:Stop()
lt:Stop()
rt:Stop()
end
end)
usi.InputBegan:Connect(function(input)
if input.KeyCode == Enum.KeyCode.W and equipped and not moving then
moving = true
move:FireServer("Start")
while moving do
bk:Play()
bk.Stopped:Wait()
cp:Play()
wait(3)
end
elseif input.KeyCode == Enum.KeyCode.A and equipped then
move:FireServer("Left")
lt:Play()
elseif input.KeyCode == Enum.KeyCode.D and equipped then
move:FireServer("Right")
rt:Play()
elseif input.KeyCode == Enum.KeyCode.S and equipped then
move:FireServer("Back")
end
end)
usi.InputEnded:Connect(function(input)
if input.KeyCode == Enum.KeyCode.W and equipped and moving then
moving = false
move:FireServer("Stop")
elseif input.KeyCode == Enum.KeyCode.A and equipped then
move:FireServer("StopLeft")
lt:Stop()
elseif input.KeyCode == Enum.KeyCode.D and equipped then
move:FireServer("StopRight")
rt:Stop()
elseif input.KeyCode == Enum.KeyCode.S and equipped then
move:FireServer("StopBack")
end
end)
usi.InputChanged:Connect(function(input)
if input.KeyCode == Enum.KeyCode.A and equipped then
move:FireServer("Left")
elseif input.KeyCode == Enum.KeyCode.D and equipped then
move:FireServer("Right")
elseif input.KeyCode == Enum.KeyCode.S and equipped then
move:FireServer("Back")
end
end)
Your issue is that by directly setting the velocity, you are telling the skateboard to ignore gravity and just go in a straight line.
A simple improvement is to change your MaxForce's Y component to be 0. That way you’re letting gravity do the work in the vertical direction. This wouldn’t quite solve your issue for turning but it might help.
A better solution would be to not use BodyVelocity at all and instead use BodyForce (or a VectorForce | Roblox Creator Documentation). That way, you’re embracing the physics engine rather than fighting with it. You may need to get a little fancier with this because just applying a continuous force to something will cause it to accelerate indefinitely.
Then you can set the orientation of your skateboard with this. This depends on how your skateboard works. For example, if you use a BodyGyro you would change the goal CFrame based on the normal of the ground.
Oh so is it not physics based? That makes it harder to move. I guess you can adjust the weld based on the normal, but it would be hard to rotate the character too.
I don’t think you understand what it is I’m trying to do. The weld works perfectly, I just need the skateboard oriented properly on the y-axis so it aligns with the ground. I have looked at multiple other posts on the dev forum which explain how to orient a part based on the ground normal, but none of them work for the board.
So, I believe your Humanoid is in PlatformStand, which basically makes the HumanoidRootPart always face upwards, like the character is standing, but not be affected by the charactercontroller’s physics in other ways.
This means your character can’t rotate. Your board is welded to the character, so it can’t rotate too.
To rotate the stuff, you’ll basically need to change how the constraints (welds, motor6s, etc) connect to the HumanoidRootPart, which is the part that can’t rotate b|c of the humanoid state.
So, a few steps:
Get the ground normal
Create a function that gets the desired position and orientation of the skateboard based on the ground normal and the position of the humanoidrootpart
Set the .Position of the skateboard and the .Rotation of the skateboard (Position and Rotation can adjust welds, dont use CFrame)
If you want to rotate the character too, you can do the same thing with the lower torso (or upper, again, I forgot which)
The character’s HRP is welded directly to the skateboard by means of a script. The character’s Animate script is also disabled, all animations are stopped, and a skateboard idle animation is played. I guess I’m using physics?
I think that’s based on the HumanoidState then, it’s basically the same deal. If you were using physics you’d have something like a BodyGyro or an AlignOrientation.