I’d like to improve my script which it’s goal is to constantly find what parts are touching with my tool’s handle and if those parts can be ignored or not, it’s taking a lot of script activity and I need it to update in quite a fast rate, any help would be nice.
repeat wait(0.5) until game:GetService("ReplicatedStorage").GlobalReady.Value == true
local Player = script.Parent.Parent.Parent.Parent or _G.Players:GetPlayerFromCharacter(script.Parent.Parent.Parent)
local Wand = script.Parent.Parent
local function IsInTable(TableValue, ToFind)
local Found = false
for _, v in pairs(TableValue) do
if v == ToFind then
Found = true
break
end
end
return Found
end
local function GetTouchingParts(Part)
local Connection = Part.Touched:Connect(function() end)
local Results = Part:GetTouchingParts()
Connection:Disconnect()
return Results
end
while _G.RunService.Heartbeat:Wait() do
local Results = GetTouchingParts(Wand.Handle)
local ResultsToRemove = {}
for i, v in pairs(Results) do
if v:IsDescendantOf(Player.Character) or v:IsDescendantOf(workspace.Objects) then
table.insert(ResultsToRemove, i)
else
local Shield = workspace.Shields:FindFirstChild(Player.Name.."'s Shield")
if Shield ~= nil then
if v == Shield then
table.insert(ResultsToRemove, i)
end
end
local Found = IsInTable(_G.TransparentObjects, v)
if Found == true then
table.insert(ResultsToRemove, i)
end
end
end
local RealNumber = #Results - #ResultsToRemove
if Player.Name == "royee354" and Wand.Print.Value == true then
print(RealNumber)
print(unpack(Results))
end
if RealNumber == 0 then
Wand.Locked.Value = false
else
Wand.Locked.Value = true
end
end
If a value is a boolean value, you do not need to check if it is true.
game:GetService("ReplicatedStorage").GlobalReady.Value == true
can be changed to game:GetService("ReplicatedStorage").GlobalReady.Value
Regarding the IsInTable function, after the if v == ToFind then statement, you can just return true and remove the break. You will need to keep return Found at the end in case it is never found.
So the main thing using activity here is the :GetTouchingParts(). That is an incredibly expensive operation so we gotta find something else.
I would try making a ray starts at the side closest to the characters body and ends just slightly passed the tip of the wand.
Also if you use whitelist for ray casting you can get even better results.
Where would you start the ray exactly? What I’m trying to prevent is shooting through walls which is possible when the projectile ray is fired from inside of a part, so idk where would I fire that ray to check if the tip is touching anything
When it comes to wallshooting for anything involving projectiles (especially firearms), I send out two raycasts: one from the player’s head to check if the character’s area is obstructed and then another one as the actual projectile ray, which can double over into a second obstruction check. The second is usually at the tip of the item, so for a gun it’d be the muzzle and for a wand it’d be right at the center of the tip.
If your code works and you’re just looking for people to look over it, you could put it under Code Review next time. Also, even though not strictly related to the performance issues, I would like to chime in with a couple things that could be improved.
Your first line is a looped check:
repeat wait(0.5) until game:GetService("ReplicatedStorage").GlobalReady.Value == true
This can be changed to an event based approach, since these kinds of loops are bad practice. I recommend:
local ready = game:GetService('ReplicatedStorage').GlobalReady
if not ready.Value then
ready.Changed:Wait()
end
This checks if the value of ready is false, and if it is, it’ll wait for as soon as it changes into true to continue the :Wait().
Second, your Player line is a bit confusing. Relying on a .Parent to be nil sounds like an accident waiting to happen. I don’t have a lot of advice on how to fix this since I don’t know how the rest of your game works, but I’d say find a way to keep a unified reference to your player somewhere.
As for your IsInTable, if it’s an array, you can probably get away with replacing it with a call to table.find(). Such as local Found = table.find(_G.TransparentObjects, v) ~= nil.
Lastly, although again I don’t have any specific advice, your final loop seems a bit redundant. Try to find an event that aligns with the behavior you want instead, because a heartbeat loop will have spent a lot of dead time just wasting resources.
And how often do you check for the obstruction checks, I put a heartbeat loop on mine as it seems the only thing that will prevent someone from being able to cast through walls, but im not sure its the most efficient choice
Only when the player passes firing input (click) and the client or server, depending on how I implement the networking and workflow of projectile casting, determines that it’s appropriate to start casting a shot. There’s no reason to check for obstructions if the player did not request to fire their tool.
Also if you don’t mind me asking, what is your way of firing your projectiles? That’s just so I can improve my own wand system, what I’m doing currently is generating cframes with a ray on the server and moving the projectile on all clients by tweening it every time a new cframe is being generated using a value that’s constantly updated each step and is inside the bullet