Hello! The release of Roblox’s new FPS unlocking update has created a new bug in my rail-grinding system. Because it’s binded to the client’s framerate, some players will go faster or slower along the rail depending on how high or low their framerate is. Note: This script isn’t mine, I forked it (no offense to the original scripter but it’s documented quite badly) so I have no idea where to put deltaTime to fix the problem.
Below is the script.
Script
--[[
↢---- 【 Axis Code - Open Source 】 ----↣
Location:⟹ StarterPlayer〵StarterCharacterScripts〵RailGrinding․client․lua
Purpose:⟹ Rail Grinding
Author:⟹ Axis \ AxisTab
Fork: Robotstics
Instructions:⟹ 〖
1:⇶ Place The Script In StarterCharacterScripts
2:⇶ Make A Part In Workspace, Name it Rail
3:⇶ Change The Id Of The RailGrind Animation To Your Own
4:⇶ Your Done, Have Fun!
〗
Comment:⟹ Credit is not required but it is appriciated, also do not claim as your own please! :)
]]--
-- ⤹ Services ⤸ --
local run_service = game:GetService("RunService");
local user_input_service = game:GetService("UserInputService");
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- ⤹ Configuartion ⤸ --
local config = {
-- ⤹ Simple Configuration ⤸ --
speed_cap = math.huge, --⤾ How Fast Can He Go On The Rail? (Adjust To Fit Your Needs)
rail_accel = 1.2, --⤾ How Much Do We Accelerate Going Down?
rail_decel = 0.8, --⤾ How Much Do We Slow Down When Goin Up?
rail_friction = 0.002, --⤾ How Much We Slow Down Over Time
turn_rail = false, --⤾ Do We Change Direction When Moving Backwards?
animation = ReplicatedStorage.Assets.Animations["Grind Tricks"]:WaitForChild("FootGrind"), --⤾ What Animation Plays When We're On A Rail?
attachment_offset = CFrame.new(0, -2.5, 0), --⤾ Offset For The Rail Particle
attachment_angle = CFrame.Angles(0, 0, 0), --⤾ Offset Rotation For The Rail Particle
-- ⤹ Advanced ⤸ --
debounce = 10, --⤾ How Long To Wait Before You Can Get On After Leaving A Rail?
hip_offset = 3.5, --⤾ Change This For Custom Characters (How Much He Is Above Or Below The Rail)
turn_spd = 0.05, --⤾ What Is Our Speed Set To When Changing From Forward To Backwards?
rbx_amplif = 64, --⤾ Used For Velocity Conversion
min_spd = 0.75, --⤾ Minimum Speed We Can Go? (-1 = None)
anim_priority = Enum.AnimationPriority.Action --⤾ Priority Of The Animation
};
-- Module --
local MiscFunctions = require(ReplicatedStorage.Modules.MiscFunctions)
-- ⤹ Variables ⤸ --
local character = script.Parent;
local humanoid = character:WaitForChild("Humanoid");
local animator = humanoid:WaitForChild("Animator")
local HumanoidRootPart = character:WaitForChild("HumanoidRootPart");
local rail_particle = Instance.new("Attachment");
rail_particle.Parent = HumanoidRootPart;
rail_particle.CFrame = config.attachment_offset * config.attachment_angle;
local load_animation = animator:LoadAnimation(config.animation);
load_animation.Priority = config.anim_priority;
local contact_sound = script:WaitForChild("ContactSound");
local rail_sound = script:WaitForChild("LoopSound");
local GrindBindable = ReplicatedStorage.Remotes:WaitForChild("GrindBindable")
-- ⤹ Attach Particle ⤸ --
local particles = {}
for _, particle in pairs(script:WaitForChild("RailParticles"):GetChildren()) do
particle.Parent = rail_particle;
table.insert(particles, particle);
end
-- ⤹ Rail Specific ⤸ --
local rail = nil;
local rail_debounce = 0;
local rail_dir = 0;
local rail_origin = 0;
local rail_ang = CFrame.Angles(0, 0, 0);
local rail_speed = 0
local on_rail = false;
local cur_pos = 0;
local event_fire_attempts = 0
-- ⤹ Parameters ⤸ --
local params = RaycastParams.new();
params.FilterType = Enum.RaycastFilterType.Exclude;
params.FilterDescendantsInstances = {character};
-- ⤹ Functions ⤸ --
local function GetRailRelative(rail: BasePart)
local axis = rail.CFrame:VectorToObjectSpace(rail.Position);
local player_axis = rail.CFrame:VectorToObjectSpace(HumanoidRootPart.Position);
local offset = player_axis - axis;
-- ⤹ Return ⤸ --
return offset, player_axis;
end
-- ⤹ Handle Jumping ⤸ --
user_input_service.JumpRequest:Connect(function()
-- ⤹ If We Are On A Rail ⤸ --
if on_rail then
-- ⤹ Rail Speed ⤸ --
local add = rail.CFrame.LookVector * (rail_speed * config.rbx_amplif);
if rail_dir == -1 then
-- ⤹ Flip Cause We Are Going Backwards ⤸ --
add = -add;
end
-- ⤹ Set Velocity & Debounce ⤸ --
local jump_vel = rail.CFrame.UpVector * humanoid.JumpPower
HumanoidRootPart.AssemblyLinearVelocity = add + jump_vel;
rail_debounce = config.debounce;
-- ⤹ Jump & Reset Rail Variable ⤸ --
humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
on_rail = false;
end
end)
-- ⤹ Rail Physics Bind ⤸ --
run_service:BindToRenderStep("__RailUpdate.core", Enum.RenderPriority.Camera.Value, function(dt)
-- ⤹ Rail Debounce Timer ⤸ --
rail_debounce = math.max(0, rail_debounce-1);
-- ⤹ Do Physics ⤸ --
if on_rail then
if event_fire_attempts == 0 then
GrindBindable:Fire(on_rail, humanoid:GetState())
print("Grinding: "..tostring(on_rail))
end
-- ⤹ Rotate If We Are Moving Backwards ⤸ --
local offset = rail_dir == 1 and 0 or math.rad(180);
if not config.turn_rail then
offset = rail_origin == 1 and 0 or math.rad(180);
end
rail_ang = rail_ang:Lerp((rail.CFrame - rail.CFrame.p) * CFrame.Angles(0, offset, 0), 0.1 + (rail_speed/4));
-- ⤹ Move The Character ⤸ --
HumanoidRootPart.CFrame = CFrame.new((rail.CFrame * CFrame.new(0, 0, cur_pos) * CFrame.new(0, humanoid.HipHeight + config.hip_offset, 0)).Position) * rail_ang
-- ⤹ Anchor The Player ⤸ --
HumanoidRootPart.AssemblyLinearVelocity = Vector3.zero
-- ⤹ Rail Speed ⤸ --
local add = rail.CFrame.LookVector;
if rail_dir == -1 then
add = -add;
end
-- ⤹ Check For A Rail ⤸ --
local down_part = workspace:Raycast(HumanoidRootPart.Position + (add * rail_speed), (rail.CFrame - rail.CFrame.p).UpVector * -4, params);
if down_part and down_part.Instance and down_part.Instance.Name == "Rail" then
-- ⤹ If Not Our Current Rail ⤸ --
if down_part.Instance ~= rail then
rail = down_part.Instance;
local offset = GetRailRelative(down_part.Instance);
cur_pos = offset.Z
end
else
-- ⤹ Fall Of The Rail ⤸ --
on_rail = false;
GrindBindable:Fire(on_rail, humanoid:GetState())
HumanoidRootPart.AssemblyLinearVelocity = add * rail_speed * config.rbx_amplif;
humanoid:ChangeState(Enum.HumanoidStateType.Freefall)
end
local forward_upward_part = workspace:Raycast(HumanoidRootPart.Position + (add*rail_speed), HumanoidRootPart.CFrame.LookVector, params)
if forward_upward_part and forward_upward_part.Instance and forward_upward_part.Instance.Name == "Rail" then
if forward_upward_part.Instance ~= rail then
rail = forward_upward_part.Instance;
local offset = GetRailRelative(forward_upward_part.Instance);
cur_pos = offset.Z
end
end
-- ⤹ Variables For Slope Detection ⤸ --
local a1a = add;
local dotp = (a1a.unit):Dot((Vector3.new(0, -1, 0)).Unit);
local slope = -dotp;
local acc = 0;
-- ⤹ Check? ⤸ --
if math.abs(slope) > 0.1 then
if slope < 0 then
-- ⤹ Moving Down, Accelerate ⤸ --
acc += math.abs(slope) * config.rail_accel;
else
-- ⤹ Moving Up, Slow Down ⤸ --
acc -= math.abs(slope) * config.rail_decel;
end
end
-- ⤹ Calculate Rail Speed ⤸ --
rail_speed += (acc / config.rbx_amplif)
if rail_speed <= 0 and slope ~= -0 then
-- ⤹ Change Direction, Since We Are Going Too Slow ⤸ --
if rail_dir == 1 then
rail_dir = -1;
else
rail_dir = 1;
end
-- ⤹ Accelerate ⤸ --
rail_speed = config.turn_spd;
end
-- ⤹ Friction ⤸ --
rail_speed = math.max(0, rail_speed - config.rail_friction)
-- ⤹ Speed Cap ⤸ --
if math.abs(rail_speed) > config.speed_cap then
rail_speed = math.sign(rail_speed) * config.speed_cap;
end
-- ⤹ Minimum Speed ⤸ --
if math.abs(rail_speed) < config.min_spd and config.min_spd ~= -1 then
rail_speed = math.sign(rail_speed) * config.min_spd;
end
-- ⤹ Move ⤸ --
if rail_dir == 1 then
cur_pos -= rail_speed;
else
cur_pos += rail_speed;
end
-- ⤹ Rail Debounce ⤸ --
if not on_rail then
rail_debounce = config.debounce;
end
elseif HumanoidRootPart.AssemblyLinearVelocity.Y <= 0.1 and rail_debounce <= 0 then
-- ⤹ Unanchor The HumanoidRootPart ⤸ --
HumanoidRootPart.Anchored = false
event_fire_attempts = 0
-- ⤹ Get Rails ⤸ --
for _, temp_rail in pairs(workspace:GetPartBoundsInRadius(HumanoidRootPart.Position, 2.5)) do
-- ⤹ Check If It's Actually A Rail ⤸ --
if temp_rail.Name == "Rail" then
local offset, player_axis = GetRailRelative(temp_rail);
local player_axis2 = temp_rail.CFrame:VectorToObjectSpace(HumanoidRootPart.Position + HumanoidRootPart.Velocity);
-- ⤹ Are We Going Backwards? ⤸ --
local offset2 = player_axis - player_axis2;
if offset2.Z < 0 then
-- ⤹ Yes ⤸ --
rail_dir = -1;
elseif offset2.Z > 0 then
-- ⤹ No ⤸ --
rail_dir = 1;
end
-- ⤹ Can We Go On The Rail ⤸ --
if math.abs(offset.Z) < (temp_rail.Size.Z/2) then
-- ⤹ Set Rail Part & Speed ⤸ --
rail = temp_rail;
rail_origin = rail_dir
rail_speed = (HumanoidRootPart.Velocity - Vector3.new(0, HumanoidRootPart.Velocity.Y, 0)).Magnitude/config.rbx_amplif;
-- ⤹ Set The HumanoidRootPart Angle & Rail Position ⤸ --
rail_ang = HumanoidRootPart.CFrame - HumanoidRootPart.CFrame.p;
cur_pos = offset.Z;
-- ⤹ We Are Now On The Rail ⤸ --
on_rail = true;
if event_fire_attempts == 0 then
event_fire_attempts = 1
GrindBindable:Fire(on_rail, humanoid:GetState())
end
-- ⤹ Contact Sound ⤸ --
contact_sound:Play();
-- ⤹ Enable Particles ⤸ --
for i=1, #particles do
particles[i].Enabled = true
end
end
end
end
else
-- ⤹ Unanchor The Root ⤸ --
HumanoidRootPart.Anchored = false
GrindBindable:Fire(on_rail, humanoid:GetState())
end
-- ⤹ Rail Sound ⤸ --
if not on_rail then
rail_sound:Stop();
else
if not rail_sound.IsPlaying then
rail_sound:Play();
end
end
-- ⤹ Animation ⤸ --
if not on_rail and load_animation.IsPlaying then
load_animation:Stop();
-- ⤹ Enable Particles ⤸ --
for i=1, #particles do
particles[i].Enabled = false
end
elseif on_rail and not load_animation.IsPlaying then
MiscFunctions.StopAllCharAnimations(animator)
load_animation:Play();
end
end)
And here is a clip of the bug in action. Note the FPS count and the speed of the grind.
External MediaThank you in advance.