This script in Roblox detects and highlights the nearest interactable object in front of a player’s character. It uses Inverse Kinematics (IK) to make the character’s head follow these objects if they’re within a specified radius. The system updates as players join or leave the game, ensuring smooth interaction and cleanup.
Create a Tag named “interactable” and tag it Part or Object you can put the tagged object in different Folder or Same Folder
Create a Folder Named “Ignore” on WorkSpace to block the LookAt
Showcase:
If The Clip Wont Start or Work Copy and paste fr
https://www.youtube.com/shorts/QTU4Ds1VEqU
Create Local Script in StarterPlayerScripts
Setting Up Services and Variables
local RunService = game:GetService("RunService")
local CollectionService = game:GetService("CollectionService")
local Radius = 10
local Parts = {}
- RunService: In this script it is used to run functions continuously, such as updating animations or interactions.
- CollectionService: Think of this as a tagging system. It helps us find and group objects in the game that are tagged with specific tag, like “interactable.”
- Radius: This defines how far away something can be for a player to interact with it. I have set it to 10 units.
- Parts: A table that we’ll use to keep track of special parts associated with each player, which helps us manage where they’re looking.
Checking If an Object is In Front of the Character
local function IsInFrontOfCharacter(character, position)
Declaring IsInFrontOfCharacter function to determine if a given position (like an interactable object) is in front of the character and not behind them or to the side.
local rootPart = character.HumanoidRootPart
local rootPosition = rootPart.Position
local direction = (position - rootPosition).unit
local lookVector = rootPart.CFrame:vectorToWorldSpace(Vector3.new(0, 0, -1))
local toPosition = (position - rootPosition).unit
local isInFront = lookVector:Dot(toPosition) > 0
Here, we figure out the direction from the character to the object and compare it with the direction the character is facing. We use a dot product to check if the object is in front of the character (this is a bit of math magic that helps us determine direction).
if not isInFront then
return false
end
If the object isn’t in front, we can immediately say “nope, not in front” and return false.
local ray = Ray.new(rootPosition, direction * (position - rootPosition).Magnitude)
local hitPart, _ = workspace:FindPartOnRay(ray)
if hitPart and hitPart:IsDescendantOf(workspace.Ignore) then
-- if part is blocked by a wall or obstacle
return false
end
return true
We then send out an invisible line (a “ray”) from the character to the object to see if anything is blocking the view. If the object is behind a wall or some obstacle (not in our “ignore” list), we return false. If everything is clear, we return true, meaning the object is in front and visible.
Finding the Nearest Interactable Object
local function GetNearestInteractable(player)
- Declaring a function that looks for the closest object that the player can interact with.
local character = player.Character
if not character then
return nil
end
- First, we check if the player has a character (sometimes characters might not be loaded or present).
local headPosition = character.Head.Position
local nearestInteractable = nil
local nearestDistance = Radius
- We get the position of the character’s head and set up a starting point for our search.
nearestInteractable
will store the closest object we find, andnearestDistance
helps us keep track of how far away it is.
local interactables = {}
for _, interactable in pairs(CollectionService:GetTagged("interactable")) do
table.insert(interactables, interactable)
end
- We gather all objects tagged as “interactable” into a list.
table.sort(interactables, function(a, b)
local distanceA = (headPosition - a.Position).Magnitude
local distanceB = (headPosition - b.Position).Magnitude
return distanceA < distanceB
end)
- We sort these objects by how close they are to the player’s head, with the nearest ones first.
for _, interactable in ipairs(interactables) do
local distance = (headPosition - interactable.Position).Magnitude
if distance <= Radius and IsInFrontOfCharacter(character, interactable.Position) then
nearestInteractable = interactable
nearestDistance = distance
break
end
end
- We go through the sorted list and check each object to see if it’s within our interaction radius and in front of the character. We stop at the first one that meets these criteria.
return nearestInteractable
- Finally, we return the closest valid object, or
nil
if there aren’t any.
Setting Up for a CharacterAdded
local function CharacterAdded(character)
This function sets up everything when a new character (player) is added to the game.
local player = game.Players:GetPlayerFromCharacter(character)
if not player then
return
end
We identify which player the character belongs to. If we can’t find a player, we stop.
local target = Parts[player]
local defaultPosition = character:WaitForChild("HumanoidRootPart").CFrame:pointToWorldSpace(Vector3.new(0, 0, -7))
local isInteracting = false
We prepare a part (target) that helps track where the character is looking. We also define a default position for it and set isInteracting
to false initially.
local ik = Instance.new("IKControl")
ik.Type = Enum.IKControlType.LookAt
ik.ChainRoot = character:WaitForChild("UpperTorso")
ik.EndEffector = character:WaitForChild("Head")
ik.Weight = 0.999
ik.Parent = character.Humanoid
We create an IKControl instance, which makes the character’s head and body move naturally when looking at objects.
local function UpdateIK()
This function updates the character’s IK control based on nearby interactable objects.
local nearestInteractable = GetNearestInteractable(player)
if nearestInteractable then
local headPosition = player.Character.Head.Position
local targetPosition = nearestInteractable.Position
local distanceToInteractable = (headPosition - targetPosition).Magnitude
if distanceToInteractable <= Radius then
local direction = (targetPosition - headPosition).unit
local ray = Ray.new(headPosition, direction * distanceToInteractable)
local hitPart, _ = workspace:FindPartOnRay(ray)
if hitPart and hitPart:IsDescendantOf(workspace.Ignore) then
nearestInteractable = nil
end
else
nearestInteractable = nil
end
end
if nearestInteractable then
isInteracting = true
ik.Weight = 0.999 -- Enable IK behavior
ik.Target = nearestInteractable
target.Position = nearestInteractable.Position
else
isInteracting = false
ik.Weight = 0 -- disable IK behavior
if player.Character then
local headPosition = player.Character.Head.Position
local lookVector = player.Character.Head.CFrame:vectorToWorldSpace(Vector3.new(0, 0, -7))
target.Position = target.Position:Lerp(headPosition + lookVector, 0.2)
end
end
end
This part determines if there’s a valid interactable object nearby. If so, it adjusts the IK control so the character looks at the object. If not, it stops the IK control.
character.Humanoid.Died:Connect(function()
ik:Destroy()
target:Destroy()
end)
If the character dies, we clean up by destroying the IK control and target part.
RunService.RenderStepped:Connect(UpdateIK)
We connect the UpdateIK
function to the game loop, so it updates every frame.
Handling New Players
local function PlayerAdded(player)
This function is called whenever a new player joins the game.
local Target = Instance.new("Part")
Target.Anchored = true
Target.CanCollide = false
Target.CanQuery = false
Target.CanTouch = false
Target.Transparency = 0.5
Target.Size = Vector3.new(1, 1, 1)
Target.Parent = workspace
Parts[player] = Target
if player.Character then
CharacterAdded(player.Character)
end
player.CharacterAdded:Connect(function()
CharacterAdded(player.Character)
end)
We create a transparent part for the player to indicate where they’re looking, and store it in the Parts
table. If the player already has a character, we set up the interaction system. We also set up an event to do this whenever the player’s character spawns.
Cleaning Up When a Player Leaves
local function PlayerRemoved(player)
if Parts[player] then
Parts[player]:Destroy()
Parts[player] = nil
end
end
This function is called when a player leaves the game. It removes the target part associated with them from the game and the Parts
table.
Setting Up for Current Players
for _, player in ipairs(game.Players:GetPlayers()) do
PlayerAdded(player)
end
Overall Script:
local RunService = game:GetService("RunService")
local CollectionService = game:GetService("CollectionService")
local Radius = 10
local Parts = {}
local function IsInFrontOfCharacter(character, position)
local rootPart = character.HumanoidRootPart
local rootPosition = rootPart.Position
local direction = (position - rootPosition).unit
local lookVector = rootPart.CFrame:vectorToWorldSpace(Vector3.new(0, 0, -1))
local toPosition = (position - rootPosition).unit
local isInFront = lookVector:Dot(toPosition) > 0
if not isInFront then
return false
end
local ray = Ray.new(rootPosition, direction * (position - rootPosition).Magnitude)
local hitPart, _ = workspace:FindPartOnRay(ray)
if hitPart and hitPart:IsDescendantOf(workspace.Ignore) then
-- if part is blocked by a wall or obstacle
return false
end
return true
end
local function GetNearestInteractable(player)
local character = player.Character
if not character then
return nil
end
local headPosition = character.Head.Position
local nearestInteractable = nil
local nearestDistance = Radius
local interactables = {}
for _, interactable in pairs(CollectionService:GetTagged("interactable")) do --tag Name
table.insert(interactables, interactable)
end
table.sort(interactables, function(a, b)
local distanceA = (headPosition - a.Position).Magnitude
local distanceB = (headPosition - b.Position).Magnitude
return distanceA < distanceB
end)
for _, interactable in ipairs(interactables) do
local distance = (headPosition - interactable.Position).Magnitude
if distance <= Radius and IsInFrontOfCharacter(character, interactable.Position) then
nearestInteractable = interactable
nearestDistance = distance
break
end
end
return nearestInteractable
end
local function CharacterAdded(character)
local player = game.Players:GetPlayerFromCharacter(character)
if not player then
return
end
local target = Parts[player]
local defaultPosition = character:WaitForChild("HumanoidRootPart").CFrame:pointToWorldSpace(Vector3.new(0, 0, -7))
local isInteracting = false
local ik = Instance.new("IKControl")
ik.Type = Enum.IKControlType.LookAt
ik.ChainRoot = character:WaitForChild("UpperTorso")
ik.EndEffector = character:WaitForChild("Head")
ik.Weight = 0.999
ik.Parent = character.Humanoid
local function UpdateIK()
local nearestInteractable = GetNearestInteractable(player)
if nearestInteractable then
local headPosition = player.Character.Head.Position
local targetPosition = nearestInteractable.Position
local distanceToInteractable = (headPosition - targetPosition).Magnitude
if distanceToInteractable <= Radius then
local direction = (targetPosition - headPosition).unit
local ray = Ray.new(headPosition, direction * distanceToInteractable)
local hitPart, _ = workspace:FindPartOnRay(ray)
if hitPart and hitPart:IsDescendantOf(workspace.Ignore) then
nearestInteractable = nil
end
else
nearestInteractable = nil
end
end
if nearestInteractable then
isInteracting = true
ik.Weight = 0.999 -- Enable IK behavior
ik.Target = nearestInteractable
target.Position = nearestInteractable.Position
else
isInteracting = false
ik.Weight = 0 -- disable IK behavior
if player.Character then
local headPosition = player.Character.Head.Position
local lookVector = player.Character.Head.CFrame:vectorToWorldSpace(Vector3.new(0, 0, -7))
target.Position = target.Position:Lerp(headPosition + lookVector, 0.2)
end
end
end
character.Humanoid.Died:Connect(function()
ik:Destroy()
target:Destroy()
end)
RunService.RenderStepped:Connect(UpdateIK)
end
local function PlayerAdded(player)
local Target = Instance.new("Part")
Target.Anchored = true
Target.CanCollide = false
Target.CanQuery = false
Target.CanTouch = false
Target.Transparency = 0.5
Target.Size = Vector3.new(1, 1, 1)
Target.Parent = workspace
Parts[player] = Target
if player.Character then
CharacterAdded(player.Character)
end
player.CharacterAdded:Connect(function()
CharacterAdded(player.Character)
end)
end
local function PlayerRemoved(player)
if Parts[player] then
Parts[player]:Destroy()
Parts[player] = nil
end
end
for _, player in ipairs(game.Players:GetPlayers()) do
PlayerAdded(player)
end
game.Players.PlayerAdded:Connect(PlayerAdded)
game.Players.PlayerRemoving:Connect(PlayerRemoved)
bye bye.