Issue with regional fog

I’m trying to make a regional fog system for my game so that at least 3 different areas can have different fog ambiences. I tried making a part that has the script parented directly under the part so that when touched it will switch to the desired fog, once the player has stepped outside of that part it will return to the standard fog.

I am recieving this error:

Workspace.ImportantAssetStorage.FogPartOutside.Script:31: attempt to index nil with 'Name'

I am using this script:

local Lighting = game:GetService("Lighting")

local detectionPart = script.Parent
local localPlayer = game.Players.LocalPlayer


local function enableFog()
	Lighting.FogColor = Color3.fromRGB(0, 0, 15)
	Lighting.FogEnd = 750
	Lighting.Brightness = 0
	Lighting.Ambient = Color3.fromRGB(0, 0, 0)
	Lighting.OutdoorAmbient = Color3.fromRGB(0, 0, 0)
	game.Lighting:SetMinutesAfterMidnight(300)
end

local function disableFog()
	Lighting.FogColor = Color3.fromRGB(40, 70, 100)
	Lighting.Brightness = 0
	Lighting.FogEnd = 1000
	Lighting.FogStart = 0
end

detectionPart.Touched:Connect(function()
end)

local touching = false

while true do
	local foundPlayer = false
	for i,v in ipairs(detectionPart:GetTouchingParts()) do
		if v.Parent.Name == localPlayer.Name then
			foundPlayer = true
		end
	end

	if touching and not foundPlayer then
		disableFog()
		touching = false
	end

	if not touching and foundPlayer then
		enableFog()
		touching = true
	end

	wait(.5)
end

What am I doing wrong to cause this error?

2 Likes

The error is because localPlayer is nil. Is this in a server script?

1 Like

No, the script is parented to the part in question.

Can you take a screenshot of the explorer menu?

1 Like

image

1 Like

Ah yeah, that’s a server script, where there is no such “LocalPlayer”…if I have time I will come up with a new script soon but otherwise your first step should be using a LocalScript and putting it in either StarterGui or StarterPlayerScripts.

2 Likes

You should handle visual aspects locally, and it makes sense if you want a player to see fog when in an area, that only they see the fog. So, you should use a LocalScript and put it in either StarterPlayerScripts or PlayerGui.

Rather than using a while loop, you should use other methods of collision checking. Here I see using Part.Touched and Part.TouchEnded as being arguably one of the most effective methods.

Because this is a LocalScript somewhere in the Player, we need to set a variable to the part we want to collision detect.

local FogPartOutside = workspace.ImportantAssetStorage.FogPartOutside


But if you have multiple of these it would be worth setting up a system where it loops through all of them, so I’ll do that too. This will loop through the ImportantAssetStorage folder and insert an element for each child named “FogPartOutside” into the FogParts table.

What I will do then is loop through the table and connect .Touched and .TouchEnded for each element, and store the connetions so we can disconnect the Touched initially, preventing stacking.

First we will set it up like this:

local Player = game:GetService("Players").LocalPlayer -- The player whose client is running the game 
local Lighting = game:GetService("Lighting")

local FogParts = {} -- Fog parts which will be set after looping through ImportantAssetStorage
local Connections = {} -- We will store the .Touched connections here so we can disconnect them afterwards, making it only activate when initially touched 

for i,v in pairs(workspace:WaitForChild("ImportantAssetStorage"):GetChildren()) do 
    if v.Name == "FogPartOutside" then 
       table.insert(FogParts, v)
    end
end

From there we need to set up the functions we will be using, we can still use the old ones you were using before, but we need to add a bit of code to the EnableFog function to disconnect the .Touched connection of the part that called it.

local function EnableFog(Zone)
	if Connections[Zone] then -- Ensures that the touch only happens initially  
		Connections[Zone]:Disconnect()
		Connections[Zone] = nil 
	end
	
	Lighting.FogColor = Color3.fromRGB(0, 0, 15)
	Lighting.FogEnd = 750
	Lighting.Brightness = 0
	Lighting.Ambient = Color3.fromRGB(0, 0, 0)
	Lighting.OutdoorAmbient = Color3.fromRGB(0, 0, 0)
	Lighting:SetMinutesAfterMidnight(300)
end

local function DisableFog()
	Lighting.FogColor = Color3.fromRGB(40, 70, 100)
	Lighting.FogEnd = 1000
	Lighting.FogStart = 0
	Lighting.Brightness = 0
	Lighting.Ambient = Color3.fromRGB(138, 138, 138) -- Added this because it wasn't reset before, you can change the default 
	Lighting.OutdoorAmbient = Color3.fromRGB(128, 128, 128) -- Added this because it wasn't reset before, you can change the default 
--	Lighting:SetMinutesAfterMidnight(?) -- You might want to reset this as well
end

Now that we have this set up we can loop through the fog detection parts and connect their .Touched and .TouchEnded events.

for i,v in pairs(FogParts) do 
	v.CanTouch = true -- Make sure that the part can trigger .Touched and .TouchEnded events 
	
    -- Putting this in a function so we don't have to reuse code 
	local function Connect()
		Connections[v] = v.Touched:Connect(function(Hit) -- When the player is in the part / stored in a table with the index being the part, so that it can be referenced in the EnableFog function 
			if Hit:IsDescendantOf(Player.Character) then -- Makes sure the LocalPlayer touched the part and not any other player 
				EnableFog(v) -- Enables the fog, passing an argument of the part, which is used in the function parameter to disable it's connection from the Connections table and prevent stacking 
			end
		end)
	end
	
	Connect() -- Call the function once to create the initial .Touched event 
	
	v.TouchEnded:Connect(function(Hit) -- When the player is no longer inside the part 
		if Hit:IsDescendantOf(Player.Character) then -- Makes sure the LocalPlayer stopped touching the part and not any player 
			DisableFog() -- Disables the fog 
			Connect() -- Calls the function to create a new .Touched event, as the last one was disconnected after the EnableFog function was called
		end
	end)
end


So our entire script looks like this

local Player = game:GetService("Players").LocalPlayer
local Lighting = game:GetService("Lighting")

local FogParts = {} 
local Connections = {} 

for i,v in pairs(workspace.ImportantAssetStorage:GetChildren()) do 
	if v.Name == "FogPartOutside" then 
		table.insert(FogParts, v)
	end
end

local function EnableFog(Zone)
	if Connections[Zone] then -- Ensures that the touch only happens initially  
		Connections[Zone]:Disconnect()
		Connections[Zone] = nil 
	end

	Lighting.FogColor = Color3.fromRGB(0, 0, 15)
	Lighting.FogEnd = 750
	Lighting.Brightness = 0
	Lighting.Ambient = Color3.fromRGB(0, 0, 0)
	Lighting.OutdoorAmbient = Color3.fromRGB(0, 0, 0)
	Lighting:SetMinutesAfterMidnight(300)
end

local function DisableFog()
	Lighting.FogColor = Color3.fromRGB(40, 70, 100)
	Lighting.FogEnd = 1000
	Lighting.FogStart = 0
	Lighting.Brightness = 0
	Lighting.Ambient = Color3.fromRGB(138, 138, 138) -- Added this because it wasn't reset before, you can change the default 
	Lighting.OutdoorAmbient = Color3.fromRGB(128, 128, 128) -- Added this because it wasn't reset before, you can change the default 
	--	Lighting:SetMinutesAfterMidnight(?) -- You might want to reset this as well
end

