What causes this script to break after publishing? STUDIO BUG?

I have open sourced code for a custom character movement system but it breaks when I publish the game to roblox. Here’s the code…not sure why it would break after publishing to roblox as before publishing it works fine:

--[[


Looking at this script a year later, yeah, I don't know what I was thinking.


]] 


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

local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Character:WaitForChild("Humanoid")
local HRP = Character:WaitForChild("HumanoidRootPart")
local VectorForce = HRP:WaitForChild("VectorForce")
local Rotator = HRP:WaitForChild("Rotator")

local PlayerScripts = Player:WaitForChild("PlayerScripts")
local PlayerModule = require(PlayerScripts:WaitForChild("PlayerModule"))
local Controls = PlayerModule:GetControls()

local PlrGui = Player.PlayerGui

--[[
All of this is pretty janky, so gl
tbh even I don't know what exactly I was aiming for with this
]]

--vars
local IsJumping = false
local IsLanded = false

local Mobile = false
local MobileJump = false

local function CustomJump()
	if IsLanded == true and IsJumping == false and MobileJump == false then
		IsLanded = false
		MobileJump = true
	end
end

--check if player has touch gui, this is bad practice but it's only for the sake of the Demo

if Controls["touchGui"] then
	local currentTouchGui = PlrGui:WaitForChild("TouchGui")
	local touchControlFrame = currentTouchGui:WaitForChild("TouchControlFrame")
	local JumpButton = touchControlFrame:WaitForChild("JumpButton")
	
	if JumpButton then
		JumpButton.Activated:Connect(CustomJump)
	end
	Mobile = true
end

---- Settings
local MASS = 10 -- Could use GetMass, but slime and it's baseparts are set to massless
local ACCELERATION = 120
local MAX_VELOCITY = 13

local LIMITER_SUM = 0 
local RATE = 60 -- limit runservice to 60fps
local RAYLIMITER_SUM = 0
local RAY_RATE = 20 -- limit runservice to 20fps for the raycasts

local Viscosity = MASS * ACCELERATION / MAX_VELOCITY

---- State variables

local ZERO_VEC = Vector3.new()
local PROJ_VEC = Vector3.new(1, 0, 1)
local Y_VEC = Vector3.new(0, -1.5, 0)
local PROJ_Y_VEC = Vector3.new(0, 1, 0)

--sound dir
local Sounds = {
	["Died"] = "rbxassetid://12631617531",
	["Landing"] = "rbxassetid://12631617618",
	["Jumping"] = "rbxassetid://12631617689",
}

local SoundInst = {}

local function ReplaceSound()
	for ind, Inst in pairs(HRP:GetChildren()) do
		if Inst:IsA("Sound") then
			if Sounds[Inst.Name] then
				Inst.SoundId = Sounds[Inst.Name]
				SoundInst[Inst.Name] = Inst
			end
		end
	end
end
ReplaceSound()

---- Initialize
Humanoid:ChangeState(Enum.HumanoidStateType.Physics)
Humanoid.CameraOffset = Vector3.new(0,-1,0)

RunService.Heartbeat:Connect(function(dT)
	LIMITER_SUM = LIMITER_SUM + dT
	RAYLIMITER_SUM = RAYLIMITER_SUM + dT
	
	if LIMITER_SUM >= 1/RATE then
		local moveVec = Controls:GetMoveVector()	
		local cameraCF = workspace.CurrentCamera.CFrame
		moveVec = (-cameraCF.LookVector * moveVec.Z + cameraCF.RightVector * moveVec.X) * PROJ_VEC
		moveVec = moveVec.Unit -- All these 4 lines do is get the direction we are moving in
		local vel = HRP.Velocity * PROJ_VEC -- velocity on the x-z plane
		local velOnY = HRP.Velocity * PROJ_Y_VEC
		local drag = -vel * Viscosity
		local jump = ZERO_VEC -- start at the 0 vector in case we aren't even jumping

			--[[
				PC & mobile check for jumping,
				It has to be in seperate blocks because the mobile and 
				PC controller module behave differently for the jump logic
				and TouchGui jump button isn't the same as Spacebar keycode
			]]--
		if Controls.activeController and Controls.activeController:GetIsJumping() and not IsJumping then
			local result = workspace:Raycast(
				HRP.Position,
				Vector3.new(0, -2, 0),
				RaycastParams.new()
			)
			
			--[[
			Raycast downwards to check if the player jumps above the certain distance (2 dist. on Y axis)
			if no raycast resul then start falling
			]]--


			if result then 
				IsJumping = true --if there's a result this variable will stay true
				IsLanded = false
				coroutine.wrap(function()
					local t = tick()
					while tick() - t < 0.1 do
						RunService.Heartbeat:Wait()
					end
					IsJumping = false 
				end)()
			end
		elseif Mobile == true and MobileJump and not IsJumping then
			local result = workspace:Raycast(
				HRP.Position,
				Vector3.new(0, -2, 0),
				RaycastParams.new()
			)
			if result then
				IsJumping = true
				
				coroutine.wrap(function()
					local t = tick()
					while tick() - t < 0.1 do
						RunService.Heartbeat:Wait()
					end
					
					IsJumping = false
					MobileJump = false
				end)()
			end
		end

		if IsJumping then 
			-- on jump
			SoundInst["Jumping"]:Play()
			jump = Vector3.new(0, MASS * workspace.Gravity * 3, 0) -- add value to jump
		else 
			-- on falling down
			if not IsLanded and RAYLIMITER_SUM >= 1/RAY_RATE then
				if velOnY.Y < 0 then
					-- fire a ray twoards the ground to check if the player has landed
					local result = workspace:Raycast(
						HRP.Position,
						Y_VEC,
						RaycastParams.new()
					)
					if result then
						--if player lands
						IsLanded = true
						MobileJump = false
						SoundInst["Landing"]:Play()
					end
				end
				-- limit raycast
				RAYLIMITER_SUM = RAYLIMITER_SUM - 1/RAY_RATE 
			end
		end

		if moveVec.Magnitude > 0 then
			-- thrust is (nan, nan, nan) if plr isnt moving
			local thrust = MASS * ACCELERATION * moveVec
			VectorForce.Force = thrust + drag + jump -- calculate vectorforce
			Rotator.PrimaryAxis = moveVec -- rotate slime to the latest direction it was moving in
		else
			VectorForce.Force = drag * 6 + jump -- slow down
		end

		LIMITER_SUM = LIMITER_SUM - 1/RATE
	end
end)


--some algorithms were taken off of the devforum but I forgot where I got them from so sadly I won't be able to credit anyone.

Here’s the file if you want to see for yourself:
charactermodel (1).rbxl (66.8 KB)