Help needed with rail-grinding system!

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    A simply rail grinding system similar to those seen in 3D sonic games, no boosters or momentum work needed though.

  2. What is the issue? Include screenshots / videos if possible!
    As of right now, you just get flung out of existence. No idea how the hell i’d fix it. Here is a video. https://gyazo.com/b52d2982004a351f3ae53cf0e2dac1d7

  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I’ve tried getting help from twitter and the devforum. I’ve gotten nowhere and I’m stumped.

Here is the code so far.

--\\
--||
--|| grind rail system
--|| made by kizu, some snippets of code from an open sourced game.
--||
--//

--\\ Services

local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

--\\ Constants

local PLAYER = Players.LocalPlayer

local GrindSpeed = 50
local CanGrind = true

--\\ Variables

local Character = PLAYER.Character
repeat wait() until Character.HumanoidRootPart

Humanoid = Character:WaitForChild("Humanoid")
RootPart = Character:WaitForChild("HumanoidRootPart")

local Rotation = CFrame.new()

local Grinding = false
local CurrentRail = nil
local RailOffset = 0
local Direction = -1
local CurrentRails = {}
local finished = false

--\\ Functions

for _, part in pairs(workspace.Rails:GetDescendants()) do
	if part:IsA("BasePart") then
		part.Touched:connect(function(hit)
			if hit.Parent.Name == PLAYER.Name then

				PLAYER.PlayerGui.DEBUGui.Touching.Text = "Touching: "..part.Name

				if CanGrind then

					CurrentRail = part

					for i,v in pairs(part.Parent:GetChildren()) do
						if v:IsA("BasePart") and v.Name:match("Part") then
							table.insert(CurrentRails, v)
						end
					end
					
					for i,v in pairs(CurrentRails) do
					print(v)
					end
					
					CanGrind = false
					PLAYER.PlayerGui.DEBUGui.Grinding.Text = "Grinding: Yes"

					repeat wait() until CurrentRail

					local offset = CurrentRail.CFrame:pointToObjectSpace(RootPart.Position)

					if offset.Y > 0 then


						local lookOffset = CurrentRail.CFrame:vectorToObjectSpace(RootPart.CFrame.lookVector)

						Direction = lookOffset.Z <= 0 and -1 or 1
						RailOffset = offset.Z 
						Grinding = true


					end
				end
			end
		end)
	end
end


--\\ initiate

RunService.Heartbeat:Connect(function()

	if Character and Humanoid.Health > 0 then

		if Grinding and CurrentRail then

			CanGrind = false

			Humanoid.AutoRotate = false



			for i_,RAIL in pairs(CurrentRails) do

				repeat
					PLAYER.PlayerGui.DEBUGui.RailSize.Text = "RailSize: "..CurrentRail.Size.Z
					PLAYER.PlayerGui.DEBUGui.RailOffset.Text = "RailOffset: "..RailOffset
					CurrentRail = RAIL

					print(CurrentRail.Name)

					RootPart.CFrame = CurrentRail.CFrame * CFrame.new(0, 1.1 + 2.6, RailOffset) * CFrame.Angles(0, (Direction == 1 and math.pi or 0), 0)
					RailOffset = RailOffset + (Direction * GrindSpeed)
					RootPart.Velocity = CurrentRail.CFrame.lookVector * GrindSpeed * (-Direction)


					CanGrind = false

					Humanoid.PlatformStand = true

					if Direction == -1 then
						finished = RailOffset < -(CurrentRail.Size.Z / 2.5)
						print("finished: ".. -(CurrentRail.Size.Z / 2.5))

					elseif Direction == 1 then
						finished = RailOffset > CurrentRail.Size.Z / 2.5
						print("finished: ".. CurrentRail.Size.Z / 2.5)

					end

					if finished then
						CanGrind = true
						print("DONE!")
					end

				until finished

			end

			CanGrind = true

			--RootPart.CFrame = CurrentRail.CFrame * CFrame.new(0, 1.1 + 2.6, RailOffset) * CFrame.Angles(0, (Direction == 1 and math.pi or 0), 0)
			--RailOffset = RailOffset + (Direction * GrindSpeed)
			--RootPart.Velocity = CurrentRail.CFrame.lookVector * GrindSpeed * (-Direction)

			--Humanoid.PlatformStand = true


		end

		Grinding = false


		if Grinding == true then
			if Humanoid:GetState() ~= Enum.HumanoidStateType.PlatformStanding and Grinding == true then
				CanGrind = false
				Humanoid.PlatformStand = true
				Humanoid.AutoRotate = false
			end

		elseif (Grinding == false) then
			if Humanoid:GetState() == Enum.HumanoidStateType.PlatformStanding and Grinding == false  then
				Grinding = false
				CurrentRail = nil 
				CanGrind = true
				Humanoid.PlatformStand = false
				Humanoid.AutoRotate = true
				CurrentRail = nil
				PLAYER.PlayerGui.DEBUGui.RailSize.Text = "RailSize: "
				PLAYER.PlayerGui.DEBUGui.RailOffset.Text = "RailOffset: "
				print("DONE WITH RAILS CLEARING TABLE")
				table.clear(CurrentRails)
			end
		end

	end
	if Humanoid.Jump == true and Grinding then
		Grinding = false
		Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
	end
end)

It reads numbered rails from 001 to whatever the amount of rails it ends at. Help would be much appreciated!

Here’s a place file demo of the script:
rail demo.rbxl (37.2 KB)

i know i posted this before but now everything should be easier to read and better to try

4 Likes