Parts spawning to fast!

Hey there I’m creating a type of rhythm game. I then thought instead of just mapping all the songs which would take for ever. Why don’t I just code it using sound.PlaybackLoudness. I came up with something that does what I want but also doesn’t. My problem is that instead of it spawning one part at a time it spawns TONS and I cant seem to fix it. Thanks for your time hope you can help!

[VIDEOS] (CODE BELOW)


CODE

local maxPlaybackLoudness = 20 --// Higher the more difficult
game:GetService("RunService").RenderStepped:Connect(function(dt)
	local loudness = sound.PlaybackLoudness * 0.15
	local amplitude = math.clamp(loudness / maxPlaybackLoudness, 0, 1)
	amplitude = math.floor(amplitude * 1.5) --// Remove decimals
	
	if (amplitude == 1) then
		local randomSpawn = starts:GetChildren()[math.random(1, #starts:GetChildren())]
		CreatePart(randomSpawn) --// This just creates the new part
	end
end)
2 Likes

So, RenderStepped probably isn’t the one to use here since you aren’t coding anything for the camera or character. Consider switching to Heartbeat.

You’ll still have the same problem of the parts being created many times per second. The dt value is the time in seconds since the last time RenderStepped or Heartbeat ran, so you can use that to adjust the pause between when parts are created. For example, you could declare another variable up by maxPlaybackLoudness called “counter” that keeps a running total of seconds since the last time you created a part. You’d initialize it to 0 and then inside the RunService loop you’d add dt to that variable. When the value of that variable reaches 1, then one second will have passed since you initialized/reset the counter variable. You’d then put your part creation code within an if/then block that checks that counter:

--...

-- declare this counter (local counter = 0) up above, outside the RunService loop
counter = counter + dt

-- Use ">=" since the counter will regularly overshoot whatever value you compare it to.
-- Set the '1.5' to be the num of seconds you want between potential part spawns
if counter >= 1.5 then  
    counter = 0    -- reset the counter
    if amplitude == 1 then
        -- create your part
    end
end

Calling :GetChildren() from within a RunService loop is not recommended. Do you need to get the children on that starts object a bunch of times per second or can it be collected ahead of time and stored before you enter the loop?

Anyway, I’m having a hard time visualizing how you get the kind of rhythm info needed for a game from just the loudness of the soundtrack, so I can’t help much in that regard, but hopefully some of these suggestions can help you further test your ideas. Happy coding.

Use wait()
to slow down functions. :slight_smile:

1 Like

Its pretty normal if you use HeartBeat or RenderStepped

@Astr0Derp thats not a good idea for this type of things

HeartBeat runs about 60 times in a second, while render stepped runs about 60-80 times in a seconds

10 times a second is alot for this

Use

while wait(0.25) do

This will run about 4 times a second, and if your music is a slow music than you can go for 0.5 seconds as running 2 times a second is pretty good

That’s not true — Heartbeat is fired after physics calculations, which is around 60 times/second. RenderStepped is fired after every frame is rendered on the client, so usually around 60 times/second (60 fps).

1 Like

But its about music, and it can run abouit 30-50 times

Instead of running it every time RenderStepped fires, would running it every X game tick be a solution?

local function TickWait(n)
    n = n or 1
    for _ = 1, n do
        RunService.Heartbeat:Wait()
    end
end

while true do
    TickWait(15) -- 1/4 of a second, or 0.25
    -- code body
end

I figured someone would show a way to do it with other loops. I wanted to touch on using RunService with a delta as part of a first reply. A while loop with wait(n) is also where I’d start when trying something like this. I don’t think using Heartbeat would be an issue though. Listening in on those events isn’t a big performance hit unless you’re trying to call :GetChildren in there 50 times a sec, which was something else I wanted to comment on. So…I think we’re more or less on the same page.

I think the main point WoodX was making is that any of the per frame RunService events are overkill here. Code for the sound stuff MarsMaid is talking about just doesn’t need to execute anywhere near that fast, so there’s a lot of “wasted motion”.

Essentially, the choices so far are to use the fast firing of RunService events with some sort of counter scheme to space out the execution of code (like you and I proposed, and how it would be done with RenderStepped if that was the best RunService option to use–which it isn’t), or to use a loop with wait(n) that would yield the thread until n seconds (or so) passed to slow things down. I don’t know enough about how these things are implemented to offer a definitive opinion one way or the other. We could probably whip up an approach that uses the Task library too without much trouble. For all I know, these are all equivalent at some level and this discussion is moot.

At a surface level, the wait(n) is easier to implement and probably more appropriate for this problem as stated. Heartbeat becomes more attractive as the delay time drops near or below the minimum duration of wait() or maybe if you’re wanting to control timing of more than one thing within the same “loop”, which is possible with Heartbeat, a counter, and some modulo. Any of the suggestions above should help MarsMaid slow down the creation of blocks in the rhythm game script–with maybe the exception of using wait() to slow event handler code for a RunStepped event. :thinking: Don’t do that.

Its not really on beat now with the music now, also sorry for late response I was away for a while.

local counter = 0
game:GetService("RunService").Heartbeat:Connect(function(dt)
	local loudness = sound.PlaybackLoudness * 0.4
	local amplitude = math.clamp(loudness / maxPlaybackLoudness, 0, 1)
	amplitude = math.floor(amplitude * 1.5) --// Remove decimals
	
	counter = counter + dt
	if (counter >= 1.5) then  
		counter = 0    -- reset the counter
		if (amplitude == 1) then
			local randomSpawn = starts:GetChildren()[math.random(1, #starts:GetChildren())]
			CreatePart(randomSpawn) --// This just creates the new part
		end
	end
end)

Can compare “counter” in the (counter >= 1.5) to a lower number like 0.1. That will make it go faster. You can adjust this to taste. Also, is it possible to do something like:

local counter = 0
local startsChildren = starts:GetChildren()   -- set this up top
game:GetService (etc....)

-- then
local randomSpawn = startsChildren [math.random(1, #startsChildren)]

…so you aren’t calling GetChildren constantly inside that Heartbeat handler, or do the contents of “start” change all the time? There are other ways to do that if the contents of “start” need to change.

At any rate, that code (and the alternatives suggested by others above) will allow you to slow down the rate at which those parts are being created. Matching the beat is a separate problem. Currently, you’re following along with the loudness value of the soundtrack and creating parts when the sound is loud and not when it’s quieter. The result, visually, is something like this (where ‘o’ are parts and ‘_’ are no parts):
oooooooo_______oooooo______ooooooooo__ooooooooooooo

The song is moving forward in time and your code is sampling the loudness at a constant rate. Before we adjusted the code, it was sampling it very quickly and creating a lot of parts. By slowing it down to 1 sample every 1.5 seconds (what the if (counter >= 1.5) is doing), we’ve dramatically reduced the number of boxes being created, but they are still being created effectively the same way as before. They aren’t on the beat now because they weren’t on it before. Let me see if I can illustrate…

So, the overall pattern hasn’t changed. If you speed it up to 1 sample per 0.1 sec (as mentioned above) you’d get something more like pattern below, which would look more like what you were seeing earlier at full speed, but with fewer blocks.
o o o o o _ _ _ _ _ o o o o _ _ _ _o o o o o o _ o o o o o o o o o

Right now (at 1.5), the sample rate is too slow so it’s more like:
___o__________o ___________________o_________o

But the pattern is still essentially the same. The problem here is that you aren’t really tracking a beat. It’s just mapping swings in the volume. It does nothing until the loudness hits your threshold and then it draws a bunch of boxes until the loudness falls below the threshold again. That technique would probably work fine if there was nothing in the soundtrack but a beat, but that doesn’t sound like what you’re aiming for. That’s what I was referring to in my first post when I said, “I’m having a hard time visualizing how you get the kind of rhythm info needed for a game from just the loudness of the soundtrack…”. If you want the players to do something like headbang or jump around or strum instuments while the loudness is high and then stop when it’s lower, then you can probably dial the sample rate in to something that would be smooth without generating too many parts. If you need to have parts generated on the beat or on the notes, then I don’t know if that’s possible to automate with audio methods we have access to in Roblox. Anyway, try setting that 1.5 number to something else between 0 and 1 and see if you get a flow rate you’re happy with.

Edit: Searched around a bit and found these two threads. The first may help you with automating the process using the loudness. The second is an open source game that uses a diff technique. You may have seen these already, but figured I’d update the post just in case you hadn’t.
Best way to create rhythm for my rhythm game? - Help and Feedback / Scripting Support - DevForum | Roblox

[Open Source] RoBeats (Rhythm Game) Custom Server Template - Resources / Community Resources - DevForum | Roblox