Rope Swing Tech Demo [Open Source] 3/4/2025

Ever wanted to make a realistic swing set or a rope swing? Well I made a rope swing check this out!


Then grab yourself a copy of the model in this place!
Rope Swing Tech Demo - Roblox
There is a custom animation located in the R15 rig to perform the rope swinging animation.

The rope object is made of spheres and ropes, the player touches a sphere grabs on to it and allows you to swing on the rope.

Since creating this I upgraded it to a ropeswing service architecture where the model is tagged as a “RopeModel” and the ropes are formatted and individual ropes can be connected by tagging them as “RopeSwing”

This is another Tech Demo in my series of generally useful Open Sourced tech demos. I make these for my own project on a blank baseplate and if they are useful I post them here! That being said you can check out my profile Topics for more Tech Demos and Resources!

image

Great for those levels where you want to clear a gap!
What’s unique about these rope swings are that they allow you to gain momentum by swinging back and forth on the rope without any invasive modifications of the Character.
image
image

image
Made to Swing from Rope to Rope*
I made this model to appropriately clear this gap! But using it for something like vine swinging works too


What kind of tech is used?
For this we are exploiting the behavior of falling in a direction. When your character is connected to a rope you can control your swing direction using movement controls. Align Orientation is currently being used in addition to Motor6D Welding to object space, and an animation to present the character on the rope. Besides that some debounce mechanism.

Create Varying Ropes
Located in a module called RopeCreator in the workspace
which provides functions to generate perfect chains of ropes of whatever length and fidelity you choose!

These are different than regular ropes because they are like chains of ropes with ball shaped joints.
With the algorithm to generate ropes you can control the fidelity of the rope, more joints= more realistic rope.

The source code is as follows. There are some comments in this code since I’ve been experimenting with different things.

local ropes={}
local function weldmotor(Root1,Root2)
	local w=Instance.new("Motor6D")
	w.Part0,w.Part1 = Root1,Root2
	w.C0 = Root2.CFrame:toObjectSpace(Root1.CFrame):inverse()
	w.Parent = Root1
	w.Name=Root2.Name.."Joint"
	return w
end
task.wait()
local climbanim=script.RopeSwing
--for i,v in script.Parent:GetChildren() do 
local function formatrope(v)
	if v.ClassName=="Part" and v:FindFirstChildOfClass("Attachment") and v.Anchored==false then
			local connection=v.Touched:Connect(function(hit)
			local hum= hit.Parent:FindFirstChild("Humanoid")
			
			if hum and ropes[hum.DisplayName]==nil then
				local key= v.Parent.ClassName=="Model" and v.Parent.PrimaryPart~=nil and tostring(v.Parent.PrimaryPart.Position)..hum.DisplayName or nil
				local currentmodel=key~=nil and ropes[key]==nil or true
				print(ropes[key])
				print(key)
				if currentmodel and (key==nil or ropes[key]==nil) then
					if key then
						ropes[key]=true
					end
					
				v.CanTouch=false
				v.CanCollide=false
				local climb=hum:LoadAnimation(climbanim)
				climb:Play()
				hum.AutoRotate=false
				local lostconnection=nil
				ropes[hum.DisplayName]=true
				local hand=hit.Parent:FindFirstChild("HumanoidRootPart")
				v.Anchored=true
				hand.Anchored=true
				local swingdirection=(hand.CFrame.Position-hand.CFrame:ToWorldSpace(CFrame.new(0,0,-60)).Position)
				local rope=v:FindFirstChildOfClass("RopeConstraint")
				local c=nil
				for t,o in v.Parent:GetChildren() do
					if o.ClassName=="Part" then
						o.AssemblyLinearVelocity*=.2

					end
				end
				if rope then
				 	c=Instance.new("AlignOrientation")
					local orientation=CFrame.new(rope.Attachment1.Parent.CFrame.Position,rope.Attachment0.Parent.CFrame.Position)-rope.Attachment1.Parent.CFrame.Position
					c.Attachment0=hand:FindFirstChild("RootAttachment")
					c.Attachment1=rope.Attachment0
					c.Parent=hand
					--while (hand.Parent and motor.Parent and rope.Parent) do 
					--	--local orientation=CFrame.new(rope.Attachment0.Parent.CFrame.Position,rope.Attachment1.Parent.CFrame.Position)-rope.Attachment1.Parent.CFrame.Position+hand.Position
					--	local orientation=CFrame.new(hand.Position,v.Parent.PrimaryPart.Position)
					--	c.CFrame=orientation
					--	task.wait()
					--end
				end
				hand.CFrame=v.CFrame:ToWorldSpace(CFrame.new(0,-1,0))
				local motor= weldmotor(v,hand)
				hand.AssemblyLinearVelocity=swingdirection
				local speed=hum.WalkSpeed
				hum.WalkSpeed=48
				v.Anchored=false
				hand.Anchored=false
				--v.AssemblyLinearVelocity*=2
				lostconnection=hum.AncestryChanged:Once(function()
					ropes[hum.DisplayName]=nil	
					v.CanTouch=true
					--v.CanCollide=true
				end)
				--hum:ChangeState(Enum.HumanoidStateType.Flying)
				hum:GetPropertyChangedSignal("Jump"):Once(function()
					if lostconnection then
						lostconnection:Disconnect()
					end
					motor:Destroy()
					climb:Stop()
					climb:Destroy()
					if c then 
					c:Destroy()
					end
					for t,o in v.Parent:GetChildren() do
						if o.ClassName=="Part" then
							o.AssemblyLinearVelocity*=.2
							
						end
					end
					hum.AutoRotate=true
					hum.WalkSpeed=speed
					--hand.AssemblyLinearVelocity=(hand.CFrame.Position-hand.CFrame:ToWorldSpace(CFrame.new(0,10,-20)).Position)

					--c:Destroy()
					ropes[hum.DisplayName]=nil
					task.wait(.1)
					hum:ChangeState(Enum.HumanoidStateType.Jumping)
					task.wait(.1)
					hum:ChangeState(Enum.HumanoidStateType.Jumping)
					task.wait(2)
					if key then
						ropes[key]=nil
					end
					
					v.CanTouch=true
					v.CanCollide=true

				end)
				
				--hum.Jumping:Once(function()
			
				--end)
				end
			end
	end)
	return connection
		--table.insert(ropes,v)
	end
