I have an Iterator that loops through Collectionservice and runs a function to it while its being looped
local RunService = game:GetService("RunService")
local Collectionservice = game:GetService("CollectionService")
local GuiCollisionService = require(game:GetService("ReplicatedStorage").GuiCollisionService)
local event
function OnTouch(b)
local a = b.Parent.Parent.Parent.Parent.DragableFrame
local touching = GuiCollisionService.isColliding(a,b)
if touching == true then
b.UIStroke.Enabled = true
else
b.UIStroke.Enabled = false
end
end
RunService.RenderStepped:Connect(function()
for i, b in pairs(Collectionservice:GetTagged("regions")) do
if b.Parent.Parent.Parent.Parent.Parent.Parent.Playback.Aplay.paused.Value == false then
coroutine.wrap(OnTouch)(b)
end
end
end)
but then its gets ober 500 or more items in the table, its starts to lag.
is there anything i can do to improve performance?
Use event-based programming (“listeners”) instead of polling (“lookups”).
So replace this
RunService.RenderStepped:Connect(function()
for i, b in pairs(Collectionservice:GetTagged("regions")) do
if b.Parent.Parent.Parent.Parent.Parent.Parent.Playback.Aplay.paused.Value == false then
coroutine.wrap(OnTouch)(b)
end
end
end)
with this:
for _, region in Collectionservice:GetTagged("regions") do
local paused = not region.Parent.Parent.Parent.Parent.Parent.Parent.Playback.Aplay.paused
local changedSignal = paused:GetPropertyChangedSignal("Value")
if not paused.Value then
coroutine.wrap(OnTouch)(b)
end
changedSignal:Connect(function()
if not paused.Value then
coroutine.wrap(OnTouch)(b)
end
end)
end
That way you’re only running if not paused.Value then coroutine.wrap(OnTouch)(b) end when paused actually changes, instead of checking 100s of things every frame. Oh, and once at the start of the game so things that start off un-paused also call OnTouch.
If you need to support regions getting added or removed during the game, you’ll have to change it to this:
local TagS = game:GetService("CollectionService")
local regionDebris = {}
function clean(obj) --Alternatively, check out the Maid or Trove libraries, they're pretty great and support lots of value types
if typeof(obj) == "RBXScriptConnection" then
obj:Disconnect()
else
error(`Cannot clean {obj}`)
end
end
function setupRegion(region)
local debris = {}
regionDebris[region] = debris
local paused = not region.Parent.Parent.Parent.Parent.Parent.Parent.Playback.Aplay.paused
local changedSignal = paused:GetPropertyChangedSignal("Value")
local function update()
if not paused.Value then
coroutine.wrap(OnTouch)(region)
end
end
--Update immediately
update()
--Update when paused.Value changes, until region gets cleaned up
table.insert(debris, changedSignal:Connect(update))
end
function cleanupRegion(region)
local debris = regionDebris[region]
assert(debris, `Cannot clean up {region} as it's not currently set up!`)
regionDebris[region] = nil
for _, obj in debris do
clean(obj)
end
end
for _, region in TagS:GetTagged("regions") do
setupRegion(region)
end
TagS:GetInstanceAddedSignal("regions"):Connect(setupRegion)
TagS:GetInstanceRemovedSignal("regions"):Connect(cleanupRegion)
Oh, is that a Sound or something? I thought it was a BoolObject. That changes things a bit, try this version of setupRegion instead:
function setupRegion(region)
local debris = {}
regionDebris[region] = debris
local aPlay = not region.Parent.Parent.Parent.Parent.Parent.Parent.Playback.Aplay
local changedSignal = aPlay:GetPropertyChangedSignal("Paused")
local function update()
if not aPlay.Paused then
coroutine.wrap(OnTouch)(region)
end
end
--Update immediately
update()
--Update when paused.Value changes, until region gets cleaned up
table.insert(debris, changedSignal:Connect(update))
end
Use events instead of .RenderStepped to track changes.
Fixed code:
local RunService = game:GetService("RunService")
local Collectionservice = game:GetService("CollectionService")
local GuiCollisionService = require(game:GetService("ReplicatedStorage").GuiCollisionService)
local function OnTouch(b)
local a = b.Parent.Parent.Parent.Parent.DragableFrame
local touching = GuiCollisionService.isColliding(a, b)
b.UIStroke.Enabled = touching
end
local function InitializeRegion(region)
local boolValue = region.Parent.Parent.Parent.Parent.Parent.Parent.Playback.Aplay.paused
boolValue.Changed:Connect(function(newValue)
if not newValue then
OnTouch(region)
end
end)
if not boolValue.Value then
OnTouch(region)
end
end
Collectionservice:GetInstanceAddedSignal("regions"):Connect(InitializeRegion)
for i, region in pairs(Collectionservice:GetTagged("regions")) do
task.spawn(InitializeRegion, region)
end
(Using a lot of coroutines or task.spawn can cause lag, so if you don’t have to wrap it in a coroutine or task.spawn, just remove it. I’m only keeping it just incase your code needs it.)
but then it only detects if the play button is clicked, after that its stops detecting. the play head doesnt acivate the note anymore until i pause then play it which only playes the ones its currenly touching befor it stops working
Tell me if this works (Lag should be minimal but if it’s still to the level of before, tell me):
local RunService = game:GetService("RunService")
local Collectionservice = game:GetService("CollectionService")
local GuiCollisionService = require(game:GetService("ReplicatedStorage").GuiCollisionService)
local refreshRate = 0.2
local function OnTouch(a, b)
b.UIStroke.Enabled = GuiCollisionService.isColliding(a, b)
end
local function InitializeRegion(region)
local boolValue = region.Parent.Parent.Parent.Parent.Parent.Parent.Playback.Aplay.paused
local a = region.Parent.Parent.Parent.Parent.DragableFrame
boolValue.Changed:Connect(function(newValue)
while not boolValue.Value do
OnTouch(a, region)
task.wait(refreshRate)
end
end)
while not boolValue.Value do
OnTouch(a, region)
task.wait(refreshRate)
end
end
Collectionservice:GetInstanceAddedSignal("regions"):Connect(InitializeRegion)
for i, region in pairs(Collectionservice:GetTagged("regions")) do
task.spawn(InitializeRegion, region)
end
its about slower than my original one. and sice the refresh rate is greater than renderstepped, it doesnt notes doesnt play on time. i changed to make it less but it made my fps drop from 60(not playing) to 34(when playing) the song runs on fps so i know then its not right when the song’s tempo is fluctuating
but this needs to be constanly checked in order for it to keep up. and since there so many of them, how would i do it without lag
this is the collision code
function GuiCollisionService.isColliding(guiObject0, Character)
if not typeof(guiObject0) == "Instance" or not typeof(Character) == "Instance" then error("argument must be an instance") return end
local ap1 = guiObject0.AbsolutePosition
local as1 = guiObject0.AbsoluteSize
local sum = ap1 + as1
local ap2 = Character.AbsolutePosition
local as2 = Character.TextBounds
local sum2 = ap2 + as2
local corners0 = getCorners(guiObject0)
local corners1 = getCorners(Character)
local edges = {
{
a = corners1.topleft,
b = corners1.bottomleft
},
{
a = corners1.topleft,
b = corners1.topright
},
{
a = corners1.bottomleft,
b = corners1.bottomright
},
{
a = corners1.topright,
b = corners1.bottomright
}
}
local collisions = 0
for _, corner in pairs(corners0) do
for _, edge in pairs(edges) do
if intersects(corner, edge) then
collisions += 1
end
end
end
if collisions%2 ~= 0 then
return true
end
if (ap1.x < sum2.x and sum.x > ap2.x) and (ap1.y < sum2.y and sum.y > ap2.y) then
return true
end
return false
end
Value checks are extremely more performant than those collision checks. Also, you don’t have to constantly check. Just check when the value changes and apply the functions needed. It’s a way better solution than doing 50000 collision checks (which even 1 collision check would equate to 30+ value checks) a second.
Why don’t you group all the notes by bar, and then check the bar’s bounds instead of each individual note. And when you’ve iterated over the item, remove it from the list. This would significantly reduce iteration count.
i tried using this rendering script but it lags everytime the scrollingframe moves
local tagservice = game:GetService("CollectionService")
local stop = game.ReplicatedStorage.Remotes.StopSound
local guiInset = game:GetService("GuiService"):GetGuiInset()
local function isInScreen(b)
local pos = b.AbsolutePosition
return pos.X <= b.Parent.Parent.Parent.Parent.Parent.AbsoluteSize.X and pos.X >= 0
end
script.Parent.canvas.Changed:Connect(function(CanvasPosition)
if CanvasPosition ~= ("CanvasPosition") then return end
for i, b in pairs(tagservice:GetTagged(script.Parent.Parent.ID.Value)) do
coroutine.resume(coroutine.create(function()
if isInScreen(b) then
tagservice:AddTag(b,"regions")
b.Visible = true
else
if tagservice:HasTag(b,"Selected") then return end
tagservice:RemoveTag(b,"regions")
b.Visible = false
end
end))
end
end)