We’re excited to announce the full release of pivot functionality in Roblox Studio! Over the next couple weeks we will be rolling the pivot functionality out to all Studio users:
- We will begin with a small 10% rollout. If you don’t see the pivot editor, don’t worry: You aren’t part of the rollout yet but you can still enable the pivot functionality yourself by turning on the “Pivot Editor” beta feature.
- Is the pivot functionality disrupting your Studio experience? For the time being you can opt out of the rollout by disabling the “Pivot Editor” beta feature. We will eventually remove the beta feature and the ability to opt out, so if you opt out, please reply with the issues that caused you to do so.
Here’s a link to the DevHub documentation, and the following is recap of the key pivot functionalities for those who weren’t following the beta progress:
Each part and model now has its own configurable “pivot” around which modeling tools and some Lua APIs manipulate it. When you select tools that manipulate the position of objects (Move, Scale, Rotate, or any community plugin that sets
DraggerService.ShowPivotIndicator = true), you will see pivot indicators drawn on top of those objects showing where their pivots are:
This release brings a couple changes to the visuals of the Move, Scale, and Rotate tools. The handles of these tools will be shown around the pivot. The Move tool’s handles have also been moved inwards, such that they display directly around the pivot, rather than out at the edges of the selection.
Alongside that change, you can now “summon” the handles of the Move, Scale, and Rotate tools directly to your cursor by holding down the Tab key. This allows you to manipulate the selection easily regardless of where your camera is looking and adds additional functionality to the Rotate tool:
Okay, that sounds great, but how do I actually set where the pivot is…?
There is now a “Pivot” section in the Model tab, with an “Edit Pivot” tool which allows you to set where on a given part or model the pivot is located:
When you select an object with the Edit Pivot tool, you will be able to modify the pivot in 3 different ways:
- Moving the pivot with a set of Move handles.
- Rotating the pivot with a set of Rotate handles.
- Freeform dragging the pivot by dragging the pivot indicator directly.
When the “Snap” toggle in the Pivot section is turned off, the pivot can be freely moved. When it is turned on, the pivot will snap to “key points” on the bounding boxs of objects:
You can also edit where an object’s pivot is numerically via the properties pane. Models and parts work slightly differently in this regard:
- Parts have “Pivot Offset” properties, which specifies the local offset of the pivot from the CFrame of the part.
- Models without a PrimaryPart have “World Pivot” properties, which specify the location of the model’s pivot in world space.
- The pivot of models with a
PrimaryPartis exactly equal to the pivot of their
PrimaryPart(The Properties panel shows the “Pivot Offset” properties of the selected model’s
PrimaryPartfor convenience in this case).
As seen above, we’ve also added “Origin Position” / “Origin Orientation” properties to models and parts which let you move a part or model around via its pivot.
NOTE: These Lua APIs are already live on production, you can begin using them whenever you want!
Finally, there are Lua APIs to both change where a part or model’s pivot is, and move that part or model via the pivot point:
CFrame BasePart.PivotOffset– A property specifying the offset of a BasePart’s pivot from its CFrame.
CFrame Model.WorldPivot– A property specifying the pivot of a Model in world space. Only applies for models which do not have their
CFrame BasePart/Model:GetPivot()– Returns the pivot of a part or model in world space.
void BasePart/Model:PivotTo(CFrame targetPivot)– Moves the part or model (and all of its descendant parts and models) such that the pivot is now equal to
For example, to move a model 2 units “up” relative to its pivot, you could to the following:
local model = ... model:PivotTo(model:GetPivot() * CFrame.new(0, 2, 0))
This means that
:PivotTo() now provides a uniform API for moving both parts and models around.
:PivotTo() also has a few advantages over the other APIs that could be used for this before such as
:PivotTo()operates on the pivot, which can be configured to whatever reference point on the object is the most useful, such as the hinge location on a door.
:PivotTo()does not require you setting a
BulkMoveToinfrastructure and some other internal optimizations, so it has better performance than the other methods.
:PivotTo()preserves part and model offsets, so you will not experience floating point drift when using it to repeatedly move an object!
We mentioned but glossed over the statement "the pivot of a model with a
PrimaryPart is equal to the pivot of that
PrimaryPart" above, but it’s actually a very interesting point to discuss! Why would we make models with a
PrimaryPart behave like this?
The main thing to observe is what happens for models without a
PrimaryPart: What happens to the pivot of those models when parts underneath them get moved individually or thanks to physics simulation? The pivots of those models will actually stay in the exact same place rather than attempting to “follow” any of the parts under the model.
This is quite useful for editing purposes. If you have a static model containing some of your level geometry, you can move around things within that model without worrying about messing up the location of the pivot.
But what do you do if you have a model that does get physically simulated, such as a car that people are going to be driving around, and you want the pivot to move along with it? You set the
PrimaryPart! Since the pivot of a model with a
PrimaryPart is equal to the pivot of that
PrimaryPart, setting the
PrimaryPart effectively tells the engine which of the parts in the model the pivot should “follow” when the model gets moved.
Incidentally, this behavior also makes
:PivotTo into a drop-in improved replacement for
:SetPrimaryPartCFrame: The default pivot of a model with a
PrimaryPart is equal to the pivot of the
PrimaryPart, which is by default equal to the CFrame of that
PrimaryPart, so the two will behave exactly the same for existing models.
When importing meshes through the Properties panel, or through the Avatar Importer plugin, the pivot of the imported MeshParts will be set to the origin of the coordinate space that you were importing from so that the offset information of those MeshParts is no longer lost. EDIT: You will only get this behavior if the “Set Pivot of Imported Parts” setting in the Studio settings is set to true.
You will not get this behavior with the Bulk Importer, but we’re currently working on asset import workflow changes that will extend this behavior to all imports and provide it more detailed options.
Model:SetPrimaryPartCFrame()will be deprecated at some point in the near future.
Model:PivotTo()act as drop-in replacements for them (i.e.: You can directly find-replace your calls to
SetPrimaryPartCFramewith calls to
PivotTowithout changing how your code works)
Along with the release of the pivot API, we soft-removed
Model:ResetOrientationToIdentity()– These functions no longer do anything. This had no noticable impact as we contacted the couple potentially affected devs ahead of time.
The reason you need to specify a
PrimaryPart when creating a package is to provide a reference point around which that package will be updated when applying updates.
But wait… pivot is also a reference point, and every model has a pivot. Couldn’t we use that as the reference point? Yes! We will be using it for that! We will be updating packages soon to use the pivot as the reference point instead of
PrimaryPart, meaning we will be able to drop the
PrimaryPart requirement. (This will be a backwards compatible change for the same reason that
:PivotTo() is a drop-in replacement for
- We decided not to offer a way to turn it off for now, as we think that only showing it when you have a tool that makes use of it selected is non-intrusive enough. We’re open to feedback on this.
- No. Deprecated doesn’t mean something is going to be removed, being deprecated is usually a standardized way to point people at the better modern alternative to an older API. Almost all deprecated APIs continue to function like they did before to support legacy scripts, and in the rare case where we do actually remove an API we’ll make sure that the affected devs are aware.
- You use
PivotToto move the object from Lua code. The reason we used these methods instead of a property is that property changes from Lua in the Roblox engine are supposed to be simple independent operations, but moving a model involves setting many properties of many objects. Thus methods fit better than a property.
- Earlier in the beta, we treated the pivot of a multi-selection as the pivot of the most recently selected of those objects. Later on we changed the pivot back to being the center of the bounds of the multi-selected objects. If you liked the earlier behavior, let us know, we could add an option for it in the future.
- Give handle summoning a try! So far the feedback indicates that handle summoning is very successful in supporting effective editing without the need for the Move handles to be out at the edges of objects.
- It is still working, it’s just been moved behind a default false option for the time being, change “Set Pivot of Imported Parts” in the Studio settings to true to enable the behavior. It will be coming back in a more configurable form by default with our future unified asset importing process.
If you read to the end, thanks for your patience reading this exceedingly long announcement post, and thanks to @tnavarts, @LuckyKobold, @wengawenga, @4thchamber, and @JoshSedai for their hard work on this feature.