Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Detail objects for Roblox. Distant objects get parented to nil.
-- A lightweight module for hiding "detail" objects that get too far away from the camera
--
-- Usage:
-- Tag objects with Detail_Small and Detail_Big
-- If they get too far from the camera, they get parented to nil
-- (Tries to account for the objects getting deleted or parented around by other processes, results may vary!)
--
-- MCR, 2021
local CollectionService = game:GetService("CollectionService")
local module = {}
--Settings
module.debugging = false
--Big objects disapear after x units
module.bigObjectDistance = 800
--Small objects disapear after x units
module.smallObjectDistance = 400
--Check when the camera has moved ~x units
module.stepSize = 10
--Internals
module.records = {}
module.lastGrid = nil
module.setup = false
function UpdateVis(record, cameraPos)
if (record.parent == nil) then
return
end
--see if we're in the scene
local dist = (record.mainPart.Position - cameraPos).magnitude
if (dist > record.distance) then
--Can see it
if(record.visible == true) then
if (module.debugging == true) then
print("Detail hiding", record.instance.Name)
end
record.visible = false
record.instance.Parent = nil
end
else
--can't see it
if (record.visible == false) then
if (module.debugging == true) then
print("Detail showing", record.instance.Name)
end
record.visible = true
record.instance.Parent = record.parent
end
end
end
local function OnDetailAdded(instance, dist)
-- This triggers on every parenting
if (module.records[instance] ~= nil) then
return
end
--We only care about models with primaryparts, and baseparts
if (instance:IsA("Model") == false and instance:IsA("BasePart") == false) then
warn("Only models and baseparts allowed: ", instance.Name )
return
end
--
if (instance:IsA("Model") and instance.PrimaryPart == nil) then
warn("Model ", instance.Name , " has no primarypart")
return
end
--Good to go
if (module.debugging == true) then
print("Detail Adding ",instance.Name)
end
--Time to set up a record to watch the instance
local record = {}
record.instance = instance
record.visible = true
record.distance = dist
--Figure out which 'part' to track the position of
--If its a model, we track its primarypart
if (instance:IsA("Model") and instance.PrimaryPart ~= nil) then
record.mainPart = instance.PrimaryPart
else
--otherwise track the instance directly
record.mainPart = instance
end
--Do some work to figure out the intended parent when we're in the scene
--easy check - Do we have a parent right now?
if (game.Workspace:IsAncestorOf(record.instance) == true) then
record.parent = instance.Parent
end
--Slightly less easy check - watch for ancestory changed and check again (eg: we got parented to game.lighting)
instance.AncestryChanged:Connect(function(child, parent)
--Do we have a parent?
if (game.Workspace:IsAncestorOf(record.instance) == true) then
record.parent = instance.Parent
end
end)
--Add a dummy connection to a property of instance, so we can tell when the mainPart gets Destroyed()
record.instanceSignal = record.mainPart:GetPropertyChangedSignal("Name"):Connect(function() end)
module.records[instance] = record
end
local function OnDetailAddedBig(instance)
OnDetailAdded(instance, module.bigObjectDistance)
end
local function OnDetailAddedSmall(instance)
OnDetailAdded(instance, module.smallObjectDistance)
end
function Setup()
if (module.setup == true) then
return
end
module.setup = true
--Hook up the tags
for key,instance in pairs(CollectionService:GetTagged("Detail_Big")) do
OnDetailAddedBig(instance)
end
CollectionService:GetInstanceAddedSignal("Detail_Big"):Connect(OnDetailAddedBig)
for key,instance in pairs(CollectionService:GetTagged("Detail_Small")) do
OnDetailAddedSmall(instance)
end
CollectionService:GetInstanceAddedSignal("Detail_Small"):Connect(OnDetailAddedSmall)
end
game:GetService("RunService").Heartbeat:Connect(function()
Setup()
local pos = game.workspace.CurrentCamera.CFrame.Position
local step = module.stepSize
local grid = Vector3.new(math.floor(pos.x/step),math.floor(pos.y/step),math.floor(pos.z/step))
--Make sure the camera has moved into a new 4 unit grid
if (module.lastGrid == nil or module.lastGrid ~= grid) then
--Check the vis
for instance,record in pairs(module.records) do
--Did we get destroyed since last time?
if (record.instanceSignal == nil or record.instanceSignal.Connected == false) then
if (module.debugging == true) then
print("Detail destroyed", instance.Name)
end
module.records[instance] = nil
continue
end
--Update
UpdateVis(record, pos)
end
end
module.lastGrid = grid
end)
return module
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment