Hello!
I have been working on an extension for the Roblox Studio Engine for just over 2 weeks, and I’ve found something weird to say the least with my particle system. Honestly, the video will explain it much better than I can, but basically the system lags after a bit of use (not client, only the particles).
Here is when I got into the game:
And here is after 30 seconds:
Here is the file (ParticleEmitter):
--[[
Blade Engine / Physically Based Particle Emitter
(BladeParticleEmitter)
made by melee
DO NOT EDIT ANYTHING BELOW HERE UNLESS YOU KNOW WHAT YOU ARE DOING!
-- EXAMPLE CODE, SERVER SCRIPT
local module = require(path.to.this.module) -- change this value
local particleEmitter = module.New()
particleEmitter.PositionOffset = Vector3.new(0, 5, 0)
particleEmitter:Emit(particleEmitter, "Continuous")
]]--
local ParticleEmitter = {}
ParticleEmitter.__index = ParticleEmitter
type self = {
-- // DEFAULT PARTICLE EMITTER PROPERTIES
-- // DO NOT EDIT
-- Appearance
Color : Color3 | BrickColor | ColorSequence,
LightEmission : number,
LightInfluence : number,
Orientation : Enum.ParticleOrientation,
Size : number | NumberSequence,
Squash : number | NumberSequence,
Texture : Texture,
Transparency : number | NumberSequence,
ZOffset : number,
-- Data
Archivable : boolean,
Name : string,
Parent : Instance,
-- Emission
EmissionDirection : Enum.NormalId,
Enabled : boolean,
LifetimeMin : number,
LifetimeMax : number,
Rate : number,
RotationMin : number,
RotationMax : number,
RotSpeedMin : number,
RotSpeedMax : number,
SpeedMin : number,
SpeedMax : number,
SpreadAngle : Vector2,
-- EmitterShape
Shape : Enum.ParticleEmitterShape,
EmitterSize : Vector3,
ShapeInOut : Enum.ParticleEmitterShapeInOut,
ShapeStyle : Enum.ParticleEmitterShapeStyle,
-- Flipbook
-- none
-- Motion
Acceleration : Vector3,
-- Particles
Drag : number,
LockedToPart : boolean,
TimeScale : boolean,
VelocityInheritance : number,
WindAffectsDrag : boolean,
-- // CUSTOM PROPERTIES
-- Data
PositionOffset : Vector3,
Visualize : boolean,
-- Particles
HasEnvironmentPhysics : boolean,
Velocity : Vector3,
}
export type BladeParticleEmitter = typeof(setmetatable({} :: self, ParticleEmitter))
function ParticleEmitter.New() : BladeParticleEmitter
local self = setmetatable({} :: self, ParticleEmitter)
-- set all values to default
-- Appearance
self.Color = Color3.new(255, 255, 255)
self.LightEmission = 0
self.LightInfluence = 1
self.Orientation = Enum.ParticleOrientation.FacingCamera
self.Size = 1
self.Squash = 0
self.Texture = "rbxasset://textures/particles/sparkles_main.dds"
self.Transparency = 0
self.ZOffset = 0
-- Data
self.Archivable = true
self.Name = "BladeDefaultParticleEmitter"
self.Parent = workspace
self.PositionOffset = Vector3.new(0, 0, 0)
self.Visualize = false
-- Emission
self.EmissionDirection = Enum.NormalId.Top
self.Enabled = true
self.LifetimeMin = 5
self.LifetimeMax = 10
self.Rate = 20
self.RotationMin = 0
self.RotSpeedMax = 0
self.RotSpeedMin = 0
self.RotSpeedMax = 0
self.SpeedMin = 5
self.SpeedMax = 5
self.SpreadAngle = Vector2.new(0, 0)
-- EmitterShape
self.Shape = Enum.ParticleEmitterShape.Box
self.EmitterSize = Vector3.new(2, 2, 2)
self.ShapeInOut = Enum.ParticleEmitterShapeInOut.Outward
self.ShapeStyle = Enum.ParticleEmitterShapeStyle.Volume
-- Flipbook
-- none
-- Motion
self.Acceleration = Vector3.new(0, 0, 0)
-- Particles
self.Drag = 0
self.LockedToPart = false
self.TimeScale = 1
self.VelocityInheritance = 0
self.WindAffectsDrag = false
self.HasEnvironmentPhysics = false
return self
end
-- Helper Function from Roblox Documentation
local function EvaluateColorSequence(sequence: ColorSequence, time: number)
-- If time is 0 or 1, return the first or last value respectively
if time == 0 then
return sequence.Keypoints[1].Value
elseif time == 1 then
return sequence.Keypoints[#sequence.Keypoints].Value
end
-- Otherwise, step through each sequential pair of keypoints
for i = 1, #sequence.Keypoints - 1 do
local thisKeypoint = sequence.Keypoints[i]
local nextKeypoint = sequence.Keypoints[i + 1]
if time >= thisKeypoint.Time and time < nextKeypoint.Time then
-- Calculate how far alpha lies between the points
local alpha = (time - thisKeypoint.Time) / (nextKeypoint.Time - thisKeypoint.Time)
-- Evaluate the real value between the points using alpha
return Color3.new(
(nextKeypoint.Value.R - thisKeypoint.Value.R) * alpha + thisKeypoint.Value.R,
(nextKeypoint.Value.G - thisKeypoint.Value.G) * alpha + thisKeypoint.Value.G,
(nextKeypoint.Value.B - thisKeypoint.Value.B) * alpha + thisKeypoint.Value.B
)
end
end
end
function ParticleEmitter:Emit(self : BladeParticleEmitter, emissionType : "Continuous" | "Burst")
-- roblox doesn't support 'self' type-checking by default
-- orange squiggle is normal
local edir = Vector3.FromNormalId(self.EmissionDirection) -- convert emission direction to usable normal
if emissionType == "Continuous" then
local spawnRate = 1 / self.Rate -- default value is 1 / 20 seconds, 50 ms (doesn't matter, calculated through RunService.Heartbeat on server)
while self.Enabled and task.wait(spawnRate) do
--print("Spawning Particle!")
-- set values
local part = Instance.new("Part")
part.Parent = self.Parent
part.Name = self.Name
if self.Parent == workspace then
part.Position = Vector3.new(0, 0, 0) + self.PositionOffset + Vector3.new(
math.random(-(self.EmitterSize.X), (self.EmitterSize.X)),
math.random(-(self.EmitterSize.Y), (self.EmitterSize.Y)),
math.random(-(self.EmitterSize.Z), (self.EmitterSize.Z))
)
else
part.Position = self.Parent.Position + self.PositionOffset + Vector3.new(
math.random(-(self.EmitterSize.X), (self.EmitterSize.X)),
math.random(-(self.EmitterSize.Y), (self.EmitterSize.Y)),
math.random(-(self.EmitterSize.Z), (self.EmitterSize.Z))
)
end
part.Size = Vector3.new(1, 1, 1)
part.Anchored = true
part.CanCollide = false
part.CanTouch = false
part.CastShadow = false
part.Transparency = 1
part.CanQuery = true
part.TopSurface, part.BottomSurface = Enum.SurfaceType.Smooth, Enum.SurfaceType.Smooth
local billboard = Instance.new("BillboardGui")
billboard.Parent = part
billboard.Adornee = part
billboard.Size = UDim2.fromScale(1, 1)
billboard.ClipsDescendants = false
local particle = Instance.new("ImageLabel")
particle.Parent = billboard
particle.Image = self.Texture
particle.Size = UDim2.fromScale(1, 1)
particle.BackgroundTransparency = 1
local timeSinceSpawn = 0
local particleLifeTime = math.random(self.LifetimeMin, self.LifetimeMax)
self.Velocity = math.random(self.SpeedMin, self.SpeedMax) * edir
if self.HasEnvironmentPhysics then
else
end
local connnection = game:GetService("RunService").Stepped
connnection:Connect(function(step, dt)
part.Position += self.Velocity * dt
if typeof(self.Color) == "ColorSequence" then
timeSinceSpawn += dt
local timeLeft = particleLifeTime - timeSinceSpawn
local colorTime = timeLeft / particleLifeTime
local particleColorAtCurrentTime = EvaluateColorSequence(self.Color, colorTime)
particle.ImageColor3 = particleColorAtCurrentTime
end
end)
task.delay(particleLifeTime, function()
part:Destroy()
billboard:Destroy()
particle:Destroy()
connnection:Disconnect()
end)
end
elseif emissionType == "Burst" then
else
warn("'" .. emissionType .. "' is not a valid emission type!")
end
end
return ParticleEmitter
I don’t know where to go from here, so any help is greatly appreciated.