Eye of Horus (Micro-error detector)

  1. https://create.roblox.com/store/asset/89367854250100/eye-of-horus
    EYE_of_Horus(Plugin).rbxm (13.6 KB)

  2. It catches minute errors in parts, and if you click the model while AutoEdit is turned on, it corrects minute errors in increments of 0.0625. (1/16)

  3. It is good for people who use units of 0.5, 0.25, and 0.125.

  4. You can modify the attached file to use it as your own plugin.

local toolbar = plugin:CreateToolbar("eye of horus")
local button = toolbar:CreateButton("eye of horus", "UI is on", "rbxassetid://12128496201")

local selectionService = game:GetService("Selection")
local CoreGui = game:GetService("CoreGui")

local screenGui = script.ScreenGui
screenGui.Name = "PluginUI"
screenGui.Parent = CoreGui

local uiVisible = true
-- UI toggle
button.Click:Connect(function()
	uiVisible = not uiVisible
	screenGui.Enabled = uiVisible
end)

local Autoedit = false
local Autoedit_Button = screenGui.Frame.Autoedit

local Display_Mode = true
local Display_Mode_Button = screenGui.Frame.Display_Mode


local Display_Folder = CoreGui:FindFirstChild("EyeOfHorusDisplay")
if not Display_Folder then
	Display_Folder = Instance.new("Folder")
	Display_Folder.Name = "EyeOfHorusDisplay"
	Display_Folder.Parent = CoreGui
end






-- Autoedit reset and toggle
local function updateAutoeditButton()
	if Autoedit then
		Autoedit_Button.Text = "Autoedit: ON"
		Autoedit_Button.TextColor3 = Color3.fromRGB(0, 255, 0)
	else
		Autoedit_Button.Text = "Autoedit: OFF"
		Autoedit_Button.TextColor3 = Color3.fromRGB(255, 0, 0)
	end
end
updateAutoeditButton()

local function updateDisplay_ModeButton()
	if Display_Mode then
		Display_Mode_Button.Text = "Display Mode: BillboardGui"	
	else
		Display_Mode_Button.Text = "Display Mode: Highlight"
	end
end
updateDisplay_ModeButton()
--SNAP 
local SNAP_STEP = 0.0625

local function snapNumber(num)
	return math.round(num / SNAP_STEP) * SNAP_STEP
end

local function snapVector3(vec)
	return Vector3.new(
		snapNumber(vec.X),
		snapNumber(vec.Y),
		snapNumber(vec.Z)
	)
end

-- tables
local connections = {}
local activeHighlights = {}
local updateTimers = {}
local isCorrecting = false 


-- reset highlights
local function clearAllHighlights()
	for obj, hl in pairs(activeHighlights) do
		if hl and hl.Parent then
			hl:Destroy()
		end
	end
	table.clear(activeHighlights)
end


local function updateHighlight(obj, hasError)
	if hasError then
		if not activeHighlights[obj] then
			if Display_Mode then
				local box = Instance.new("BoxHandleAdornment")
				box.Name = "ErrorVisual"
				box.Size = obj.Size + Vector3.new(0.05, 0.05, 0.05) -- 원본보다 약간 크게 (겹침 방지)
				box.Adornee = obj -- 기준이 될 파츠
				box.AlwaysOnTop = true -- 벽 너머로도 보이게 설정
				box.ZIndex = 10
				box.Transparency = 0.5 -- ForceField 느낌을 위해 반투명 설정
				box.Color3 = Color3.fromRGB(255, 0, 0)
				box.Parent = Display_Folder -- Display_Folder가 CoreGui 안에 있어도 됨

				local bill = Instance.new("BillboardGui")
				bill.Size = UDim2.new(0,50,0,50)
				bill.AlwaysOnTop = true
				bill.MaxDistance = 1000000
				bill.Adornee = obj -- 파츠에 직접 붙임
				bill.Parent = box

				local image = Instance.new("ImageLabel")
				image.Size = UDim2.new(1,0,1,0)
				image.BackgroundTransparency = 1
				image.Image = "rbxassetid://13517960032"
				image.Parent = bill

				activeHighlights[obj] = box
			else
				local hl = Instance.new("Highlight")
				hl.Adornee = obj
				hl.FillColor = Color3.fromRGB(255, 0, 0)
				hl.OutlineColor = Color3.fromRGB(255, 0, 0)
				hl.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop
				hl.Name = "ErrorHighlight"
				hl.Parent = Display_Folder
				activeHighlights[obj] = hl
			end
		end
	else
		if activeHighlights[obj] then
			activeHighlights[obj]:Destroy()
			activeHighlights[obj] = nil
		end
	end
end

