Would like to improve some bit of code (updating tables)

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. :slight_smile:

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

I think could be replaced by:

Wand.Locked.Value = not (RealNumber == 0)
1 Like
  1. 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

  1. 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.
1 Like

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.

2 Likes

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.

1 Like

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