Pulse.Aim
Pulse.Aim handles the common geometric and camera problems that come up when building targeting features. It does the math — you provide the list of targets and the rules.
FOV check
Is a world position inside a pixel-radius circle centred on the screen?
if Pulse.Aim.inFOV(part.Position, radius()) then
-- part is visible on screen and within the circle
end
Internally this projects the world position to screen space and checks pixel distance from the screen centre. Returns false if the position is off-screen or behind the camera.
Screen distance
Pixel distance from the screen centre to a world position:
local dist = Pulse.Aim.screenDist(nape.Position)
Returns math.huge if behind the camera. Useful for sorting targets yourself when findNearest isn't flexible enough.
Camera snap (lookAt)
Move the camera to face a world position:
Pulse.Aim.lookAt(nape.Position)
This drives VirtualInputManager:SendMouseMovementEvent to keep the camera delta in sync with the rest of the engine. Call it inside a BindToRenderStep handler with RenderPriority.Last for smooth lock-on.
Note: lookAt requires your executor to support VirtualInputManager. Most do, but not all. Test it and handle the case where it doesn't work.
Finding the nearest target
local nearest = Pulse.Aim.findNearest(candidates, {
fovRadius = radius(), -- only consider entities inside the FOV (optional)
maxDist = 500, -- world-space distance cap in studs (optional)
getRoot = getAimRoot, -- function returning the position part (optional)
filter = function(t) -- additional filter (optional)
return not func.IsTitanDead(t)
end,
})
candidates is any array of models or parts — you build this list yourself. Pulse.Aim doesn't know where your game stores targets.
getRoot is a function from entity to BasePart. Pulse uses HumanoidRootPart by default. For large NPCs where the HRP is at the waist (often off-screen), provide a custom getter that returns the head or nape:
local function getAimRoot(entity)
local hitboxes = entity:FindFirstChild("Hitboxes")
local nape = hitboxes and hitboxes:FindFirstChild("Nape")
if nape then return nape end -- nape is on-screen when you're looking at them
return entity:FindFirstChild("Head") or entity:FindFirstChild("HumanoidRootPart")
end
This matters. If you use HumanoidRootPart for a large titan, the root is at the waist — potentially off-screen even when the titan's nape is visible. Your FOV check will report "no target" incorrectly.
Lock-on locker
For persistent lock-on (aimbot), use the locker:
local _lock = Pulse.Aim.locker({
getPos = function(target)
-- return the world position to aim at
local nape = target:FindFirstChild("Hitboxes")
and target.Hitboxes:FindFirstChild("Nape")
return nape and nape.Position
end,
validate = function(target)
-- return false to release automatically
if not target or not target.Parent then return false end
return not func.IsTitanDead(target)
end,
})
_lock:lock(nearest) -- start locking onto target
_lock:release() -- release
_lock:getTarget() -- current target, or nil
_lock:aim() -- call every frame — snaps camera, returns false if target lost
Full usage in a RenderStepped loop:
RS:BindToRenderStep("__AimbotTrack__", Enum.RenderPriority.Last.Value, function()
-- Check right-click to lock
local rmb = UIS:IsMouseButtonPressed(Enum.UserInputType.MouseButton2)
if rmb and not _wasRMB then
if _lock:getTarget() then
_lock:release()
else
local nearest = Pulse.Aim.findNearest(getCandidates(), {
fovRadius = Aimbot.radius(),
getRoot = getAimRoot,
})
if nearest then
_lock:lock(nearest)
end
end
end
_wasRMB = rmb
-- Aim each frame
if not _lock:aim() then
-- target was lost
Pulse.Log.debug("Aimbot", "lock lost")
end
end)
Diagnosing "no target found"
When findNearest finds no result, it logs a trace entry showing exactly why each candidate was skipped:
TRC [Aim] findNearest: no result { n=34, filter=0, noRoot=34, fov=0, dist=0 }
Enable TRC in the overlay log to see it. The fields:
| Field | Skipped because |
|---|---|
filter | Your filter function returned false |
noRoot | getRoot returned nil — no root part found |
fov | Outside the fovRadius pixel circle |
dist | Beyond maxDist |
noRoot=34 with 34 candidates means every entity has no HumanoidRootPart — you need a custom getRoot. fov=34 means they're all off-screen or behind the camera.
Limitations and known issues
findNearest returns the first candidate if all pass the same distance. If multiple targets have identical screen distances (unusual but possible), the first one in the input array wins.
lookAt on some executors uses internal APIs. It works on most, but verify it works in your executor before shipping.
Large NPC FOV checks need a custom getRoot — see the note in the findNearest section above. This is a common source of "no target in FOV" bugs.
The locker doesn't auto-acquire. You have to call _lock:lock(target) explicitly. The locker manages the lock-on once you've given it a target — it doesn't search for targets on its own.