There are various problems with legacy joints (Welds/Motors) that make it hard to use them. WeldConstraints aim to improve some of these areas, but they don’t make enough of a difference that my workflow is affected – in some cases, WeldConstraints make it even worse. As a developer, joints are still too difficult to use.
Use Case | Legacy | Constraint |
---|---|---|
Intuitive API | ![](upload://mhST7HF7JkoDNCktb0ui77ToB3f.png) | ![](upload://mhST7HF7JkoDNCktb0ui77ToB3f.png) |
Easy to tweak through Properties | ![](upload://mhST7HF7JkoDNCktb0ui77ToB3f.png) | ![](upload://mhST7HF7JkoDNCktb0ui77ToB3f.png) |
Versatile | ![](upload://eIBbUhVdr6tR8jjjvaZ2NnUQwUF.png) | ![](upload://mhST7HF7JkoDNCktb0ui77ToB3f.png) |
Easy to manipulate visually | ![](upload://mhST7HF7JkoDNCktb0ui77ToB3f.png) | ![](upload://eIBbUhVdr6tR8jjjvaZ2NnUQwUF.png) |
Easy to hot swap | ![](upload://mhST7HF7JkoDNCktb0ui77ToB3f.png) | ![](upload://mhST7HF7JkoDNCktb0ui77ToB3f.png) |
Automatically weld models | ![](upload://mhST7HF7JkoDNCktb0ui77ToB3f.png) | ![](upload://mhST7HF7JkoDNCktb0ui77ToB3f.png) |
Original issues
Legacy joints have always been a pain to use. They require programming knowledge to use since they can only be configured through the command line or scripts, and they’ve always been rough around the edges. For example:
Legacy joint API is convoluted
If I want to connect two parts and have them rotate around a specified point (e.g. shoulder of an arm), with Welds/Motors that looks like:
local motor = Instance.new("Motor6D")
motor.Part0 = part0; motor.Part1 = part1;
motor.C0 = shoulderOffsetFromTorso
motor.C1 = shoulderOffsetFromArm
motor.Parent = part0
Huh? What are those weird C0/C1 properties? They’re such a cryptic way to define offsets, especially compared to the Attachments we have now. And what if I don’t care about the rotational pivot point? It’s not obvious that I can just treat C0 as CFrame and ignore C1. There was also a change some time ago that attempts to automatically determine Part0 based on its size in relation to Part1, which further convolutes legacy joints because the part a developer explicitly sets as the root may not end up being the root part.
Dragging/resizing a model breaks joints
This is not a tools issue because there’s no way to define behavior for dragging welds. Do you move only the C0? Only the C1? Both? C0/C1 can be used in too many ways for Studio tools to reliably modify them when components are moved/scaled. A new class with more clearly defined behavior is needed to support this.
Difficult to swap out
It’s common for games to allow players to customize their equipment in some way. Space games allow players to customize the turrets on their ship, FPS games allow players to customize the attachments on their weapons, and social games allow players to customize their appearance. All of these relationships can be easily defined with two attachments, but to connect these two attachments I have to manually weld them together:
local weld = Instance.new("Weld")
weld.Part0 = attachment0.Parent
weld.Part1 = attachment1.Parent
weld.C0 = attachment0.CFrame
weld.C1 = attachment1.CFrame
weld.Parent = attachment0.Parent
All I’m doing is prettying up the format of the already-existing data provided by attachments for welds. This is more annoying than it needs to be.
Automatically welding a model
It’s annoying to automatically weld a model with legacy joints. Open up free model tools and “Weld Script”, “Weld”, or “Welder” is in every single one. If even added _G.weldModel() to the command line with a plugin so I could easily connect selected models. It was kind of dumb how the same script had to be copy+pasted everywhere because Roblox didn’t support such a common use case.
Difficult to tweak
Legacy joint offsets are controlled through CFrame. If I wanted to make a minor tweak to the offset, I have to set its CFrame through the command line since CFrame can’t be edited in the properties window. Visual editing (i.e. using move/scale tools) would help, but that doesn’t address all use cases. E.g. copy+invert joint offset on other side of body, or align two joints on an axis. The properties window is great for this.
Addressed changes
WeldConstraints were created to address some of these problems:
Dragging/resizing a model breaks joints
This has substantially improved with WeldConstraints. I can freely adjust my models without breaking joints.
Automatically welding a model
There’s now a ribbon tool that connects all touching parts in a model. This is a better than the old behavior, but the benefits are entirely the result of a tooling change. The ribbon tool could just as easily use Welds instead of WeldConstraints if it ignored parts that were already connected to another, so WeldConstraints aren’t contributing to any of the benefits here.
Difficult to tweak
Since WeldConstraints update when object Position/Orientation change, it’s significantly easier to tweak them since they can be edited in the properties window.
Remaining problems
Even though WeldConstraints solved some issues, there are still some that remain, and new problems introduced by WeldConstraints:
Joint API is still convoluted
WeldConstraints are only really useful for static models that are manipulated visually like an anchored door. When scripts interact with them, they’re even more convoluted: who would think that they have to manually bring the parts together, when that’s the job of a joint? That’s such a backwards way of thinking. To interact with the WeldConstraint, you have to tweak properties (CFrame) and call methods (SetPrimaryPartCFrame) that aren’t even related to WeldConstraint.
Use of WeldConstraints are limited
WeldConstraints aren’t good fits for joints that move – they don’t support arbitrary rotational pivots for character joints/etc, are vulnerable to physics, and can’t be used with models since SetPrimaryPartCFrame gradually offsets the model’s components. All of the downsides of legacy joints are still present since we can’t use WeldConstraints for these use cases.
Dragging/resizing a model breaks joints
Even though we’ve seen major improvements with WeldConstraints, the benefits are diminished because WeldConstraints are limited in use. Only static models see this benefit.
Difficult to tweak
Again, improvements here, but with limited effect due to low amount of use cases WeldConstraints are suitable for. This improvements are also only
Automatically welding a model
The ribbon tool helps, but a number of core problems remain. Every time I add a part to the model, I have to re-click weld since it’s not automatic. There are also catastrophic repercussions when I move around items: “Wait, why is this barrel floating in midair after the floor is destroyed? Oh, it’s still attached to some other floor from before I moved it to its current location.” Also, if I want to break an object in half for decoration, now I have to go and hunt down hidden joints so the two halves are no longer connected.
Difficult to swap out
There’s no real incentive for me to use WeldConstraints for hot-swappable attachments. The code for using WeldConstraints/Welds is nearly the same, with WeldConstraints being a bit annoying since I have to convert object to world CFrame:
Prepare:
local weld = Instance.new("Weld")
weld.Part0 = cannonMount
weld.C0 = cannonMount.CannonAttachment.CFrame
weld.Parent = cannonMount
Attach via Weld:
weld.Part1 = cannon.PrimaryPart
weld.C1 = cannon.PrimaryPart.CannonAttachment.CFrame
Prepare:
local weldConstraint = Instance.new("WeldConstraint")
weldConstraint.Part0 = cannonMount
weldConstraint.Parent= cannonMount
Attach via WeldConstraint
weldConstraint.Part1 = cannon.PrimaryPart
cannon:SetPrimaryPartCFrame(cannonMount.CFrame * cannonMount.CannonAttachment.CFrame * cannon.Primarypart.CannonAttachment.CFrame:inverse())
Future Changes
Versatility and Intuitive API
Joints have to have a CFrame, and only a single CFrame (unlike C0/C1/Transform) – it’s super weird to edit another object’s property and for it to affect the joint. It’s not explicit enough. Joints also need to support some form of offset (i.e. pivot around shoulder instead of center of arm), and that offset needs to be only an offset, and not combined with the joint’s CFrame like C0. An intuitive way to accomplish this is by allowing Part0/Part1 to take either parts or attachments, and updating the property names to reflect that.
This not only allows two parts to be conveniently stitched together without the overhead of attachments, while making it just as pleasant to use attachments when they’re needed. Compare:
local joint = Instance.new("Joint")
joint.Object0 = upperTorso.ShoulderSocketRight
joint.Object1 = rightUpperArm.ShoulderSocketRight
joint.CFrame = CFrame.new(0,0,math.pi/2) --T pose
local motor = Instance.new("Motor6D")
joint.Part0 = upperTorso
joint.Part1 = rightUpperArm
joint.C0 = upperTorso.ShoulderSocketRight.CFrame
joint.C1 = rightUpperArm.ShoulderSocketRight.CFrame
joint.Transform = CFrame.new(0,0,math.pi/2) --Non-archivable
OR
joint.C0 = joint.C0 * CFrame.new(0,0,math.pi/2) --Archivable
Easy to manipulate visually
With joint offset and CFrame property separated as displayed in the above example, moving them with the Studio tools becomes trivial. Leave the attachment offset alone, if there is any, and just write to the CFrame property of the joint.
Easy to hot swap
Trivial with proposed API
Prepare:
local joint = Instance.new("Joint")
joint.Object0 = cannonMount.CannonAttachment
weld.Parent = cannonMount
Attach via Joint:
joint.Object1 = cannon.PrimaryPart.CannonAttachment
Easy to tweak through properties
CFrame values should be editable through the properties window, and in a user-friendly way. We should see Position/Orientation vectors for a CFrame, and if there’s sufficient use case, a matrix editor. This would probably be need to be a much larger project in consolidating BasePart/Attachment/etc Position/Orientation into CFrame since the properties would no longer be needed.
Automatically weld models
This behavior should be truly automatic. When I make tools like a fishing rod or gun, moveable clutter like barrels and crates, or solid bodies like vehicles, I should just be able to make my model worry-free and have it automatically work when I go in-game. No manually pressing “re-weld” whenever I make a change. Compared to Unity where they already do this, we’ve really dropped the ball on this one.
There should be an object I can insert similar to UI Constraints that affect the whole model, or a property of Model, that automatically join its children to the PrimaryPart unless they’re connected by another constraint/joint (servo/etc). Child models would have their PrimaryPart joined to their parent’s PrimaryPart, or have all its children attached to the parent’s PrimaryPart if the child model had none.