3D Text UI
A lightweight, optimized standalone script for rendering 3D interactive text labels in the world. It features a dual-loop automatic culling system for high performance, proximity scaling, and support for "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. Note: Callbacks are now defined directly inside each option via the action property.
options = {
{
label = "PICK UP", -- The action text displayed
key = 38, -- The FiveM Control Index (38 is E)
keyName = "E", -- (Optional) The visual key name. Falls back to Lib47.Keys if omitted.
action = function() -- The function triggered upon interaction
print("Item picked up!")
end,
isVisible = function() -- (Optional) Determines if the option shows up at all
return true
end,
canInteract = function() -- (Optional) If false, the option is visible but disabled/grayed out
return true
end
},
{
label = "INSPECT",
key = 74, -- 74 is H
keyName = "H",
hold = 3, -- (Optional) Seconds button must be held to trigger
action = function()
print("Inspected!")
end
}
}🛠️ API Reference
This script exposes functions and assigns them to the global Lib47 table. It utilizes an automatic slow culling loop (checking within 30.0 units) and a fast interaction loop, meaning you no longer need to check distances manually in your own threads.
1. RegisterTextUi3d (Static Mode)
Best for static, permanent locations (e.g., Shops, Duty points, ATMs). You register it once, and the script's internal culling handles the rest.
- Usage:
Lib47.RegisterTextUi3d(data) - Returns:
string(The Interaction ID, derived from coordinates).
2. ShowTextUi3d (Dynamic/Immediate Mode)
Best for dynamic entities (like dropping an item on the ground). This registers the point temporarily. If you walk away, it will eventually be culled out unless updated.
- Usage:
Lib47.ShowTextUi3d(data) - Returns:
string(Interaction ID).
3. HideTextUi3d & RemoveTextUi3d
Hides or completely removes an interaction point.
- Usage:
Lib47.HideTextUi3d(id)orLib47.RemoveTextUi3d(id)
📝 Code Examples
Example A: Static Location (Register Mode)
Use this for permanent locations. Run this code once (e.g., at resource start). You do not need a loop.
CreateThread(function()
local position = vector3(373.59, 328.58, 103.68)
-- Register the point once, the script handles distance and culling automatically
Lib47.RegisterTextUi3d({
coords = position,
distance = 2.5,
maxDistance = 5.0,
options = {
{
label = "PICK UP",
key = 38,
keyName = "E",
action = function()
print("Player picked up item")
end
},
{
label = "INSPECT",
key = 74,
keyName = "H",
hold = 3, -- 3 Second Hold
action = function()
print("Player inspected item")
end
}
},
})
end)Example B: Dynamic State & Conditional Logic
Use the isVisible and canInteract functions to dynamically change how the text behaves based on the player's state (e.g., job, money, or items).
CreateThread(function()
local atmCoords = vector3(100.0, 100.0, 20.0)
Lib47.RegisterTextUi3d({
coords = atmCoords,
distance = 2.0,
options = {
{
label = "USE ATM",
key = 38,
keyName = "E",
isVisible = function()
-- Only show if the player is not in a vehicle
return not IsPedInAnyVehicle(PlayerPedId(), false)
end,
canInteract = function()
-- Visible, but grayed out if the player is dead
return not IsEntityDead(PlayerPedId())
end,
action = function()
print("Accessing ATM...")
end
}
}
})
end)Example C: Using Exports directly
If you do not use the Lib47 global wrapper, you can use the resource exports directly via the Interface wrapper.
exports['ak47_lib']:RegisterTextUi3d({
coords = vector3(100.0, 100.0, 20.0),
options = {
{
label = "TEST",
key = 38,
keyName = "E",
action = function()
print("Clicked")
end
}
}
})