Help With Attaching Parts

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    Trying to make a tool that will let you place and attach a thruster wherever you click.
  2. What is the issue? Include screenshots / videos if possible!
    Glitching around when placed. At first, everything would attach to the same spot including thrusters, which would glitch around and move the map.
  3. What solutions have you tried so far? Did you look for solutions on the Creator Hub?
    Motor6Ds, Welds, WeldConstraints,Ropes,Springs
    After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!
-- This is an example Lua code block



Please do not ask people to write entire scripts or design entire systems for you. If you can’t answer the three questions above, you should probably pick a different category.

If you want to attach it to a character you can try using AllighnPosition.

Well, I can’t help you unless you show us your code, the thruster’s properties, and it’s descendants.

If you share those I will be able to help you further :slight_smile:

Server Script: local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local ServerStorage = game:GetService(“ServerStorage”)
local thrusterEvent = ReplicatedStorage:WaitForChild(“ThrusterRequest”)
local thrusterTemplate = ServerStorage:WaitForChild(“RandomObjects”):FindFirstChild(“Thruster”)

if not thrusterTemplate then
warn(“Thruster not found in RandomObjects”)
return
end

thrusterEvent.OnServerEvent:Connect(function(player, target, hitPos, surfaceNormal)
if not target or not target:IsA(“BasePart”) then return end

-- Clone the thruster and parent it to the target first (to prevent teleporting)
local thruster = thrusterTemplate:Clone()
thruster.Anchored = false
thruster.Parent = target

-- Set the CFrame relative to the target (so no physics conflict)
local localOffset = target.CFrame:toObjectSpace(CFrame.new(hitPos, hitPos + surfaceNormal)) * CFrame.Angles(0, math.rad(180), 0)
thruster.CFrame = target.CFrame * localOffset

-- Use a regular Weld (rigid, no constraint weirdness)
local weld = Instance.new("Weld")
weld.Part0 = target
weld.Part1 = thruster
weld.C0 = CFrame.new() -- Target remains still
weld.C1 = target.CFrame:toObjectSpace(thruster.CFrame)
weld.Parent = thruster

end)
Local Script: local tool = script.Parent
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local replicatedStorage = game:GetService(“ReplicatedStorage”)
local thrusterEvent = replicatedStorage:WaitForChild(“ThrusterRequest”)

tool.Activated:Connect(function()
local target = mouse.Target
if not target or not target:IsA(“BasePart”) then return end

local hitPos = mouse.Hit.Position
local surfaceNormal = (mouse.Hit.Position - target.Position).Unit

thrusterEvent:FireServer(target, hitPos, surfaceNormal)

end)

Thruster is using a linear velocity

Maybe try vector force? I honestly don’t know.

Is the issue that the thrusters are not attaching to the correct location? Or is the issue that the thrusters are not pushing the object correctly?

They push, i just have the force disabled for now. The thrusters wont attach right.

I would suggest using WeldConstraints instead, and instead of just giving the mouse hit position, calculate and send the offset of the hit position from the target part position to fix lag related issues.

Ok, I am in studio recreating the scripts you posted, and not necessarily an issue, however, every tool I have ever seen, will have the remote event, inside the tool, not in replicated storage.

Ill post here again when I get you something working.

Not exactly the best practice, having remotes for every single tool results in increased resource usage.

1 Like

Its more about keeping things modular. Each tool is sort of stand alone, so each tool has its resources within it. The only way its really using a lot of resources is if you have a LOT of tools in your inventory like hundreds. Also, by having the resources inside of the tool instance, if the tool gets destroyed, the events get destroyed and in so doing, the scripts die, and any connections get disconnected.

Edit:
I don’t want this response to come off as if I am trying to argue against what @nowodev was saying.
@nowodev has a valid point about always wanting to try and be conservative with your resources, and I agree, with a game that has LOTS of tools this could be a problem. I just didn’t feel it was of any concern in this particular case. I always appreciate to hear different viewpoints, thats how we learn and grow.

Here is the place file if you want to check it out
Thruster.rbxl (64.1 KB)

I have the tool structure as shown.

Here is the Server script

local tool = script.Parent
local thrusterEvent = tool:WaitForChild("ThrusterRequest")

local thrusterTemplate = tool:WaitForChild("Thruster"):Clone()
local thrusterTemplateScale = 1
for _,i in pairs(thrusterTemplate:GetDescendants()) do
	if i:IsA("BasePart") then 
		i.CanCollide = false  
	end
end
thrusterTemplate:ScaleTo(thrusterTemplateScale) --because the thruster may be smaller in hand than actually placed

thrusterEvent.OnServerEvent:Connect(function(player, ServerData)
	if ServerData then
		local target = ServerData.Instance
		local c0 = ServerData.C0
		local thruster = thrusterTemplate:Clone()
		
		local cf = target.CFrame:ToWorldSpace(c0)
		thruster:PivotTo(cf)
		
		local weld = Instance.new("Weld")
		
		weld.Part0 = target
		weld.Part1 = thruster.PrimaryPart
		
		weld.C0 = c0
		weld.C1 = thruster.PrimaryPart.PivotOffset
		
		weld.Parent = thruster
		
		thruster.Parent = target
	end
end)

