Model

Show Deprecated

Models are container objects, meaning they group objects together. They are best used to hold collections of BaseParts and have a number of functions that extend their functionality.

Models are intended to represent geometric groupings. If your grouping has no geometric interpretation, for instance a collection of Scripts, use a Folder instead.

Models whose constituent parts are joined together with joints (so that they can move around or be destroyed via physics simulation) usually have a PrimaryPart set, as it specifies which part within the model the pivot and bounding box will "follow" as the model moves. Static models which stay in one place do not benefit from having a primary part set.

Models have a wide range of applications, including Roblox player characters. They also have a number of unique behaviors that are important to keep in mind:

As with all Instance types, the fact that a parent Model is replicated to a client does not guarantee that all its children are replicated. This is particularly important if these instances are being accessed by code running on the client, such as in a LocalScript. Using ModelStreamingMode with values such as Atomic can ensure that the entire model and all of its descendants are present if the parent model exists on the client, or you can use WaitForChild() when atomicity is not desired.

Code Samples

Basic Model Instantiation

local function groupObjects(objectTable)
local model = Instance.new("Model")
for _, object in pairs(objectTable) do
object.Parent = model
end
return model
end
local objects = {
Instance.new("Part"),
Instance.new("Part"),
}
groupObjects(objects)

Summary

Properties

Properties inherited from PVInstance

Methods

  • AddPersistentPlayer(playerInstance: Player):void

    Sets this model to be persistent for the specified player. Model.ModelStreamingMode must be set to PersistentPerPlayer for behavior to be changed as a result of addition.

  • Returns a description of a volume that contains all parts of a Model.

  • Returns the size of the smallest bounding box that contains all of the BaseParts in the Model, aligned with the Model.PrimaryPart if it is set.

  • Returns all the Player objects that this model object is persistent for. Behavior varies based on whether this method is called from a Script or a LocalScript.

  • Returns the canonical scale of the model, which defaults to 1 for newly created models and will change as it is scaled via Model/ScaleTo.

  • MoveTo(position: Vector3):void

    Moves the PrimaryPart to the given position. If a primary part has not been specified, the root part of the model will be used.

  • RemovePersistentPlayer(playerInstance: Player):void

    Makes this model no longer persistent for specified player. Model.ModelStreamingMode must be set to PersistentPerPlayer for behavior to be changed as a result of removal.

  • ScaleTo(newScaleFactor: number):void

    Sets the scale factor of the model, adjusting the sizing and location of all descendant Instances such that they have that scale factor relative to their initial sizes and locations when scale factor was 1.

  • TranslateBy(delta: Vector3):void

    Shifts a Model by the given Vector3 offset, preserving the model's orientation. If another BasePart or Terrain already exists at the new position then the Model will overlap said object.

Methods inherited from PVInstance

Properties

read parallel

Sets the level of detail on the model for experiences with instance streaming enabled.

When set to StreamingMesh, a lower resolution "imposter" mesh (colored, coarse mesh that wraps around all child parts of the model) renders outside the streaming radius.

When set to Disabled or Automatic, lower resolution meshes will not be displayed.

ModelStreamingMode

read parallel

Controls how Models are streamed in and out when instance streaming is enabled. Behavior depends on the selected enum. Has no effect when streaming is not enabled.

This should only be changed in Studio and in Scripts, never in LocalScripts (doing so can result in undefined behavior).

PrimaryPart

read parallel

Points to the primary part of the Model. The primary part is the BasePart that acts as the physical reference for the pivot of the model. That is, when parts within the model are moved due to physical simulation or other means, the pivot will move in sync with the primary part. If the primary part is not set, the pivot will remain at the same location in world space even if parts within the model are moved.

Note that when setting this property, it must be a BasePart that is a descendant of the model. If you try to set Model.PrimaryPart to a BasePart that is not a descendant of the model, it will be set to that part but reset to nil during the next simulation step — this is legacy behavior to support scripts which assume they can temporarily set the primary part to a BasePart which isn't a descendant of the model.

