Rate my DOF code

Okay so I’ve been working on a quick dynamic DOF script that works well with my custom third person camera but I believe that the process of updating the DOF is very expensive so I want some ideas on if my code can be optimized any further or should I just keep it as is.

-- Custom DOF script made by Exilon

-- !You are free to use this script however you want!
---------------------------------------------------------

-- Get services and variables
local DOF = game:GetService("Lighting").DepthOfField
local camera = game.Workspace.CurrentCamera
local tween = game:GetService("TweenService")
local ignoreList = {}
local transparencyThreshold = 0.75 -- Feel free to edit this if you have translucent parts in your game

-- Remove the ability for the DOF to focus on translucent objects above a threshold
for i, v in game.Workspace:GetDescendants() do
	if v:IsA("Part") or v:IsA ("MeshPart") then
		if v.Transparency < transparencyThreshold then
			table.insert(ignoreList, v)
			print("Added: " .. v.Name)
		end
	end
end

-- Create the raycast parameters and apply the ignore list
local rayCastParams = RaycastParams.new()
rayCastParams.FilterDescendantsInstances = ignoreList
rayCastParams.FilterType = Enum.RaycastFilterType.Blacklist

-- Create the "eye adjustment" tween effect settings
tweenInfo = TweenInfo.new(
	0.5,
	Enum.EasingStyle.Quart,
	Enum.EasingDirection.In
)

-- Pause the script until dependencies are met
repeat wait() until camera and DOF and workspace

-- Refresh the DOF focus distance based on view
while wait(0.5) do
	-- Fire the raycast
	local result = workspace:Raycast(camera.CFrame.Position, camera.CFrame.LookVector * 1000, rayCastParams)
	if result then
		-- Adjust the "eyes" of the player
		tween:Create(DOF, tweenInfo, {FocusDistance = ((result.Position - camera.CFrame.Position).Magnitude)}):Play()
	end
end

Feel free to use and share this script as you like. I’d love a credit though.

EDIT: The transparency threshold needs work so here is what I have that works!

-- Custom DOF script made by Exilon

-- !You are free to use this script however you want!
---------------------------------------------------------

-- Get services and variables
local DOF = game:GetService("Lighting").DepthOfField
local camera = game.Workspace.CurrentCamera
local tween = game:GetService("TweenService")
local ignoreList = {}
local transparencyThreshold = 1 -- Broken. Ill fix

-- Remove the ability for the DOF to focus on translucent objects above a threshold
for i, v in game.Workspace:GetDescendants() do
	if v:IsA("Part") or v:IsA ("MeshPart") then
		if v.Transparency == transparencyThreshold then
			table.insert(ignoreList, v)
			print("Added: " .. v.Name)
		end
	end
end

-- Create the raycast parameters and apply the ignore list
local rayCastParams = RaycastParams.new()
rayCastParams.FilterDescendantsInstances = ignoreList
rayCastParams.FilterType = Enum.RaycastFilterType.Blacklist

-- Create the "eye adjustment" tween effect settings
tweenInfo = TweenInfo.new(
	0.5,
	Enum.EasingStyle.Quart,
	Enum.EasingDirection.In
)

-- Pause the script until dependencies are met
repeat wait() until camera and DOF and workspace

-- Refresh the DOF focus distance based on view
while wait(0.5) do
	-- Fire the raycast
	local result = workspace:Raycast(camera.CFrame.Position, camera.CFrame.LookVector * 1000, rayCastParams)
	if result then
		-- Adjust the "eyes" of the player
		tween:Create(DOF, tweenInfo, {FocusDistance = ((result.Position - camera.CFrame.Position).Magnitude)}):Play()
	end
end
2 Likes

Here’s what I’d change

  1. Don’t loop through every descendant of the game to detect if it’s transparent. Instead, use CollectionService to tag each part you don’t want to raycast OR disable CanQuery if they aren’t supposed to be collideable.

  2. Don’t do polling or busy waiting. That repeat until line is extremely inefficient and moreover is just checking static for static variables. CurrentCamera and workspace will always be loaded, and you should use WaitForChild with a timeout for DepthOfField.

I recommend reading up on this too!

  1. Use Tween.Completed:Wait() instead of waiting 0.5 when you have a Result since it’ll guarantee the Tween finished before you loop again.
local DOF = game:GetService("Lighting"):WaitForChild("DepthOfField")
local Camera = workspace.CurrentCamera
local TweenService = game:GetService("TweenService")
local CollectService = game:GetService("CollectionService")

local RayParams = RaycastParams.new()
RayParams.FilterDescendantsInstances = CollectService:GetTagged("Transparent")
RayParams.FilterType = Enum.RaycastFilterType.Blacklist

TWEEN_INFO = TweenInfo.new(
	0.5,
	Enum.EasingStyle.Quart,
	Enum.EasingDirection.In
)

while game:IsLoaded() do
	local Result = workspace:Raycast(Camera.CFrame.Position, Camera.CFrame.LookVector*1000, RayParams)

	if Result then
		local FocusDistance = (Result.Position - Camera.CFrame.Position).Magnitude
		local DOF_Tween = TweenService:Create(DOF, TWEEN_INFO, {["FocusDistance"] = FocusDistance})
		DOF_Tween:Play()
		
		DOF_Tween.Completed:Wait()
	else
		task.wait(0.5)
	end
end
3 Likes

Awesome! I’ll try this. Just a question, what is task.wait() for? Is it just another way of using wait()?

2 Likes

Hey just a little question, What difference does It make when I tag something or add every item to a list. The only one I can think of is that I only have to tag them once and then I can grab a list of all of them at once but my script just does that anyways. Is it there in case I want to do anything else with the transparent parts?

Edit: I’m trying this anyways so I wrote this little script to run when the game has loaded to add the “Transparent” tag to transparent items:

local collections = game:GetService("CollectionService")

game.Loaded:Connect(function()
	for i, v in game:GetDescendants() do
		if v.Transparency == 1 then
			collections:AddTag(v, "Transparent")
		end
	end
end)

But the DOF script starts before any tags have been added and it only reads the list once at the start of the script so my given solution is to have it be read on every iteration but then I worry about the performance. Any ideas?

1 Like

CollectionService is supposed to be used to tag instances before runtime, but unforunately lacks a built-in visual editor (Dunno why).

Here’s an awesome plugin someone made that may help!

2 Likes

Awesome. Just wondering, is there a period in the games cycle where all the workspace assets are loaded but the scripts aren’t run yet? Here is a quote explaining my problem:

But the DOF script starts before any tags have been added and it only reads the list once at the start of the script so my given solution is to have it be read on every iteration but then I worry about the performance. Any ideas?

I would much just add tags via script than have to do it manually because im lazy and automation and all that but thanks for the plugin anyways.