I just finished making a complex equipment system for weapons and tools in my game. In a new script I’ve made, it’s supposed to handle adding the actual weapon onto the character model. It works generally okay, but for some reason studio just seems to refuse to add the weapon to the correct CFrame.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ToolModule = require(ReplicatedStorage:WaitForChild("ToolModule"))
local ToolData = ToolModule.getToolData()
local player = game:GetService("Players").LocalPlayer
local function characterToolSetup()
local PlayerData = ReplicatedStorage.Remotes.GetData:InvokeServer(player)
local character = player.Character
for _, object in pairs(character:GetDescendants()) do
for _, item in pairs(ToolData) do
if object.Name == item.Name then
object:Destroy()
end
end
if object.Name == "AttachmentPart" then
object:Destroy()
end
end
if PlayerData.HotbarTool1 ~= 0 then
local tool = nil
for _, item in pairs(ToolData) do
if item.ToolId == PlayerData.HotbarTool1 then
tool = item
end
end
if tool then
local toolModel = tool.Model:clone()
local toolAttachment = tool.Model.Parent:WaitForChild("AttachmentPart"):clone()
local toolWeld = Instance.new("Weld")
local attachmentWeld = Instance.new("Weld")
toolAttachment.Parent = character
toolAttachment.CFrame = character:WaitForChild("Torso").CFrame
attachmentWeld.Parent = character.Torso
attachmentWeld.Part0 = character.Torso
attachmentWeld.Part1 = toolAttachment
toolModel.Parent = toolAttachment
toolModel:PivotTo(toolAttachment.ToolAttachment.WorldCFrame)
toolWeld.Parent = toolAttachment
toolWeld.Part0 = toolAttachment
toolWeld.Part1 = toolModel:WaitForChild("Handle")
end
end
end
characterToolSetup()
player.CharacterAdded:Connect(characterToolSetup)
ReplicatedStorage.Remotes.CharacterToolSetup.OnClientEvent:Connect(characterToolSetup)
The script is supposed to stick the weapon to the same CFrame as the tool attachment on the torso but for some reason it just sticks the weapon into the center of the player’s torso.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ToolModule = require(ReplicatedStorage:WaitForChild("ToolModule"))
local ToolData = ToolModule.getToolData()
local player = game:GetService("Players").LocalPlayer
local function characterToolSetup()
local PlayerData = ReplicatedStorage.Remotes.GetData:InvokeServer(player)
local character = player.Character
for _, object in pairs(character:GetDescendants()) do
for _, item in pairs(ToolData) do
if object.Name == item.Name then
object:Destroy()
end
end
if object.Name == "AttachmentPart" then
object:Destroy()
end
end
if PlayerData.HotbarTool1 ~= 0 then
local tool = nil
for _, item in pairs(ToolData) do
if item.ToolId == PlayerData.HotbarTool1 then
tool = item
end
end
if tool then
local toolModel = tool.Model:Clone()
local referenceAttachment = tool.Model.Parent:WaitForChild("AttachmentPart"):FindFirstChildOfClass("Attachment")
local toolAttachment = tool.Model.Parent:WaitForChild("AttachmentPart"):Clone()
toolAttachment.Parent = character
toolAttachment.CFrame = character:WaitForChild("Torso").CFrame
local attachmentWeld = Instance.new("Weld")
attachmentWeld.Part0 = character.Torso
attachmentWeld.Part1 = toolAttachment
attachmentWeld.Parent = toolAttachment
toolModel.Parent = toolAttachment
toolModel:PivotTo(toolAttachment.ToolAttachment.WorldCFrame)
local toolWeld = Instance.new("ManualWeld")
toolWeld.Part0 = toolAttachment
toolWeld.Part1 = toolModel:WaitForChild("Handle")
toolWeld.C0 = toolAttachment.ToolAttachment.CFrame
toolWeld.C1 = toolModel.Handle:FindFirstChild("HandleAttachment").CFrame
toolWeld.Parent = toolAttachment
end
end
end
characterToolSetup()
player.CharacterAdded:Connect(characterToolSetup)
ReplicatedStorage.Remotes.CharacterToolSetup.OnClientEvent:Connect(characterToolSetup)
The problem is the weld. When you create a normal Weld and only set Part0 and Part1, Roblox automatically resets the offset so Part1 snaps to the center of Part0. That’s why your weapon ends up inside the torso even though you pivoted it earlier.
Your PivotTo call runs, but as soon as the weld is created the offset gets recalculated and the model moves.
You need to preserve the offset by setting C0 (or C1) when you create the weld, or just use a WeldConstraint instead so Roblox keeps the current position.
For example, after positioning the model:
WeldConstraint keeps the current world position instead of snapping the parts together.
Also make sure you pivot after the model is parented to workspace/character, otherwise WorldCFrame values from attachments can be incorrect. Once the model is in the character and pivoted, adding the WeldConstraint will keep it exactly where it should be.
oh my god thank you so much this was like the smallest issue however it was roadblocking most development and it was soooooo annoying. I never really understood the difference between welds and weldConstraints so thank you also for explaining both.
I know that this post has already been solved, however I have another question that is still somewhat relevant:
What if I want to use a Motor6 so that I can animate a part? How can I script a motor6 to be added without the offset thingy setting the weapon to the center of another part?
The snapping only happens because the Motor6D defaults C0 and C1 to identity, which means Roblox aligns both part origins together.
If you calculate the offset first and assign it to C0, the current position is preserved. That prevents the weapon from snapping to the center.
So the flow is basically: place the weapon where you want it, compute the relative CFrame, then create the Motor6D using that offset. Once that’s done, the Motor6D keeps that transform and animations can move it from there without resetting the position.