The general rule for models is that:

  • Models whose parts are joined together via physical joints such as WeldConstraints or Motor6Ds should have a primary part assigned. For example, Roblox character models have their Model.PrimaryPart set to the HumanoidRootPart by default.
  • Static (usually Anchored) models which stay in one place unless a script explicitly moves them don't require a Model.PrimaryPart and tend not to benefit from having one set.

Code Samples

Throwing Dice

-- Create a dice model with two halves and attach them together
local diceModel = Instance.new("Model")
diceModel.Name = "ChanceCube"
local diceTop = Instance.new("Part")
diceTop.Size = Vector3.new(4, 2, 4)
diceTop.Position = Vector3.new(0, 1, 0)
diceTop.Color = Color3.new(0, 0, 1)
diceTop.Parent = diceModel
local diceBottom = diceTop:Clone()
diceBottom.Position = Vector3.new(0, -1, 0)
diceBottom.Color = Color3.new(1, 0, 0)
diceBottom.Parent = diceModel
local weld = Instance.new("WeldConstraint")
weld.Part0 = diceTop
weld.Part1 = diceBottom
weld.Parent = diceModel
-- Put the dice up in the air above the workspace origin (does not require a primary part)
diceModel.Parent = workspace
diceModel:PivotTo(CFrame.new(0, 10, 0))
-- Assign the primary part before physical simulation
-- Without this line, the script will always output the same thing and the bounding box of the model will not change orientation
diceModel.PrimaryPart = diceTop
-- Wait a bit before rolling the dice (let it settle onto the floor)
for i = 5, 1, -1 do
print("Rolling dice in...", i)
task.wait(1)
end
diceTop:ApplyAngularImpulse(Vector3.new(15000, 1000, 5000))
diceTop:ApplyImpulse(Vector3.new(0, 3000, 0))
task.wait(1)
-- Wait for the roll to complete
while diceTop.AssemblyLinearVelocity.Magnitude > 0.1 or diceTop.AssemblyAngularVelocity.Magnitude > 0.1 do
task.wait()
end
-- Get the dice orientation, impacted by the primary part
local orientation = diceModel:GetBoundingBox()
if orientation.YVector.Y > 0.5 then
print("It's the boy!")
else
print("It's his mother!")
end

Scale

not replicated
not scriptable
read parallel

Setting this property in the properties pane will scale the model as though Model/ScaleTo was called on it, scaling all descendant Instances in the model such that the model has the specified scale factor relative to its original size.

This property is only available in Studio and will throw an error if used in a Script or LocalScript. Model/ScaleTo and Model/GetScale should be used from scripts.

WorldPivot

not replicated
read parallel

This property determines where the pivot of a Model which does not have a set Model.PrimaryPart is located. If the Model does have a PrimaryPart, the pivot of the Model is equal to the pivot of that primary part instead, and this WorldPivot property is ignored.

For a newly created Model, its pivot will be treated as the center of the bounding box of its contents until the first time its Model.WorldPivot property is set. Once the world pivot is set for the first time, it is impossible to restore this initial behavior.

Most commonly, moving the model with the Studio tools, or with model movement functions such as PVInstance:PivotTo() and Model:MoveTo(), will set the world pivot and thus end this new model behavior.

The purpose of this behavior is to allow Lua code to get a sensible pivot simply by creating a new model and parenting objects to it, avoiding the need to explicitly set Model.WorldPivot every time you create a model in code.


local model = Instance.new("Model")
workspace.BluePart.Parent = model
workspace.RedPart.Parent = model
model.Parent = workspace
print(model:GetPivot()) -- Currently equal to the center of the bounding box containing "BluePart" and "RedPart"
model:PivotTo(CFrame.new(0, 10, 0)) -- This works without needing to explicitly set "model.WorldPivot"

Code Samples

Reset Pivot

local function resetPivot(model)
local boundsCFrame = model:GetBoundingBox()
if model.PrimaryPart then
model.PrimaryPart.PivotOffset = model.PrimaryPart.CFrame:ToObjectSpace(boundsCFrame)
else
model.WorldPivot = boundsCFrame
end
end
resetPivot(script.Parent)

Methods

AddPersistentPlayer

void

