-- MultiViewCamera.lua v11 -- StarterPlayerScripts > LocalScript local Players = game:GetService("Players") local RunService = game:GetService("RunService") local UserInputService = game:GetService("UserInputService") local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") local mainCam = workspace.CurrentCamera -- ══════════════════════════════════════════ -- ★ 使用者設定 -- ══════════════════════════════════════════ -- 偵測設定 local TEAM_CHECK = false -- true = 只標記敵對玩家(不同隊伍) local WALL_CHECK = false -- true = 只在視線沒被牆壁遮擋時才標記 -- 視窗尺寸(不需動) local W = 196 local H = 126 local BAR = 22 local GAP = 6 local PAD_RIGHT = 10 local PAD_TOP = 8 -- 視角設定 local CONFIGS = { {name = "L", angle = math.rad( 60)}, {name = "B", angle = math.rad(180)}, {name = "R", angle = math.rad(-60)}, } -- 圈圈設定 local CIRCLE_BASE = 22 local CIRCLE_MIN = 8 local CIRCLE_MAX = 40 local MAX_CIRCLES = 8 -- ══════════════════════════════════════════ -- 橫幅位置(視窗從橫幅下方開始) -- ══════════════════════════════════════════ local BANNER_H = 36 local BANNER_GAP = 4 local WIN1_Y = PAD_TOP + BANNER_H + BANNER_GAP -- ══════════════════════════════════════════ -- ScreenGui -- ══════════════════════════════════════════ local gui = Instance.new("ScreenGui") gui.Name = "MultiViewGUI" gui.ResetOnSpawn = false gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling gui.IgnoreGuiInset = true gui.Parent = playerGui -- ══════════════════════════════════════════ -- 頂部橫幅(螢幕正上方置中) -- ══════════════════════════════════════════ local BANNER_W = 480 local topBanner = Instance.new("Frame") topBanner.Name = "TopBanner" topBanner.Size = UDim2.new(0, BANNER_W, 0, BANNER_H) topBanner.Position = UDim2.new(0.5, -BANNER_W / 2, 0, PAD_TOP) topBanner.BackgroundColor3 = Color3.fromRGB(20, 5, 5) topBanner.BorderSizePixel = 0 topBanner.ZIndex = 20 topBanner.Visible = false topBanner.Parent = gui Instance.new("UICorner", topBanner).CornerRadius = UDim.new(0, 8) local bStroke = Instance.new("UIStroke", topBanner) bStroke.Color = Color3.fromRGB(255, 55, 55); bStroke.Thickness = 1.8 -- 左側警示圖示 local bIcon = Instance.new("TextLabel", topBanner) bIcon.Size = UDim2.new(0, 32, 1, 0); bIcon.Position = UDim2.new(0, 6, 0, 0) bIcon.BackgroundTransparency = 1; bIcon.Text = "[!]" bIcon.TextColor3 = Color3.fromRGB(255, 80, 80); bIcon.Font = Enum.Font.GothamBold bIcon.TextSize = 16; bIcon.ZIndex = 21 -- 主要文字 local bannerLabel = Instance.new("TextLabel", topBanner) bannerLabel.Size = UDim2.new(1, -46, 1, 0) bannerLabel.Position = UDim2.new(0, 40, 0, 0) bannerLabel.BackgroundTransparency = 1 bannerLabel.Text = "" bannerLabel.TextColor3 = Color3.fromRGB(255, 130, 130) bannerLabel.Font = Enum.Font.GothamBold bannerLabel.TextSize = 16 -- 大字 bannerLabel.TextXAlignment = Enum.TextXAlignment.Left bannerLabel.TextTruncate = Enum.TextTruncate.AtEnd bannerLabel.ZIndex = 21 -- 右側設定指示(TEAM / WALL 狀態) local settingLbl = Instance.new("TextLabel", topBanner) settingLbl.Size = UDim2.new(0, 110, 1, 0) settingLbl.Position = UDim2.new(1, -114, 0, 0) settingLbl.BackgroundTransparency = 1 settingLbl.Font = Enum.Font.Gotham settingLbl.TextSize = 11 settingLbl.TextXAlignment = Enum.TextXAlignment.Right settingLbl.ZIndex = 21 local function refreshSettingLbl() local tc = TEAM_CHECK and "TEAM:ON" or "TEAM:OFF" local wc = WALL_CHECK and "WALL:ON" or "WALL:OFF" local tcColor = TEAM_CHECK and "rgb(255,210,60)" or "rgb(100,100,130)" local wcColor = WALL_CHECK and "rgb(60,210,255)" or "rgb(100,100,130)" -- 不用 RichText,改用簡單組合 settingLbl.Text = tc .. " " .. wc settingLbl.TextColor3 = Color3.fromRGB(160, 160, 180) end refreshSettingLbl() -- ══════════════════════════════════════════ -- 拖曳 -- ══════════════════════════════════════════ local drag = nil UserInputService.InputChanged:Connect(function(inp) if drag and inp.UserInputType == Enum.UserInputType.MouseMovement then local d = inp.Position - drag.startMouse drag.frame.Position = UDim2.new( 0, drag.startPos.X.Offset + d.X, 0, drag.startPos.Y.Offset + d.Y ) end end) UserInputService.InputEnded:Connect(function(inp) if inp.UserInputType == Enum.UserInputType.MouseButton1 then drag = nil end end) -- ══════════════════════════════════════════ -- 圈圈 -- ══════════════════════════════════════════ local function makeCircle(parent) local c = Instance.new("Frame", parent) c.BackgroundTransparency = 1; c.BorderSizePixel = 0 c.ZIndex = 15; c.Visible = false; c.AnchorPoint = Vector2.new(0.5, 0.5) Instance.new("UICorner", c).CornerRadius = UDim.new(1, 0) local st = Instance.new("UIStroke", c) st.Color = Color3.fromRGB(255, 55, 55); st.Thickness = 2.2 local nl = Instance.new("TextLabel", c) nl.Name = "NL"; nl.Size = UDim2.new(2.8, 0, 0, 13) nl.Position = UDim2.new(-0.9, 0, 1, 2) nl.BackgroundTransparency = 1 nl.TextColor3 = Color3.fromRGB(255, 200, 200) nl.Font = Enum.Font.GothamBold; nl.TextSize = 9 nl.Text = ""; nl.TextTruncate = Enum.TextTruncate.AtEnd; nl.ZIndex = 16 return c, nl end -- ══════════════════════════════════════════ -- 建立視窗 -- ══════════════════════════════════════════ local windows = {} local function buildWindow(cfg, idx) local py = WIN1_Y + (idx - 1) * (H + BAR + GAP) local frame = Instance.new("Frame") frame.Name = "Win"..idx frame.Size = UDim2.new(0, W, 0, H + BAR) frame.Position = UDim2.new(1, -(W + PAD_RIGHT), 0, py) frame.BackgroundColor3 = Color3.fromRGB(8,10,18) frame.BorderSizePixel = 0; frame.ZIndex = 5; frame.ClipsDescendants = false frame.Parent = gui Instance.new("UICorner", frame).CornerRadius = UDim.new(0, 7) local fst = Instance.new("UIStroke", frame) fst.Color = Color3.fromRGB(45,115,255); fst.Thickness = 1.2; fst.Transparency = 0.2 local bar = Instance.new("Frame", frame) bar.Name = "Bar"; bar.Size = UDim2.new(1,0,0,BAR) bar.BackgroundColor3 = Color3.fromRGB(14,22,48); bar.BorderSizePixel = 0; bar.ZIndex = 6 Instance.new("UICorner", bar).CornerRadius = UDim.new(0, 7) local fix = Instance.new("Frame", bar) fix.Size = UDim2.new(1,0,0.5,0); fix.Position = UDim2.new(0,0,0.5,0) fix.BackgroundColor3 = Color3.fromRGB(14,22,48); fix.BorderSizePixel = 0; fix.ZIndex = 6 local nameLbl = Instance.new("TextLabel", bar) nameLbl.Size = UDim2.new(0,24,1,0); nameLbl.Position = UDim2.new(0,6,0,0) nameLbl.BackgroundTransparency = 1; nameLbl.Text = cfg.name nameLbl.TextColor3 = Color3.fromRGB(145,192,255); nameLbl.Font = Enum.Font.GothamBold nameLbl.TextSize = 11; nameLbl.TextXAlignment = Enum.TextXAlignment.Left; nameLbl.ZIndex = 7 local fovLbl = Instance.new("TextLabel", bar) fovLbl.Size = UDim2.new(0,52,1,0); fovLbl.Position = UDim2.new(0,30,0,0) fovLbl.BackgroundTransparency = 1; fovLbl.Text = "FOV:70" fovLbl.TextColor3 = Color3.fromRGB(65,145,255); fovLbl.Font = Enum.Font.Gotham fovLbl.TextSize = 9; fovLbl.TextXAlignment = Enum.TextXAlignment.Left; fovLbl.ZIndex = 7 local closeBtn = Instance.new("TextButton", bar) closeBtn.Size = UDim2.new(0,18,0,18); closeBtn.Position = UDim2.new(1,-21,0,2) closeBtn.BackgroundColor3 = Color3.fromRGB(175,38,38); closeBtn.Text = "X" closeBtn.TextColor3 = Color3.fromRGB(255,255,255); closeBtn.Font = Enum.Font.GothamBold closeBtn.TextSize = 9; closeBtn.BorderSizePixel = 0; closeBtn.ZIndex = 8 Instance.new("UICorner", closeBtn).CornerRadius = UDim.new(0, 4) local vp = Instance.new("ViewportFrame", frame) vp.Name = "VP"; vp.Size = UDim2.new(1,0,1,-BAR); vp.Position = UDim2.new(0,0,0,BAR) vp.BackgroundColor3 = Color3.fromRGB(20,20,30); vp.BorderSizePixel = 0 vp.LightColor = Color3.fromRGB(255,255,255); vp.LightDirection = Vector3.new(-1,-2,-1) vp.Ambient = Color3.fromRGB(255,255,255); vp.ZIndex = 5; vp.ClipsDescendants = true Instance.new("UICorner", vp).CornerRadius = UDim.new(0, 7) local cc = Instance.new("Frame", vp) cc.Name = "CC"; cc.Size = UDim2.new(1,0,1,0) cc.BackgroundTransparency = 1; cc.BorderSizePixel = 0; cc.ZIndex = 14 local circles = {} for i = 1, MAX_CIRCLES do local c, nl = makeCircle(cc) circles[i] = {frame = c, nl = nl} end local worldModel = Instance.new("WorldModel", vp) local vpCam = Instance.new("Camera") vpCam.CameraType = Enum.CameraType.Scriptable vpCam.FieldOfView = mainCam.FieldOfView vpCam.CFrame = mainCam.CFrame vpCam.Parent = vp; vp.CurrentCamera = vpCam bar.InputBegan:Connect(function(inp) if inp.UserInputType == Enum.UserInputType.MouseButton1 then drag = {frame = frame, startMouse = inp.Position, startPos = frame.Position} end end) local reopenBtn = Instance.new("TextButton", gui) reopenBtn.Size = UDim2.new(0,W,0,18) reopenBtn.Position = UDim2.new(1, -(W+PAD_RIGHT), 0, py) reopenBtn.BackgroundColor3 = Color3.fromRGB(14,22,48) reopenBtn.Text = cfg.name.." [open]" reopenBtn.TextColor3 = Color3.fromRGB(100,160,255); reopenBtn.Font = Enum.Font.GothamBold reopenBtn.TextSize = 9; reopenBtn.BorderSizePixel = 0; reopenBtn.Visible = false; reopenBtn.ZIndex = 5 Instance.new("UICorner", reopenBtn).CornerRadius = UDim.new(0, 4) closeBtn.MouseButton1Click:Connect(function() frame.Visible = false; reopenBtn.Visible = true end) reopenBtn.MouseButton1Click:Connect(function() frame.Visible = true; reopenBtn.Visible = false end) windows[idx] = { name = cfg.name, frame = frame, vpCam = vpCam, vp = vp, worldModel = worldModel, fovLbl = fovLbl, angle = cfg.angle, circles = circles, hasPlayer = false, } end for i, cfg in ipairs(CONFIGS) do buildWindow(cfg, i) end -- ══════════════════════════════════════════ -- 地圖 Clone -- ══════════════════════════════════════════ local function cloneMapInto(wm) local cs = {} for _, p in pairs(Players:GetPlayers()) do if p.Character then cs[p.Character] = true end end for _, obj in pairs(workspace:GetChildren()) do if not obj:IsA("Camera") and not obj:IsA("Terrain") and not cs[obj] and obj.Archivable then local ok, clone = pcall(function() return obj:Clone() end) if ok and clone then for _, s in pairs(clone:GetDescendants()) do if s:IsA("Script") or s:IsA("LocalScript") or s:IsA("ModuleScript") then pcall(function() s:Destroy() end) end end clone.Parent = wm end end end end -- ══════════════════════════════════════════ -- 角色管理 -- ══════════════════════════════════════════ local charData = {} local function buildPartMap(real, clone) local m = {} for _, rp in pairs(real:GetDescendants()) do if rp:IsA("BasePart") then local cp = clone:FindFirstChild(rp.Name, true) if cp and cp:IsA("BasePart") then m[rp] = cp end end end return m end local function addChar(p, char) if not char then return end task.wait(0.5) if not char.Parent then return end char.Archivable = true for _, d in pairs(char:GetDescendants()) do pcall(function() d.Archivable = true end) end local uid = tostring(p.UserId) if charData[uid] then for _, c in pairs(charData[uid].clones) do pcall(function() c:Destroy() end) end charData[uid] = nil end local data = {realChar = char, clones = {}, partMap = {}} charData[uid] = data for _, win in pairs(windows) do local ok, clone = pcall(function() return char:Clone() end) if ok and clone then for _, s in pairs(clone:GetDescendants()) do if s:IsA("Script") or s:IsA("LocalScript") or s:IsA("ModuleScript") then pcall(function() s:Destroy() end) end end for _, part in pairs(clone:GetDescendants()) do if part:IsA("BasePart") then part.LocalTransparencyModifier = 0 part.Transparency = math.min(part.Transparency, 0.5) end end clone.Name = "CHAR_"..uid; clone.Parent = win.worldModel data.clones[win.worldModel] = clone data.partMap[win.worldModel] = buildPartMap(char, clone) end end end local function removeChar(p) local uid = tostring(p.UserId) if charData[uid] then for _, c in pairs(charData[uid].clones) do pcall(function() c:Destroy() end) end charData[uid] = nil end end for _, p in pairs(Players:GetPlayers()) do if p.Character then task.spawn(addChar, p, p.Character) end p.CharacterAdded:Connect(function(c) addChar(p, c) end) p.CharacterRemoving:Connect(function() removeChar(p) end) end Players.PlayerAdded:Connect(function(p) p.CharacterAdded:Connect(function(c) addChar(p, c) end) p.CharacterRemoving:Connect(function() removeChar(p) end) end) Players.PlayerRemoving:Connect(function(p) removeChar(p) end) task.spawn(function() if not player.Character then player.CharacterAdded:Wait() end player.Character:WaitForChild("HumanoidRootPart", 10) task.wait(0.5) for _, win in pairs(windows) do cloneMapInto(win.worldModel) end end) player.CharacterAdded:Connect(function(char) char:WaitForChild("HumanoidRootPart", 10) task.wait(0.5) for _, win in pairs(windows) do for _, child in pairs(win.worldModel:GetChildren()) do if not string.find(child.Name, "CHAR_") then child:Destroy() end end cloneMapInto(win.worldModel) end end) -- ══════════════════════════════════════════ -- 牆壁檢查(Raycast) -- 從本地角色 HRP 射向目標 HRP, -- 看中間是否有不透明的 BasePart 阻擋 -- ══════════════════════════════════════════ local raycastParams = RaycastParams.new() raycastParams.FilterType = Enum.RaycastFilterType.Exclude local function isVisible(targetHRP) local myChar = player.Character if not myChar then return true end local myHRP = myChar:FindFirstChild("HumanoidRootPart") if not myHRP then return true end -- 排除自己和目標的角色,避免射到自身 local exclude = {myChar, targetHRP.Parent} raycastParams.FilterDescendantsInstances = exclude local origin = myHRP.Position local direction = targetHRP.Position - origin local result = workspace:Raycast(origin, direction, raycastParams) -- 沒打到任何東西 = 可見;打到東西表示被牆擋住 return result == nil end -- ══════════════════════════════════════════ -- 判斷是否標記 -- ══════════════════════════════════════════ local function shouldMark(p, hrp) if p == player then return false end if TEAM_CHECK and player.Team == p.Team then return false end if WALL_CHECK and not isVisible(hrp) then return false end return true end -- ══════════════════════════════════════════ -- 投影 3D → VP 2D -- ══════════════════════════════════════════ local function projectToVP(vpCam, vp, worldPos) local sp, onScreen = vpCam:WorldToViewportPoint(worldPos) if not onScreen or sp.Z <= 0 then return nil, nil end local vs = vpCam.ViewportSize if vs.X == 0 or vs.Y == 0 then return nil, nil end local rx, ry = sp.X / vs.X, sp.Y / vs.Y if rx < 0 or rx > 1 or ry < 0 or ry > 1 then return nil, nil end local ab = vp.AbsoluteSize return Vector2.new(rx * ab.X, ry * ab.Y), sp.Z end -- ══════════════════════════════════════════ -- RenderStepped -- ══════════════════════════════════════════ RunService.RenderStepped:Connect(function() local cf = mainCam.CFrame local fov = mainCam.FieldOfView for _, win in pairs(windows) do win.hasPlayer = false if win.frame.Visible then win.vpCam.CFrame = cf * CFrame.Angles(0, win.angle, 0) win.vpCam.FieldOfView = fov win.fovLbl.Text = "FOV:" .. tostring(math.floor(fov + 0.5)) end end -- 角色同步 for _, data in pairs(charData) do local rc = data.realChar if rc and rc.Parent then for _, win in pairs(windows) do if win.frame.Visible then local pm = data.partMap[win.worldModel] if pm then for rp, cp in pairs(pm) do if rp.Parent and cp.Parent then cp.CFrame = rp.CFrame cp.LocalTransparencyModifier = 0 end end end end end end end -- 偵測 + 圈圈 for _, win in pairs(windows) do if not win.frame.Visible then for _, c in pairs(win.circles) do c.frame.Visible = false end else local ci = 0 for _, p in pairs(Players:GetPlayers()) do if p.Character then local hrp = p.Character:FindFirstChild("HumanoidRootPart") if hrp and shouldMark(p, hrp) then local pos2D, depth = projectToVP(win.vpCam, win.vp, hrp.Position) if pos2D then ci = ci + 1 if ci <= MAX_CIRCLES then local obj = win.circles[ci] local r = math.clamp(CIRCLE_BASE * (10 / math.max(depth, 1)), CIRCLE_MIN, CIRCLE_MAX) obj.frame.Size = UDim2.new(0, r*2, 0, r*2) obj.frame.Position = UDim2.new(0, pos2D.X, 0, pos2D.Y) obj.frame.Visible = true obj.nl.Text = p.DisplayName win.hasPlayer = true end end end end end for i = ci + 1, MAX_CIRCLES do win.circles[i].frame.Visible = false end end end -- 橫幅(置中頂部) local found = {} for _, win in pairs(windows) do if win.hasPlayer then table.insert(found, win.name) end end if #found > 0 then topBanner.Visible = true bannerLabel.Text = "Player spotted: " .. table.concat(found, " | ") else topBanner.Visible = false end end) print("[MultiViewCamera v11] OK") print(" TEAM_CHECK = " .. tostring(TEAM_CHECK)) print(" WALL_CHECK = " .. tostring(WALL_CHECK))