@rbxts/gizmos 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +95 -0
- package/out/index.d.ts +155 -0
- package/out/init.lua +486 -0
- package/package.json +40 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Colin Miller
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# @rbxts/gizmos
|
|
2
|
+
|
|
3
|
+
Debug drawing library for Roblox with support for lines, rays, shapes, raycasts, and more.
|
|
4
|
+
|
|
5
|
+
## Credits
|
|
6
|
+
|
|
7
|
+
Original Luau implementation by **sg3cko**
|
|
8
|
+
[DevForum Post](https://devforum.roblox.com/t/roblox-gizmos-for-debug-drawing-now-available/3294608)
|
|
9
|
+
|
|
10
|
+
TypeScript package by Colin Miller (SnarlyZoo)
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @rbxts/gizmos
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import Gizmos from "@rbxts/gizmos";
|
|
22
|
+
|
|
23
|
+
// Draw colored shapes
|
|
24
|
+
Gizmos.setColor("red");
|
|
25
|
+
Gizmos.drawLine(Vector3.zero, new Vector3(0, 10, 0));
|
|
26
|
+
|
|
27
|
+
Gizmos.setColor("blue");
|
|
28
|
+
Gizmos.drawSphere(new Vector3(5, 5, 5), 2);
|
|
29
|
+
|
|
30
|
+
// Visualize raycasts
|
|
31
|
+
const params = new RaycastParams();
|
|
32
|
+
const result = workspace.Raycast(origin, direction, params);
|
|
33
|
+
Gizmos.drawRaycast(origin, direction, result);
|
|
34
|
+
|
|
35
|
+
// Draw text in world space
|
|
36
|
+
Gizmos.drawText(new Vector3(0, 10, 0), "Player spawn point");
|
|
37
|
+
|
|
38
|
+
// Log to screen
|
|
39
|
+
Gizmos.log("Frame:", tick());
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- **Shapes**: Lines, rays, paths, points, cubes, circles, spheres, pyramids
|
|
45
|
+
- **Raycasts**: Visualize raycasts, spherecasts, and blockcasts with hit detection
|
|
46
|
+
- **Text**: World-space and on-screen text rendering
|
|
47
|
+
- **Colors**: Built-in color presets (red, green, blue, yellow, cyan, magenta, orange, purple, white, gray, black)
|
|
48
|
+
- **Trailing paths**: Track object movement over time
|
|
49
|
+
- **Auto-clearing**: Automatically clears each frame (configurable)
|
|
50
|
+
|
|
51
|
+
## API
|
|
52
|
+
|
|
53
|
+
### Drawing
|
|
54
|
+
|
|
55
|
+
- `setColor(color: string | Color3)` - Set drawing color
|
|
56
|
+
- `setTransparency(value: number)` - Set transparency (0-1)
|
|
57
|
+
- `drawLine(from: Vector3, to: Vector3)` - Draw a line
|
|
58
|
+
- `drawRay(origin: Vector3, direction: Vector3)` - Draw a ray with arrow
|
|
59
|
+
- `drawPath(points: Vector3[], closed?: boolean, dotsSize?: number)` - Draw connected path
|
|
60
|
+
- `drawPoint(position: Vector3, size?: number)` - Draw point marker
|
|
61
|
+
- `drawCube(position: Vector3 | CFrame, size: Vector3)` - Draw wireframe cube
|
|
62
|
+
- `drawCircle(position: Vector3, radius: number, normal?: Vector3)` - Draw circle
|
|
63
|
+
- `drawSphere(position: Vector3 | CFrame, radius: number)` - Draw sphere
|
|
64
|
+
- `drawPyramid(position: Vector3 | CFrame, size: number, height: number)` - Draw pyramid
|
|
65
|
+
- `drawCFrame(cf: CFrame, size?: number, color?: Color3)` - Draw CFrame axes
|
|
66
|
+
|
|
67
|
+
### Raycasts
|
|
68
|
+
|
|
69
|
+
- `drawRaycast(origin: Vector3, direction: Vector3, result?: RaycastResult)` - Visualize raycast
|
|
70
|
+
- `drawSpherecast(origin: Vector3, radius: number, direction: Vector3, result?: RaycastResult)` - Visualize spherecast
|
|
71
|
+
- `drawBlockcast(cf: CFrame, size: Vector3, direction: Vector3, result?: RaycastResult)` - Visualize blockcast
|
|
72
|
+
|
|
73
|
+
### Text & Logging
|
|
74
|
+
|
|
75
|
+
- `drawText(position: Vector3, ...args: unknown[])` - Draw text at world position
|
|
76
|
+
- `log(...args: unknown[])` - Log to on-screen display
|
|
77
|
+
|
|
78
|
+
### Utilities
|
|
79
|
+
|
|
80
|
+
- `addToPath(name: string, position: Vector3, dotsSize?: number)` - Add point to trailing path
|
|
81
|
+
- `forceUpdate()` - Manually clear and update gizmos
|
|
82
|
+
- `test()` - Run visual test of all features
|
|
83
|
+
|
|
84
|
+
### Properties
|
|
85
|
+
|
|
86
|
+
- `clear: boolean` - Auto-clear each frame (default: true)
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
MIT
|
|
91
|
+
|
|
92
|
+
## Links
|
|
93
|
+
|
|
94
|
+
- [Original DevForum Post](https://devforum.roblox.com/t/roblox-gizmos-for-debug-drawing-now-available/3294608)
|
|
95
|
+
- [GitHub Repository](https://github.com/cmmiller26/rbxts-gizmos)
|
package/out/index.d.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gizmos - Debug drawing library for Roblox
|
|
3
|
+
*
|
|
4
|
+
* Original Luau implementation by sg3cko
|
|
5
|
+
* https://devforum.roblox.com/t/roblox-gizmos-for-debug-drawing-now-available/3294608
|
|
6
|
+
*
|
|
7
|
+
* TypeScript package by Colin Miller (SnarlyZoo)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface Gizmos {
|
|
11
|
+
/**
|
|
12
|
+
* Whether to automatically clear gizmos each frame
|
|
13
|
+
* @default true
|
|
14
|
+
*/
|
|
15
|
+
clear: boolean;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Set the color for subsequent draw calls
|
|
19
|
+
* @param color Color name ('red', 'green', 'blue', 'yellow', 'cyan', 'magenta', 'orange', 'purple', 'white', 'gray', 'black') or Color3 value
|
|
20
|
+
*/
|
|
21
|
+
setColor(color: string | Color3): void;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Set the transparency for subsequent draw calls
|
|
25
|
+
* @param value Transparency value from 0 (opaque) to 1 (fully transparent)
|
|
26
|
+
*/
|
|
27
|
+
setTransparency(value: number): void;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Draw a line between two points
|
|
31
|
+
* @param from Start position
|
|
32
|
+
* @param to End position
|
|
33
|
+
*/
|
|
34
|
+
drawLine(from: Vector3, to: Vector3): void;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Draw a ray with an arrow head
|
|
38
|
+
* @param origin Starting position of the ray
|
|
39
|
+
* @param direction Direction and length of the ray
|
|
40
|
+
*/
|
|
41
|
+
drawRay(origin: Vector3, direction: Vector3): void;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Draw a path connecting multiple points
|
|
45
|
+
* @param points Array of Vector3 positions to connect
|
|
46
|
+
* @param closed Whether to connect the last point back to the first
|
|
47
|
+
* @param dotsSize Optional size of dots to draw at each point (0 = no dots)
|
|
48
|
+
*/
|
|
49
|
+
drawPath(points: Vector3[], closed?: boolean, dotsSize?: number): void;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Draw a point marker at a position
|
|
53
|
+
* @param position Location to draw the point
|
|
54
|
+
* @param size Size of the point marker
|
|
55
|
+
*/
|
|
56
|
+
drawPoint(position: Vector3, size?: number): void;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Draw a wireframe cube
|
|
60
|
+
* @param position Center position or CFrame of the cube
|
|
61
|
+
* @param size Size of the cube on each axis
|
|
62
|
+
*/
|
|
63
|
+
drawCube(position: Vector3 | CFrame, size: Vector3): void;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Draw a wireframe circle
|
|
67
|
+
* @param position Center position of the circle
|
|
68
|
+
* @param radius Radius of the circle
|
|
69
|
+
* @param normal Normal vector defining the circle's plane (defaults to Vector3.yAxis)
|
|
70
|
+
*/
|
|
71
|
+
drawCircle(position: Vector3, radius: number, normal?: Vector3): void;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Draw a wireframe sphere
|
|
75
|
+
* @param position Center position or CFrame of the sphere
|
|
76
|
+
* @param radius Radius of the sphere
|
|
77
|
+
*/
|
|
78
|
+
drawSphere(position: Vector3 | CFrame, radius: number): void;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Draw a wireframe pyramid
|
|
82
|
+
* @param position Base center position or CFrame of the pyramid
|
|
83
|
+
* @param size Size of the pyramid's base
|
|
84
|
+
* @param height Height of the pyramid
|
|
85
|
+
*/
|
|
86
|
+
drawPyramid(position: Vector3 | CFrame, size: number, height: number): void;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Draw a CFrame as colored axes (red = right, green = up, blue = -look)
|
|
90
|
+
* @param cf CFrame to visualize
|
|
91
|
+
* @param size Length of the axes
|
|
92
|
+
* @param color Optional single color for all axes (defaults to RGB for XYZ)
|
|
93
|
+
*/
|
|
94
|
+
drawCFrame(cf: CFrame, size?: number, color?: Color3): void;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Draw text at a world position
|
|
98
|
+
* @param position World position to display the text
|
|
99
|
+
* @param args Values to display (strings, numbers, Vector3, CFrame are automatically formatted)
|
|
100
|
+
*/
|
|
101
|
+
drawText(position: Vector3, ...args: unknown[]): void;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Log text to the on-screen debug display
|
|
105
|
+
* @param args Values to log (strings, numbers, Vector3, CFrame are automatically formatted)
|
|
106
|
+
*/
|
|
107
|
+
log(...args: unknown[]): void;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Visualize a raycast with its result
|
|
111
|
+
* @param origin Starting position of the raycast
|
|
112
|
+
* @param direction Direction and maximum distance of the raycast
|
|
113
|
+
* @param result RaycastResult (green if hit, red if miss)
|
|
114
|
+
*/
|
|
115
|
+
drawRaycast(origin: Vector3, direction: Vector3, result: RaycastResult | undefined): void;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Visualize a spherecast with its result
|
|
119
|
+
* @param origin Starting position of the spherecast
|
|
120
|
+
* @param radius Radius of the sphere
|
|
121
|
+
* @param direction Direction and maximum distance of the spherecast
|
|
122
|
+
* @param result RaycastResult (green if hit, red if miss)
|
|
123
|
+
*/
|
|
124
|
+
drawSpherecast(origin: Vector3, radius: number, direction: Vector3, result: RaycastResult | undefined): void;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Visualize a blockcast (box-shaped raycast) with its result
|
|
128
|
+
* @param cf CFrame defining position and orientation of the block
|
|
129
|
+
* @param size Size of the block on each axis
|
|
130
|
+
* @param direction Direction and maximum distance of the blockcast
|
|
131
|
+
* @param result RaycastResult (green if hit, red if miss)
|
|
132
|
+
*/
|
|
133
|
+
drawBlockcast(cf: CFrame, size: Vector3, direction: Vector3, result: RaycastResult | undefined): void;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Add a point to a named trailing path (useful for visualizing movement over time)
|
|
137
|
+
* @param name Unique identifier for this path
|
|
138
|
+
* @param position Position to add to the path
|
|
139
|
+
* @param dotsSize Optional size of dots to draw at each point (0 = no dots)
|
|
140
|
+
*/
|
|
141
|
+
addToPath(name: string, position: Vector3, dotsSize?: number): void;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Manually force an update and clear of all gizmos
|
|
145
|
+
*/
|
|
146
|
+
forceUpdate(): void;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Run a visual test showcasing all gizmo drawing functions
|
|
150
|
+
*/
|
|
151
|
+
test(): void;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
declare const Gizmos: Gizmos;
|
|
155
|
+
export = Gizmos;
|
package/out/init.lua
ADDED
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
local Gizmos = {}
|
|
2
|
+
|
|
3
|
+
local wfh: WireframeHandleAdornment = nil
|
|
4
|
+
local label: TextLabel = nil
|
|
5
|
+
local commands = {}
|
|
6
|
+
local trailers = {}
|
|
7
|
+
|
|
8
|
+
local hitColor, missColor = 'green', 'red'
|
|
9
|
+
|
|
10
|
+
Gizmos.clear = true
|
|
11
|
+
|
|
12
|
+
local colors = {
|
|
13
|
+
red = Color3.new(1, 0, 0),
|
|
14
|
+
green = Color3.new(0, 1, 0),
|
|
15
|
+
blue = Color3.new(0, 0, 1),
|
|
16
|
+
yellow = Color3.new(1, 1, 0),
|
|
17
|
+
cyan = Color3.new(0, 1, 1),
|
|
18
|
+
magenta = Color3.new(1, 0, 1),
|
|
19
|
+
orange = Color3.new(1, 0.5, 0),
|
|
20
|
+
purple = Color3.new(0.5, 0, 1),
|
|
21
|
+
white = Color3.new(1, 1, 1),
|
|
22
|
+
gray = Color3.new(0.5, 0.5, 0.5),
|
|
23
|
+
black = Color3.new(0, 0, 0),
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
local function findOrMakeGizmos()
|
|
27
|
+
wfh = wfh or workspace:FindFirstChild('Gizmos')
|
|
28
|
+
if not wfh then
|
|
29
|
+
wfh = Instance.new('WireframeHandleAdornment')
|
|
30
|
+
wfh.Name = 'Gizmos'
|
|
31
|
+
wfh.Parent = workspace
|
|
32
|
+
assert(workspace.WorldPivot:FuzzyEq(CFrame.identity), 'workspace is expected to have identity CFrame')
|
|
33
|
+
wfh.Color3 = Color3.new(1, 1, 1)
|
|
34
|
+
wfh.Adornee = workspace
|
|
35
|
+
wfh.AlwaysOnTop = true
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
local function helper_getGui()
|
|
40
|
+
local localPlayer = game:GetService('Players').LocalPlayer
|
|
41
|
+
if localPlayer then
|
|
42
|
+
return localPlayer:WaitForChild('PlayerGui', 3)
|
|
43
|
+
else
|
|
44
|
+
return game:GetService('StarterGui')
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
local function findOrMakeLabel()
|
|
49
|
+
local gui = helper_getGui()
|
|
50
|
+
if not gui then return end
|
|
51
|
+
label = gui:FindFirstChild('TextLabelGizmos', true)
|
|
52
|
+
if not label then
|
|
53
|
+
local screengui = Instance.new('ScreenGui', gui)
|
|
54
|
+
screengui.Name = 'ScreenGuiGizmos'
|
|
55
|
+
label = Instance.new('TextLabel', screengui)
|
|
56
|
+
label.Name = 'TextLabelGizmos'
|
|
57
|
+
label.BackgroundTransparency = 1
|
|
58
|
+
label.Position = UDim2.new(0, 10, 1, -10)
|
|
59
|
+
label.AnchorPoint = Vector2.new(0,1)
|
|
60
|
+
label.TextXAlignment = Enum.TextXAlignment.Left
|
|
61
|
+
label.TextYAlignment = Enum.TextYAlignment.Bottom
|
|
62
|
+
label.FontFace = Font.fromEnum(Enum.Font.RobotoMono)
|
|
63
|
+
label.TextColor3 = Color3.new(1,1,1)
|
|
64
|
+
label.TextStrokeColor3 = Color3.new(0,0,0)
|
|
65
|
+
label.TextStrokeTransparency = 0.5
|
|
66
|
+
label.TextSize = 14
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
local Trailer = {}
|
|
71
|
+
Trailer.__index = Trailer
|
|
72
|
+
|
|
73
|
+
function Trailer.new(limit)
|
|
74
|
+
local defaultLimit = 300
|
|
75
|
+
local self = setmetatable({}, Trailer)
|
|
76
|
+
self.limit = limit or defaultLimit
|
|
77
|
+
self.values = {}
|
|
78
|
+
return self
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
function Trailer:addValue(value)
|
|
82
|
+
table.insert(self.values, value)
|
|
83
|
+
if #self.values > self.limit then
|
|
84
|
+
table.remove(self.values, 1)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
function Trailer:getValues()
|
|
89
|
+
return self.values
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
local function setColor(color: string | Color3)
|
|
93
|
+
local color3 = if typeof(color) == 'string' then colors[color] else color
|
|
94
|
+
wfh.Color3 = color3
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
local function setTransparency(value: number)
|
|
98
|
+
wfh.Transparency = value
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
local function drawLine(from: Vector3, to: Vector3)
|
|
102
|
+
wfh:AddLine(from, to)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
local function helper_getPerpendicularVector(v: Vector3): Vector3
|
|
106
|
+
local perp: Vector3 = Vector3.new(-v.y, v.x, 0)
|
|
107
|
+
if perp.Magnitude == 0 then
|
|
108
|
+
perp = Vector3.new(0, -v.z, v.y)
|
|
109
|
+
end
|
|
110
|
+
return perp.Unit
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
local function drawRay(origin: Vector3, direction: Vector3)
|
|
114
|
+
local endPoint = origin + direction
|
|
115
|
+
wfh:AddLine(origin, endPoint)
|
|
116
|
+
|
|
117
|
+
local arrowLength, arrowAngle = direction.Magnitude/20, math.rad(30)
|
|
118
|
+
|
|
119
|
+
local dir = direction.Unit
|
|
120
|
+
local perp = helper_getPerpendicularVector(dir)
|
|
121
|
+
local left = endPoint - dir * arrowLength + perp * arrowLength * math.tan(arrowAngle)
|
|
122
|
+
local right = endPoint - dir * arrowLength - perp * arrowLength * math.tan(arrowAngle)
|
|
123
|
+
wfh:AddLine(endPoint, left)
|
|
124
|
+
wfh:AddLine(endPoint, right)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
local function drawPath(points: {Vector3}, closed: boolean?, dotsSize: number?)
|
|
128
|
+
closed = closed or false
|
|
129
|
+
dotsSize = dotsSize or 0
|
|
130
|
+
wfh:AddPath(points, closed)
|
|
131
|
+
if dotsSize > 0 then
|
|
132
|
+
for _, point in ipairs(points) do
|
|
133
|
+
drawCube(point, Vector3.one * dotsSize)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
local function drawPoint(pos: Vector3, size: number?)
|
|
139
|
+
size = size or 0.1
|
|
140
|
+
wfh:AddLines({
|
|
141
|
+
pos - Vector3.xAxis * size, pos + Vector3.xAxis * size,
|
|
142
|
+
pos - Vector3.yAxis * size, pos + Vector3.yAxis * size,
|
|
143
|
+
pos - Vector3.zAxis * size, pos + Vector3.zAxis * size})
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
local function drawCube(pos: Vector3 | CFrame, size: Vector3)
|
|
147
|
+
local cf = if typeof(pos) == 'Vector3' then CFrame.new(pos) else pos
|
|
148
|
+
local halfSize = size * 0.5
|
|
149
|
+
local min = -halfSize
|
|
150
|
+
local max = halfSize
|
|
151
|
+
local v = {
|
|
152
|
+
cf * min,
|
|
153
|
+
cf * Vector3.new(max.x, min.y, min.z),
|
|
154
|
+
cf * Vector3.new(max.x, min.y, max.z),
|
|
155
|
+
cf * Vector3.new(min.x, min.y, max.z),
|
|
156
|
+
cf * Vector3.new(min.x, max.y, min.z),
|
|
157
|
+
cf * Vector3.new(max.x, max.y, min.z),
|
|
158
|
+
cf * max,
|
|
159
|
+
cf * Vector3.new(min.x, max.y, max.z),
|
|
160
|
+
}
|
|
161
|
+
wfh:AddLines({
|
|
162
|
+
v[1], v[2],
|
|
163
|
+
v[2], v[3],
|
|
164
|
+
v[3], v[4],
|
|
165
|
+
v[4], v[1],
|
|
166
|
+
v[5], v[6],
|
|
167
|
+
v[6], v[7],
|
|
168
|
+
v[7], v[8],
|
|
169
|
+
v[8], v[5],
|
|
170
|
+
v[1], v[5],
|
|
171
|
+
v[2], v[6],
|
|
172
|
+
v[3], v[7],
|
|
173
|
+
v[4], v[8],
|
|
174
|
+
})
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
local function drawCircle(pos: Vector3, radius: number, normal: Vector3?)
|
|
178
|
+
local segments = 16
|
|
179
|
+
normal = normal or Vector3.yAxis
|
|
180
|
+
local cf = CFrame.lookAlong(pos, normal)
|
|
181
|
+
local angle = 2 * math.pi / segments
|
|
182
|
+
|
|
183
|
+
local points = {}
|
|
184
|
+
for i = 1, segments do
|
|
185
|
+
local localpoint = Vector3.new(math.cos(i * angle), math.sin(i * angle), 0) * radius
|
|
186
|
+
local point = cf:PointToWorldSpace(localpoint)
|
|
187
|
+
table.insert(points, point)
|
|
188
|
+
end
|
|
189
|
+
wfh:AddPath(points, true)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
local function drawSphere(pos: Vector3 | CFrame, radius: number)
|
|
193
|
+
local cf = if typeof(pos) == 'Vector3' then CFrame.new(pos) else pos
|
|
194
|
+
drawCircle(cf.Position, radius, cf.Rotation * Vector3.xAxis)
|
|
195
|
+
drawCircle(cf.Position, radius, cf.Rotation * Vector3.yAxis)
|
|
196
|
+
drawCircle(cf.Position, radius, cf.Rotation * Vector3.zAxis)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
local function drawPyramid(pos: Vector3 | CFrame, size: number, height: number)
|
|
200
|
+
local cf = if typeof(pos) == 'Vector3' then CFrame.new(pos) else pos
|
|
201
|
+
local hsize = size/2
|
|
202
|
+
local points = {
|
|
203
|
+
cf * Vector3.new( hsize, 0, hsize),
|
|
204
|
+
cf * Vector3.new(-hsize, 0, hsize),
|
|
205
|
+
cf * Vector3.new(-hsize, 0, -hsize),
|
|
206
|
+
cf * Vector3.new( hsize, 0, -hsize),
|
|
207
|
+
cf * Vector3.new(0, height, 0),
|
|
208
|
+
}
|
|
209
|
+
wfh:AddPath({points[1], points[2], points[3], points[4], points[1], points[5], points[2]}, false)
|
|
210
|
+
wfh:AddPath({points[3], points[5], points[4]}, false)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
local function drawCFrame(cf: CFrame, size: number, color: Color3)
|
|
214
|
+
size = size or 1
|
|
215
|
+
local color3 = wfh.Color3
|
|
216
|
+
local pos = cf.Position
|
|
217
|
+
if color ~= nil then
|
|
218
|
+
setColor(color)
|
|
219
|
+
drawLine(pos, pos + cf.RightVector * size)
|
|
220
|
+
drawLine(pos, pos + cf.UpVector * size)
|
|
221
|
+
drawLine(pos, pos + -cf.LookVector * size)
|
|
222
|
+
else
|
|
223
|
+
setColor('red')
|
|
224
|
+
drawLine(pos, pos + cf.RightVector * size)
|
|
225
|
+
setColor('green')
|
|
226
|
+
drawLine(pos, pos + cf.UpVector * size)
|
|
227
|
+
setColor('blue')
|
|
228
|
+
drawLine(pos, pos + -cf.LookVector * size)
|
|
229
|
+
end
|
|
230
|
+
wfh.Color3 = color3
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
local function helper_formatText(...)
|
|
234
|
+
local args = {...}
|
|
235
|
+
local text = ''
|
|
236
|
+
for _, v in ipairs(args) do
|
|
237
|
+
local str
|
|
238
|
+
if typeof(v) == 'string' then
|
|
239
|
+
str = v
|
|
240
|
+
elseif typeof(v) == 'number' then
|
|
241
|
+
str = string.format('%.3f', v)
|
|
242
|
+
elseif typeof(v) == 'Vector3' then
|
|
243
|
+
str = string.format('(%.3f, %.3f, %.3f)', v.x, v.y, v.z)
|
|
244
|
+
elseif typeof(v) == 'CFrame' then
|
|
245
|
+
local rx,ry,rz = v:ToOrientation()
|
|
246
|
+
rx, ry, rz = math.deg(rx), math.deg(ry), math.deg(rz)
|
|
247
|
+
local pos = v.Position
|
|
248
|
+
str = string.format('pos=(%.3f, %.3f, %.3f) rot=(%.3f, %.3f, %.3f)', pos.x, pos.y, pos.z, rx, ry, rz)
|
|
249
|
+
else
|
|
250
|
+
str = tostring(v)
|
|
251
|
+
end
|
|
252
|
+
text = text .. ' ' .. str
|
|
253
|
+
end
|
|
254
|
+
return text
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
local function drawText(position: Vector3, ...)
|
|
258
|
+
local args = {...}
|
|
259
|
+
local text = helper_formatText(unpack(args))
|
|
260
|
+
local size = nil
|
|
261
|
+
wfh:AddText(position, text, size)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
local function log(...)
|
|
265
|
+
if label then
|
|
266
|
+
local args = {...}
|
|
267
|
+
local text = helper_formatText(unpack(args))
|
|
268
|
+
label.Text = label.Text .. '\n' .. text
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
local function helper_drawHit(hit: RaycastResult)
|
|
273
|
+
drawCircle(hit.Position, 0.15, hit.Normal)
|
|
274
|
+
drawRay(hit.Position, hit.Normal * 0.3)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
local function helper_drawRaycast(cf: CFrame, direction: Vector3, result: RaycastResult, shape: number, size: number | Vector3)
|
|
278
|
+
local color3 = wfh.Color3
|
|
279
|
+
local travel
|
|
280
|
+
if result then
|
|
281
|
+
setColor(hitColor)
|
|
282
|
+
travel = direction.Unit * result.Distance
|
|
283
|
+
helper_drawHit(result)
|
|
284
|
+
else
|
|
285
|
+
setColor(missColor)
|
|
286
|
+
travel = direction
|
|
287
|
+
end
|
|
288
|
+
if shape == 1 then
|
|
289
|
+
drawSphere(cf, size)
|
|
290
|
+
drawSphere(cf + travel, size)
|
|
291
|
+
elseif shape == 2 then
|
|
292
|
+
drawCube(cf, size)
|
|
293
|
+
drawCube(cf + travel, size)
|
|
294
|
+
end
|
|
295
|
+
drawRay(cf.Position, travel)
|
|
296
|
+
wfh.Color3 = color3
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
local function drawRaycast(origin: Vector3, direction: Vector3, result: RaycastResult)
|
|
300
|
+
helper_drawRaycast(CFrame.new(origin), direction, result, 0)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
local function drawSpherecast(origin: Vector3, radius: number, direction: Vector3, result: RaycastResult)
|
|
304
|
+
helper_drawRaycast(CFrame.lookAlong(origin, direction), direction, result, 1, radius)
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
local function drawBlockcast(cf: CFrame, size: Vector3, direction: Vector3, result: RaycastResult)
|
|
308
|
+
helper_drawRaycast(cf, direction, result, 2, size)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
function Gizmos:setColor(color: string | Color3)
|
|
312
|
+
table.insert(commands, function()
|
|
313
|
+
setColor(color)
|
|
314
|
+
end)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
function Gizmos:setTransparency(value: number)
|
|
318
|
+
table.insert(commands, function()
|
|
319
|
+
setTransparency(value)
|
|
320
|
+
end)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
function Gizmos:drawLine(from: Vector3, to: Vector3)
|
|
324
|
+
table.insert(commands, function()
|
|
325
|
+
drawLine(from, to)
|
|
326
|
+
end)
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
function Gizmos:drawRay(origin: Vector3, direction: Vector3)
|
|
330
|
+
table.insert(commands, function()
|
|
331
|
+
drawRay(origin, direction)
|
|
332
|
+
end)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
function Gizmos:drawPath(points: {Vector3}, closed: boolean?, dotsSize: number?)
|
|
336
|
+
table.insert(commands, function()
|
|
337
|
+
drawPath(points, closed, dotsSize)
|
|
338
|
+
end)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
function Gizmos:drawPoint(position: Vector3, size: number?)
|
|
342
|
+
table.insert(commands, function()
|
|
343
|
+
drawPoint(position, size)
|
|
344
|
+
end)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
function Gizmos:drawCube(position: Vector3 | CFrame, size: Vector3)
|
|
348
|
+
table.insert(commands, function()
|
|
349
|
+
drawCube(position, size)
|
|
350
|
+
end)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
function Gizmos:drawCircle(position: Vector3, radius: number, normal: Vector3?)
|
|
354
|
+
table.insert(commands, function()
|
|
355
|
+
drawCircle(position, radius, normal)
|
|
356
|
+
end)
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
function Gizmos:drawSphere(position: Vector3 | CFrame, radius: number)
|
|
360
|
+
table.insert(commands, function()
|
|
361
|
+
drawSphere(position, radius)
|
|
362
|
+
end)
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
function Gizmos:drawPyramid(position: Vector3 | CFrame, size: number, height: number)
|
|
366
|
+
table.insert(commands, function()
|
|
367
|
+
drawPyramid(position, size, height)
|
|
368
|
+
end)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
function Gizmos:drawCFrame(cf: CFrame, size: number?, color: Color3?)
|
|
372
|
+
table.insert(commands, function()
|
|
373
|
+
drawCFrame(cf, size, color)
|
|
374
|
+
end)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
function Gizmos:drawText(position: Vector3, ...)
|
|
378
|
+
local args = {...}
|
|
379
|
+
table.insert(commands, function()
|
|
380
|
+
drawText(position, unpack(args))
|
|
381
|
+
end)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
function Gizmos:log(...)
|
|
385
|
+
local args = {...}
|
|
386
|
+
table.insert(commands, function()
|
|
387
|
+
log(unpack(args))
|
|
388
|
+
end)
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
function Gizmos:drawRaycast(origin: Vector3, direction: Vector3, result: RaycastResult)
|
|
392
|
+
table.insert(commands, function()
|
|
393
|
+
drawRaycast(origin, direction, result)
|
|
394
|
+
end)
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
function Gizmos:drawSpherecast(origin: Vector3, radius: number, direction: Vector3, result: RaycastResult)
|
|
398
|
+
table.insert(commands, function()
|
|
399
|
+
drawSpherecast(origin, radius, direction, result)
|
|
400
|
+
end)
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
function Gizmos:drawBlockcast(cf: CFrame, size: Vector3, direction: Vector3, result: RaycastResult)
|
|
404
|
+
table.insert(commands, function()
|
|
405
|
+
drawBlockcast(cf, size, direction, result)
|
|
406
|
+
end)
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
function Gizmos:addToPath(name: string, position: Vector3, dotsSize: number?)
|
|
410
|
+
if not trailers[name] then
|
|
411
|
+
trailers[name] = Trailer.new()
|
|
412
|
+
end
|
|
413
|
+
trailers[name]:addValue(position)
|
|
414
|
+
table.insert(commands, function()
|
|
415
|
+
drawPath(trailers[name]:getValues(), false, dotsSize)
|
|
416
|
+
end)
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
function Gizmos:forceUpdate()
|
|
420
|
+
wfh:Clear()
|
|
421
|
+
if label then
|
|
422
|
+
label.Text = ''
|
|
423
|
+
end
|
|
424
|
+
for _, command in ipairs(commands) do
|
|
425
|
+
command()
|
|
426
|
+
end
|
|
427
|
+
commands = {}
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
function Gizmos:test()
|
|
431
|
+
local p = Vector3.new(0, 0, 10)
|
|
432
|
+
local x, y, z = Vector3.xAxis, Vector3.yAxis, Vector3.zAxis
|
|
433
|
+
local function n() p += Vector3.xAxis*2 end
|
|
434
|
+
|
|
435
|
+
Gizmos:setColor('white')
|
|
436
|
+
Gizmos:setTransparency(0)
|
|
437
|
+
Gizmos:drawLine(p, p + y) n()
|
|
438
|
+
Gizmos:drawRay(p, y) n()
|
|
439
|
+
Gizmos:drawPath({ p+Vector3.new(-0.3,0,-0.3), p+Vector3.new(0.4,0,0), p+Vector3.new(0.1,0,0.5), p+Vector3.new(0.6,0,0.9)}) n()
|
|
440
|
+
Gizmos:drawPoint(p) n()
|
|
441
|
+
Gizmos:drawCube(p + y*0.5, Vector3.one) n()
|
|
442
|
+
Gizmos:drawCircle(p, 0.5) n()
|
|
443
|
+
Gizmos:drawSphere(p+y*0.5, 0.5) n()
|
|
444
|
+
Gizmos:drawPyramid(p, 1, 1) n()
|
|
445
|
+
Gizmos:drawCFrame(CFrame.new(p)) n()
|
|
446
|
+
Gizmos:drawText(p, 'Hello') n()
|
|
447
|
+
Gizmos:drawRaycast(p, z, nil) n()
|
|
448
|
+
Gizmos:drawSpherecast(p, 0.3, z, nil) n()
|
|
449
|
+
Gizmos:drawBlockcast(CFrame.new(p), Vector3.one*0.6, z, nil) n()
|
|
450
|
+
Gizmos:log('Log')
|
|
451
|
+
|
|
452
|
+
local colors = {'red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'purple', 'magenta', 'black', 'gray', 'white'}
|
|
453
|
+
p = Vector3.new(0, 0, 12)
|
|
454
|
+
for _, color in ipairs(colors) do
|
|
455
|
+
Gizmos:setColor(color)
|
|
456
|
+
Gizmos:drawCircle(p, 0.15)
|
|
457
|
+
p += Vector3.xAxis*1
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
findOrMakeGizmos()
|
|
462
|
+
findOrMakeLabel()
|
|
463
|
+
|
|
464
|
+
local function Update(dt)
|
|
465
|
+
local t = tick()
|
|
466
|
+
if t ~= wfh:GetAttribute('lastUpdateTime') then
|
|
467
|
+
wfh:SetAttribute('lastUpdateTime', t)
|
|
468
|
+
if Gizmos.clear then
|
|
469
|
+
wfh:Clear()
|
|
470
|
+
if label then
|
|
471
|
+
label.Text = ''
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
end
|
|
475
|
+
for _, command in ipairs(commands) do
|
|
476
|
+
command()
|
|
477
|
+
end
|
|
478
|
+
commands = {}
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
local RunService = game:GetService('RunService')
|
|
482
|
+
if RunService:IsRunning() then
|
|
483
|
+
RunService:BindToRenderStep('name', Enum.RenderPriority.Camera.Value-1, Update)
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
return Gizmos
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rbxts/gizmos",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "TypeScript port of sg3cko's Gizmos library - Debug drawing utilities for Roblox including shapes, raycasts, paths, and world-space text.",
|
|
5
|
+
"main": "out/init.lua",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "rbxtsc",
|
|
8
|
+
"watch": "rbxtsc -w",
|
|
9
|
+
"prepublishOnly": "npm run build"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [],
|
|
12
|
+
"author": "Colin Miller",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"type": "commonjs",
|
|
15
|
+
"types": "out/index.d.ts",
|
|
16
|
+
"files": [
|
|
17
|
+
"out",
|
|
18
|
+
"!**/*.tsbuildinfo"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/cmmiller26/rbxts-gizmos.git"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@rbxts/compiler-types": "^3.0.0-types.0",
|
|
29
|
+
"@rbxts/types": "^1.0.891",
|
|
30
|
+
"@typescript-eslint/eslint-plugin": "^8.46.4",
|
|
31
|
+
"@typescript-eslint/parser": "^8.46.4",
|
|
32
|
+
"eslint": "^9.39.1",
|
|
33
|
+
"eslint-config-prettier": "^10.1.8",
|
|
34
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
35
|
+
"eslint-plugin-roblox-ts": "^1.3.0",
|
|
36
|
+
"prettier": "^3.6.2",
|
|
37
|
+
"roblox-ts": "^3.0.0",
|
|
38
|
+
"typescript": "^5.9.3"
|
|
39
|
+
}
|
|
40
|
+
}
|