Parameters

playerInstance: Player
Default Value: "nil"

Returns

void

GetBoundingBox

void

This function returns a description of a volume that contains all BasePart children within a Model. The volume's orientation is based on the orientation of the PrimaryPart, and matches the selection box rendered in Studio when the model is selected. The description is provided in the form of a CFrame orientation and Vector3 size.

Mirroring the behavior of Terrain:FillBlock(), it returns a CFrame representing the center of that bounding box and a Vector3 representing its size.

If there is no PrimaryPart for the model, the BoundingBox will be aligned to the world axes.

Example

Pictured below is a Model with a pink semitransparent Part whose CFrame and Size have been set to the return values of this function called on the model.

A model of an Observation Tower with a pink semitransparent part representing the volume returned by GetBoundingBox

Usage


local model = workspace.Model
local part = workspace.Part
local orientation, size = model:GetBoundingBox()
part.Size = size
part.CFrame = orientation

Returns

void

A CFrame representing the orientation of the volume followed by a Vector3 representing the size of the volume.

GetExtentsSize

Returns the size of the smallest bounding box that contains all of the BaseParts in the Model. If Model.PrimaryPart exists then the bounding box will be aligned to that part. If a primary part has not been set then the function will chose a part in the model to align the bounding box to. As the selection of this part is not deterministic it is recommended to set a Model.PrimaryPart to get consistent results with this function.

Note this function only returns the size of the smallest bounding box, and the developer must employ their own method to obtain the position of the bounding box.


Returns

The Vector3 extents size of the Model.

Code Samples

Model GetExtentsSize

local model = Instance.new("Model")
model.Parent = workspace
local RNG = Random.new()
for _ = 1, 5 do
local part = Instance.new("Part")
part.Anchored = true
part.Size = Vector3.new(RNG:NextNumber(0.05, 5), RNG:NextNumber(0.05, 5), RNG:NextNumber(0.05, 5))
part.Parent = model
end
print(model:GetExtentsSize())

GetPersistentPlayers

When this method is called from a Script, it returns all the Player objects that this model is persistent for. When called from a LocalScript, this method only checks if this model is persistent for the LocalPlayer.


Returns

A table with all the Player objects that this model object is persistent for.

GetScale

Models contain a persistent canonical scale factor, which starts out at 1 for newly created models and changes as the model is scaled by calling Model/ScaleTo. This function returns the current canonical scale factor of the model.

The current scale factor does not directly impact the size of Instances under the model. It is used for content authoring and scripting purposes to remember how the model has been scaled relative to its original size.

Within a given session, the model will cache the precise original size information of the descendant Instances after the first Model/ScaleTo call. This means that calling ScaleTo(x) followed by ScaleTo(1) will get you back exactly the original configuration of the model with no floating point drift. Avoiding floating point drift is the motivation for having a ScaleTo function instead of a ScaleBy function.

The scale factor does impact engine behavior in one way: The scale factor of a model will be applied to joint offsets of animations played on an AnimationController under that model, so that animated rigs will correctly play back animations even when scaled.


Returns

The current canonical scale factor of the model.

Code Samples

Substituting in a replacement model using PivotTo and ScaleTo

