I’m creating a push system similar to the one in Fall Guys for a king of the hill game. I’m wondering how I can detect the closest player to the character, and run a function if it’s in arms reach.
Something like
mouse.Button1Up:Connect(function()
local closestplayer = -- find who's closest to the player who fired the event
local Distance = (closestplayer - player).Magnitude
if distance < maxdistance then
end)
I would recommend using a ray cast from the player’s humanoid root part to detect if there is a character in front of them and you would need to multiply the ray’s direction (ray.direction) by the max distance a player can be from the player who clicked.
You do not need to raycast to find the nearest player. The easiest way is a standard iteration:
local function FindNearestPlayer(Player)
local Character = Player.Character or Player.CharacterAdded:Wait()
local Nearest, NearestDistance = nil, math.huge
for i, OtherPlayer in ipairs(game:GetPlayers()) do
local OtherCharacter = OtherPlayer.Character
local Distance = (Character.Position - OtherCharacter.Position).Magnitude
if Distance < NearestDistance then
Nearest, NearestDistance = OtherPlayer, Distance
end
end
return Nearest, NearestDistance
end
While standard iteration is the simplest approach, iteration + raycasting should be more effective for your needs, that is, an actual arm’s reach and not only proximity.
Raycasting should eliminate all cases where the player is close enough, but is concealed behind an object.
All of this is covered in the comments in the appended script, along with replaced mouse event for UserInputService.InputBegan (you could also replace it with ContextActionService for easier mobile support).
local PlayerService = game:GetService("Players")
local UIS = game:GetService("UserInputService")
local ARM_REACH = 10
local localPlayer = PlayerService.LocalPlayer
local character = localPlayer.Character or localPlayer.CharacterAdded:Wait()
local hrp = character:WaitForChild("HumanoidRootPart")
-- Ignore player's own character while casting rays.
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {character}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
local function FindNearestReachablePlayer(): (Player, number)
local reachablePlayer: Player
local distance = math.huge
for _,otherPlayer in PlayerService:GetPlayers() do
-- Skip all players without characters/root parts.
local primaryPart = otherPlayer.Character and otherPlayer.Character.PrimaryPart
if not primaryPart then continue end
-- Find the distance between the primary parts.
local mag = (hrp.CFrame.Position - primaryPart.CFrame.Position).Magnitude
-- If the distance is out of arm's reach, skip.
-- If there's a closer reachable player already, skip as well.
if mag > ARM_REACH or mag > distance then continue end
-- Raycast towards the nearby player.
local raycastResult = workspace:Raycast(
hrp.CFrame.Position, primaryPart.CFrame.Position - hrp.CFrame.Position, raycastParams
)
if raycastResult and raycastResult.Instance then
-- Have we hit something other than that player?
-- That means the player is possibly behind an obstacle.
if PlayerService:GetPlayerFromCharacter(raycastResult.Instance.Parent) ~= otherPlayer then
continue
end
-- Update the variables with the new information.
distance = mag
reachablePlayer = otherPlayer
end
end
return reachablePlayer, distance
end
UIS.InputBegan:Connect(function(inputObj, gameProcessed)
if gameProcessed then return end
if inputObj.UserInputType == Enum.UserInputType.MouseButton1 then
print(FindNearestReachablePlayer())
end
end)
P.S. Although the code works, it has not extensively tested. You should look for various edge cases like raycasting through water.