I’m trying to make an immersive game in which you battle in first person. Unfortunately, because your body turns invisible, it doesn’t have a shadow. Is there any way to counter this besides making it visible with Transparency or LocalTransparencyModifier?
I’m trying to avoid doing that (you don’t need to replicate it by the way you can just use the LocalTransparencyModifier property). Essentially I need to be able to cast shadows on parts that are not visible to the client.
The only way around this that I can think of is creating a duplicate of a player’s character behind the actual player far enough so that the player won’t see this clone. It will show the player’s shadow in first person, however you need to constantly position and rotate the clone’s body parts to the player’s body parts to avoid a static shadow effect.
local player = game.Players.LocalPlayer
local camera = workspace.CurrentCamera
local lighting = game:GetService("Lighting")
local rs = game:GetService("RunService")
local activeChar
local reg = {}
local function spawn(func,...)
local b = Instance.new("BindableEvent")
b.Event:connect(func)
b:Fire(...)
end
local projector = Instance.new("Model",c)
projector.Name = "ShadowProjector"
local function onChildAdded(child)
if child:IsA("BasePart") then
local clone = child:Clone()
clone.Name = clone.Name
clone.Anchored = true
for _,v in pairs(clone:GetChildren()) do
if v:IsA("JointInstance") then
v:Destroy()
end
end
if clone.Transparency < 1 then
clone.Transparency = 0
end
clone.Parent = projector
reg[child] = clone
child.ChildAdded:Connect(function (child2)
if child2:IsA("SpecialMesh") then
clone:ClearAllChildren()
child2:Clone().Parent = clone
end
end)
elseif child:IsA("Accoutrement") then
spawn(function ()
local handle = child:WaitForChild("Handle", 1)
if handle then
onChildAdded(handle)
end
end)
elseif child:IsA("CharacterMesh") then
local clone = child:clone()
clone.Parent = projector
reg[child] = clone
end
end
local function update()
-- Clean up garbage
for realObj,projectorObj in pairs(reg) do
if not realObj:IsDescendantOf(activeChar) then
projectorObj:Destroy()
reg[realObj] = nil
end
end
-- Update Shadow
local sunPos = lighting:GetSunDirection() * 10000
if sunPos.Y > 0 then -- If the sun is actually out...
local cf = camera.CFrame
local focus = camera.Focus
local focusPoint = focus.Position
local dist = (focus.Position - cf.Position).magnitude
if dist < 1 then -- If we are near first person...
local facing = sunPos.Unit:Dot(cf.LookVector)
if facing < 0 then -- If we are looking away from the sun...
local sunDist = (focusPoint - sunPos).magnitude
local rootPart = activeChar:FindFirstChild("HumanoidRootPart")
if rootPart and reg[rootPart] then
local projectorRoot = reg[rootPart]
local objCF = rootPart.CFrame
local objPos = objCF.Position
local objRot = objCF - objPos
-- Project the HumanoidRootPart towards the sun relative to the camera's position.
local realDist = (focusPoint - objPos).Magnitude
local lerpScale = (realDist / sunDist) * 9
local projectPos = focusPoint:Lerp(sunPos, lerpScale)
local camOffset = objPos - focusPoint
projectorRoot.CFrame = CFrame.new(projectPos + camOffset) * objRot
-- CFrame all shadow limbs relative to the shadow HumanoidRootPart with
-- an object space relative to their corresponding real offsets.
for realObj, projectorObj in pairs(reg) do
if realObj ~= rootPart and realObj:IsA("BasePart") then
local offset = objCF:ToObjectSpace(realObj.CFrame)
projectorObj.CFrame = projectorRoot.CFrame * offset
projectorObj:BreakJoints()
end
end
-- All conditions were met, render the shadow.
projector.Parent = camera
return
end
end
end
end
-- A condition wasn't met, stop rendering the shadow.
projector.Parent = nil
end
local function onCharacterAdded(char)
-- Reset everything.
if next(reg) ~= nil then
projector:ClearAllChildren()
reg = {}
end
local ph = Instance.new("Humanoid")
ph.Name = "Projector"
ph.Parent = projector
ph:ChangeState("Physics")
activeChar = char
for _,v in pairs(char:GetChildren()) do
spawn(onChildAdded, v)
end
char.ChildAdded:connect(onChildAdded)
end
if player.Character then
onCharacterAdded(player.Character)
end
player.CharacterAdded:Connect(onCharacterAdded)
rs:BindToRenderStep("FirstPersonShadows", 201, update)
lighting.Changed:Connect(update)
put this into starterplayerscript with the name FirstPersonShadows