Grindrailing system using PrismaticConstraints

This is my first DevForum post so please correct anything I do wrong

Hey! I’ve been trying to make a rail grinding system but I’ve been only able to prototype 1 segment out of the entire railgrind model I want to make, so I temporarily named it to Zipline.

Here’s what I tried so far:
I used PrismaticConstraints to align the player onto the grindrail and set the velocity using an Attribute to move them across. Then in a while loop, I compare the constraint’s CurrentPosition to the distance between the Attachments to detect if they reached the end of the grindrail, then break out of the loop.

After that, I thought manually placing the ziplines together would work that way, but I sometimes just end up getting stuck between the two segments.

The goal is to find a way to link individual “ziplines” (which I’ll refer to as railgrind segments) together in a way creates a grindrail.

Note that this code is made using CollectionService and placed inside of StarterPlayerScripts

local CS = game:GetService('CollectionService')
local UIS = game:GetService('UserInputService')

local player = game.Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local HRP = char:WaitForChild("HumanoidRootPart")
local playerState = require(script.Parent.Parent:WaitForChild('PlayerStates'))
--PlayerStates being a module placed inside of StarterPlayerScripts too

local prismaticConstraint
--Referenced later to detect if the player wants to jump off the rail

for _, zipline in pairs(CS:GetTagged("Zipline")) do
	local ziplinePart = zipline:WaitForChild("ZiplinePart")
	local node0 = ziplinePart:WaitForChild("Node0")
	local node1 = ziplinePart:WaitForChild("Node1")
	local constraint = ziplinePart:WaitForChild("PrismaticConstraint")
	prismaticConstraint= constraint
	local velocity = zipline:GetAttribute("Velocity") or 35
	constraint.Attachment0 = node0
	local offset = 3
	
	local dist = math.floor((node1.WorldPosition - node0.WorldPosition).Magnitude) + 2
	zipline:SetAttribute("Touched",false)
	ziplinePart.Touched:Connect(function(hit)
		local humanoid = hit.Parent:FindFirstChildWhichIsA("Humanoid")
		if humanoid and not zipline:GetAttribute("Touched") then
			zipline:SetAttribute("Touched",true)
			local char = humanoid.Parent
			local HRP = char:WaitForChild("HumanoidRootPart")
			
			HRP:WaitForChild("RootAttachment").Position -= Vector3.new(0,offset,0)
			--Reposition attachment so player properly positions onto the rail
			
			constraint.Attachment1 = HRP:WaitForChild("RootAttachment")
			constraint.LimitsEnabled = true
			constraint.UpperLimit = dist
			constraint.Enabled = true
			constraint.Velocity = velocity
			
			while constraint.CurrentPosition < dist do
				playerState.grindrailing = true
				HRP.CFrame = CFrame.lookAt(HRP.Position, node1.WorldPosition + Vector3.new(0,offset,0))
				task.wait()
				
				if constraint.CurrentPosition >= dist or playerState.sliding == true or constraint.Enabled ~= true then
					constraint.Enabled = false
					constraint.Attachment1 = nil
					playerState.grindrailing = false
					HRP:WaitForChild("RootAttachment").Position += Vector3.new(0,offset,0)
					break
				end
			end
			
			task.wait(.1)
			zipline:SetAttribute("Touched",false)
		end
	end)
end

--Cancel the grindrailing
UIS.InputBegan:Connect(function(input, gp)
	if gp or input.KeyCode ~= Enum.KeyCode.Space then
		return
	end

	prismaticConstraint.Enabled = false
	prismaticConstraint.Attachment1 = nil

	playerState.grindrailing = false
end)

Thank you in advance : P

1 Like

Why don’t you just use that one, scale it and then duplicate it instead of making more than one?

1 Like

I just tried this but it seems like it randomly picks between the segments to count as an actual part of the grindrail.
I’ll use table.sort to get more consistent results

1 Like

Could you show a video of using one realy long part?

1 Like

Here you go

1 Like

Ok, maybe try putting a block at the start of every segment, and make the player go to that block by using I don’t know, TweenService

I remember making a system similar with TweenService but I found some problems along the way

  1. I would have to determine the speed across each segment with a set duration like 0.3, which can be tedious

  2. If I have segments that are different lengths, then the set duration would throw off the illusion at traveling at a set speed
    For example: If one segment is really long but I don’t want the player to move too fast then I would probably set the duration to 1. If there’s a shorter segment for maybe a tighter turn (smaller segments being used to smooth out the turn) then I would image the player to slow down drastically depending on how short it is.

Sorry if this is a bit much, I’m just looking for a consistent solution to be flexible with any grindrail I make in the future

Then sorry, i don’t know :person_shrugging: I’m not a programmer, hope you solve the problem :smiley:

1 Like

It’s okay, I appreciate the cooperation

2 Likes

2024 Update:

Recently @04robot48 has reached out about the grind railing system which prompted me to try and solve this issue, and I did!

Here’s the place with the new system: PrismaticConstraint Grindrail.rbxl (59.0 KB)

An issue with the remastered system (as of now) is when you transition between segments it pauses for a brief moment before continuing smoothly. Let me know if you have any ideas on how to solve this!

Thank you and sorry for leaving this post in the dark for so long!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.