end
local function formatropemodel(m)
	local modelconnections={}
	for i,v in m:GetChildren() do 
		local connection=formatrope(v)
		if connection then
			table.insert(modelconnections,connection)
		end
	end
	return modelconnections
end
local function disconnectconnections(array)
	for i,v in array do 
		v:Disconnect()
	end
end
game.CollectionService:GetInstanceAddedSignal("RopeModel"):Connect(formatropemodel)
print(game.CollectionService:GetTagged("RopeModel") )
for i,m in game.CollectionService:GetTagged("RopeModel") do 
	local modelconnections=formatropemodel(m)
	--print("Connecting model")
	m.AncestryChanged:Once(function()
		--print("Disconnecting")
		disconnectconnections(modelconnections)
		modelconnections=nil
	end)
end
game.CollectionService:GetInstanceAddedSignal("RopeSwing"):Connect(formatrope)
for i,v in game.CollectionService:GetTagged("RopeSwing") do 
	local connection=formatrope(v)
	if connection then
		connection={connection}
	v.AncestryChanged:Once(function()
		disconnectconnections(connection)
		connection=nil
	end)
	end
end

RopeCreator Module


local function getprimitive()
	local primitive=Instance.new("Part")
	primitive.Shape=Enum.PartType.Ball
	primitive.Size=Vector3.new(.25,.25,.25)
	local attachment=Instance.new("Attachment")	
	attachment.Parent=primitive
	return primitive,attachment
end
local function formatrope(primitive,attachment,previous)
	local rope=Instance.new("RopeConstraint")
	rope.Color=BrickColor.new("Dark taupe")
	rope.Parent=primitive
	rope.Attachment0=previous
	rope.Thickness=.25
	rope.Visible=true
	rope.Attachment1=attachment
	return rope
end


local function createrope(cframe,length,fidelity)
	local model=Instance.new("Model")
	local previous=nil
	local fidelity=math.max(.125,fidelity or 3)
	local ropescale= math.floor(length/fidelity+.5)
	local densityscale=math.log(length)
	local maxdensity=30*densityscale
	local mindensity=15*densityscale
	local increment=densityscale*2
	for i=0, ropescale do
		local primitive,attachment=getprimitive()
		primitive.Name="Rope"..i
		primitive.Parent=model
		if model.PrimaryPart==nil then
			model.PrimaryPart=primitive	
		end
		primitive.CustomPhysicalProperties=PhysicalProperties.new(math.max(10,maxdensity-(i*increment)),1,1)
		primitive.BrickColor=BrickColor.new("Dark taupe")
		primitive.CFrame=cframe:ToWorldSpace(CFrame.new(0,-i*fidelity,0))
		if previous then
			local rope=formatrope(primitive,attachment,previous)
			rope.Length=fidelity
		else 
		primitive.Anchored=true
		end
		previous=attachment
	end 
	model:AddTag("RopeModel")
	model.Name="Rope"
	model.Parent=workspace
	return model
end 
--createrope(CFrame.new(0,27,0),20,3)
--createrope(CFrame.new(0,17,20),10,3) 
--createrope(CFrame.new(0,37,40),30,3)

return createrope
12 Likes

Video example?

Thanks for providing this!

Your example model/stage reminds me of something you’d see on the N64.

1 Like

Ummmmmmmmmm, interesting!

It seems you have the initial version ? or you are attempting to use the animation reference object as a rope swing. Either way that image is a misrepresentation of this since the ‘ropeswingservice’ script does not have the bug of connecting to 2 separate rope models at a time but the initial version does because I made the ropeswingservice to handle multiple ropes.

My instructions for use are use the rope primitive provided or use the module called RopeCreator in the workspace to generate a rope of a specified length. place your rope, and the rope swing works pretty well given the constraints. I’ve been contemplating this for a while, so happy to have it all done! Development for this took about a couple hours.

Also the proximity prompts have been removed.

Rope swing usage #2


Please share how you are using the rope swing mechanic!
I think I may change it to compute the angle which the humanoidrootpart is angled at by using each rope’s attachment0 position

local orientation=CFrame.new(rope.Attachment1.Parent.CFrame.Position,rope.Attachment0.Parent.CFrame.Position)-rope.Attachment1.Parent.CFrame.Position

so that the character is angled like the rope unlike now.

Hi.

All I did was go into your demo game and jump on the rope the lady was on.