Light Simulation - How to Improve?

Hello! I am currently working on a light simulation game that has absorbers, reflecters, infractors and even logic gates. The absorbers and mostly the reflectors have been done, but I got a problem.

As you can see, not all of the light particles are deflected. I have 2 scripts in my game that control all the light particles.

Reflection Detector & Manager
local LightFolder = game.Workspace.SimulationParts.LightParticles

local GlobalLightProperties = require(game.ReplicatedStorage.LightPropertySet)
local RunService = game:GetService("RunService")


local NameIgnoreList = {"Light","Laser"}
local ParentIgnoreList = {"BaseParts","TheBox"}






local SubT1 = {Instance=nil,Distance=nil,Normal=nil}

function SimLightFol(LightF:{})
	local LightSpeed = GlobalLightProperties.LightSpeed
	local RefreshTime = GlobalLightProperties.RefreshTime
	local DetectionDistance = GlobalLightProperties.DectectionDistance
	local LifeTime = GlobalLightProperties.LifeTime
	local ol_params = OverlapParams.new()
	ol_params.FilterType = Enum.RaycastFilterType.Blacklist
	ol_params.FilterDescendantsInstances = {game.Workspace.BaseParts}
	local PNB = workspace:GetPartsInPart(game.Workspace.BaseParts.TheBox.Ref,ol_params)

	for order, Light in pairs(LightF) do
		if not table.find(PNB,Light) then
			Light:Destroy()
		end
		local PartsInLight = workspace:GetPartsInPart(Light)
		if PartsInLight then
			for _, part in pairs(PartsInLight) do
				if not table.find(NameIgnoreList,part.Name) then
					local Rot = Light.Orientation
					Light.Orientation = Rot-(Vector3.new(180,180,180))
				end
			end
		end
		local rayOrigin = Light.Position
		local rayDirection = Light.CFrame.RightVector*180
		local rayParams = RaycastParams.new()
		rayParams.FilterType = Enum.RaycastFilterType.Blacklist
		rayParams.FilterDescendantsInstances = {LightFolder:GetChildren()}
		local rayR = workspace:Raycast(rayOrigin,rayDirection,rayParams)
		local InLightDet = workspace:GetPartsInPart(Light)[1]
		if rayR or (((InLightDet~=nil)and (not table.find(NameIgnoreList,InLightDet))) and InLightDet.ClassName=="Part")then
			if not table.find(NameIgnoreList,((rayR or SubT1).Instance or InLightDet).Name) then
				local Dis = (rayR or SubT1).Distance or (InLightDet.Position-Light.Position).Magnitude
				local Ins = (rayR or SubT1).Instance or InLightDet
				local Rot = Light.Orientation
				local RNorm = (rayR or SubT1).Normal 
				local Norm = (RNorm or InLightDet.Orientation/45)*90
				if Dis <= DetectionDistance then
					local RotX,LightRotationY,RotZ = Rot.X,Rot.Y,Rot.Z
					local NormX,NormY,NormZ = Norm.X,Norm.Y,Norm.Z
					local RotNorm = Rot+Norm
					Light.Orientation = Rot+Norm-(Vector3.new(180,180,180)-Rot)
				end
			end
		end
	end 
end


while task.wait() do
	SimLightFol(LightFolder:GetChildren())
end
Light Movement Manager
local LightFolder = game.Workspace.SimulationParts.LightParticles

local GlobalLightProperties = require(game.ReplicatedStorage.LightPropertySet)
local TweenService = game:GetService("TweenService")

local NameIgnoreList = {"Light,Laser"}

local RunService = game:GetService("RunService")

function CFPS(s)
	local a = {(workspace:GetRealPhysicsFPS()/60),(60/workspace:GetRealPhysicsFPS())}
	return a[s]
end

function SimLightFol(LightF)
	local LightSpeed = GlobalLightProperties.LightSpeed
	local RefreshTime = GlobalLightProperties.RefreshTime
	local DetectionDistance = GlobalLightProperties.DectectionDistance
	local LifeTime = GlobalLightProperties.LifeTime
	
	for order, Light in pairs(LightF) do
		local Tween = TweenService:Create(Light,TweenInfo.new((RefreshTime*1.2*CFPS(2)/2), Enum.EasingStyle.Linear, Enum.EasingDirection.Out), {CFrame = (Light.CFrame+(Light.CFrame.RightVector*(LightSpeed*RefreshTime*CFPS(1)/2)/1.2))*CFrame.Angles(0,0,0)})
		Tween:Play()
	end
end

while true do
	wait(GlobalLightProperties.RefreshTime*CFPS(2)*1.2)
	if #LightFolder:GetChildren() > 0 then
		SimLightFol(LightFolder:GetChildren())
	end
end

Extra Info:

  • The light particle’s speed is based on the physic fps.
  • Light Particles outside of the box are automaticly deleted.
  • FPS averages at about 30-40,
  • Plans are made to add NOT, OR, XOR and AND gates that would work with light particles entering and exiting instead of values like 0 or 1. Not yet sure how to implement it but that is far from my priority right now.

3 Likes

I realised that the recording did not go well, so here is the sped up version:

Interesting but a better way to simulate light faster would be beams/raycasts (you could place attachments where the ray hits and create an attachment from the origin to the hit position)

Then that would make lenses and logic very hard, or for logic, pretty much impossible.

I am less concerned about the design and more about how to make my script more effiecent and to detect all collisions.

My fps also tanked in that recording, but usually at 30 or even 60 fps, the Light moves fast enough.

you can put all lenses in a folder or name them “Lense” and if the ray hits a part in the folder or with that name you can cast another ray from the hit position (and ignore the lense)

I only said beams so you can see it in real-time. It’s not really important to the functionality

I’m still pretty sure raycasts would run even faster and be able to handle more rays.

Interesting prototype, Well I believe there’s more room for improvement, especially for the performance. But all that aside good job!

1 Like

image
I got folders in place, but here is the thing. I do not want it to be instant and my priority is currently to figure out how to detect rays more frequently and react to them.

1 Like

Yes, the performance is around 25-35 fps for me, may be highers for others.
The recording tanked my fps and it was at about 3 fps. But I agree, there is room for improvement, and I sure do have to look into the performance.

1 Like

image
Here is what runs the function every heatbeat. I know that running things multiple times per heartbeat may be impossible, but .Stepped,.RenderStepped and .HeartBeat all have small differences, making it somewhat possible to fire 3 times a frame. But that would sacrifice some performance for better detecting rays.


Lines 31 to 58 detect raycasts and the parts in the light particle.
But :GetPartsInParts hadn’t added any significant changes to hit detection,
and sometimes the light go too fast and in the next frame, the light is already passed the wall as it has not passed the .5 detection distance.

Can functions be called multiple times a heartbeat?

Here I marked points where some colisions aren’t detected.