Skip to content

Instantly share code, notes, and snippets.

@howmanysmall
Created February 23, 2020 22:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save howmanysmall/d2ec7e781c9a02093d12374715f3b1ed to your computer and use it in GitHub Desktop.
Save howmanysmall/d2ec7e781c9a02093d12374715f3b1ed to your computer and use it in GitHub Desktop.
--[=[
--Copyright boatbomber 2019--
--Given under a BSD 3-Clause License--
Explanation of license: https://tldrlegal.com/license/bsd-3-clause-license-(revised)
--FEATURES--
Creates droplets of "water" on the screen, with a distortion effect, giving great immersion
for games that have rainy environments.
Droplets will not spawn if the player is indoors or under cover of some sort.
Droplets will not spawn if the camera is pointed down, as that is avoiding "getting rain in the eyes".
--WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING--
THIS PRODUCT RELIES ON GLASS MATERIAL, THUS SHARING ALL THE LIMITATIONS OF GLASS.
Non-opaque objects are currently not visible through glass.
This includes, but is not limited to, transparent parts, decals on transparent
parts, particles, and world-space gui objects.
Additionally, it only looks right for users with graphic settings of at least 8.
Hence, I've set it to only spawn droplets if the user has the graphics set high enough.
--WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING--
--]=]
local Workspace = game:GetService("Workspace")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
-- Constants
local Settings = {
-- Rate: How many droplets spawn per second
-- Rate = 64;
Rate = 8;
-- Size: How large the droplets roughly are (in studs)
Size = 0.1;
-- Tint: What color the droplets are tinted (leave as nil for a default realistic light blue)
Tint = Color3.fromRGB(226, 244, 255);
-- Fade: How long it takes for a droplet to fade
Fade = 1.5;
}
local UpVec = Vector3.new(0, 1, 0)
local DROPLET_SIZE = Vector3.new(1, 1, 1)
local EMPTY_CFRAME = CFrame.new()
----------------------------------------------------------------------------
--- Variables ------------------------------------------------------------
----------------------------------------------------------------------------
--Player related
local Camera = Workspace.CurrentCamera
local Player = Players.LocalPlayer
local GameSettings = UserSettings().GameSettings
local CanShow = GameSettings.SavedQualityLevel.Value >= 8
--Raycasting
local ignoreList = {Player.Character or Player.CharacterAdded:Wait()}
local IgnoreLength = 1
--Localizing
local ipairs = ipairs
--Settings localized
local Rate = Settings.Rate
local Size = Settings.Size
local Tint = Settings.Tint or Color3.fromRGB(226, 244, 255)
local Fade = Settings.Fade
--Fade tween
local fadeInfo = TweenInfo.new(Fade, Enum.EasingStyle.Sine, Enum.EasingDirection.In)
local strechInfo = TweenInfo.new(Fade / 1.05, Enum.EasingStyle.Quint, Enum.EasingDirection.In)
local fadeGoal = {Transparency = 1}
----------------------------------------------------------------------------
--- Prefab Basic Objects -------------------------------------------------
----------------------------------------------------------------------------
--Droplet holder
local ScreenBlock = Instance.new("Part")
ScreenBlock.Size = Vector3.new(2, 2, 2)
ScreenBlock.Transparency = 1
ScreenBlock.Anchored = true
ScreenBlock.CanCollide = false
ScreenBlock.Parent = Camera
local ScreenBlockCFrame = EMPTY_CFRAME
RunService:BindToRenderStep("ScreenRainUpdate", Enum.RenderPriority.Camera.Value + 1, function()
ScreenBlockCFrame = Camera.CFrame
ScreenBlock.CFrame = ScreenBlockCFrame
end)
----------------------------------------------------------------------------
--- Functions ------------------------------------------------------------
----------------------------------------------------------------------------
local function DestroyDroplet(d)
wait(Fade)
-- Proper GC
for _, Child in ipairs(d:GetChildren()) do
local Index = table.find(ignoreList, Child)
if Index then
ignoreList[Index] = ignoreList[IgnoreLength]
ignoreList[IgnoreLength] = nil
IgnoreLength = IgnoreLength - 1
end
end
local Index = table.find(ignoreList, d)
if Index then
ignoreList[Index] = ignoreList[IgnoreLength]
ignoreList[IgnoreLength] = nil
IgnoreLength = IgnoreLength - 1
end
d:Destroy()
end
--Returns whether the given position is under cover
local function UnderObject(pos, l)
l = l or 120
-- raycast
local hit, position = Workspace:FindPartOnRayWithIgnoreList(Ray.new(pos, UpVec * l), ignoreList)
if hit then
return hit.Transparency ~= 1 and true or UnderObject(position + UpVec, l - (pos - position).Magnitude)
else
return false
end
end
--DEBUG
--local function UnderObject(pos, l)
-- l = l or 120
-- -- raycast
-- local hit, position = Workspace:FindPartOnRayWithIgnoreList(Ray.new(pos, UpVec * l), ignoreList)
-- if hit then
-- return (hit.Transparency ~= 1 and not string.find(hit.Name, "Droplet") and not string.find(hit.Name, "Extrusion")) and true or UnderObject(position + UpVec, l - (pos - position).Magnitude)
-- else
-- return false
-- end
--end
--Creates a random droplet on screen
local function CreateDroplet()
--Setup
local stretch = 1 + math.random(15) / 10
local RunAmount = math.random(4)
local Tweens = table.create(RunAmount * 2 + 2)
local TweensLength = 0
local SizeOffset = math.random((Size / 3) * -10, (Size / 3) * 10) / 10
local Scale = Size + SizeOffset
local MeshScale = Vector3.new(Scale, Scale, Scale)
--Main droplet object
local DropletMain = Instance.new("Part")
DropletMain.Material = Enum.Material.Glass
DropletMain.CFrame = EMPTY_CFRAME
DropletMain.CanCollide = false
DropletMain.Transparency = 0.5
DropletMain.Name = "Droplet_Main"
DropletMain.Color = Tint
DropletMain.Size = DROPLET_SIZE
local Mesh = Instance.new("SpecialMesh")
Mesh.MeshType = Enum.MeshType.Sphere
Mesh.Scale = MeshScale
Mesh.Parent = DropletMain
--Create droplet extrusions
for i = 1, RunAmount do
local eSizeOffset = math.random(
(Size / 3) * -100,
(Size / 3) * 100
) / 100
local ExtrusionCFrame = CFrame.new(Vector3.new(
math.random(-(Size * 40), Size * 40) / 100,
math.random(-(Size * 40), Size * 40) / 100,
0
))
local ExtrusionScale = Size / 1.5 + eSizeOffset
local ExtrusionMeshScale = Vector3.new(ExtrusionScale, ExtrusionScale, ExtrusionScale)
local Extrusion = Instance.new("Part")
Extrusion.Material = Enum.Material.Glass
Extrusion.CFrame = ExtrusionCFrame
Extrusion.CanCollide = false
Extrusion.Transparency = 0.5
Extrusion.Name = "Extrusion_" .. i
Extrusion.Color = Tint
Extrusion.Size = DROPLET_SIZE
local ExtrusionMesh = Instance.new("SpecialMesh")
ExtrusionMesh.MeshType = Enum.MeshType.Sphere
ExtrusionMesh.Scale = ExtrusionMeshScale
ExtrusionMesh.Parent = Extrusion
Extrusion.Parent = DropletMain
local weld = Instance.new("Weld")
weld.C0 = ExtrusionCFrame:Inverse() * EMPTY_CFRAME
weld.Part0 = Extrusion
weld.Part1 = DropletMain
weld.Parent = Extrusion
IgnoreLength = IgnoreLength + 1
TweensLength = TweensLength + 1
ignoreList[IgnoreLength] = Extrusion
Tweens[TweensLength] = TweenService:Create(Extrusion, fadeInfo, fadeGoal)
TweensLength = TweensLength + 1
Tweens[TweensLength] = TweenService:Create(ExtrusionMesh, strechInfo, {
Scale = Vector3.new(ExtrusionScale, ExtrusionScale * stretch, ExtrusionScale);
Offset = Vector3.new(0, -(ExtrusionScale * stretch) / 2.05, 0);
})
end
IgnoreLength = IgnoreLength + 1
TweensLength = TweensLength + 1
ignoreList[IgnoreLength] = DropletMain
Tweens[TweensLength] = TweenService:Create(DropletMain, fadeInfo, fadeGoal)
TweensLength = TweensLength + 1
Tweens[TweensLength] = TweenService:Create(Mesh, strechInfo, {
Scale = Vector3.new(Scale, Scale * stretch, Scale);
Offset = Vector3.new(0, -(Scale * stretch) / 2.05, 0);
})
local NewCFrame = ScreenBlockCFrame:ToWorldSpace(CFrame.new(
math.random(-100, 100) / 100,
math.random(-100, 100) / 100,
-1
))
DropletMain.CFrame = NewCFrame
local weld = Instance.new("Weld")
weld.C0 = NewCFrame:Inverse() * ScreenBlockCFrame
weld.Part0 = DropletMain
weld.Part1 = ScreenBlock
weld.Parent = DropletMain
for _, t in ipairs(Tweens) do
t:Play()
end
local DestroyRoutine = coroutine.create(DestroyDroplet)
coroutine.resume(DestroyRoutine, DropletMain)
DropletMain.Parent = ScreenBlock
end
local function OnGraphicsChanged()
CanShow = GameSettings.SavedQualityLevel.Value >= 8
end
GameSettings:GetPropertyChangedSignal("SavedQualityLevel"):Connect(OnGraphicsChanged)
----------------------------------------------------------------------------
--- Functionality Loop ---------------------------------------------------
----------------------------------------------------------------------------
math.randomseed(tick())
while true do
wait(1 / Rate)
--Only render droplets if:
--Camera isn't looking down
--Render settings are high enough
--Camera isn't under an awning or roof or something
-- debug.profilebegin("NEW_IF_THEN")
if ScreenBlockCFrame.LookVector.Y > -0.4 and CanShow and not UnderObject(ScreenBlockCFrame.Position) then
-- debug.profilebegin("NEW_CREATE_DROPLET")
CreateDroplet()
-- debug.profileend()
end
-- debug.profileend()
end
--[=[
--Copyright boatbomber 2019--
--Given under a BSD 3-Clause License--
Explanation of license: https://tldrlegal.com/license/bsd-3-clause-license-(revised)
--FEATURES--
Creates droplets of "water" on the screen, with a distortion effect, giving great immersion
for games that have rainy environments.
Droplets will not spawn if the player is indoors or under cover of some sort.
Droplets will not spawn if the camera is pointed down, as that is avoiding "getting rain in the eyes".
--WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING--
THIS PRODUCT RELIES ON GLASS MATERIAL, THUS SHARING ALL THE LIMITATIONS OF GLASS.
Non-opaque objects are currently not visible through glass.
This includes, but is not limited to, transparent parts, decals on transparent
parts, particles, and world-space gui objects.
Additionally, it only looks right for users with graphic settings of at least 8.
Hence, I've set it to only spawn droplets if the user has the graphics set high enough.
--WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING-- --WARNING--
--]=]
local Settings = {
-- Rate: How many droplets spawn per second
Rate = 8; -- 64
-- Size: How large the droplets roughly are (in studs)
Size = 0.1;
-- Tint: What color the droplets are tinted (leave as nil for a default realistic light blue)
Tint = Color3.fromRGB(226, 244, 255);
-- Fade: How long it takes for a droplet to fade
Fade = 1.5;
};
----------------------------------------------------------------------------
--- Variables ------------------------------------------------------------
----------------------------------------------------------------------------
--Services
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
--Player related
local Camera = workspace.CurrentCamera
local Player = game.Players.LocalPlayer
local Mouse = Player:GetMouse()
local GameSettings = UserSettings().GameSettings
--Raycasting
local ignoreList = {Player.Character or Player.CharacterAdded:Wait()}
--Localizing
local ipairs = ipairs
local instance = Instance.new
local rgbColor = Color3.fromRGB
local random = math.random
local v3, cf = Vector3.new, CFrame.new
local UpVec = v3(0,1,0)
--Settings localized
local Rate = Settings.Rate
local Size = Settings.Size
local Tint = Settings.Tint or rgbColor(226, 244, 255)
local Fade = Settings.Fade
--Fade tween
local fadeInfo = TweenInfo.new(Fade,Enum.EasingStyle.Sine, Enum.EasingDirection.In)
local strechInfo = TweenInfo.new(Fade/1.05,Enum.EasingStyle.Quint, Enum.EasingDirection.In)
local fadeGoal = {Transparency = 1}
----------------------------------------------------------------------------
--- Prefab Basic Objects -------------------------------------------------
----------------------------------------------------------------------------
--Droplet holder
local ScreenBlock = instance("Part")
ScreenBlock.Size = v3(2,2,2)
ScreenBlock.Transparency = 1
ScreenBlock.Anchored = true
ScreenBlock.CanCollide = false
ScreenBlock.Parent = Camera
RunService:BindToRenderStep("ScreenRainUpdate", Enum.RenderPriority.Camera.Value + 1, function() ScreenBlock.CFrame = Camera.CFrame end)
--Droplet object
local Dropet_Prefab = instance("Part")
Dropet_Prefab.Material = Enum.Material.Glass
Dropet_Prefab.CanCollide = false
Dropet_Prefab.Transparency = 0.5
Dropet_Prefab.Name = "Droplet_Main"
Dropet_Prefab.Color = Tint
Dropet_Prefab.Size = v3(1,1,1)
local ObjectMesh = instance("SpecialMesh")
ObjectMesh.MeshType = Enum.MeshType.Sphere
ObjectMesh.Parent = Dropet_Prefab
----------------------------------------------------------------------------
--- Functions ------------------------------------------------------------
----------------------------------------------------------------------------
--Welds together two objects
local function Weld(a, b)
local weld = Instance.new("Weld")
weld.C0 = a.CFrame:inverse() * b.CFrame
weld.Part0 = a
weld.Part1 = b
weld.Parent = a
return weld
end
local function DestroyDroplet(d)
wait(Fade)
d:Destroy()
end
--Returns whether the given position is under cover
local function UnderObject(pos,l)
local ray = Ray.new(pos, UpVec * (l or 120))
-- raycast
local hit, position = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
if hit then
return hit.Transparency ~= 1 and true or UnderObject(position+UpVec, (l or 120) - (pos-position).Magnitude)
else
return false
end
end
--DEBUG
--local function UnderObject(pos,l)
-- local ray = Ray.new(pos, UpVec * (l or 120))
-- -- raycast
-- local hit, position = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
-- if hit then
-- return (hit.Transparency ~= 1 and not string.find(hit.Name, "Droplet") and not string.find(hit.Name, "Extrusion")) and true or UnderObject(position+UpVec, (l or 120) - (pos-position).Magnitude)
-- else
-- return false
-- end
--end
--Creates a random droplet on screen
local function CreateDroplet()
--Setup
local stretch = 1+(random(1,15)/10)
local Tweens = {};
--Main droplet object
local DropletMain = Dropet_Prefab:Clone()
local SizeOffset = random((Size/3)*-10,(Size/3)*10)/10
DropletMain.Mesh.Scale = v3(Size+SizeOffset,Size+SizeOffset,Size+SizeOffset)
DropletMain.CFrame = cf()
--Create droplet extrusions
for i=1, random(1,4) do
local Extrusion = Dropet_Prefab:Clone()
Extrusion.Name = "Extrusion_"..i
local eSizeOffset = random((Size/3)*-100,(Size/3)*100)/100
Extrusion.Mesh.Scale = v3((Size/1.5)+eSizeOffset, (Size/1.5)+eSizeOffset, (Size/1.5)+eSizeOffset)
Extrusion.CFrame = cf(v3(random(-(Size*40),(Size*40))/100,random(-(Size*40),(Size*40))/100,0))
Extrusion.Parent = DropletMain
Weld(Extrusion, DropletMain)
ignoreList[#ignoreList+1] = Extrusion
Tweens[#Tweens+1] = TweenService:Create(Extrusion, fadeInfo, fadeGoal)
local s,o = Extrusion.Mesh.Scale, Extrusion.Mesh.Offset
Tweens[#Tweens+1] = TweenService:Create(Extrusion.Mesh, strechInfo, {
Scale = v3(s.X, s.Y*(stretch), s.Z);
Offset = v3(0,-(s.Y*stretch)/2.05,0);
})
end
ignoreList[#ignoreList+1] = DropletMain
Tweens[#Tweens+1] = TweenService:Create(DropletMain, fadeInfo, fadeGoal)
local s,o = DropletMain.Mesh.Scale, DropletMain.Mesh.Offset
Tweens[#Tweens+1] = TweenService:Create(DropletMain.Mesh, strechInfo, {
Scale = v3(s.X, s.Y*(stretch), s.Z);
Offset = v3(0,-(s.Y*stretch)/2.05,0);
})
DropletMain.CFrame = ScreenBlock.CFrame:toWorldSpace(cf(random(-100,100)/100, random(-100,100)/100, -1))
Weld(DropletMain, ScreenBlock)
for _, t in ipairs(Tweens) do
t:Play()
end
local DestroyRoutine = coroutine.create(DestroyDroplet)
coroutine.resume(DestroyRoutine, DropletMain)
DropletMain.Parent = ScreenBlock
end
----------------------------------------------------------------------------
--- Functionality Loop ---------------------------------------------------
----------------------------------------------------------------------------
math.randomseed(tick())
while wait(1/Rate) do
--Only render droplets if:
--Camera isn't looking down
--Render settings are high enough
--Camera isn't under an awning or roof or something
-- debug.profilebegin("OLD_IF_THEN")
if (Camera.CFrame.lookVector.Y>-0.4) and (GameSettings.SavedQualityLevel.Value >= 8) and (not UnderObject(Camera.CFrame.Position)) then
-- debug.profilebegin("OLD_CREATE_DROPLET")
CreateDroplet()
-- debug.profileend()
end
-- debug.profileend()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment