Making particles stop and disappear when they "hit" an object?

I would like to make weather for my game but because building is involved I would also like the rain textured particles stop and disappear when they hit objects. I thought of using acual objects that destroy themselves when they hit things but that can cause a lot of lag compared to using particles. the particles are also going to be server sided as they will be coming from clouds moving over the map.


I know that this doesn’t answer your question but why you creating the particles on the server and not simply rendering them on every client. You can easily render them on the client by using the FireAllClients event of a RemoteEvent.


You just adjust the lifetime of the particles, and find the sweet spot for them to disapear. I think this is an easy solution around just a bunch of other things.


that would be good if I knew were all objects would be but players have the freedom to build anywere on the map so for something like this I would need to constantly find the height and position required to make it appear that the particles are being blocked.


I think OP wants a solution where the particles do not go through ceilings, consider it as “dynamic” raindrops.

Adding on to what @mircostaff said, you could achieve this desired effect by rendering the particles on the client. Use raycasts to detect collisions, and temporarily disable that emitter for the client while they are indoors.


I know the basics on raycasting, but how do I rig it up with particleemitters?


Assuming you have a hierarchy similar to this:

Create a raycast at an interval of your choice from the base part. Do know that your interval will require experimentation. From there you can just draw a ray facing downwards towards the baseplate.

You can read more on rays on the developer wiki. Ray | Documentation - Roblox Creator Hub


I have a part with a ray casting downward, how can I adjust the emitters lifetime based on how far the part the ray hit is?

1 Like

If you raycast to find the distance the particle has to travel, and you know the speed of the particle emitters, you can use the formula

speed = distance/time

and rearrange it to get

time = distance/speed

Simply use the distance and speed values, and set the ParticleEmittter’s lifetime accordingly.


this is what I put together:

while true do


local ray =,,-125,0))

local parts = workspace:FindPartOnRay(ray,script.Parent)

if parts ~= nil then

local PaEm = script.Parent.ParticleEmitter

local distancenumrange = ray:Distance(ray:ClosestPoint(parts.Position))

PaEm.Lifetime = / PaEm.Speed)

elseif parts == nil then




I get an error stating “attempting to perform arithmetic on “Speed” a userdata value” "

1 Like

Ah, yes. That’s because a particle emitter has a minimum speed or maximum speed. Try PaEm.Speed.Min instead (assuming the min and max speed are the same)


so no errors after adding .min, and the lifetime is changing. but the particles wont stop right at the obstacle
this is some data when I ran the game:

emitter speed is 120

min/max lifetime for emitter is 248193744

the position of the emitters parent (a part) is 144.75, 112, 115.062

Are the min and max values of the particle emitters speed the same?

yes, both min and max for speed is 120

Okay, well since we’re using

time = distance/speed

And time is becoming massive, there are 2 options; either distance value is massive or speed is incredibly small. Try printing out the values for distance, speed, time and the part which the ray is hitting?

without anything but the baseplate in the way:

print(distancenumrange) = 32041408512
print(PaEm.Speed) = 120 120
print(parts.Name) = Baseplate

the particles dont stop at the baseplate but rather keep going down

heres a script that for the most part does change the lifetime to stop at an obstacle but I cant change the ray`s distance without the particles falling out of sync:

while true do

local ray =,,-50,0))
local parts = workspace:FindPartOnRay(ray,script.Parent)

if parts ~= nil then
	local distancenumrange = --get the ray distance--
	local firstconvert = tostring(distancenumrange)--sub the number into (num,num)--
	local secondconvert = string.sub(firstconvert,1,1)--get number to the left--
	local thirdconvert = string.sub(firstconvert,3,3)--get number to the right--
	local fourthconvert = secondconvert..thirdconvert --combine both numbers--
	local finalconvert = "0."..fourthconvert --convert to a number a particle emitter`s lifetime can use--
	script.Parent.ParticleEmitter.Lifetime =
elseif parts  == nil then

changing the ray higher or lower than 50 causes the particles to stop too high or too low.

1 Like

Basically have a bunch of particle emitter spawns above your character that you’re constantly CFraming, and do a raycast check for each part consistently.

If you detect a part directly under or over that specific particle emitter spawn, disable the particle emitter. This way, you can create the “effect” of rain directly around your character, which reduces lag overall and is more efficient overall.


local model = game.ReplicatedStorage.Assets.RainModel:Clone()

game:GetService"RunService":BindToRenderStep("RainCheck", Enum.RenderPriority.Last.Value, function()
    model:SetPrimaryPartCFrame(character.Head.Position +, 40, 0))
    for _, part in next, model:GetChildren() do
        local ray = +, 100, 0),, -1, 0)*120) -- raycast down to slightly above the floor
        local hitPart = workspace:FindPartOnRayWithIgnoreList(ray, {model, character})
        if hitPart then
            part.RainParticle.Enabled = false

This is very rough and it might not give you the exact desired behavior, but this is how I handle rain in games that I make.


Yeah, so distancenumrange is coming out absurdly high. I’ve never used these functions before:


And I’m not really familiar with these functions. Could these be the issue that’s making the distance extremely big?

I feel like an easier way to get the distance between the 2 parts would be by using the next parameters of FindPartOnRay, and then using the 2 y values to find the difference. Here’s an example:

local hit, hitPosition = FindPartOnRay() -- findpartonray also returns the position

local myPosition = script.Parent.Position
local distance = myPosition.Y - hitPosition.Y -- find the difference in positions

interestingly if I change to speed along with the ray distance I can adjust it just right to have them syncronized.