for i,v in pairs(FogParts) do 
	v.CanTouch = true -- Make sure that the part can trigger .Touched and .TouchEnded events 

	local function Connect() -- Putting this in a function so we don't have to reuse code 
		Connections[v] = v.Touched:Connect(function(Hit) -- When the player is in the part / stored in a table with the index being the part, so that it can be referenced in the EnableFog function
			if Hit:IsDescendantOf(Player.Character) then  -- Makes sure the LocalPlayer touched the part and not any other player 
				EnableFog(v) -- Enables the fog, passing an argument of the part, which is used in the function parameter to disable it's connection from the Connections table and prevent stacking 
			end
		end)
	end

	Connect() -- Call the function once to create the initial .Touched event 

	v.TouchEnded:Connect(function(Hit) -- When the player is no longer inside the part 
		if Hit:IsDescendantOf(Player.Character) then  -- Makes sure the LocalPlayer stopped touching the part and not any player 
			DisableFog() -- Disables the fog 
			Connect() -- Call the function to create a new .Touched event, as the last one was disconnected after the EnableFog function was called
		end
	end)
end

Voila, regional fog! (Note the lighting in the video is different initially because I didn’t set the properties the same as your DisableFog function. This shouldn’t be a problem)

All with a single script, and we can easily add more fog parts in the future.
image

It is imperative that this is a LocalScript and put somewhere in the Player, like in StarterGui

If there are any issues with this please let me know. Hopefully I helped :slight_smile:

2 Likes

Thanks for all the help! This is very good but roblox is giving me the following error when running the script.

Players.thekingorespawn.PlayerScripts.RegionalFog:43: Expected '(' when parsing function, got 'Connections'

I recorded a short video of the script in action in-game to try and help solve the issue.

Ah, I can see that on line 42 it says local function Connect instead of local function Connect(), missing the () after “Connect”

It still isn’t working for whatever reason, although now there is no error listed in the output. I have recorded another short video now with the fogpart translucent for testing purposes.

I have relocated the localscript to be parented under StarterGui now instead of StarterPlayerScripts, no change.

image
Oh, I can see the part is very small. I designed the script to work more like zones rather than triggers. It’s possible to do triggers as well but the script would need to be modified. I would assume this is why it isn’t working. Basically the fog will activate while the player is inside the parts and deactivate when they exit them.

The part is not very small, the part is very large, encompassing a good portion of the map.


Oh then that’s weird. Are you sure the parts are named “FogPartOutside” and are inside a folder in workspace called “ImportantAssetStorage”?
image

image
Think so.

Ohh I see what the problem is. Going back to the video where you showed the script,

You can copy and paste the script I made if you want, as you must’ve accidently put it outside the loop. Basically it should be

for i,v in pairs(FogParts) do 
	v.CanTouch = true -- Make sure that the part can trigger .Touched and .TouchEnded events 

	local function Connect() -- Putting this in a function so we don't have to reuse code 
		Connections[v] = v.Touched:Connect(function(Hit) -- When the player is in the part / stored in a table with the index being the part, so that it can be referenced in the EnableFog function
			if Hit:IsDescendantOf(Player.Character) then  -- Makes sure the LocalPlayer touched the part and not any other player 
				EnableFog(v) -- Enables the fog, passing an argument of the part, which is used in the function parameter to disable it's connection from the Connections table and prevent stacking 
			end
		end)
	end

	Connect() -- Call the function once to create the initial .Touched event 

	v.TouchEnded:Connect(function(Hit) -- When the player is no longer inside the part 
		if Hit:IsDescendantOf(Player.Character) then  -- Makes sure the LocalPlayer stopped touching the part and not any player 
			DisableFog() -- Disables the fog 
			Connect() -- Call the function to create a new .Touched event, as the last one was disconnected after the EnableFog function was called
		end
	end)
end

But instead you ended the for loop and put Connect and the TouchEnded connection outside of it.

I did that when porting the script from StarterPlayerScripts to StarterGui. I did notice these red underlines in the script, perhaps they have something to do with it? image

image
There is a typo there “ends” when it should be “end”

This helped but the issue still persists, Here’s a full run-through of the code I’ve put together to see if you can spot something

Hmm, the only thing that I can think of is that the named parts aren’t loaded fast enough since your map is very large. We can wait for them by adding this to the script near the beginning:

workspace:WaitForChild("ImportantAssetStorage"):WaitForChild("FogPartOutside")

Basically anything that has to be replicated from the server like parts should be waited for. This wasn’t a problem in the video I showed because it was an empty baseplate.