Moving Parts on the Client (much more complicated than it seems)

Hi,

I am making a parkour game on Roblox. The levels in it are randomly generated. My parkour game has kill bricks, but it also has moving parts.

These moving parts are being moved by the server. I do not want this, because my players may be in a laggy area, so the kill bricks may kill them if they server doesn’t update their client but kills them when it intersects with their body.

What I’m asking is, how can I move parts exclusively on the client? I know how to do it normally, you would just give the path of the object you want to move, but the game is randomly generated levels, some of which may have the same name, so I can’t. I wish I could insert LocalScripts INSIDE of Parts, and use their relative path (script.Parent.Position etc.)

I’ve thought about using ModuleScripts, but I can’t unrequire a ModuleScript, so once a Tower has finished, this code will continue looping, moving parts that aren’t there, thus causing an error.

I’ve also thought about using LocalScripts, and I’ve even wrote a little script, but it doesn’t work. I move the scripts into a folder in StarterPlayerScripts. I also notice it messes up a GUI of mine. Here is my script:

local Tower = game.Workspace.Tower
local TowerScripts = game.StarterPlayer.StarterPlayerScripts.TowerScripts

game.ReplicatedStorage.Events.TowerChanged.OnClientEvent:Connect(function()
	LoadTowerScripts()
end)

function LoadTowerScripts()
	TowerScripts:ClearAllChildren()
	local TowerDescendants = Tower:GetDescendants()
	for i,v in pairs(TowerDescendants) do
		if v.ClassName == "LocalScript" then
			local ScriptClone = v:Clone()
			ScriptClone.Parent = TowerScripts
			
		end
	end
end

I know a game that does this: Tower of Hell - Roblox
In this game, you can see other players going through moving kill bricks, and not dying. You can also prove they are doing it on the client by right clicking and holding on the top bar of the window (if you are on windows, this pauses your client). When you unhold, the moving parts are in the same position they were in before you pause your client.

How do I go about accomplishing this? Thank you, I know this is a very strange issue that I’m asking about.

My game is being released tomorrow, so I’d appreciate if I got feedback quickly.

3 Likes

Hi!
I do stuff like this all of the time.

Localscripts, as you know, won’t actually execute if you just place them anywhere in a scene. (It’d be super nice if they did HINT HINT ROBLOX)

So my solution is to “fix” this, and make it so that they do.
My current best setup works like this:

Add the following LocalScript to ReplicatedFirst

--A cooldown
local cooldown = 0
local cooldownTime = 0.5

--Storage folder for localscripts
local folder = Instance.new("Folder")
folder.Name = "ScriptStorage"
folder.Parent = game.ReplicatedStorage

--Every frame
game:GetService('RunService').Stepped:connect(function(totaltime, deltaTime)
	
	--Scan every now and then
	cooldown = cooldown - deltaTime

	if (cooldown <0) then
		local parts = game.Workspace:GetDescendants()
		
		for key,part in pairs(parts)do
			if (part:IsA("LocalScript")) then --Add other checks here if you need to?
				
				
				--Create a property on the localscript so we know who the original parent is
				local prop = Instance.new("ObjectValue")
				prop.Name = "ParentValue"
				prop.Value = part.Parent
				prop.Parent = part
				
				--Move the script to somewhere that'll execute it
				part.Parent = folder 
				
			end
		end
		cooldown = cooldownTime
	end
	
	--Note you'd have to do some kind of disable check if streamingout ever removed anything, but it never does!	
	
end)

This watches for any new localscripts in the main scene, and if so, moves them to replicated storage and marks who their parent was.

Now in a localscript you can do stuff like this:

local parentObjectValue = script:FindFirstChild("ParentValue")
local parent = parentObjectValue.Value
--Do whatever you want on the client with "parent" now

local origin = parent.Position

game:GetService('RunService').Stepped:connect(function(totaltime, deltaTime)
	
	--Just a dumb example!
	parent.Position = origin + Vector3.new(0,math.sin(totaltime) * 3.0 ,0 )
	
end)

This should let you have localscripts on ANY part in a scene, and they’ll actually execute locally.

25 Likes

It worked! Now my game will work for people who might have really bad ping! Thanks bud! I’ll bookmark your post. Really happy right now.

1 Like

Update:
I edited it a lot to fit my needs, but thank you! I tried doing something similar, but I never thought to put a value in the script!

Edited script:

--Storage folder for localscripts
local folder = Instance.new("Folder")
folder.Name = "ScriptStorage"
folder.Parent = game.Players.LocalPlayer.PlayerScripts



game.ReplicatedStorage:WaitForChild("Events").TowerChanged.OnClientEvent:Connect(function()
	folder:ClearAllChildren()
	local TowerDescendants = game.Workspace.Tower:GetDescendants()
	for _,v in pairs(TowerDescendants)do
	    if (v:IsA("LocalScript")) then --Add other checks here if you need to?
	
	        --Create a property on the localscript so we know who the original parent is
	        local prop = Instance.new("ObjectValue")
	        prop.Name = "ParentValue"
	        prop.Value = v.Parent
	        prop.Parent = v
	
	        --Move the script to somewhere that'll execute it
	        v.Parent = folder 
	
	    end
	end
end)

Thank you!!!

4 Likes

For anyone seeing this in the future… use the collection service, it’s way more efficient for tracking scene changes! Also use the new script context ability to have localScripts anywhere!

8 Likes