I’m doing a separate project for testing skills, which is basically a “Button Simulator” type game. I tried using the .Touched event for when a player touches a button, but it just doesn’t work well. The event doesn’t fire continuously; it only fires once, even when the player is standing on the part.
Also, I want it to be client-sided, and fire the server to update the stats and request the server for checking if the stat is greater than or equal to the minimum. I thought about using a Heartbeat connection for checking locally if the player is within at least 0.05 studs away from the part, but wouldn’t it be way too laggy? Imagine I have 300 buttons in the game; it would be massively laggy running through 300 buttons each frame.
Imagine I have 300 buttons in the game; it would be massively laggy running through 300 buttons each frame.
This is the cost of doing your own touch detection system. If you really want to do this, I would suggest having one central heartbeat function (instead of 300 different ones) and do all your checks in there. Alternatively you could have a loop running every 0.05 seconds or however long you want doing the same checks. I would look into using GetPartBoundsInRadius(), as I believe it’s faster than calculating magnitude every frame.
Alright. I would still say that it’s going to be expensive no matter what you do, if you want to do it custom. I would also say only fire the events when there’s something notable.
Instead of checking every button every frame, you can check for buttons around the player, every frame. Since you’ll make it client sided, you only have to check around the character, that’s 1 operation instead of 300
Can you make an example? Like, a code to demonstrate what you mean? (I just figured out using Raycast would be kinda weird, since I would need to add various raycast casting to all directions)
As mentioned earlier, it’s inefficient to check every button in the game individually. A more optimized approach is to check only for buttons that are close enough the the local player.
Instead of relying on the .Touched event, which does fire “continously,” I reccomend using the :GetTouchingParts() method. This functionr eturns a list of all parts currently touching a specified BasePart.
By periodically calling :GetTouchingParts() (say: each heartbeat, or every 0.05 seconds) on the buttons in range of the player, you can check if any of the touching parts are associated with the player. If so, you can trigger the appropriate function while the player remains in contact with the button.
Yes, that’s exactly what I meant! Just one small detail:
You’re currently checking wether the distance from the player to the button is greater than the minimum distance:
This should actually be less, otherwise you’ll end up checking buttons that are far away instead of the nearby ones:
if (button.Position - HRP.Position).Magnitude <= minDistance then
And if you’re into the more technical side of things (I like to nerd out on security): if the client is making changes to data on the sever, it is curtial to also verify the player’s distance to the button on the server side. Otherwise, a malicious player could potentially activate buttons from a distance, bypassing any local checks.
But if that’s not your jam, no worries! Either way, yes, this is what I meant!
Thanks! This may work. Regarding how to detect if the button:GetTouchingParts() array has player parts, I would use a for loop for that?
local function HasPlayerParts(TouchingParts : {BasePart}) : boolean
for _, part in TouchingParts do
if part:FindFirstAncestor(char.Name) then
return true
end
end
return false
end
RS.Heartbeat:Connect(function()
for _, button in buttons do
if (button.Position - HRP.Position).Magnitude < minDistance then
local touching = button:GetTouchingParts()
if touching and HasPlayerParts(touching) then
end
end
end
end)
The code you’ve created looks great! This is exactly what I had in mind!
I’m still missing a bit of context regarding your specific use case, but from what I understand, you want a function to be called on each heartbeat. That’s exactly what your current setup does. The only thing left is to insert the function you’d like to call when the button is pressed inside this if statement:
I’ve made it to work. I’ve used worldRoot:GetPartsInPart() to check if there are any parts inside it. Thanks for the help, though!
EDIT: The new script is here:
local RS = game:GetService("RunService")
local CS = game:GetService("CollectionService")
local RpS = game:GetService("ReplicatedStorage")
local events = RpS.Events
local unreliables = events.Unreliables
local updateStats = unreliables.UpdateStats
local buttons : {BasePart} = CS:GetTagged("Button")
local char = script.Parent
local HRP : BasePart = char:WaitForChild("HumanoidRootPart")
local minDistance = 5
local params = OverlapParams.new()
params.FilterType = Enum.RaycastFilterType.Include
params.FilterDescendantsInstances = (function()
local parts = {}
for _, basepart in char:GetDescendants() do
if basepart:IsA("BasePart") then
table.insert(parts, basepart)
end
end
return parts
end)()
local connection : RBXScriptConnection
local function IsTouching(button : BasePart) : boolean
return #workspace:GetPartsInPart(button, params) > 0
end
local function HandleNearButton(button : BasePart) : ()
if connection.Connected then
connection:Disconnect()
end
updateStats:FireServer(button)
task.wait(.5)
end
local function FindNearButton() : ()
for _, button in buttons do
if (button.Position - HRP.Position).Magnitude < minDistance then
while IsTouching(button) do
print("Is touching "..button.Name)
HandleNearButton(button)
end
if not connection.Connected then
connection = RS.Heartbeat:Connect(FindNearButton)
end
end
end
end
connection = RS.Heartbeat:Connect(FindNearButton)