local CollectionService = game:GetService("CollectionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Find all the models with the tag we want to replace
local items = CollectionService:GetTagged("Tree")
local newModel = ReplicatedStorage.FancyTreeReplacementModel
for _, item in items do
-- Make the new item and scale / position it where the old one was
local newItem = newModel:Clone()
newItem:ScaleTo(item:GetScale())
newItem:PivotTo(item:GetPivot())
-- Add the same tag to the replacement
CollectionService:AddTag(newItem, "Tree")
-- Delete the old item and parent the new one
newItem.Parent = item.Parent
item:Destroy()
end

MoveTo

void

Moves the PrimaryPart to the given position. If a primary part has not been specified, the root part of the model will be used, but the root part is not deterministic and it is recommended that you always set a primary part when using MoveTo().

If there are any obstructions where the model is to be moved, such as Terrain or other BaseParts, the model will be moved vertically upward until there is nothing in the way. If this behavior is not desired, PVInstance:PivotTo() should be used instead.

Note that rotation is not preserved when moving a model with MoveTo(). It is recommended to use either TranslateBy() or PVInstance:PivotTo() if the current rotation of the model needs to be preserved.

Parameters

position: Vector3

The Vector3 the Model is moved to.


Returns

void

Code Samples

Model MoveTo

local START_POSITION = Vector3.new(-20, 10, 0)
local END_POSITION = Vector3.new(0, 10, 0)
local model = Instance.new("Model")
model.Parent = workspace
local part1 = Instance.new("Part")
part1.Size = Vector3.new(4, 4, 4)
part1.Position = START_POSITION
part1.Anchored = true
part1.BrickColor = BrickColor.new("Bright yellow")
part1.Parent = model
local part2 = Instance.new("Part")
part2.Size = Vector3.new(2, 2, 2)
part2.Position = START_POSITION + Vector3.new(0, 3, 0)
part2.Anchored = true
part2.BrickColor = BrickColor.new("Bright blue")
part2.Parent = model
model.PrimaryPart = part1
model.Parent = workspace
local obstruction = Instance.new("Part")
obstruction.Name = "Obstruction"
obstruction.Size = Vector3.new(10, 10, 10)
obstruction.Position = Vector3.new(0, 10, 0)
obstruction.Anchored = true
obstruction.BrickColor = BrickColor.new("Bright green")
obstruction.Parent = workspace
task.wait(3)
model:MoveTo(END_POSITION)

RemovePersistentPlayer

void

Parameters

playerInstance: Player
Default Value: "nil"

Returns

void

ScaleTo

void

Models contain a persistent canonical scale factor, which starts out at 1 for newly created models. This function scales the model, around the pivot location, relative to how it would look at a scale factor of 1. To accomplish this it does two things:

  • Sets the current scale factor of the model to the specified value
  • Resizes and repositions all descendant Instances accordingly

The scaling of locations is done around the pivot location.

All "geometric" properties of descendant Instances will be scaled. That obviously includes the sizes of parts, but here are some other examples of properties which are scaled:

  • The length of joints like WeldConstraints, and Class.Rope|Ropes
  • Physical velocities and forces like Hinge.MaxServoTorque
  • Visual properties like sizes of particle emitters
  • Other length properties like Sound.RollOffMinDistance

Parameters

newScaleFactor: number

Returns

void

TranslateBy

void

Shifts a Model by the given Vector3 offset, preserving the model's orientation. If another BasePart or Terrain already exists at the new position then the Model will overlap said object.

The translation is applied in world space rather than object space, meaning even if the model's parts are orientated differently it will still move along the standard axis.

Parameters

delta: Vector3

The Vector3 to translate the Model by.


Returns

void

Code Samples

Model TranslateBy

local START_POSITION = Vector3.new(-20, 10, 0)
local END_POSITION = Vector3.new(0, 10, 0)
local model = Instance.new("Model")
local part1 = Instance.new("Part")
part1.Size = Vector3.new(4, 4, 4)
part1.CFrame = CFrame.new(START_POSITION) * CFrame.Angles(0, math.rad(45), 0)
part1.Anchored = true
part1.BrickColor = BrickColor.new("Bright yellow")
part1.Parent = model
local part2 = Instance.new("Part")
part2.Size = Vector3.new(2, 2, 2)
part2.CFrame = part1.CFrame * CFrame.new(0, 3, 0)
part2.Anchored = true
part2.BrickColor = BrickColor.new("Bright blue")
part2.Parent = model
model.PrimaryPart = part1
model.Parent = workspace
local obstruction = Instance.new("Part")
obstruction.Name = "Obstruction"
obstruction.Size = Vector3.new(10, 10, 10)
obstruction.Position = Vector3.new(0, 10, 0)
obstruction.Transparency = 0.5
obstruction.Anchored = true
obstruction.BrickColor = BrickColor.new("Bright green")
obstruction.Parent = workspace
task.wait(3)
-- use TranslateBy to shift the model into the obstruction
model:TranslateBy(END_POSITION - START_POSITION)

Events