Orient skateboard to ground

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:

And this is what I want it to look like:

You can try the skateboard out here: Gears Test Place - Roblox

Video of skateboard floating because of this problem

Thanks in advance!

1 Like

I dont quite see the issue, are you sure this is not just a problem with the Model? Do you mind sharing the code?

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.

2 Likes

Wow, thank you so much! It doesn’t float anymore!

You can also get the direction/normal of the ground with a raycast downwards then set the orientation with that.

I have seen people doing that, and I have tried their solutions, but none of them work for me. Could you elaborate?

1 Like

You can get the normal using Workspace:RayCast then get the RaycastResult.Normal. This is a vector that points upwards/away from the ground.

https://developer.roblox.com/en-us/articles/Raycasting


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.

I am using a BodyVelocity for skateboard propulsion. Could you please provide a code example?

How do you keep the skateboard facing upwards currently?

What do you mean? I weld the character’s HRP to the skateboard.

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.

The character doesn’t need to be rotated. I just need the skateboard to be parallel to the ground.

You can adjust the weld then. It would probably look weird though to have the player not standing on the skateboard.

Notice in this picture how the character is rotated so it is standing nicely on the board.

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 humanoid is actually not in PlatformStand.

Do you know what you’re using to stop the character from falling over? Are you using a Humanoid/HumanoidState or are you using physics?

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.