Simple change to optimize your spatial query methods/touched events

Introduction

Hello there, in this tutorial you will learn how to optimize your touched events which utilize the player’s character. Such as killbricks. To make them more responsive/reactive and reduce delay. This tutorial can also be applied to other detection methods (eg hitboxes/gun systems) that utilize spatial queries such as raycasting, getpartsboundsinbox etc.

Tutorial

Let’s take killbricks as an example. Many scripters, with years of experience. Handle killbricks like this.

Part.Touched:Connect(function(Hit)
local Character = Hit.Parent
local Humanoid = Character:FindFirstChild("Humanoid")
if Humanoid then
Humanoid.Health = 0
end
end)

What is the problem here? Well, findfirstchild. You see, findfirstchild is 20% slower than the dot operator and almost takes almost 8 times longer than simply storing a reference to an object.

This means that the killbrick’s effects will be delayed. Making your killbrick feel less reactive. However, there is a very simple fix that will allow you to get rid of this small delay aswell.

Solution

In this example of the killbrick. We can verify if the basepart is a child of a player character through the use of the :GetPlayerFromCharacter method that is available from the Players service.

Part.Touched:Connect(function(Hit)
local Character = Hit.Parent
local IsPlayer = Players:GetPlayerFromCharacter(Character)
if IsPlayer then
Character.Humanoid.Health = 0
end
end)

Now, our killbrick’s effects will be more responsive! In the example of a killbrick and in other similiar scenarios utilizing touched events, the delay is quite minimal and not very problematic because well it’s just a killbrick (although the solution is so easy to implement you might aswell do it). But depending on your system and the state of the client/server the delay could actually mean alot more. Here’s another example.

Let’s say you have a gun system/hitbox that utilizes raycasting or other spatial query methods for hit detection. You use findfirstchild to check if the detected basepart is part of a player’s character and contains a humanoid. The delay is much more problematic here where you’d want the hit detections to have very few delays obviously since a hitbox/gun system is much more important than a simple killbrick.

The fix again would be to use other methods to verify a player character other than findfirstchild. Such as :GetPlayerFromCharacter.

Also, in a live game your server/client may be slower for whatever reason. And findfirstchild utilized in these systems only enhances the problem.

This is not to bash on findfirstchild or say that you should avoid using it. It definitely has its uses. But many scripters even with years of experience are applying it in the wrong way. The most common incorrect application of findfirstchild is with killbricks.

2 Likes

You only really notice the delay from FindFirstChild in extremely hot loops, such as creating like a few thousand entities based on instances, but for stuff like hit detection it’s microscopically problematic. You’re gonna have more slow downs from the algorithm/method youre using for hit detection than a findfirstchild, considering most instances dont really have that many children

4 Likes

cant tell if this is a troll post or not lol, findfirstchild and dot indexing run at a fraction of a millisecond

3 Likes

That is not true. FindFirstChild() and other non yielding methods do not delay anything, unless they start reducing your fps. I am also doubtfull that :GetPlayerFromCharacter() is faster, or signficiantly faster, than :FindFirstChild() (not that it matters though…). I don’t have any benchmarks

In your example code, there will be delay if it is ran from the server. If ran from the client, there wont be any delay

:FindFirstChild() doesn’t affect anything

To everyone who is saying the difference is very small. I agree, I did a benchmark test myself using os.clock to check which route is faster, getplayerfromcharacter or findfirstchild. And getplayerfromcharacter is faster by a very small margin It’s a very small optimization. But why would you even want such a small delay to your effects? When there is an easy fix for it? The code is basically the same length and getplayerfromcharacter can actually be seen as a cleaner/more understandable method in systems.

local IsPlayer = game.Players:GetPlayerFromCharacter(Character)
if IsPlayer then
-- Effects here
end
local Humanoid = basepart.Parent:FindFirstChild("Humanoid")
if Humanoid then
-- Effects here
end

Besides all that, it’s bad practice to use findfirstchild like this. In the example of a killbrick, tons of scripters will utilize findfirstchild humanoid to check if a player touched the part. But you don’t need to use findfirstchild here since there are other methods available. Findfirstchild should only be used when necessary.

1 Like

They do not delay anything. Yes I agree, but in these situations since findfirstchild is 20% slower than using the dot operator your effects have a small delay. Maybe I should’ve clarified more clearly what I meant by delay and effect.

In the example of that killbrick, using findfirstchild to check for a humanoid is slower than using getplayerfromcharacter. So your “effects” (i’m referring to setting the humanoid’s health to 0 in the killbrick as the effect) will be delayed. I said the same word again lol but hopefully that made sense.

Are you referring to the killbrick example? There will still be a small delay for your effects whether its on server/client since using findfirstchild to check for a humanoid is slower than using getplayerfromcharacter.

Findfirstchild will be your added bonus on the side to an already slow server/client. Since the effects will have an extra little delay.

Yes I agree, that’s why I said there is a very minimal delay. But my question is why would you even want a small delay there when the fix is very simple to do? Especially in hitboxes/gun systems where you’d want to prevent delays as much as you possibly can.

That’s not true, getplayerfromcharacter is equivalent to looping through every player object and checking if their character matches the character you inputted. Which is faster by a bit. And I am confused by you saying that instances dont really have many children? A player’s character contains quite alot of children i’d say but eh fair enough I guess we have different definitions of many.

I am not too sure how you use the word delay, but in this case, it is not the right word