local function updateUI(obj)
	if not obj or not obj.Parent then return end

	screenGui.Frame.PX.Text = obj.Position.X
	screenGui.Frame.PY.Text = obj.Position.Y
	screenGui.Frame.PZ.Text = obj.Position.Z

	screenGui.Frame.OX.Text = obj.Rotation.X
	screenGui.Frame.OY.Text = obj.Rotation.Y
	screenGui.Frame.OZ.Text = obj.Rotation.Z

	screenGui.Frame.SX.Text = obj.Size.X
	screenGui.Frame.SY.Text = obj.Size.Y
	screenGui.Frame.SZ.Text = obj.Size.Z

	if obj.CanCollide == true then
		screenGui.Frame.cancollide.Text = "CanCollide = true"
		screenGui.Frame.cancollide.TextColor3 = Color3.fromRGB(0, 255, 0)
	else
		screenGui.Frame.cancollide.Text = "CanCollide = false"
		screenGui.Frame.cancollide.TextColor3 = Color3.fromRGB(255, 0, 0)
	end

	if obj.CanTouch == true then
		screenGui.Frame.cantouch.Text = "CanTouch = true"
		screenGui.Frame.cantouch.TextColor3 = Color3.fromRGB(0, 255, 0)
	else
		screenGui.Frame.cantouch.Text = "CanTouch = false"
		screenGui.Frame.cantouch.TextColor3 = Color3.fromRGB(255, 0, 0)
	end

	if obj.Massless == true then
		screenGui.Frame.massless.Text = "Massless = true"
		screenGui.Frame.massless.TextColor3 = Color3.fromRGB(0, 255, 0)
	else
		screenGui.Frame.massless.Text = "Massless = false"
		screenGui.Frame.massless.TextColor3 = Color3.fromRGB(255, 0, 0)
	end

	if obj.Anchored == true then
		screenGui.Frame.anchor.Text = "Anchored = true"
		screenGui.Frame.anchor.TextColor3 = Color3.fromRGB(0, 255, 0)
	else
		screenGui.Frame.anchor.Text = "Anchored = false"
		screenGui.Frame.anchor.TextColor3 = Color3.fromRGB(255, 0, 0)
	end

	if obj:IsA("MeshPart") then
		screenGui.Frame.CollisionFidelity.Text = "CollisionFidelity = " .. obj.CollisionFidelity.Name
	end
end

----------------------------------
local inspectionQueue = {}
local isProcessingQueue = false
local MAX_PARTS_PER_FRAME = 25

local function performCheckAndFix(obj, canFix)
	if isCorrecting then return end

	local hasError = false

	-- 1. Position/Size
	for _, prop in ipairs({"Position", "Size"}) do
		local currentVal = obj[prop]
		local snappedVal = snapVector3(currentVal)
		if (currentVal - snappedVal).Magnitude > 0.000001 then
			if Autoedit and canFix then
				isCorrecting = true
				obj[prop] = snappedVal
				isCorrecting = false
			else
				hasError = true
			end
		end
	end

	-- 2. Rotation
	local currentRot = obj.Rotation
	local snappedRot = snapVector3(currentRot)
	if (currentRot - snappedRot).Magnitude > 0.01 then
		if Autoedit and canFix then
			isCorrecting = true
			obj.Rotation = snappedRot
			isCorrecting = false
		else
			hasError = true
		end
	end
	updateHighlight(obj, hasError)
end

local function processInspectionQueue()
	if isProcessingQueue then return end
	isProcessingQueue = true

	while #inspectionQueue > 0 do
		local batchCount = 0
		while #inspectionQueue > 0 and batchCount < MAX_PARTS_PER_FRAME do
			local taskData = table.remove(inspectionQueue, 1)
			local obj = taskData.obj
			if obj and obj.Parent then
				if screenGui.Enabled then
					updateUI(obj)
				end
				performCheckAndFix(obj, taskData.canFix)
			end
			batchCount += 1
		end
		task.wait() 
	end
	isProcessingQueue = false
end

local function checkAndFixErrors(obj, canFix)
	table.insert(inspectionQueue, {obj = obj, canFix = canFix})
	processInspectionQueue()
end

-- Autoedit 
Autoedit_Button.MouseButton1Click:Connect(function()
	Autoedit = not Autoedit
	updateAutoeditButton()
end)

Display_Mode_Button.MouseButton1Click:Connect(function()
	Display_Mode = not Display_Mode
	updateDisplay_ModeButton()
end)

-- main logic

selectionService.SelectionChanged:Connect(function()
	-- 1. 초기화
	for _, conn in ipairs(connections) do conn:Disconnect() end
	table.clear(connections)
	table.clear(inspectionQueue)
	clearAllHighlights()

	local selectedObjects = selectionService:Get()
	if #selectedObjects == 0 then return end

	local partsToInspect = {}
	local canFixMap = {}


	-- 2. 대상 수집
	for _, root in ipairs(selectedObjects) do
		if root:IsA("BasePart") then
			table.insert(partsToInspect, root)
			canFixMap[root] = true
		end
		for _, descendant in ipairs(root:GetDescendants()) do
			if descendant:IsA("BasePart") then
				if not canFixMap[descendant] then
					table.insert(partsToInspect, descendant)
					canFixMap[descendant] = true
				end
			end
		end
	end

	-- 3. 대기열에 삽입 및 이벤트 연결
	for _, part in ipairs(partsToInspect) do
		table.insert(inspectionQueue, {obj = part, canFix = canFixMap[part]})
		local conn = part.Changed:Connect(function(prop)
			if prop == "Position" or prop == "Rotation" or prop == "Size" then
				checkAndFixErrors(part, canFixMap[part])
			end
		end)
		table.insert(connections, conn)
	end

	-- 4. 초기 스캔 실행
	processInspectionQueue()
end)

:eyes: