If I have a Part (Part A) and another Part (Part B) parented to an Attachment that is then parented to Part A, How would I keep the relative position of Part B the same no matter what happens to the size of Part A?
For example, if Part B is positioned close to the edge of Part A and then Part A’s size changes, Part B’s position would then update to be set to close to the edge of the new size.
That should make it so the position of the attachment scales relatively to part A. If you’d like to have a part stay the same distance from the edge of part A, you could add an attachment to the edge of A then add an attachment to that attachment that has the displacement from the edge (by add attachment you might need to add a part with the attachments).
This works somewhat, but if Part A is consistently spinning then it kind of falls apart, the parts stop lining up and end up drifting away after enough position updates.
Am I using the code correctly? It didn’t seem to work when I was using the attachment
local partA = script.Parent
local partB = partA.Attachment.Partb
local partAOldSize = partA.Size
local function onChange()
local partANewSize = partA.Size
local scale = partANewSize / partAOldSize
local relativeScale = partA.CFrame:VectorToWorldSpace(scale)
local newPartBPosition = (partB.Position - partA.Position) * relativeScale + partA.Position
partB.CFrame = partB.CFrame - partB.Position + newPartBPosition
partAOldSize = partANewSize
end
partA:GetPropertyChangedSignal("Size"):Connect(function()
onChange()
end)
I apologise if I am doing something wrong.
Also would it be possible to change the code so if the position of PartA changes PartB updates to the same position as well? To give the illusion of moving with PartA. I can do it already but I was wondering if there was an easier way.
Oh, that’s an oversight of mine. First, it doesn’t update when the part moves. Second, the size is being stored every frame so the imprecision of the numbers being stored builds up every time.
Please try this code instead:
local RunService = game:GetService("RunService")
local partA = ...
local partB = ...
-- The values used to calculate the position of B are stored once, so no precision can build up over many times setting the position of B
local originalPartASize = partA.Size
local originalOffset = partA.CFrame:ToObjectSpace(partB.CFrame)
RunService.RenderStepped:Connect(function()
-- Calculate the position of the part if there were no scale changes
local unscaledPartBCFrame = partA.CFrame * originalOffset
-- Calculate the desired position based on the overal scale change
local scale = partA.Size / originalPartASize
local relativeScale = partA.CFrame:VectorToWorldSpace(scale)
local newPartBPosition = (unscaledPartBCFrame.Position - partA.Position) * relativeScale + partA.Position
-- Replace the position with the newly calculated position
local scaledPartBCFrame = unscaledPartBCFrame - unscaledPartBCFrame.Position + newPartBPosition
-- Set the CFrame of partB to the calculated one
partB.CFrame = scaledPartBCFrame
end)
This works! But I have another problem (I’m so sorry for asking you constantly instead of just trying myself, I’ve been trying to figure this out on my own for like over a week and I just cant be bothered anymore… lol)
If PartA is spinning, it messes up the movement of the PartB(s), and if the size of the Part changes on the Y axis, it offsets the position of the PartB(s)
Maybe you could try and add an “offset” CFrameValue to each PartB upon their creation, set it to PartB’s CFrame - PartA’s CFrame, and then whenever the size or position of PartA changes, set every PartB’s CFrame to PartA’s CFrame + the PartB’s offset value CFrame?
WeldConstraint did somewhat work on a previous version of this although I ran into some kind of issue that I can’t remember. I want a system that can do three things:
Update the position of PartBs to be relative to the scale of partA’s size
Update the position of PartBs whenever PartA moves
Make sure this works while partA is moving/spinning/flipping and if PartBs are added while partA is spinning/flipping (This causes problems in all the ways I’ve tried)
The code I used is just this.
local partA = script.Parent
local partB = partA.Partb
local RunService = game:GetService("RunService")
RunService.Heartbeat:Connect(function()
print(partB.CFrame * partA.CFrame:Inverse()) -- To get the offset to put inside the CFrameValue inside of PartB
partB.CFrame = (partA.CFrame * partB.Offset.Value)
end)
I know this is inefficient and laggy but this is just to check code works before I bother adding it to the actual game. PartB already exists in this situation so I add the CFrameValue to the part directly in studio
The code “works”, but all it does is update and follow the position of PartA if it moves, it doesn’t care about the size of partA changing. Sorry if this is just me being stupid, I have been trying to figure this out for so long now that my brain is just tired haha
Oh, sure, but like
First off what the hell does Inverse() do I’m not a math guy I’m a scripting guy
Second off could you maybe add a part that said something about the parts “Original Size” to the CFrameValue (maybe another value inside of it) and added something like partB.CFrame = (partA.CFrame * partB.Offset.Value) + ((OriginalSize - partA.Size.Y) / 2)?
:Inverse() just returns the values of the CFrame but negative (I think…), so doing partB.CFrame * PartA.Cframe:Inverse() is the equivalent of doing partB.CFrame - PartA.CFrame
This code works better but now I am running into the issue with spinning/flipping…
To be honest if this is too hard I might end up just ignoring the relative scale part and just making sure it follows the position, which would mean going back to my WeldConstraint setup which eh isn’t that bad
To be honest I really don’t know why that code wouldn’t work with spinning…
I am assuming you set the Offset value correctly (including rotation) and that it’s a CFrame value, so if they aren’t please say so.
Also, what is the issue with WeldConstraints in the first place?
The CFrame value is indeed set to the offset, the rotation is just 0, 0, 0 as neither PartA or PartB have any rotation applied to them upon server start.
I do not remember the issue with WeldConstraints specifically, but if I had to make a guess, probably something to do with updating the position relatively when partA changes size.
Hmm.
But, I have had some weird results with testing, but maybe I just put in the offset wrong…
In this case, Part A is at 0, 0.5, 0 and Part B is at -4.125, 1.5, -5.25 (ontop of Part A which is 1 stud tall)
Offset is set to -4.125, 1, -5.25.
There are 2 scripts, one inside Part A to make it spin 100 times (which is probably irrelevant but ill put it here anyway)
Spinning Script
local p = script.Parent
local ts = game:GetService("TweenService")
local ti = TweenInfo.new(100, Enum.EasingStyle.Linear, Enum.EasingDirection.In, 1, false)
local tg = {}
tg.Orientation = Vector3.new(0, 3600, 0)
local tw = ts:Create(p, ti, tg)
tw:Play()
And the second script is in PartB and how it moves with PartA.
local p = script.Parent
local pa = workspace.PartA
game:GetService("RunService").Heartbeat:Connect(function()
p.CFrame = pa.CFrame * script.Parent.Offset.Value:Inverse()
end)
And… the results are interesting…
I could record it but I can’t find the file path to the video… so…
It worked… CORRECTLY!
PartB perfectly followed the PartA while it was spinning…
But, PartB happened to be upside down, which is fine.
I have yet to test this with PartA moving, and I will now.
EDIT: I have tested it with PartA moving, and it works perfectly! However, Vertical Sizing doesn’t work, but that could be fixed easily. Let me take the code for making it go up properly, and change the offset to put PartB ontop of PartA.
Good news! I’ve managed to tweak the script just enough for it to work with Vertical Sizing!
As well as changing the offsets Y value to -1, I’ve changed Part B’s script a little!
local p = script.Parent
local pa = workspace.PartA
local ogs = pa.OriginalSize
game:GetService("RunService").Heartbeat:Connect(function()
if ogs.Value.Y == pa.Size.Y then
p.CFrame = (pa.CFrame * script.Parent.Offset.Value:Inverse())
else
p.CFrame = (pa.CFrame * script.Parent.Offset.Value:Inverse()) + Vector3.new(0, ((pa.Size.Y - ogs.Value.Y) / 2), 0)
end
end)
Now, it works with Moving, Spinning, and Vertical Sizing, all at the same time!