Model
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:
- When a Humanoid and a Part named Head are parented under a model, a name/health GUI will appear over the model; see Character Name/Health Display for details.
- If a part's position on the Y axis hits the Workspace.FallenPartsDestroyHeight value, and it was the last object inside of a Model, the model will be destroyed as well.
Code Samples
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
Sets the level of detail on Model instances when streaming is enabled.
Controls the model streaming behavior on Model instances when streaming is enabled.
Points to the primary part of the Model.
Determines where the pivot of a Model which does not have a set Model.PrimaryPart is located.
Methods
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 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.
Moves the PrimaryPart to the given position. If a primary part has not been specified, the root part of the model will be used.
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.
Events
Properties
LevelOfDetail
Sets the level of detail on Model instances. When Workspace.StreamingEnabled is enabled, parts outside of the streaming radius cannot be seen in live games. Using Model.LevelOfDetail, you can make these parts visible.
When set to StreamingMesh, representative meshes renders outside the streaming radius. Representative meshes, also called imposter meshes, are colored, coarse meshes that wrap around all child parts of a model.
Setting it to Disabled or Automatic (default) deletes what has been generated.
For Models with this property set to StreamingMesh, the Model Mesh, a coarse mesh representation of the Model including standard part types, mesh parts and CSG parts, is precomputed during the Studio session, and serialized and uploaded as part of the place file.
Modeling Tips:
Automatic update in Studio editing - Changes to the model or any of its children, e.g. adding/deleting/repositioning child parts, resetting colors, will automatically update the representative mesh.
Memory sharing - If you have hundreds of identical models in different sizes and orientations, it is more efficient to duplicate them over transforming to avoid individual editing unless necessary.
Outside of streaming radius - Streaming is distance based. We consider models crossing the boundary of the streaming sphere as outside. This requires the model size in StreamingMesh to be much smaller than the streaming radius.
Hierarchical models - If a model and its child or grandchild models are all set to StreamingMesh, only the ancestor, which is the top most model, will be rendered in game. For better memory performance, you may want to disable the LevelOfDetail property of child models before releasing your game.
Loose low-resolution wrapping - You can set a small workspace streaming radius value (e.g. 256 studs) to see representative meshes closer in Studio playsolo, but remember representative meshes are designed to be seen at a distance (1024 studs away). They may not capture model fidelity well to keep triangle count per model low.
Partial Streaming behavior - While a Model is not completely streamed in, the Model Mesh is rendered instead of individual parts of the model. Once all individual parts of a model are streamed in, individual parts render and the Model Mesh is ignored.
Physics Significance - Model Meshes have no physics significance. They act as non-existent with respect to raycasting, collision detection and physics simulation.
ModelStreamingMode
Controls how Model instances are streamed in and out when streaming is enabled. Behavior depends on selected enum. Has no effect when experience is not streaming enabled.
Should only be changed in Studio and in Scripts, never in LocalScripts (doing so can result in undefined behavior).
PrimaryPart
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
-- 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
WorldPivot
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 = modelworkspace.RedPart.Parent = modelmodel.Parent = workspaceprint(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
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
GetBoundingBox
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.
Usage
local model = workspace.Modellocal part = workspace.Partlocal orientation, size = model:GetBoundingBox()part.Size = sizepart.CFrame = orientation
Returns
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
Code Samples
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 it is called from a LocalScript, it only checks if this model is persistent for the Players.LocalPlayer.
Returns
MoveTo
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
Returns
Code Samples
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)
TranslateBy
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
Returns
Code Samples
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)