Here is the Client script

local RunService = game:GetService("RunService")
local tool = script.Parent
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local thrusterEvent = tool:WaitForChild("ThrusterRequest")
local ServerData = nil

--setup marker
local Marker = tool:FindFirstChild("Thruster"):Clone()
local markerScale = 1
for _,i in pairs(Marker:GetDescendants()) do
	if i:IsA("BasePart") then
		i.Transparency = .5
		i.CanCollide = false
		i.Massless = true
	end
end
Marker:ScaleTo(markerScale) --because the thruster may be smaller in hand than actually placed

local RunServiceConnection = nil

function Cleanup()
	if RunServiceConnection then
		RunServiceConnection:Disconnect()
		RunServiceConnection = nil
	end
end

local function getRotationBetween(u, v, axis)
	local dot, uxv = u:Dot(v), u:Cross(v)
	if (dot < -0.99999) then return CFrame.fromAxisAngle(axis, math.pi) end
	return CFrame.new(0, 0, 0, uxv.x, uxv.y, uxv.z, 1 + dot)
end

local function getSurfaceCF(part, lnormal)
	local transition = getRotationBetween(Vector3.yAxis, lnormal, Vector3.zAxis)
	return part.CFrame * transition
end

local RotationMode = "Normal" --Could be "Normal" "Character" "Camera"
function PlaceInstance(material,normal,inst,position)
	local cf = CFrame.new(position) --cframe is set by position by default

	if inst then --if we have an instance, then we can use that to get the proper cframe
		if RotationMode ~= "Normal" then 
			normal = normal * Vector3.new(0,1,0) 
		end
		cf = getSurfaceCF(inst,inst.CFrame:VectorToObjectSpace(normal))
		cf = (cf-cf.Position)+position
	end

	if RotationMode == "Player" then
		local target = (player.Character.HumanoidRootPart.Position * Vector3.new(1,0,1) ) + Vector3.new(0,position.Y,0)
		cf = CFrame.lookAt(position,target,Vector3.yAxis)
	end

	--apply any extra rotation
	local extraRotation = nil
	local rotation = extraRotation or Vector3.new(0,0,0)
	cf = cf * CFrame.Angles(math.rad(rotation.X),math.rad(rotation.Y),math.rad(rotation.Z))
	
	if inst then
		ServerData = {
			Instance = inst,
			C0 = inst.CFrame:ToObjectSpace(cf),
			CF = cf
		}
	end
	
	Marker:PivotTo(cf)
end


local PlaceMode = "Mouse" --can be Mouse or Camera  (Camera is best for first person)
local Mouse = player:GetMouse()
local raycastParams = RaycastParams.new()
local function onRenderStep(deltaTime)
	if on and Marker then
		raycastParams.FilterType = Enum.RaycastFilterType.Exclude
		raycastParams.IgnoreWater = true
		local filterList = {Marker,player.Character}
		local ray = nil
		if PlaceMode == "Camera" then
			ray = Ray.new(workspace.CurrentCamera.CFrame.Position, workspace.CurrentCamera.CFrame.LookVector)
		else
			ray = Mouse.UnitRay
		end

		raycastParams.FilterDescendantsInstances = filterList
		local raycastResult = workspace:Raycast(ray.Origin, ray.Direction*100, raycastParams)
		if raycastResult then
			local inst = raycastResult.Instance
			PlaceInstance(raycastResult.Material,raycastResult.Normal,raycastResult.Instance,raycastResult.Position)
		else
			PlaceInstance(nil,Vector3.yAxis,nil,ray.Origin + (ray.Direction*100))
		end

	end
end

tool.Equipped:Connect(function()
	Cleanup()
	Marker.Parent = workspace
	on = true
	RunServiceConnection = RunService.RenderStepped:Connect(onRenderStep) 	

end)

tool.Unequipped:Connect(function()
	Cleanup()
	on = false
	Marker.Parent = nil
end)

tool.Activated:Connect(function()
	if on and Marker and Marker.Parent then
		if ServerData then
			thrusterEvent:FireServer(ServerData) 
		end	
	end
end)

The client gets the position of where to attach the thruster, but it only sends the offset (C0) of the thing you are attaching it to (Instance) to the server, so that the server can make the weld and place the actual part. So if the object have moved from the time you click to the time the server gets the message (due to lag) the server can accurately place the thruster.

Also, the tool has the ‘Thruster’ at a scale of .5, so it shows up easily in hand, then the client and server scripts clone this model, and scale it back up to a scale of 1.0 so it can be used as a marker and placed as the instance at normal scale.



Hope this helps you out.

1 Like

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