[Need help] Detecting if player is in the Light or in shadow

Hi Dev Forum.
My game idea has to take place with transforming into a “beast/Monster” once you’re in the shadow.

I just don’t have any idea how to do such, and I assume RayCasting wont help (Not really advanced in Raycasting).

I looked at other Dev forums and they don’t help with what I want to achieve.

If you can help in anyway, id be very grateful. Thank you!

1 Like

Maybe you could put an invisible part in the dark or in the light that triggers a .Touched event to turn them into a monster.

I can try that, ill keep you updated!

You should try this resource:

1 Like

Paste this script into StarterPlayerScripts. It will change the color of the player’s head to red anytime they are in shadow. It does this by casting a ray from the player’s head to the sun. If it hits anything in between, then the player is in a shadow, and it does the transformation. Replace it with your monsterizing code.

local Players = game:GetService("Players")

-- Code snipped from https://create.roblox.com/docs/reference/engine/classes/Lighting#GeographicLatitude
local Lighting = game:GetService("Lighting")

local UNIT_Z = Vector3.new(0, 0, 1)
local EARTH_TILT = math.rad(23.5) -- The Earth's tilt in radians.
local HALF_SOLAR_YEAR = 182.6282 -- Half the length of an average solar year

local function getSunDirection()
	local gameTime = Lighting:GetMinutesAfterMidnight()
	local geoLatitude = Lighting.GeographicLatitude

	local dayTime = gameTime / 1440
	local sourceAngle = 2 * math.pi * dayTime

	local sunPosition = Vector3.new(math.sin(sourceAngle), -math.cos(sourceAngle), 0)
	local latRad = math.rad(geoLatitude)

	local sunOffset = -EARTH_TILT * math.cos(math.pi * (dayTime - HALF_SOLAR_YEAR) / HALF_SOLAR_YEAR) - latRad
	local sunRotation = CFrame.fromAxisAngle(UNIT_Z:Cross(sunPosition), sunOffset)

	local sunDirection = sunRotation * sunPosition
	return sunDirection
end

-- Loop to detect when in shadows
local oldHeadColor = nil
while true do
	local character = Players.LocalPlayer.Character
	if character and character:FindFirstChild("Head") then
		local hit = workspace:Raycast(character.Head.CFrame.Position, getSunDirection() * 1000)
		if hit then
			if not oldHeadColor then
				oldHeadColor = character.Head.Color
				character.Head.Color = Color3.new(1, 0, 0)
			end
		elseif not hit and oldHeadColor then 
			character.Head.Color = oldHeadColor
			oldHeadColor = nil
		end
	end
	task.wait(0.25)
end
9 Likes

Thank you ill look into it and let you know the results with the others in the bit.

Thank you for the reference, aslong with the others. Ill let you know how it is and mark the best for me the solution.

So the problem im having with this is that, no matter the sun, it still makes the head red for somereason.

I have no clue how to use the module, I just looked over the forum and it didn’t explain anything. Thank you though

Try pasting the script into a new project. Here’s a video of it working:

1 Like

Alright ill let you know how it comes out!

Is it possible to reverse this? So when the player is in light, their head goes red and in shadow, they are safe?

I would reverse it if I could but that script is too confusing for me lol

Here’s the reverse…

local Players = game:GetService("Players")

-- Code snipped from https://create.roblox.com/docs/reference/engine/classes/Lighting#GeographicLatitude
local Lighting = game:GetService("Lighting")

local UNIT_Z = Vector3.new(0, 0, 1)
local EARTH_TILT = math.rad(23.5) -- The Earth's tilt in radians.
local HALF_SOLAR_YEAR = 182.6282 -- Half the length of an average solar year

local function getSunDirection()
	local gameTime = Lighting:GetMinutesAfterMidnight()
	local geoLatitude = Lighting.GeographicLatitude

	local dayTime = gameTime / 1440
	local sourceAngle = 2 * math.pi * dayTime

	local sunPosition = Vector3.new(math.sin(sourceAngle), -math.cos(sourceAngle), 0)
	local latRad = math.rad(geoLatitude)

	local sunOffset = -EARTH_TILT * math.cos(math.pi * (dayTime - HALF_SOLAR_YEAR) / HALF_SOLAR_YEAR) - latRad
	local sunRotation = CFrame.fromAxisAngle(UNIT_Z:Cross(sunPosition), sunOffset)

	local sunDirection = sunRotation * sunPosition
	return sunDirection
end

local origHeadColor = nil

-- Loop to detect when in shadows
while true do
	local character = Players.LocalPlayer.Character
	if character and character:FindFirstChild("Head") then
		if not origHeadColor then
			origHeadColor = character.Head.Color
		end
		
		local hit = workspace:Raycast(character.Head.CFrame.Position, getSunDirection() * 1000)
		if hit then
			-- In shadow
			character.Head.Color = origHeadColor
		else
			-- In sunlight
			character.Head.Color = Color3.new(1, 0, 0)
		end
	end
	task.wait(0.25)
end
1 Like

Hey, I implemented this script into my game. Basically, when the player is in the sun and they have the vampire class they will slowly take damage. But during night time in certain areas the player will randomly die. Any ideas on why this is happening?

I ran the clock on a 24-hour loop and didn’t see any strange behavior with the script. At night, the sun will drift below the player’s feet, but near dusk and dawn the angle will be very shallow, like out toward the horizon. So, if the player is up on a bridge or ledge at night, and it’s just turned night, or is about to turn morning, it’s possible the ray that is cast (only 1,000 units long) will not hit anything, or it will fly off the edge of the map into the void and not hit anything. In either case, it will be assumed that the player is not in shadow because nothing is between it and the sun, therefore the player would be taking damage.

Rather than mess with these edge cases, the simple fix is to disable the check at night and re-enable in the morning.

1 Like