Making a optimal game loop

Hello scripters, I have been struggling with this as the main issue of this project, I have an game event that uses ray cast, and it is hooked up to an function, and I have it ran when a user types something, but once they do that it should keep looping until they say stop, but that’s where my issue is. It doesn’t loop properly, my current system is messy and anything I try with loops the ray cast “Glitches” forward then gets placed backwards, help needed, thanks in advanced.

Server sided:

game.Players.PlayerAdded:Connect(function(player)
	if table.find(Permitted, player.UserId) then
		player.Chatted:Connect(function(msg)
			if msg == "!EyeEvent" then

				if game.Workspace.HappyHome then
					LaserPart.Parent = workspace
					MinigameActive = true
				else
					Map:Clone().Parent = workspace
					LaserPart.Parent = workspace
					MinigameActive = true
				end
			elseif msg == "!EndEvent" then
				MinigameActive = false
				LaserPart:Destroy()
				wait(2)
				LaserPart.Parent = game.ReplicatedStorage
			elseif msg == "!Reset" then
				workspace.HappyHome:Destroy()
				Map:Clone().Parent = workspace
			end
		end)
	end
end)


MinigameActive = false

local target_previous = Vector3.new()

local Detector = Instance.new("Part")
Detector.Anchored = true
Detector.CanCollide = false
Detector.Transparency = 0.5
Detector.Color = Color3.fromRGB(255, 0, 0)
Detector.Size = Vector3.new(1, 1, 1) * 4


function startGame(Target)
	local origin = rayOriginPart.CFrame.Position
	local rayTarget = target_previous:Lerp(Target.Position, laser_TrackingAccuracy)
	local direction = (rayTarget - origin)
	local results = workspace:Raycast(origin, direction * 100, raycastParameters)
	if not results then
		results = {Position = rayTarget}
	else
		if results.Instance.Parent:FindFirstChild("Humanoid") and not results.Instance:IsDescendantOf(workspace.HappyHome) and not results.Instance.Parent:IsA("Accessory") then
			results.Instance.Parent:FindFirstChild("Humanoid"):TakeDamage(10)
		elseif results.Instance:IsDescendantOf(workspace.HappyHome) then
			local NewDetector = Detector:Clone()
			NewDetector.Parent = workspace
			NewDetector.Position = results.Instance.Position
			
			local PartsList = NewDetector:GetTouchingParts()
			results.Instance:Destroy()
			
			for _,Part in ipairs(PartsList) do
				if Part:IsDescendantOf(workspace.HappyHome.Home.House) then
					Part:BreakJoints()
					Part:ApplyImpulse(math.random(1, 100), math.random(100, 500), math.random(1,100))
				end
			end
			
			NewDetector:Destroy()
			wait(1)
		end
	end
	local distance = (results.Position - origin).Magnitude
	local LaserPosition = laserPart.Position
	LaserPart.Size = Vector3.new(laser_Thickness,laser_Thickness,distance)
	laserPart.CFrame = CFrame.new(origin, results.Position) * CFrame.new(0,0, -distance/2)

	target_previous = rayTarget
end

while true do
	if MinigameActive == true then
		Runservice.Stepped:Connect(function(dt)
			local target = FindNearest(rayOriginPart.Position)
			if target then
				startGame(target.HumanoidRootPart)
			end
		end)
	end
	wait(10)
end
2 Likes

The final loop is repeatedly calling Runservice.Stepped:Connect which is going to create a multitude of connections (all firing). Define the connection outside the loop and put the conditional check inside it, like so.

Runservice.Stepped:Connect(function()
  if MinigameActive then
	local target = FindNearest(rayOriginPart.Position)
	if target then
		startGame(target.HumanoidRootPart)
	end
  end
end)

You will no longer need the while loop if you do that, but you will also be doing a lot of ray casting. If you only want to check every 10 seconds, then save the connection and disconnect it afterwards…

  local cnx = Runservice.Stepped:Connect(function() 
       -- do something
      cnx:Disconnect()
  end)

Wouldn’t this rapidly spam the function? Edit: also the damage per second turns into damage per milliseconds

It’s going to spam if that MinigameActive flag remains true after StartGame is called.

so what should I do to work around this? that is the only way I could think of looping it

if you want a loop that runs at a set interval of time, you can just use a coroutine.

I want it to run after the first one is finished so it keeps going

After looking at this closer, I see that you have it setup so when the user types “!EyeEvent” you want to start the minigame, and when they type “!EndEvent” you want to end it. For this, create a BindableEvent and then Fire it with a value of true or false depending on what the user types (to enable or disable the game). Then you create a connection to that event and can just spin a normal wait loop inside that connection (no need for run service). You will need to use a RemoteEvent if this is going local->server.

BindableEvent (roblox.com)

you could put something like this where you want to start the game:

MinigameActive = true
coroutine.wrap(function()
	while MinigameActive  do
		wait(update_rate)
		
		--- your game loop logic
		if MinigameActive then
			local target = FindNearest(rayOriginPart.Position)
			if target then
				startGame(target.HumanoidRootPart)
			end
		end
	end
end)()

so that every time the amount of seconds in the update_rate variable passes your game loop logic runs.
and the to stop it you just set MinigameActive = false

Okay, so I set bindable event, connect it, put a loop inside and run my function? or take the code out of function would be better

Here’s an example. Paste this into a new workspace and create a BindableEvent in replicated storage…

local m = game.ReplicatedStorage.Event

local running = false
m.Event:Connect(function(isRunning, id)
	print("called with", id)
	running = isRunning
	while running do
		print("Is running")
		wait(0.5)
	end
	print("ending")
end)

m:Fire(true, 1)
wait(3)
m:Fire(false, 2)

Here’s the output:

  16:29:33.760  called with 1  -  Server - Script:5
  16:29:33.760   ▶ Is running (x6)  -  Server - Script:8
  16:29:36.764  called with 2  -  Server - Script:5
  16:29:36.764   ▶ ending (x2)  -  Server - Script:11

You can see it ran for a while, and then both callbacks exited.

Woah! Okay I see, that’s really cool, haven’t really looked in bindable events, that is really interesting. Thanks! I will research that, any advice before hand?

BindableEvents are for local->local or server->server. If you need to do local->server or server->local, use a RemoteEvent. Also, whenn you are reading the docs, anytime you see RBXScriptSignal that means it’s an event (they are in the Events section) but that means you can :Connect to them. Lastly, know that events stay connected until you disconnect them or the item they are connected to is destroyed. Normally, you want to stay connected until it is destroyed, but in some instances you will want to disconnect manually.

1 Like

tysm I will look into that! I been struggling with this

Looking at that code, I see that if two Fire(true) are called, you will end up with two running loops, so you will want to debounce for that case…

	print("called with", id)

        -- only allow one loop to run
       if running and isRunning then return end

	running = isRunning

sir i am having some issues with it, i messaged you about it