When talking about performance of methods, it’s not a question of delay, but, well performance. If a method is less efficient, it wont add any delay, since, if your game is running at 60 fps, there is a constant 16.66ms delay between each frame, no matter if your code take 4ms, of 8ms (as long as it doesn’t exceed 16.66ms).

However, especially if you run things on RenderStepped (now PreRender?), there can be latency (which might be what you are referring to?). If you want the user to see something as soon as possible, you have to use PreRender to execute the code right before rendering, and if you have a lot of things inside of PreRender, then it can delay the screen rendering. The effects of latency is that there will be more time between an input from the user, and a render from the screen. I am not sure where a killbrick fits into that. It would probably be preferable to keep the killbrick outside of PreRender, to prevent adding latency, and at that point, optimization is a question of performance (getting better fps) rather than reducing latency

(Also the physics step happens after the screen rendering, so if you’d want to do it in PreRender, you would have to wait for the PreRender on the next frame. Might as well do it on Hearthbeat in the same frame, which will be rendered on the next frame)

1 Like

Oh you must be thinking about performance. Performance wise none of this is problematic at all,. I’m talking about the delay of your effects (by effects i mean the code that is ran after you check if the character is a player’s character). Using getplayerfromcharacter instead of findfirstchild will mean your effects happen faster. Even marginally. Does that make sense?

if IsPlayer then
-- I count this stuff in here as effects
end

Maybe it’s because I had the word optimize. No performance wise findfirstchild/getplayerfromcharacter is not any better from each other. I don’t use coding terminology that often or know that much coding terminology and neither do I usually use it correctly lol so thats on me. I wasn’t really sure how to word it. Delay of effects is the right terminology afaik.

That makes even less sense? I’ve talked about performance specifically because performance wise it would make a difference (albeit extremely small). Does the latency explanation in my reply make sense?

theres something called premature optimization and this is exactly it. you dont need to save that fraction of a milisecond especially since killbricks arent running every single frame

either method here works and wont delay or slow down your game at all, just use what you prefer

or if you love the dot operator, wrap parent.Humanoid in a pcall (this is a joke)

1 Like

What part makes less sense? What exactly do you not understand? Using findfirstchild to check for a humanoid is slower than using getplayerfromcharacter. So the code that is meant to run after the findfirstchild check is executed slightly slower than the code that is ran after using a getplayerfromcharacter check.

When I script, I don’t consider the time it takes for a method to execute, for delays. I consider frames, and how many frames a method yields for (if it does yield). I only really consider the time a method takes when it comes to performance, ie, ensuring fps remains high

For me, having something that happens either early in a frame, or late in a frame, doesn’t really make a difference, since they happened in the same frame (and will be rendered at the same time, if they are both before, or after the rendering step)

How I see it is that both FindFirstChild() and :GetPlayerFromCharacter() run within the same frame (ie don’t yield), and both wont cause performance issues. However, if I start thinking about the code being executed on the server, then it’ll take a couple of frames for that to replicate to the client, creating a delay

However, the time it takes for FindFirstChild() or :GetPlayerFromCharacter() to execute can contribute to latency if they are executed before the rendering step, which is what I’ve mentioned in my second reply, about latency

Fair enough, I don’t really consider how long it takes my code to execute either. But in this scenario with findfirstchild is the only place where I do think about it.

Using getplayerfromcharacter in the place of findfirstchild humanoid here is very easy, has a slight benefit, and objectively looks cleaner and makes alot more sense. I can understand not wanting to change much code when your script is 2k+ lines but this is literally a 1-2 code line change. Plus, using findfirstchild humanoid in these scenarios of an hitbox or a kill brick is actually an incorrect application of the method. You should only be using findfirstchild when necessary. Not when there are better alternatives.

Like I said earlier, the delay is minimal and it doesn’t matter much in a killbrick script. But when you’re creating a hit box system for example why would you even want such a small delay??

You may say it’s premature optimisation but I disagree.

i have a projectile system that has be tested with over 80 players all firing guns at the same time. it uses FindFirstChild for the hit detection and there was never a fps drop or “delay” in the code
(being more precise, it uses raycasts to detect the hit, FindFirstChild to validate it)

now i see where you are getting from saying to use GetPlayerFromCharacter instead of checking if the model has a humanoid. that makes it so only players are detected instead of NPCs and players, and if you want that you should use GetPlayerFromCharacter

but in the pure sense of speed, using FindFirstChild or GetPlayerFromCharacter has no difference they run at speeds no human can tell the difference. most of the time just checking if the model has a humnoid is sufficient enough

premature optimization happens when you optimize something without knowing it has performance issues in the first place. and like said before the difference in speed isnt enough to “optimize” this

final thing, not as important, the use of FindFirstChild or GetPlayerFromCharacter is subjective not objective, coding style is purely on preference

2 Likes

Of course there won’t be an fps drop lol I don’t know where I am implying any performance issues like that at all.

I am suggesting getplayerfromcharacter not just to check if it’s an npc or an actual player. But because it’s also a lot cleaner and it is slightly faster.

There is a delay in your code but obviously it’s not humanly noticeable to anyone. Question is why would you want that slight delay at all? And using findfirstchild the way you are is bad practice (in my opinion).

The aim is to produce clean + optimized code. If you can optimise wherever possible very easily and make your code cleaner then why not?

Why not set an object attribute to the Humanoid for every part in the character while we’re at it

glad to see i was already writing the most optimized killbrick code there is
thank you :+1:

You can use pcall
Etc:

local try,hum = pcall(GetProperty,Character,"Humanoid")
1 Like