API to get humanoid from basepart

As a developer, it’s weirdly hard to find the appropriate humanoid that controls a particular basepart.

Consider the problem: A lava brick has been touched, and now I need to figure out what humanoid touched it and deal damage.

The most common solution is to check the parent of the part for a humanoid, then check that parent’s parent and so on until you find a humanoid or run out of parents. This solution works for unaltered characters but it doesn’t work if the humanoid is nestled into a sub-model within its overall character.

The humanoid has a root part, that root part is part of an assembly, and that assembly contains other parts. I want an API that lets me work backwards from the other part to get the humanoid.

16 Likes

Also noting that the naive single hit.Parent solution doesn’t work if an accessory part touches because it’s nested inside an Accessory instance. You’d have to do the recursive search upwards, which I do not care to do for how often I write this code.

5 Likes
part.Touched:Connect(function(hit)
    local hum = hit.Parent:FindFirstChildOfClass("Humanoid")
    if hum then
        -- code
    end
end)

Are you sure we need that? It’s pretty easy to get the Humanoid. I’m using hit.Parent because I don’t think detecting accessory hits is a good idea.

2 Likes

This bit won’t always work, nested models/accessories won’t always have a humanoid in the next hierarchy.

1 Like
part.Touched:Connect(function(hit)
    local char = hit:FindFirstAncestorOfClass("Model")
    local hum = char:FindFirstChildOfClass("Humanoid")
    if hum then
        -- code
    end
end)
2 Likes

image
Code is not gonna work in this situation, as I said in my above post.

1 Like

Why don’t you use folders instead ?

1 Like

You should instead make an invisible hitbox union that you can detect touches on.

1 Like
part.Touched:Connect(function(Object)
    while not Object:FindFirstChildOfClass("Humanoid") and Object ~= workspace do
        Object = Object.Parent
    end
    local Human = Object.Parent:FindFirstChildOfClass("Humanoid")
    if Human then
        -- code
    end
end)

Why not use while loop to go upper?

2 Likes

Even better:

part.Touched:Connect(function(Object)
    while not Object:FindFirstChildOfClass("Humanoid") and Object ~= workspace do
        Object = Object:FindFirstAncestorOfClass"Model" -- FindFirstAncestorOffClass does less work sometimes.
    end
    local Human = Object.Parent:FindFirstChildOfClass("Humanoid")
    if Human then
        -- code
    end
end)
1 Like

Hit.Parent is one of the most annoying things developers decide to use when making kill bricks. Just create a custom hitbox. Unless you use R6, then it’s a little more understandable

1 Like

Sharksie has been a developer for an extremely long time, they know how to do this manually. They’re saying it’s too annoying, not impossible. I think this is a good idea.

2 Likes

Now that you mention it, this is a bit odd.

But we don’t have dedicated API for looking on the same plane of hierarchy. Now it’s not too difficult to make these as custom functions, plus FindFirstChild & FindFirstDescendant do look at the entire plane before going down, however FindFirstAncestor doesn’t when going up.

A potential feature request could be to have the following:

  • A way to look on the current plane of hiearchy: Instance:FindFirstSibling()
  • A way to look up recursively and check the entire plane: Either a change to Instance:FindFirstAncestor() (May break current scripts), or Instance:FindFirstRelative()

This would include all the extra versions such as OfClass, WhichIsA, Is...Of and the associated Added & Removed / Removing events.

Since I mentioned custom functions before, here’s some for the Sibling series:

-- Simply grabs the parent and uses the Child functions --

local function FindFirstSibling(object: Instance, name: string): Instance?
	local parent = object.Parent
	
	return (parent and parent:FindFirstChild(name))
end

local function FindFirstSiblingOfClass(object: Instance, class: string): Instance?
	local parent = object.Parent
	
	return (parent and parent:FindFirstChildOfClass(class))
end

local function FindFirstSiblingWhichIsA(object: Instance, className: string): Instance?
	local parent = object.Parent

	return (parent and parent:FindFirstChildWhichIsA(className))
end

local function GetSiblings(object: Instance): {Instance}
	local parent = object.Parent
	
	return (parent and parent:GetChildren())
end

local function IsSiblingOf(object: Instance, sibling: Instance): boolean
	return (object.Parent == sibling.Parent)
end

local function SiblingAdded_Connect(object: Instance, func: (child: Instance) -> ()): RBXScriptConnection? -- Along with Once, Wait & ConnectParallel --
	local parent = object.Parent
	local connection: RBXScriptConnection?
	
	if parent then
		connection = parent.ChildAdded:Connect(func)
	end
	
	return connection
end

local function SiblingRemoved_Connect(object: Instance, func: (child: Instance) -> ()): RBXScriptConnection? -- Along with Once, Wait & ConnectParallel --
	local parent = object.Parent
	local connection: RBXScriptConnection?

	if parent then
		connection = parent.ChildRemoved:Connect(func)
	end

	return connection
end
5 Likes