--==================================================
-- PORTAL SYSTEM - FULL LOCAL SCRIPT
--==================================================
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local player = Players.LocalPlayer
local mouse = player:GetMouse()
--==================================================
-- SETTINGS
--==================================================
local MOVE_AMOUNT = 1
local ROTATE_AMOUNT = 15
local MIN_SIZE = 0.5
local MAX_SIZE = 100
local BASE_GIZMO_DISTANCE = 5
local BASE_HITBOX_SIZE = 2.5
local MAX_HITBOX_SIZE = 12
--==================================================
-- VARIABLES
--==================================================
local portalPart = nil
local selectedPortal = nil
local placingPortal = false
local selectingPortal = false
local currentMode = nil
local minimized = false
local scalePanelOpen = false
local rotatePanelOpen = false
--==================================================
-- SCREEN GUI
--==================================================
local gui = Instance.new("ScreenGui")
gui.Name = "PortalUI"
gui.ResetOnSpawn = false
gui.IgnoreGuiInset = true
gui.Parent = player:WaitForChild("PlayerGui")
--==================================================
-- UI COLORS
--==================================================
local PANEL_COLOR = Color3.fromRGB(20, 20, 30)
local BUTTON_COLOR = Color3.fromRGB(35, 35, 45)
local PURPLE = Color3.fromRGB(170, 80, 255)
local WHITE = Color3.fromRGB(255, 255, 255)
--==================================================
-- TWEEN SETTINGS
--==================================================
local PANEL_TWEEN_INFO = TweenInfo.new(
0.3,
Enum.EasingStyle.Quint,
Enum.EasingDirection.Out
)
local PANEL_CLOSE_TWEEN_INFO = TweenInfo.new(
0.25,
Enum.EasingStyle.Quint,
Enum.EasingDirection.In
)
--==================================================
-- MODE LABEL
--==================================================
local modeLabel = Instance.new("TextLabel")
modeLabel.Name = "ModeLabel"
modeLabel.Size = UDim2.new(0, 130, 0, 25)
modeLabel.AnchorPoint = Vector2.new(0.5, 1)
modeLabel.Position = UDim2.new(0.5, 0, 1, -10)
modeLabel.BackgroundColor3 = PANEL_COLOR
modeLabel.BackgroundTransparency = 0.15
modeLabel.TextColor3 = WHITE
modeLabel.Text = ""
modeLabel.TextScaled = true
modeLabel.Font = Enum.Font.GothamBold
modeLabel.Visible = false
modeLabel.Parent = gui
local modeCorner = Instance.new("UICorner")
modeCorner.CornerRadius = UDim.new(0, 7)
modeCorner.Parent = modeLabel
local modeStroke = Instance.new("UIStroke")
modeStroke.Thickness = 2
modeStroke.Color = PURPLE
modeStroke.Parent = modeLabel
local function updateModeText()
if currentMode then
modeLabel.Text =
"MODE: " .. currentMode
modeLabel.Visible = true
else
modeLabel.Text = ""
modeLabel.Visible = false
end
end
--==================================================
-- SELECTED NOTIFICATION
--==================================================
local selectedLabel = Instance.new("TextLabel")
selectedLabel.Name = "SelectedPortalLabel"
selectedLabel.Size = UDim2.new(0, 155, 0, 25)
selectedLabel.AnchorPoint = Vector2.new(0.5, 1)
selectedLabel.Position = UDim2.new(0.5, 0, 1, -40)
selectedLabel.BackgroundColor3 = PANEL_COLOR
selectedLabel.BackgroundTransparency = 0.15
selectedLabel.TextColor3 = WHITE
selectedLabel.Text = "SELECTED PORTAL"
selectedLabel.TextScaled = true
selectedLabel.Font = Enum.Font.GothamBold
selectedLabel.Visible = false
selectedLabel.Parent = gui
local selectedCorner = Instance.new("UICorner")
selectedCorner.CornerRadius = UDim.new(0, 7)
selectedCorner.Parent = selectedLabel
local selectedStroke = Instance.new("UIStroke")
selectedStroke.Thickness = 2
selectedStroke.Color = PURPLE
selectedStroke.Parent = selectedLabel
local selectedFadeTween = nil
local function hideSelectedNotification()
if selectedFadeTween then
selectedFadeTween:Cancel()
selectedFadeTween = nil
end
selectedLabel.Visible = false
selectedLabel.TextTransparency = 0
selectedLabel.BackgroundTransparency = 0.15
selectedStroke.Transparency = 0
end
local function showSelectedNotification()
if selectedFadeTween then
selectedFadeTween:Cancel()
end
selectedLabel.Visible = true
selectedLabel.TextTransparency = 0
selectedLabel.BackgroundTransparency = 0.15
selectedStroke.Transparency = 0
selectedFadeTween = TweenService:Create(
selectedLabel,
TweenInfo.new(
1.5,
Enum.EasingStyle.Quad,
Enum.EasingDirection.Out
),
{
TextTransparency = 1,
BackgroundTransparency = 1
}
)
local strokeFadeTween = TweenService:Create(
selectedStroke,
TweenInfo.new(
1.5,
Enum.EasingStyle.Quad,
Enum.EasingDirection.Out
),
{
Transparency = 1
}
)
selectedFadeTween:Play()
strokeFadeTween:Play()
selectedFadeTween.Completed:Connect(function()
if selectedLabel.TextTransparency >= 1 then
selectedLabel.Visible = false
end
end)
end
--==================================================
-- DRAG SYSTEM
--==================================================
local function makeDraggable(frame, dragButton)
local dragging = false
local dragStart
local startPosition
dragButton.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.Touch
or input.UserInputType == Enum.UserInputType.MouseButton1 then
dragging = true
dragStart = input.Position
startPosition = frame.Position
input.Changed:Connect(function()
if input.UserInputState == Enum.UserInputState.End then
dragging = false
end
end)
end
end)
UserInputService.InputChanged:Connect(function(input)
if dragging
and (
input.UserInputType == Enum.UserInputType.Touch
or input.UserInputType == Enum.UserInputType.MouseMovement
) then
local delta =
input.Position - dragStart
frame.Position = UDim2.new(
startPosition.X.Scale,
startPosition.X.Offset + delta.X,
startPosition.Y.Scale,
startPosition.Y.Offset + delta.Y
)
end
end)
end
--==================================================
-- BUTTON CREATOR
--==================================================
local function createButton(
parent,
name,
text,
position,
size
)
local button = Instance.new("TextButton")
button.Name = name
button.Size = size or UDim2.new(0, 70, 0, 30)
button.Position = position
button.BackgroundColor3 = BUTTON_COLOR
button.TextColor3 = WHITE
button.Text = text
button.TextScaled = true
button.Font = Enum.Font.GothamBold
button.AutoButtonColor = true
button.Parent = parent
local corner = Instance.new("UICorner")
corner.CornerRadius = UDim.new(0, 6)
corner.Parent = button
local stroke = Instance.new("UIStroke")
stroke.Thickness = 1.5
stroke.Color = PURPLE
stroke.Parent = button
return button
end
--==================================================
-- MAIN PANEL
--==================================================
local mainContainer = Instance.new("Frame")
mainContainer.Name = "MainControls"
mainContainer.Size =
UDim2.new(
0,
82,
0,
295
)
mainContainer.AnchorPoint =
Vector2.new(
0,
1
)
mainContainer.Position =
UDim2.new(
0,
6,
1,
-6
)
mainContainer.BackgroundColor3 = PANEL_COLOR
mainContainer.Parent = gui
local mainCorner = Instance.new("UICorner")
mainCorner.CornerRadius = UDim.new(0, 8)
mainCorner.Parent = mainContainer
--==================================================
-- DRAG BUTTON
--==================================================
local mainDrag = createButton(
mainContainer,
"Drag",
"DRAG",
UDim2.new(
0,
4,
0,
4
),
UDim2.new(
0,
74,
0,
22
)
)
makeDraggable(
mainContainer,
mainDrag
)
--==================================================
-- MAIN BUTTONS
--==================================================
local placeButton = createButton(
mainContainer,
"PlacePortal",
"PLACE",
UDim2.new(
0,
4,
0,
30
),
UDim2.new(
0,
74,
0,
29
)
)
local teleportButton = createButton(
mainContainer,
"Teleport",
"TELEPORT",
UDim2.new(
0,
4,
0,
63
),
UDim2.new(
0,
74,
0,
29
)
)
local selectButton = createButton(
mainContainer,
"Select",
"SELECT",
UDim2.new(
0,
4,
0,
96
),
UDim2.new(
0,
74,
0,
29
)
)
local unselectButton = createButton(
mainContainer,
"Unselect",
"UNSELECT",
UDim2.new(
0,
4,
0,
129
),
UDim2.new(
0,
74,
0,
29
)
)
local moveButton = createButton(
mainContainer,
"MoveMode",
"MOVE",
UDim2.new(
0,
4,
0,
162
),
UDim2.new(
0,
74,
0,
29
)
)
local scaleButton = createButton(
mainContainer,
"ScaleMode",
"SCALE",
UDim2.new(
0,
4,
0,
195
),
UDim2.new(
0,
74,
0,
29
)
)
local rotateButton = createButton(
mainContainer,
"RotateMode",
"ROTATE",
UDim2.new(
0,
4,
0,
228
),
UDim2.new(
0,
74,
0,
29
)
)
--==================================================
-- MINIMIZE BUTTON
--==================================================
local minimizeButton = createButton(
mainContainer,
"Minimize",
"MINIMIZE",
UDim2.new(
0,
4,
1,
-34
),
UDim2.new(
0,
74,
0,
28
)
)
--==================================================
-- HIDE BUTTONS UNTIL PORTAL EXISTS
--==================================================
teleportButton.Visible = false
selectButton.Visible = false
unselectButton.Visible = false
moveButton.Visible = false
scaleButton.Visible = false
rotateButton.Visible = false
--==================================================
-- MAIN PANEL SIZES
--==================================================
local normalMainSize =
UDim2.new(
0,
82,
0,
295
)
local minimizedMainSize =
UDim2.new(
0,
82,
0,
68
)
--==================================================
-- MINIMIZE / MAXIMIZE ANIMATION
--==================================================
minimizeButton.MouseButton1Click:Connect(function()
minimized =
not minimized
if minimized then
-- Hide all normal controls
placeButton.Visible = false
teleportButton.Visible = false
selectButton.Visible = false
unselectButton.Visible = false
moveButton.Visible = false
scaleButton.Visible = false
rotateButton.Visible = false
-- Keep DRAG at the top
mainDrag.Visible = true
-- Move MAXIMIZE under DRAG
minimizeButton.Position =
UDim2.new(
0,
4,
0,
36
)
minimizeButton.Text =
"MAXIMIZE"
TweenService:Create(
mainContainer,
PANEL_TWEEN_INFO,
{
Size = minimizedMainSize
}
):Play()
else
minimizeButton.Text =
"MINIMIZE"
TweenService:Create(
mainContainer,
PANEL_TWEEN_INFO,
{
Size = normalMainSize
}
):Play()
task.delay(
0.2,
function()
minimizeButton.Position =
UDim2.new(
0,
4,
1,
-34
)
placeButton.Visible = true
teleportButton.Visible =
portalPart ~= nil
selectButton.Visible =
portalPart ~= nil
unselectButton.Visible =
portalPart ~= nil
moveButton.Visible =
portalPart ~= nil
scaleButton.Visible =
portalPart ~= nil
rotateButton.Visible =
portalPart ~= nil
end
)
end
end)
--==================================================
-- SCALE PANEL
--==================================================
local scalePanel = Instance.new("Frame")
scalePanel.Name = "ScalePanel"
scalePanel.Size =
UDim2.new(
0,
230,
0,
230
)
scalePanel.AnchorPoint =
Vector2.new(
0.5,
0.5
)
scalePanel.Position =
UDim2.new(
0.5,
0,
1.2,
0
)
scalePanel.BackgroundColor3 = PANEL_COLOR
scalePanel.Visible = false
scalePanel.Parent = gui
local scaleCorner = Instance.new("UICorner")
scaleCorner.CornerRadius = UDim.new(0, 10)
scaleCorner.Parent = scalePanel
local scaleStroke = Instance.new("UIStroke")
scaleStroke.Thickness = 2
scaleStroke.Color = PURPLE
scaleStroke.Parent = scalePanel
--==================================================
-- SCALE TITLE
--==================================================
local scaleTitle = Instance.new("TextLabel")
scaleTitle.Size =
UDim2.new(
1,
-45,
0,
32
)
scaleTitle.Position =
UDim2.new(
0,
8,
0,
5
)
scaleTitle.BackgroundTransparency = 1
scaleTitle.Text = "SCALE PORTAL"
scaleTitle.TextColor3 = WHITE
scaleTitle.TextScaled = true
scaleTitle.Font = Enum.Font.GothamBold
scaleTitle.Parent = scalePanel
--==================================================
-- SCALE CLOSE BUTTON
--==================================================
local closeScaleButton = createButton(
scalePanel,
"CloseScale",
"X",
UDim2.new(
1,
-35,
0,
6
),
UDim2.new(
0,
28,
0,
28
)
)
--==================================================
-- SCALE DRAG
--==================================================
local scaleDrag = createButton(
scalePanel,
"ScaleDrag",
"DRAG",
UDim2.new(
0,
8,
0,
43
),
UDim2.new(
0,
60,
0,
24
)
)
makeDraggable(
scalePanel,
scaleDrag
)
--==================================================
-- SCALE TEXTBOX CREATOR
--==================================================
local function createScaleBox(
name,
labelText,
position
)
local label = Instance.new("TextLabel")
label.Name = name .. "Label"
label.Size =
UDim2.new(
0,
45,
0,
30
)
label.Position = position
label.BackgroundTransparency = 1
label.Text = labelText
label.TextColor3 = WHITE
label.TextScaled = true
label.Font = Enum.Font.GothamBold
label.Parent = scalePanel
local box = Instance.new("TextBox")
box.Name = name .. "TextBox"
box.Size =
UDim2.new(
0,
105,
0,
30
)
box.Position =
UDim2.new(
0,
58,
0,
position.Y.Offset
)
box.BackgroundColor3 = BUTTON_COLOR
box.TextColor3 = WHITE
box.PlaceholderColor3 = Color3.fromRGB(180, 180, 180)
box.Text = ""
box.PlaceholderText = "Enter value"
box.TextScaled = true
box.Font = Enum.Font.GothamBold
box.ClearTextOnFocus = false
box.Parent = scalePanel
local corner = Instance.new("UICorner")
corner.CornerRadius = UDim.new(0, 6)
corner.Parent = box
local stroke = Instance.new("UIStroke")
stroke.Thickness = 1.5
stroke.Color = PURPLE
stroke.Parent = box
return box
end
local xBox = createScaleBox(
"X",
"X",
UDim2.new(
0,
8,
0,
78
)
)
local yBox = createScaleBox(
"Y",
"Y",
UDim2.new(
0,
8,
0,
120
)
)
local zBox = createScaleBox(
"Z",
"Z",
UDim2.new(
0,
8,
0,
162
)
)
--==================================================
-- SCALE TEXTBOX UPDATE
--==================================================
local function updateScaleFromTextBox()
if not selectedPortal then
return
end
local x = tonumber(xBox.Text)
local y = tonumber(yBox.Text)
local z = tonumber(zBox.Text)
if x and y and z then
selectedPortal.Size =
Vector3.new(
math.clamp(
x,
MIN_SIZE,
MAX_SIZE
),
math.clamp(
y,
MIN_SIZE,
MAX_SIZE
),
math.clamp(
z,
MIN_SIZE,
MAX_SIZE
)
)
updateGizmo()
end
end
xBox.FocusLost:Connect(updateScaleFromTextBox)
yBox.FocusLost:Connect(updateScaleFromTextBox)
zBox.FocusLost:Connect(updateScaleFromTextBox)
--==================================================
-- ROTATE PANEL
--==================================================
local rotatePanel = Instance.new("Frame")
rotatePanel.Name = "RotatePanel"
rotatePanel.Size =
UDim2.new(
0,
230,
0,
245
)
rotatePanel.AnchorPoint =
Vector2.new(
0.5,
0.5
)
rotatePanel.Position =
UDim2.new(
0.5,
0,
1.2,
0
)
rotatePanel.BackgroundColor3 = PANEL_COLOR
rotatePanel.Visible = false
rotatePanel.Parent = gui
local rotateCorner = Instance.new("UICorner")
rotateCorner.CornerRadius = UDim.new(0, 10)
rotateCorner.Parent = rotatePanel
local rotateStroke = Instance.new("UIStroke")
rotateStroke.Thickness = 2
rotateStroke.Color = PURPLE
rotateStroke.Parent = rotatePanel
--==================================================
-- ROTATE TITLE
--==================================================
local rotateTitle = Instance.new("TextLabel")
rotateTitle.Size =
UDim2.new(
1,
-45,
0,
32
)
rotateTitle.Position =
UDim2.new(
0,
8,
0,
5
)
rotateTitle.BackgroundTransparency = 1
rotateTitle.Text = "ROTATE PORTAL"
rotateTitle.TextColor3 = WHITE
rotateTitle.TextScaled = true
rotateTitle.Font = Enum.Font.GothamBold
rotateTitle.Parent = rotatePanel
--==================================================
-- ROTATE CLOSE BUTTON
--==================================================
local closeRotateButton = createButton(
rotatePanel,
"CloseRotate",
"X",
UDim2.new(
1,
-35,
0,
6
),
UDim2.new(
0,
28,
0,
28
)
)
--==================================================
-- ROTATE DRAG
--==================================================
local rotateDrag = createButton(
rotatePanel,
"RotateDrag",
"DRAG",
UDim2.new(
0,
8,
0,
43
),
UDim2.new(
0,
60,
0,
24
)
)
makeDraggable(
rotatePanel,
rotateDrag
)
--==================================================
-- ROTATION BUTTONS
--==================================================
local rotateLeftButton = createButton(
rotatePanel,
"RotateLeft",
"LEFT",
UDim2.new(
0,
8,
0,
80
),
UDim2.new(
0,
100,
0,
38
)
)
local rotateRightButton = createButton(
rotatePanel,
"RotateRight",
"RIGHT",
UDim2.new(
0,
122,
0,
80
),
UDim2.new(
0,
100,
0,
38
)
)
local rotateTopButton = createButton(
rotatePanel,
"RotateTop",
"TOP",
UDim2.new(
0,
8,
0,
125
),
UDim2.new(
0,
100,
0,
38
)
)
local rotateBottomButton = createButton(
rotatePanel,
"RotateBottom",
"BOTTOM",
UDim2.new(
0,
122,
0,
125
),
UDim2.new(
0,
100,
0,
38
)
)
local diagonalBackButton = createButton(
rotatePanel,
"DiagonalBack",
"DIAGONAL BACK",
UDim2.new(
0,
8,
0,
175
),
UDim2.new(
0,
214,
0,
38
)
)
--==================================================
-- PANEL ANIMATION FUNCTIONS
--==================================================
local function openScalePanel()
scalePanel.Visible = true
scalePanel.Position =
UDim2.new(
0.5,
0,
1.2,
0
)
TweenService:Create(
scalePanel,
PANEL_TWEEN_INFO,
{
Position =
UDim2.new(
0.5,
0,
0.5,
0
)
}
):Play()
end
local function closeScalePanel()
local tween = TweenService:Create(
scalePanel,
PANEL_CLOSE_TWEEN_INFO,
{
Position =
UDim2.new(
0.5,
0,
1.2,
0
)
}
)
tween:Play()
tween.Completed:Connect(function()
scalePanel.Visible = false
end)
end
local function openRotatePanel()
rotatePanel.Visible = true
rotatePanel.Position =
UDim2.new(
0.5,
0,
1.2,
0
)
TweenService:Create(
rotatePanel,
PANEL_TWEEN_INFO,
{
Position =
UDim2.new(
0.5,
0,
0.5,
0
)
}
):Play()
end
local function closeRotatePanel()
local tween = TweenService:Create(
rotatePanel,
PANEL_CLOSE_TWEEN_INFO,
{
Position =
UDim2.new(
0.5,
0,
1.2,
0
)
}
)
tween:Play()
tween.Completed:Connect(function()
rotatePanel.Visible = false
end)
end
--==================================================
-- 3D GIZMO
--==================================================
local gizmo = Instance.new("Model")
gizmo.Name = "PortalGizmo"
gizmo.Parent = workspace
local gizmoParts = {}
local gizmoHitboxes = {}
local function createGizmoPart(name, color)
local part = Instance.new("Part")
part.Name = name
part.Size = Vector3.new(0.7, 0.7, 0.7)
part.Anchored = true
part.CanCollide = false
part.CanTouch = false
part.CanQuery = false
part.Material = Enum.Material.Neon
part.Color = color
part.Parent = gizmo
table.insert(
gizmoParts,
part
)
return part
end
local function createGizmoHitbox(name)
local hitbox = Instance.new("Part")
hitbox.Name = name
hitbox.Size =
Vector3.new(
BASE_HITBOX_SIZE,
BASE_HITBOX_SIZE,
BASE_HITBOX_SIZE
)
hitbox.Anchored = true
hitbox.CanCollide = false
hitbox.CanTouch = false
hitbox.CanQuery = true
hitbox.Transparency = 1
hitbox.Parent = gizmo
gizmoHitboxes[name] = hitbox
return hitbox
end
local moveLeft = createGizmoPart(
"MoveLeft",
Color3.fromRGB(255, 0, 0)
)
local moveRight = createGizmoPart(
"MoveRight",
Color3.fromRGB(255, 0, 0)
)
local moveUp = createGizmoPart(
"MoveUp",
Color3.fromRGB(0, 255, 0)
)
local moveDown = createGizmoPart(
"MoveDown",
Color3.fromRGB(0, 255, 0)
)
local moveForward = createGizmoPart(
"MoveForward",
Color3.fromRGB(0, 120, 255)
)
local moveBackward = createGizmoPart(
"MoveBackward",
Color3.fromRGB(0, 120, 255)
)
local moveLeftHitbox = createGizmoHitbox(
"MoveLeftHitbox"
)
local moveRightHitbox = createGizmoHitbox(
"MoveRightHitbox"
)
local moveUpHitbox = createGizmoHitbox(
"MoveUpHitbox"
)
local moveDownHitbox = createGizmoHitbox(
"MoveDownHitbox"
)
local moveForwardHitbox = createGizmoHitbox(
"MoveForwardHitbox"
)
local moveBackwardHitbox = createGizmoHitbox(
"MoveBackwardHitbox"
)
--==================================================
-- HIDE GIZMO
--==================================================
local function hideGizmo()
for _, part in ipairs(gizmoParts) do
part.Transparency = 1
end
for _, hitbox in pairs(gizmoHitboxes) do
hitbox.Transparency = 1
hitbox.CanQuery = false
end
end
hideGizmo()
--==================================================
-- UPDATE GIZMO
--==================================================
local function updateGizmo()
if not selectedPortal then
hideGizmo()
return
end
if currentMode ~= "MOVE" then
hideGizmo()
return
end
local cf = selectedPortal.CFrame
local size = selectedPortal.Size
local xDistance =
(size.X / 2)
+ BASE_GIZMO_DISTANCE
local yDistance =
(size.Y / 2)
+ BASE_GIZMO_DISTANCE
local zDistance =
(size.Z / 2)
+ BASE_GIZMO_DISTANCE
local largestSize =
math.max(
size.X,
size.Y,
size.Z
)
local hitboxSize =
math.clamp(
BASE_HITBOX_SIZE
+ largestSize * 0.08,
BASE_HITBOX_SIZE,
MAX_HITBOX_SIZE
)
moveLeft.CFrame =
cf
* CFrame.new(
-xDistance,
0,
0
)
moveRight.CFrame =
cf
* CFrame.new(
xDistance,
0,
0
)
moveUp.CFrame =
cf
* CFrame.new(
0,
yDistance,
0
)
moveDown.CFrame =
cf
* CFrame.new(
0,
-yDistance,
0
)
moveForward.CFrame =
cf
* CFrame.new(
0,
0,
-zDistance
)
moveBackward.CFrame =
cf
* CFrame.new(
0,
0,
zDistance
)
local visibleSize =
math.clamp(
0.7
+ largestSize * 0.025,
0.7,
3
)
for _, part in ipairs(gizmoParts) do
part.Size =
Vector3.new(
visibleSize,
visibleSize,
visibleSize
)
part.Transparency = 0
end
moveLeftHitbox.CFrame = moveLeft.CFrame
moveRightHitbox.CFrame = moveRight.CFrame
moveUpHitbox.CFrame = moveUp.CFrame
moveDownHitbox.CFrame = moveDown.CFrame
moveForwardHitbox.CFrame = moveForward.CFrame
moveBackwardHitbox.CFrame = moveBackward.CFrame
local hitboxVector =
Vector3.new(
hitboxSize,
hitboxSize,
hitboxSize
)
for _, hitbox in pairs(gizmoHitboxes) do
hitbox.Size = hitboxVector
hitbox.Transparency = 1
hitbox.CanQuery = true
end
end
--==================================================
-- MOVE PORTAL
--==================================================
local function movePortal(direction)
if not selectedPortal then
return
end
local cf = selectedPortal.CFrame
if direction == "Up" then
selectedPortal.CFrame =
selectedPortal.CFrame
+ cf.UpVector * MOVE_AMOUNT
elseif direction == "Down" then
selectedPortal.CFrame =
selectedPortal.CFrame
- cf.UpVector * MOVE_AMOUNT
elseif direction == "Left" then
selectedPortal.CFrame =
selectedPortal.CFrame
- cf.RightVector * MOVE_AMOUNT
elseif direction == "Right" then
selectedPortal.CFrame =
selectedPortal.CFrame
+ cf.RightVector * MOVE_AMOUNT
elseif direction == "Forward" then
selectedPortal.CFrame =
selectedPortal.CFrame
+ cf.LookVector * MOVE_AMOUNT
elseif direction == "Backward" then
selectedPortal.CFrame =
selectedPortal.CFrame
- cf.LookVector * MOVE_AMOUNT
end
updateGizmo()
end
--==================================================
-- ROTATE PORTAL
--==================================================
local function rotatePortal(direction)
if not selectedPortal then
return
end
local rotationAmount =
math.rad(
ROTATE_AMOUNT
)
if direction == "Left" then
selectedPortal.CFrame =
selectedPortal.CFrame
* CFrame.Angles(
0,
-rotationAmount,
0
)
elseif direction == "Right" then
selectedPortal.CFrame =
selectedPortal.CFrame
* CFrame.Angles(
0,
rotationAmount,
0
)
elseif direction == "Top" then
selectedPortal.CFrame =
selectedPortal.CFrame
* CFrame.Angles(
-rotationAmount,
0,
0
)
elseif direction == "Bottom" then
selectedPortal.CFrame =
selectedPortal.CFrame
* CFrame.Angles(
rotationAmount,
0,
0
)
end
updateGizmo()
end
local function diagonalRotatePortal()
if not selectedPortal then
return
end
selectedPortal.CFrame =
selectedPortal.CFrame
* CFrame.Angles(
0,
0,
math.rad(
ROTATE_AMOUNT
)
)
updateGizmo()
end
--==================================================
-- PLACE PORTAL
--==================================================
placeButton.MouseButton1Click:Connect(function()
placingPortal = true
selectingPortal = false
placeButton.Text = "TAP GROUND"
end)
mouse.Button1Down:Connect(function()
if placingPortal
and mouse.Target then
placingPortal = false
if portalPart then
portalPart:Destroy()
end
portalPart = Instance.new("Part")
portalPart.Name = "Portal"
portalPart.Size =
Vector3.new(
0.5,
7,
4
)
portalPart.CFrame =
CFrame.new(
mouse.Hit.Position
+ Vector3.new(
0,
3.5,
0
)
)
portalPart.Anchored = true
portalPart.CanCollide = false
portalPart.CanTouch = false
portalPart.CanQuery = true
portalPart.Material = Enum.Material.Neon
portalPart.Color =
Color3.fromRGB(
170,
0,
255
)
portalPart.Parent = workspace
local portalHighlight =
Instance.new(
"Highlight"
)
portalHighlight.Name =
"RedPortalHighlight"
portalHighlight.Adornee =
portalPart
portalHighlight.FillColor =
Color3.fromRGB(
255,
0,
0
)
portalHighlight.OutlineColor =
Color3.fromRGB(
255,
0,
0
)
portalHighlight.FillTransparency = 1
portalHighlight.OutlineTransparency = 0
portalHighlight.DepthMode =
Enum.HighlightDepthMode.AlwaysOnTop
portalHighlight.Parent =
portalPart
selectedPortal = portalPart
placeButton.Text = "PLACE"
teleportButton.Visible = true
selectButton.Visible = true
unselectButton.Visible = true
moveButton.Visible = true
scaleButton.Visible = true
rotateButton.Visible = true
currentMode = nil
updateModeText()
hideGizmo()
return
end
if selectingPortal
and mouse.Target == portalPart then
selectedPortal = portalPart
selectingPortal = false
selectButton.Text = "SELECT"
showSelectedNotification()
updateGizmo()
end
end)
--==================================================
-- SELECT
--==================================================
selectButton.MouseButton1Click:Connect(function()
if portalPart then
selectingPortal = true
selectButton.Text = "TAP PORTAL"
end
end)
--==================================================
-- UNSELECT
--==================================================
unselectButton.MouseButton1Click:Connect(function()
selectedPortal = nil
selectingPortal = false
currentMode = nil
scalePanelOpen = false
rotatePanelOpen = false
scalePanel.Visible = false
rotatePanel.Visible = false
selectButton.Text = "SELECT"
hideSelectedNotification()
updateModeText()
hideGizmo()
end)
--==================================================
-- MOVE MODE
--==================================================
moveButton.MouseButton1Click:Connect(function()
if not selectedPortal then
return
end
hideSelectedNotification()
currentMode = "MOVE"
updateModeText()
if scalePanel.Visible then
closeScalePanel()
end
if rotatePanel.Visible then
closeRotatePanel()
end
updateGizmo()
end)
--==================================================
-- SCALE MODE
--==================================================
scaleButton.MouseButton1Click:Connect(function()
if not selectedPortal then
return
end
hideSelectedNotification()
-- If scale is already open, pressing SCALE closes it
if scalePanel.Visible then
closeScalePanel()
currentMode = nil
updateModeText()
return
end
if rotatePanel.Visible then
closeRotatePanel()
end
currentMode = "SCALE"
updateModeText()
xBox.Text =
tostring(
math.floor(
selectedPortal.Size.X * 100
) / 100
)
yBox.Text =
tostring(
math.floor(
selectedPortal.Size.Y * 100
) / 100
)
zBox.Text =
tostring(
math.floor(
selectedPortal.Size.Z * 100
) / 100
)
openScalePanel()
hideGizmo()
end)
--==================================================
-- ROTATE MODE
--==================================================
rotateButton.MouseButton1Click:Connect(function()
if not selectedPortal then
return
end
hideSelectedNotification()
if rotatePanel.Visible then
closeRotatePanel()
currentMode = nil
updateModeText()
return
end
if scalePanel.Visible then
closeScalePanel()
end
currentMode = "ROTATE"
updateModeText()
openRotatePanel()
hideGizmo()
end)
--==================================================
-- SCALE X BUTTON
--==================================================
-- X/Y/Z are controlled through the text boxes.
-- Entering a number and pressing Enter or clicking away
-- updates the portal size.
--==================================================
-- ROTATE BUTTONS
--==================================================
rotateLeftButton.MouseButton1Click:Connect(function()
rotatePortal("Left")
end)
rotateRightButton.MouseButton1Click:Connect(function()
rotatePortal("Right")
end)
rotateTopButton.MouseButton1Click:Connect(function()
rotatePortal("Top")
end)
rotateBottomButton.MouseButton1Click:Connect(function()
rotatePortal("Bottom")
end)
diagonalBackButton.MouseButton1Click:Connect(function()
diagonalRotatePortal()
end)
--==================================================
-- CLOSE SCALE WITH X
--==================================================
closeScaleButton.MouseButton1Click:Connect(function()
closeScalePanel()
currentMode = nil
updateModeText()
end)
--==================================================
-- CLOSE ROTATE WITH X
--==================================================
closeRotateButton.MouseButton1Click:Connect(function()
closeRotatePanel()
currentMode = nil
updateModeText()
end)
--==================================================
-- GIZMO CLICK
--==================================================
local function handleGizmoClick(target)
if currentMode ~= "MOVE" then
return
end
if target == moveUp
or target == moveUpHitbox then
movePortal("Up")
elseif target == moveDown
or target == moveDownHitbox then
movePortal("Down")
elseif target == moveLeft
or target == moveLeftHitbox then
movePortal("Left")
elseif target == moveRight
or target == moveRightHitbox then
movePortal("Right")
elseif target == moveForward
or target == moveForwardHitbox then
movePortal("Forward")
elseif target == moveBackward
or target == moveBackwardHitbox then
movePortal("Backward")
end
end
--==================================================
-- MOBILE AND MOUSE GIZMO INPUT
--==================================================
UserInputService.InputBegan:Connect(function(input)
if input.UserInputType ==
Enum.UserInputType.Touch
or input.UserInputType ==
Enum.UserInputType.MouseButton1 then
local target = mouse.Target
if target then
handleGizmoClick(target)
end
end
end)
--==================================================
-- TELEPORT
--==================================================
teleportButton.MouseButton1Click:Connect(function()
if portalPart
and player.Character then
local root =
player.Character:FindFirstChild(
"HumanoidRootPart"
)
if root then
root.CFrame =
portalPart.CFrame
+ portalPart.CFrame.LookVector
* 1.5
end
end
end)
--==================================================
-- KEEP GIZMO ATTACHED
--==================================================
RunService.RenderStepped:Connect(function()
if selectedPortal
and currentMode == "MOVE" then
updateGizmo()
end
end)