A lightweight, optimized standalone script for rendering 3D interactive text labels in the world. It features automatic thread management, proximity scaling, and support for both "tap" and "hold" interactions with visual progress tracking.
⚙️ Data Structures
Before using the functions, understand the data tables used to configure an interaction point.
The Main Configuration Table
This table defines where the text is and how it behaves.
Property
Type
Default
Description
coords
vector3
Required
The world coordinates where the text should appear.
options
table
Required
A list of interaction options (see below).
distance
float
2.5
The distance at which the UI switches to "Full" mode (interaction enabled).
maxDistance
float
5.0
The max distance at which the text is visible at all.
scale
float
1.0
Scaling factor for the UI element.
arc
boolean
false
Visual flag passed to UI (optional style preference).
The Options Table
The options list contains the specific inputs available to the player.
options= { { key="E", -- The visual key name displayedtext="PICK UP", -- The action textcontrol=38, -- The FiveM Control Index (38 is E) }, { key="H", text="INSPECT", control=74, -- The FiveM Control Index (74 is H)hold=3-- (Optional) Seconds button must be held to trigger }}
🛠️ API Reference
This script exposes functions via exports and assigns them to the global Lib47 table (if available).
1. Register3dInteract (Static Mode)
Best for static locations (e.g., Shops, NPCs, Locations). You register it once, and the script handles the thread management internally.
Usage: Lib47.Register3dInteract(data, callback)
Returns: string (The Interaction ID, derived from coordinates).
2. Show3dInteract (Immediate Mode)
Best for dynamic scenarios where you are already running a thread (e.g., checking distance to a specific entity or vehicle).
Usage: Lib47.Show3dInteract(data, callback)
Note: When using this mode inside a loop, it must be called every frame while visible.
3. Remove3dInteract
Removes a registered interaction point or hides a dynamic one.
Usage: Lib47.Remove3dInteract(id)
📝 Code Examples
Example A: Static Location (Register Mode)
Use this for permanent locations. Run this code once (e.g., at resource start).
Example B: Dynamic Location (Loop Mode)
Use this if you are managing your own thread/loop.
Example C: Using Exports directly
If you do not use the Lib47 table, you can use the resource exports directly.
CreateThread(function()
local position = vector3(373.59, 328.58, 103.68)
-- Register the point once
Lib47.Register3dInteract({
coords = position,
distance = 2.5,
options = {
{ key = "E", text = "PICK UP", control = 38 },
{ key = "H", text = "INSPECT", control = 74, hold = 3 } -- 3 Second Hold
},
}, function(option)
-- Callback: Checks which option was triggered
if option.control == 38 then
print("Player picked up item")
end
if option.control == 74 then
print("Player inspected item")
end
end)
end)
CreateThread(function()
local position = vector3(373.02, 326.32, 103.68)
-- Define options outside the loop for memory optimization
local myOptions = {
{ key = "E", text = "PICK UP", control = 38 },
{ key = "H", text = "INSPECT", control = 74, hold = 3 }
}
while true do
local sleep = 500
local ped = PlayerPedId()
local coords = GetEntityCoords(ped)
-- Check distance manually
local dist = #(position - coords)
-- Only run logic if close enough (e.g., 20 units)
if dist < 20.0 then
sleep = 0
-- Call Show3dInteract every frame
Lib47.Show3dInteract({
coords = position,
distance = 2.5,
options = myOptions,
}, function(option)
-- Action logic
if option.control == 38 then print("38 Triggered") end
if option.control == 74 then print("74 Triggered") end
end)
end
Wait(sleep)
end
end)