Прозрачность объекта через Raycast
Créé le : 22 janvier 2025
Créé le : 22 janvier 2025
сделай код для lua в roblox studio: если обхект не видно ( например объект находиться за объектом) то у него transparensy на 0 (ну чтобы был прозрачным). если будешь делать через Raycast ну самый нормальный способ, то сделай так чтобы считыватель(камера игрока) была спереди на 2 stud и его высота была 8 stud и ширина 8 stud. так как от 1 лица если это не сделать то аксесуары (там очки и тд) мешают ему и поэтому он плохо считывает, у меня игра только от 1 лица поэтому надо чтобы считывель был спереди игрока и поэтому надо сделать считыватель больше как я написал
Ниже приведён пример скрипта на Lua (Roblox) для локального скрипта (LocalScript), который:
Transparency = 1 (чтобы он стал невидимым). Если виден, ставим Transparency = 0 (полностью видимый).Важно: В Roblox значения прозрачности работают так:
•Transparency = 0— объект полностью непрозрачный (виден).
•Transparency = 1— объект полностью прозрачный (невиден).Если вам нужно наоборот (например, при закрытии объект становится прозрачным, а при открытом виде — непрозрачным), просто поменяйте местами
0и1в коде.
lua-- Поместите этот скрипт в LocalScript (например, StarterPlayerScripts или StarterCharacterScripts). local Players = game:GetService("Players") local RunService = game:GetService("RunService") local LocalPlayer = Players.LocalPlayer -- Для отслеживания объектов, которые хотим скрывать/показывать, -- можно завести список или брать все части из workspace (как пример): local function GetCheckableParts() -- Возьмём все BasePart в workspace (исключая, при необходимости, персонажа). local parts = {} for _, obj in pairs(workspace:GetDescendants()) do if obj:IsA("BasePart") then -- чтобы не трогать детали персонажа игрока: if not obj:IsDescendantOf(LocalPlayer.Character) then table.insert(parts, obj) end end end return parts end -- Функция, которая проверяет, видна ли данная деталь (part) из набора лучей. -- Возвращает true, если хотя бы один из лучей "видит" эту деталь. local function IsPartVisible(part, origin, raycastParams, boxSize) -- boxSize определяет "половину" ширины/высоты зоны: -- Если 8×8, то сдвиги будут от -4 до +4 по X и Y (в локальных координатах камеры). local halfWidth = boxSize/2 local halfHeight = boxSize/2 -- Берём CFrame камеры (без учёта её позиции origin, которую уже сами сдвинули). local camera = workspace.CurrentCamera local camCF = camera.CFrame -- Чтобы рассчитать смещения по "экранным" осям: -- X-ось в камере = camCF.RightVector -- Y-ось в камере = -camCF.UpVector (если хотим вверх по экрану) -- -- Но часто делают упрощённо: +Y = вверх, а +X = вправо, -- тогда UpVector может использоваться напрямую с нужным знаком. local rightVec = camCF.RightVector local upVec = camCF.UpVector -- Сколько "точек" по ширине/высоте будем проверять -- (можно увеличить, если нужно более точное сканирование). local steps = 3 for i = 0, steps do for j = 0, steps do -- lerp по X от -halfWidth до halfWidth local alphaX = i / steps local offsetX = -halfWidth + (alphaX * boxSize) -- lerp по Y от -halfHeight до halfHeight local alphaY = j / steps local offsetY = -halfHeight + (alphaY * boxSize) -- Считаем смещение от origin local offset = (rightVec * offsetX) + (upVec * offsetY) -- Направление луча = (позиция part - текущая точка старта) -- Но лучше сразу задать конкретную цель: part.Position local direction = (part.Position - (origin + offset)) local result = workspace:Raycast(origin + offset, direction, raycastParams) if result then -- Если луч вернул result.Instance = part, значит часть видна хотя бы из одной точки if result.Instance == part then return true end else -- Если луч вообще ничего не задел (result == nil), значит на пути нет препятствий -- и значит part в данном направлении свободен. -- Но это может означать, что part либо "задалеко", либо Ray не достиг объекта. -- -- Чтобы уточнить, попадаем ли мы точно в part, -- можно проверить расстояние до part.Position и сравнить с длинной direction и т.д. -- Но в упрощённом виде можно считать, что если result нет, то, скорее всего, -- в пределах этого луча помех нет, и объект "виден" (хотя бы в этом направлении). -- -- Для надёжности можете добавить проверку расстояния: local distanceToPart = direction.Magnitude -- Например, если дистанция меньше 2000 studs (или другой лимит), -- считаем, что луч мог бы достичь part: if distanceToPart < 5000 then -- Считаем, что part виден (или делайте более точную проверку при желании). return true end end end end -- Если ни один луч не увидел part, значит оно закрыто. return false end -- Параметры Raycast local raycastParams = RaycastParams.new() raycastParams.FilterType = Enum.RaycastFilterType.Blacklist -- Исключаем из проверки собственного персонажа (чтобы аксессуары не мешали): raycastParams.FilterDescendantsInstances = {LocalPlayer.Character} -- Основной цикл проверки RunService.RenderStepped:Connect(function() local camera = workspace.CurrentCamera if not camera then return end -- Сместим origin вперёд на 2 stud от позиции камеры -- (чтобы не "сидеть" внутри головы и аксессуаров) local forwardOffset = 2 local origin = camera.CFrame.Position + camera.CFrame.LookVector * forwardOffset -- Выбираем части для проверки local parts = GetCheckableParts() -- Проверяем видимость каждой части for _, part in ipairs(parts) do local visible = IsPartVisible(part, origin, raycastParams, 8) -- 8 = ширина/высота зоны if visible then -- Если видимая, делаем непрозрачной (Transparency = 0) part.Transparency = 0 else -- Если закрыта, делаем прозрачной (Transparency = 1) part.Transparency = 1 end end end)
GetCheckableParts мы берём все детали из workspace, исключая детали персонажа игрока (чтобы аксессуары не портили нам Raycast и чтобы мы не пытались изменить прозрачность самого персонажа).CurrentCamera.CFrame.Position и двигаем на 2 stud вперёд по LookVector. Это нужно, чтобы не «упираться» в голову игрока и его аксессуары при первом же луче.origin + offset в сторону координаты part.Position - (origin + offset).part, значит в этом направлении часть видна.part.Transparency = 0 (полностью видим).part.Transparency = 1 (прозрачна).При желании можно усложнить/улучшить логику (например, разбивать пространство на большее/меньшее число лучей, уточнять дистанцию до объекта, делать объекты полупрозрачными и т.п.). Но общий принцип именно такой: мы проверяем лучами из точки (или зоны) перед камерой, чтобы аксессуары или сама голова игрока не мешали определять видимость объектов.
сделай так если он пропал то еще был print("yes") и надо чтобы на определенный объект который находиться в game.Workspase.Plane
Ниже приведён упрощённый пример LocalScript, который проверяет только один конкретный объект Plane (который лежит в game.Workspace.Plane). Если этот объект не виден из камеры (с учётом смещения вперёд на 2 stud и зоны 8×8), то скрипт:
Transparency = 1"yes"В противном случае (если он виден) — делает Transparency = 0.
Важно: Скрипт предназначен для работы именно в LocalScript (например, в
StarterPlayerScriptsилиStarterCharacterScripts) так как нам нужна локальная камера игрока и Raycast от лица этого игрока.
lualocal Players = game:GetService("Players") local RunService = game:GetService("RunService") local localPlayer = Players.LocalPlayer local character = localPlayer.Character or localPlayer.CharacterAdded:Wait() -- Ждём появления Camera и Plane local camera = workspace.CurrentCamera local plane = workspace:WaitForChild("Plane") -- Настраиваем RaycastParams так, чтобы персонаж игрока не мешал лучам local raycastParams = RaycastParams.new() raycastParams.FilterType = Enum.RaycastFilterType.Blacklist raycastParams.FilterDescendantsInstances = {character} -- Функция, проверяющая видимость объекта "part" набором лучей local function IsPartVisible(part, origin, raycastParams, boxSize) local cameraCF = camera.CFrame -- halfWidth / halfHeight — половина ширины/высоты зоны local halfWidth = boxSize / 2 local halfHeight = boxSize / 2 -- Векторы камеры (право/вверх) local rightVec = cameraCF.RightVector local upVec = cameraCF.UpVector -- Сколько «лучей по каждой оси» пробрасываем (можно увеличить для точности) local steps = 3 for i = 0, steps do for j = 0, steps do -- Линейная интерполяция (lerp) от -half до +half local alphaX = i / steps local alphaY = j / steps local offsetX = -halfWidth + (alphaX * boxSize) local offsetY = -halfHeight + (alphaY * boxSize) -- Сдвиг относительно origin local offset = (rightVec * offsetX) + (upVec * offsetY) local startPos = origin + offset -- Направление луча: из startPos к центру объекта (part.Position) local direction = (part.Position - startPos) -- Пробрасываем луч local result = workspace:Raycast(startPos, direction, raycastParams) if result then -- Если луч упирается прямо в "part", значит он виден (хоть в одной точке) if result.Instance == part then return true end else -- Если луч вообще ничего не задел, это значит, что по этому направлению -- препятствий нет, и можем считать, что объект виден. return true end end end -- Если ни один луч не достиг объекта, значит объект закрыт (не виден) return false end -- Будем проверять видимость каждую отрисовку кадра (RenderStepped) RunService.RenderStepped:Connect(function() -- Безопасная проверка на камеру if not camera then camera = workspace.CurrentCamera if not camera then return end end -- Сместим "origin" вперёд на 2 stud, чтобы аксессуары игрока не мешали local forwardOffset = 2 local origin = camera.CFrame.Position + camera.CFrame.LookVector * forwardOffset -- Проверяем видимость нашей Plane local visible = IsPartVisible(plane, origin, raycastParams, 8) if visible then -- Видимая: делаем непрозрачной plane.Transparency = 0 else -- Невидима: делаем прозрачной и печатаем "yes" plane.Transparency = 1 print("yes") end end)
workspace:WaitForChild("Plane"), чтобы дождаться его появления.localPlayer.Character), иначе собственные аксессуары мешают.Plane или вообще не встречает препятствий (и тем самым не заблокирован), считаем, что объект виден.RenderStepped) вычисляем origin, вызываем IsPartVisible(plane, origin, ...).
visible == true, ставим Plane.Transparency = 0;Transparency = 1 и выводим в консоль print("yes").Таким образом, когда Plane оказывается полностью закрыт, он становится прозрачным, а в «Output» (в Roblox Studio) увидите "yes".
ДА! МОЛОДЕЦ! Так и надо было делать!! Теперь что надо чтобы не только однин объект был а много (Очень много) все объекты гарантировано находяться в model "Meshes". то есть game.Workspace.Meshes . внутри его объекты но внутри meshes еще модели поэтому тебе там надо искать по именам но если не получиться то просто у меня так что внутри meshes -> bush (это модел) а внутри его много моделей с именами bsuh каждый а внутри их уже meshpart-ы, а если вернуться в meshes и пойти в wall (модел) там уже только meshpart. если тебе нужны имена их я скажу
Ниже приведён пример кода LocalScript, который:
game.Workspace.Meshes (включая любые вложенные модели).Transparency = 1 и выводит "yes" в консоль. Если виден – делает Transparency = 0.Обратите внимание: Если объектов очень много, то каждый кадр будет прогонять довольно много Raycast. Следите за производительностью. При необходимости уменьшайте частоту (например, проверять не каждый кадр, а раз в 0.1 секунды).
lualocal Players = game:GetService("Players") local RunService = game:GetService("RunService") local localPlayer = Players.LocalPlayer local character = localPlayer.Character or localPlayer.CharacterAdded:Wait() local camera = workspace.CurrentCamera -- Папка/модель, где лежат все объекты local meshesFolder = workspace:WaitForChild("Meshes") -- Собираем ВСЕ MeshPart внутри папки (и вложенных моделей) в список local function getAllMeshParts(parent) local meshParts = {} for _, descendant in ipairs(parent:GetDescendants()) do if descendant:IsA("MeshPart") then table.insert(meshParts, descendant) end end return meshParts end -- Получаем список всех MeshPart local meshParts = getAllMeshParts(meshesFolder) -- Настраиваем RaycastParams так, чтобы исключить персонаж игрока (чтобы аксессуары не мешали) local raycastParams = RaycastParams.new() raycastParams.FilterType = Enum.RaycastFilterType.Blacklist raycastParams.FilterDescendantsInstances = {character} -- Функция, проверяющая "виден ли" part из заданной точки origin local function isPartVisible(part, origin, raycastParams, boxSize) local cameraCF = camera.CFrame -- halfWidth / halfHeight — половина ширины/высоты зоны local halfWidth = boxSize / 2 local halfHeight = boxSize / 2 local rightVec = cameraCF.RightVector local upVec = cameraCF.UpVector -- Сколько «лучей» в каждом направлении local steps = 3 for i = 0, steps do for j = 0, steps do -- Линейная интерполяция (lerp) от -half до +half local alphaX = i / steps local alphaY = j / steps local offsetX = -halfWidth + (alphaX * boxSize) local offsetY = -halfHeight + (alphaY * boxSize) -- Сдвиг относительно origin local offset = (rightVec * offsetX) + (upVec * offsetY) local startPos = origin + offset -- Направление луча: из startPos к центру объекта (part.Position) local direction = (part.Position - startPos) -- Пробрасываем луч local result = workspace:Raycast(startPos, direction, raycastParams) if result then -- Если луч упирается прямо в part if result.Instance == part then return true end else -- Если луч вообще ничего не задел, считаем, что объект виден return true end end end -- Если ни один луч не увидел part, значит он не виден return false end -- Проверяем объекты каждый кадр RunService.RenderStepped:Connect(function() -- Обновляем камеру (на случай, если вдруг что-то поменяется) if not workspace.CurrentCamera then return end camera = workspace.CurrentCamera -- Сместим "origin" вперёд на 2 stud от камеры local forwardOffset = 2 local origin = camera.CFrame.Position + camera.CFrame.LookVector * forwardOffset -- Перебираем все MeshPart из папки Meshes for _, part in ipairs(meshParts) do if part.Parent == nil then -- Если объект удалён из игры, можно пропустить continue end local visible = isPartVisible(part, origin, raycastParams, 8) if visible then part.Transparency = 0 else part.Transparency = 1 print("yes") end end end)
Сбор MeshPart:
parent:GetDescendants(), чтобы обойти все уровни вложенных моделей в Meshes.MeshPart.Проверка видимости:
isPartVisible отправляет лучи из «рамки» 8×8 stud перед камерой.MeshPart (или не встречает препятствий), считаем часть видимой.Прозрачность:
Transparency на 1 и печатаем "yes".Transparency = 0.Производительность:
steps (количество лучей в сетке) или делайте проверку реже (например, раз в 0.1 сек через while wait(0.1) do вместо RenderStepped).Таким образом, все MeshPart внутри Meshes (и во всех вложенных моделях) будут автоматически скрываться (Transparency = 1 + вывод "yes"), если окажутся за какими-то препятствиями и не видны камере, или снова становиться видимыми (Transparency = 0), если ничто их не перекрывает.
он слижшком тяжелый, поэтому сделай его производительнее (у него activity 100% и rate 8/s) и сделай так чтобы он работал только на экране (то есть только спереди)
Ниже пример более оптимизированного скрипта, который:
WorldToViewportPoint: если onScreen == false или объект позади камеры (Z < 0), то даже не проверяем лучом.Таким образом, скрипт не тратит ресурсы на объекты, которые и так за спиной игрока или далеко вне экрана.
Обратите внимание:
- Если объектов очень много, вы можете разбивать их на группы и проверять в разные кадры (дополнительно оптимизирует).
- Если нужно более точное определение «закрыт / не закрыт», вы можете вернуть «сетку лучей», но тогда нагрузка, конечно, возрастёт.
lualocal Players = game:GetService("Players") local RunService = game:GetService("RunService") local localPlayer = Players.LocalPlayer local character = localPlayer.Character or localPlayer.CharacterAdded:Wait() local camera = workspace.CurrentCamera -- Папка/модель, где лежат все объекты (MeshPart) local meshesFolder = workspace:WaitForChild("Meshes") -------------------------------------------------------------------------------- -- 1. Соберём все MeshPart внутри meshesFolder (учитывая вложенные модели) -------------------------------------------------------------------------------- local function getAllMeshParts(parent) local meshParts = {} for _, descendant in ipairs(parent:GetDescendants()) do if descendant:IsA("MeshPart") then table.insert(meshParts, descendant) end end return meshParts end local meshParts = getAllMeshParts(meshesFolder) -------------------------------------------------------------------------------- -- 2. Настроим RaycastParams так, чтобы исключить персонаж игрока -- (иначе аксессуары могут мешать проверке видимости). -------------------------------------------------------------------------------- local raycastParams = RaycastParams.new() raycastParams.FilterType = Enum.RaycastFilterType.Blacklist raycastParams.FilterDescendantsInstances = {character} -------------------------------------------------------------------------------- -- 3. Функция проверки, виден ли объект (одним лучом) с учётом смещения камеры -------------------------------------------------------------------------------- local function isPartVisibleSingleRay(part, origin, raycastParams) -- Направление луча = вектор от origin до центра части (part.Position) local direction = (part.Position - origin) local result = workspace:Raycast(origin, direction, raycastParams) if result then -- Если результат касается именно part, считаем объект видимым return (result.Instance == part) else -- Если луч ничего не задел, то препятствий не было, значит объект видим return true end end -------------------------------------------------------------------------------- -- 4. Будем проверять видимость ~ раз в 0.2 секунды (для экономии ресурсов). -- Можно понизить/повысить интервал, если нужно. -------------------------------------------------------------------------------- local CHECK_INTERVAL = 0.2 local timeAccumulator = 0 RunService.RenderStepped:Connect(function(deltaTime) if not workspace.CurrentCamera then return end camera = workspace.CurrentCamera -- Накопим время между проверками timeAccumulator += deltaTime if timeAccumulator < CHECK_INTERVAL then return end timeAccumulator = 0 -- Сместим "origin" вперёд на 2 stud от позиции камеры local forwardOffset = 2 local origin = camera.CFrame.Position + camera.CFrame.LookVector * forwardOffset -- Перебираем все MeshPart for _, part in ipairs(meshParts) do if not part.Parent then -- Если MeshPart был удалён, пропускаем continue end ------------------------------------------------------------------------ -- 4.1. Сначала проверим, что объект вообще находится "в поле зрения" камеры -- (не за спиной и попадает на экран). ------------------------------------------------------------------------ local screenPos, onScreen = camera:WorldToViewportPoint(part.Position) -- screenPos — координаты на экране (x, y, z): -- z < 0 означает, что объект позади камеры -- onScreen == false означает, что объект вне видимой области экрана. -- -- Проверяем, что объект не позади камеры и действительно "на экране". if (screenPos.Z > 0) and onScreen then -------------------------------------------------------------------- -- 4.2. Если объект впереди и в поле зрения, проверяем одним лучом, -- не закрыт ли он другими деталями. -------------------------------------------------------------------- local visible = isPartVisibleSingleRay(part, origin, raycastParams) if visible then part.Transparency = 0 else part.Transparency = 1 print("yes") end else -- Можно, например, сделать так: если объект позади или за границами экрана, -- мы его вообще не трогаем (не меняем Transparency). -- Или, если нужно, наоборот скрывать такие объекты: -- part.Transparency = 1 -- print("yes") -- если хотим логировать end end end)
Сбор всех MeshPart:
getAllMeshParts(meshesFolder) возвращает список всех MeshPart во вложенных моделях.Проверки не каждый кадр
timeAccumulator, чтобы выполнять проверку только каждые 0.2 сек.RenderStepped на каждом кадре.Проверяем «впереди» и «на экране»
WorldToViewportPoint возвращает:
onScreen (bool) — попадает ли точка в видимую область экрана (по X-Y).screenPos.Z — положительная (Z > 0), если точка перед камерой, и отрицательная (Z < 0), если позади.onScreen == false или Z < 0, не тратим ресурсы на Raycast.Один луч
part.Position). Это намного дешевле, чем целая «сетка» из лучей.Raycast бьёт в сам объект, значит он не перекрыт другими деталями.Прозрачность
part.Transparency = 1 и печатаем "yes".part.Transparency = 0.meshParts на части и проверять их по очереди (например, 1/3 списка за каждую проверку). Тогда каждый объект будет обновляться реже, но равномерно.Такой скрипт уже не будет сильно грузить процессор, ведь основная нагрузка идёт, когда на каждом кадре (60 раз в секунду) делаются десятки/сотни Raycast. Теперь же мы делаем их реже (5 раз в секунду при CHECK_INTERVAL = 0.2) и только для объектов, которые действительно спереди и на экране.
да теперь у него activity 6 процентов, но и стал очеьн плохо работать теперь почти каждый объект пропадает сделай так чтобы activity был 30-40 процентов (наверно надо добавить не 1 луч а там 2 3 или 4 луча, ЕСЛИ ЧТО Я НЕ ЗНАЮ ПРОСТО ПРЕДПОЛОЖИЛ ТЫ ЛУЧШЕ ЗНАЕШЬ!!)
Ниже приведён компромиссный вариант скрипта, который:
0.1 секунды).WorldToViewportPoint).Таким образом, скрипт не будет загружать CPU так сильно, как при проверке на каждом кадре и/или большой сеткой, но в то же время будет проверять видимость точнее, чем одним лучом.
lualocal Players = game:GetService("Players") local RunService = game:GetService("RunService") local localPlayer = Players.LocalPlayer local character = localPlayer.Character or localPlayer.CharacterAdded:Wait() local camera = workspace.CurrentCamera -- Папка/модель, где лежат все объекты (MeshPart) local meshesFolder = workspace:WaitForChild("Meshes") -------------------------------------------------------------------------------- -- 1. Соберём все MeshPart внутри meshesFolder (учитывая вложенные модели) -------------------------------------------------------------------------------- local function getAllMeshParts(parent) local meshParts = {} for _, descendant in ipairs(parent:GetDescendants()) do if descendant:IsA("MeshPart") then table.insert(meshParts, descendant) end end return meshParts end local meshParts = getAllMeshParts(meshesFolder) -------------------------------------------------------------------------------- -- 2. Настроим RaycastParams, исключая персонаж игрока -------------------------------------------------------------------------------- local raycastParams = RaycastParams.new() raycastParams.FilterType = Enum.RaycastFilterType.Blacklist raycastParams.FilterDescendantsInstances = {character} -------------------------------------------------------------------------------- -- 3. Функция проверки видимости с небольшим количеством лучей (например, 3×3) -------------------------------------------------------------------------------- -- boxSize = радиус области в stud вокруг центра камеры, из которой "стреляем" лучами -- steps = количество сегментов (steps=2 даёт сетку 3×3) local function isPartVisibleSmallGrid(part, origin, raycastParams, boxSize, steps) -- Возьмём CFrame камеры, чтобы получить направления осей local camCF = camera.CFrame local halfSize = boxSize / 2 local rightVec = camCF.RightVector local upVec = camCF.UpVector -- Для шага 2 получаем сетку 3×3 (i=0..2, j=0..2) for i = 0, steps do for j = 0, steps do -- Вычислим смещения по X и Y (линейная интерполяция от -halfSize до +halfSize) local alphaX = i / steps -- меняется от 0 до 1 local alphaY = j / steps local offsetX = -halfSize + alphaX * boxSize local offsetY = -halfSize + alphaY * boxSize local startPos = origin + (rightVec * offsetX) + (upVec * offsetY) local direction = (part.Position - startPos) local result = workspace:Raycast(startPos, direction, raycastParams) if result then if result.Instance == part then -- Если луч упёрся именно в part, -- значит он виден хотя бы с одной точки. return true end else -- Если луч вообще ничего не задел, значит нет препятствий -- и объект считается видимым по этому лучу. return true end end end -- Если ни один луч не попал в part (или все были блокированы), значит объект скрыт return false end -------------------------------------------------------------------------------- -- 4. Настройки для компромисса между производительностью и точностью -------------------------------------------------------------------------------- local CHECK_INTERVAL = 0.1 -- как часто делаем проверку (секунды) local SMALL_GRID_STEPS = 2 -- steps=2 => 3×3 = 9 лучей local BOX_SIZE = 6 -- "площадь" вокруг камеры для лучей (можно 6..10 stud) local timeAccumulator = 0 -------------------------------------------------------------------------------- -- 5. Цикл проверки (работает каждые CHECK_INTERVAL секунд) -------------------------------------------------------------------------------- RunService.RenderStepped:Connect(function(deltaTime) if not workspace.CurrentCamera then return end camera = workspace.CurrentCamera timeAccumulator += deltaTime if timeAccumulator < CHECK_INTERVAL then return end timeAccumulator = 0 -- Сместим origin вперёд на 2 stud от позиции камеры local forwardOffset = 2 local origin = camera.CFrame.Position + camera.CFrame.LookVector * forwardOffset for _, part in ipairs(meshParts) do if not part.Parent then -- Если объект удалён из игры, пропускаем continue end -- Проверим, что объект вообще "спереди" и в поле зрения по экрану local screenPos, onScreen = camera:WorldToViewportPoint(part.Position) if screenPos.Z > 0 and onScreen then -- Объект спереди и в зоне экрана, проверяем маленькой сеткой лучей local visible = isPartVisibleSmallGrid(part, origin, raycastParams, BOX_SIZE, SMALL_GRID_STEPS) if visible then part.Transparency = 0 else part.Transparency = 1 print("yes") end else -- Если позади или вне экрана, можно: -- 1) Игнорировать (не менять прозрачность), чтобы сохранять текущее состояние -- 2) Или делать тоже прозрачным (например, part.Transparency = 1) -- Выберите нужное поведение: -- part.Transparency = 1 end end end)
Частота проверок:
CHECK_INTERVAL = 0.1 — это значит 10 проверок в секунду.0.05, если хотите чаще (но выше нагрузка). Увеличьте до 0.2 или 0.3, если хотите ниже нагрузку.Количество лучей:
SMALL_GRID_STEPS = 2 даёт сетку 3×3 = 9 лучей на объект.3, будет 4×4 = 16 лучей (точнее, но тяжелее).1, будет 2×2 = 4 луча (легче, но может чаще ошибаться).Размер «коробки» (boxSize):
BOX_SIZE = 6 (или 8) означает, что мы «смещаем» начало лучей на ±3 (или ±4) stud по горизонтали/вертикали перед камерой.Обработка «вне экрана»:
onScreen == false), можно либо ничего не делать (оставляем Transparency как есть), либо всегда делать объект прозрачным.Попробуйте подобрать значения так, чтобы получить целевые 30–40% загрузки (Activity). Если всё ещё мало/много — меняйте CHECK_INTERVAL и SMALL_GRID_STEPS.
все равно очень плохо и если смотришь на большое кол-во обхектов все равно начинает тормозить жутко и он на 100 работат и еще он почемуто meshpart видет как тупо блок пез дырок и поэтому он непоявляеться так как они не видны ему почени это может и поэтому плохо сработал может вернуть 1 луч и починить чтобы через дырки тоже появлялся
Ниже приведён пример, в котором мы:
WorldToViewportPoint).CollisionFidelity на PreciseConvexDecomposition (или «PreciseConvexDecomp» в скрипте). Тогда коллизия будет ближе к реальной форме модели, а не к сплошной коробке. Однако это может нагрузить физику при очень сложных/больших мешах.Важно: Если в модели есть действительно «дырки», но при этом даже «PreciseConvexDecomposition» их не воспринимает (особенно если модель слишком сложная или не «водонепроницаемая»), то Roblox может всё равно частично заливать их при разбиении. В таком случае, Ray не пройдёт. Это ограничение движка.
Также убедитесь, что свойство
part.CanQuery(по умолчаниюtrue) не сброшено вfalse, иначе Raycast не будет учитывать такой объект.
Если хотите, чтобы лучи проходили сквозь реальные отверстия в MeshPart:
MeshPart в Explorer.PreciseConvexDecomposition.lualocal Players = game:GetService("Players") local RunService = game:GetService("RunService") local localPlayer = Players.LocalPlayer local character = localPlayer.Character or localPlayer.CharacterAdded:Wait() local camera = workspace.CurrentCamera local meshesFolder = workspace:WaitForChild("Meshes") -------------------------------------------------------------------------------- -- 1. Собираем все MeshPart (если их очень много, это разово) -------------------------------------------------------------------------------- local function getAllMeshParts(parent) local meshParts = {} for _, descendant in ipairs(parent:GetDescendants()) do if descendant:IsA("MeshPart") then -- Пример: если хотим автоматически выставить PreciseConvexDecomposition: -- (Осторожнее, если мешей очень много, может вызвать лаги) -- pcall, чтобы не упало, если свойство недоступно pcall(function() descendant.CollisionFidelity = Enum.CollisionFidelity.PreciseConvexDecomposition end) table.insert(meshParts, descendant) end end return meshParts end local meshParts = getAllMeshParts(meshesFolder) -------------------------------------------------------------------------------- -- 2. Готовим RaycastParams (чтобы свой персонаж не мешал) -------------------------------------------------------------------------------- local raycastParams = RaycastParams.new() raycastParams.FilterType = Enum.RaycastFilterType.Blacklist raycastParams.FilterDescendantsInstances = {character} -- исключаем персонаж -------------------------------------------------------------------------------- -- 3. Функция проверки видимости (один луч) -------------------------------------------------------------------------------- local function isPartVisibleSingleRay(part, origin, raycastParams) -- Луч идёт от origin к центру part local direction = (part.Position - origin) local result = workspace:Raycast(origin, direction, raycastParams) if result then -- Если первым объектом на пути оказался именно наш part return (result.Instance == part) else -- Если луч вообще ни во что не упёрся, значит нет препятствий return true end end -------------------------------------------------------------------------------- -- 4. Цикл проверки через промежуток (CHECK_INTERVAL) -------------------------------------------------------------------------------- local CHECK_INTERVAL = 0.1 -- 0.1 сек = 10 проверок в секунду local timeAccumulator = 0 RunService.RenderStepped:Connect(function(deltaTime) -- Убедимся, что камера существует if not workspace.CurrentCamera then return end camera = workspace.CurrentCamera -- Накапливаем время и проверяем, пора ли запустить логику timeAccumulator += deltaTime if timeAccumulator < CHECK_INTERVAL then return end timeAccumulator = 0 -- Сместим origin вперёд от камеры, чтобы не "сидеть" в голове персонажа local forwardOffset = 2 local origin = camera.CFrame.Position + camera.CFrame.LookVector * forwardOffset for _, part in ipairs(meshParts) do if not part.Parent then -- Удалённый из игры объект пропускаем continue end -- Смотрим, находится ли объект хотя бы в поле зрения (впереди, не за спиной) local screenPos, onScreen = camera:WorldToViewportPoint(part.Position) if screenPos.Z > 0 and onScreen then -- Если объект в области видимости экрана, делаем Raycast local visible = isPartVisibleSingleRay(part, origin, raycastParams) if visible then part.Transparency = 0 else part.Transparency = 1 print("yes") end else -- Если объект позади камеры или за границами экрана, -- либо оставляем как есть, либо тоже скрываем: -- part.Transparency = 1 end end end)
Один луч:
isPartVisibleSingleRay отправляет всего один луч от камеры к центру part.Position. Если у меша есть настоящая коллизия с отверстиями (при CollisionFidelity = PreciseConvexDecomposition), и луч попадает сквозь отверстие, то мы сможем «видеть» меш за ним. Если же коллизия у меша упрощённая (Box/Hull), луч упрётся в «стенку», даже если в реальном 3D-моделе там дырка.Интервал CHECK_INTERVAL:
Проверка, что объект «впереди»:
WorldToViewportPoint даёт screenPos.Z. Если Z <= 0, значит точка за спиной. Если onScreen == false, значит объект за пределами экрана (слева, справа, сверху или снизу). Тогда мы можем пропустить Raycast.Установка CollisionFidelity:
getAllMeshParts, пытается сделать part.CollisionFidelity = Enum.CollisionFidelity.PreciseConvexDecomposition. Не всегда это сработает — в зависимости от версии движка, а также если часть слишком сложная. Но обычно в новых версиях Roblox это доступно.distance > 200 studs), можно сразу сделать его прозрачным или не проверять.CanCollide = false) и/или CanQuery = false для чисто декоративных объектов, через которые мы хотим свободно «видеть». Тогда эти детали вообще не будут мешать Raycast.Таким образом, вы получите более-менее приемлемую производительность и сможете пускать луч сквозь реальные дыры в мешах, если коллизия настроена правильно.
смотри все во 1 я заметил что работает как будто не навсе а на часть небольшую, во 2 надо добавить две проверки как раз спереди чтобы он только работал (Что ты уже сделал вроде) и если он не видит дальше то дальше проверять не надо так он только сильнее нагружает, и все равно они то появляються то исчезают
Ниже — более «жёсткий» и упрощённый подход, который:
Важно понимать: этот метод наивный, и годится только если у вас сцена устроена так, что объекты, стоящие дальше «по линии взгляда», будут всегда перекрыты объектами ближе к камере. Например, «коридор» или «стена за стеной». Если у вас есть сцена, где справа/слева или в щелях могут быть видны дальние объекты, данный упрощённый подход «пропустит» их, потому что «посчитает» всё дальнее перекрытым.
Если у вас сложная геометрия (дырки в мешах, объекты рядом, не по одной линии), то придётся делать более сложную логику (или смириться с тем, что объекты могут «моргать»).
Раз в 0.1 секунды (или другой интервал) мы:
distance = (part.Position - cameraPos).Magnitude.distance.Идём по списку от ближнего к дальнему:
Transparency=0).Transparency=1) и (по желанию) запоминаем, что «на этом расстоянии» теперь всё дальше точно «закрыто» — и прекращаем цикл (не проверяем следующие объекты).
Такой «коридорный» принцип наиболее агрессивен в плане «экономии вычислений» и не даст «глубоко» видеть объекты, которые стоят сбоку. Но если вам важно просто отключать всё позади первого препятствия в прямом луче камеры, это будет работать быстро.
lualocal Players = game:GetService("Players") local RunService = game:GetService("RunService") local localPlayer = Players.LocalPlayer local character = localPlayer.Character or localPlayer.CharacterAdded:Wait() local camera = workspace.CurrentCamera local meshesFolder = workspace:WaitForChild("Meshes") -------------------------------------------------------------------------------- -- 1. Собираем все MeshPart -------------------------------------------------------------------------------- local function getAllMeshParts(parent) local meshParts = {} for _, descendant in ipairs(parent:GetDescendants()) do if descendant:IsA("MeshPart") then table.insert(meshParts, descendant) end end return meshParts end local meshParts = getAllMeshParts(meshesFolder) -------------------------------------------------------------------------------- -- 2. RaycastParams, чтобы исключить персонаж -------------------------------------------------------------------------------- local raycastParams = RaycastParams.new() raycastParams.FilterType = Enum.RaycastFilterType.Blacklist raycastParams.FilterDescendantsInstances = {character} -------------------------------------------------------------------------------- -- 3. Проверка одним лучом -------------------------------------------------------------------------------- local function isPartVisibleSingleRay(part, origin, raycastParams) local direction = part.Position - origin local result = workspace:Raycast(origin, direction, raycastParams) if result then return (result.Instance == part) else -- Если луч вообще ни во что не упёрся, значит нет препятствий return true end end -------------------------------------------------------------------------------- -- 4. Периодичность проверки -------------------------------------------------------------------------------- local CHECK_INTERVAL = 0.1 local timeAccumulator = 0 RunService.RenderStepped:Connect(function(deltaTime) if not workspace.CurrentCamera then return end camera = workspace.CurrentCamera timeAccumulator += deltaTime if timeAccumulator < CHECK_INTERVAL then return end timeAccumulator = 0 -- Точка начала луча (чуть впереди камеры) local forwardOffset = 2 local origin = camera.CFrame.Position + camera.CFrame.LookVector * forwardOffset -- Сначала соберём тех, кто реально в поле зрения local partsInView = {} for _, part in ipairs(meshParts) do if part.Parent == nil then continue end -- Проверяем: part спереди и на экране? local screenPos, onScreen = camera:WorldToViewportPoint(part.Position) if onScreen and screenPos.Z > 0 then -- Запоминаем расстояние local dist = (part.Position - origin).Magnitude table.insert(partsInView, { part = part, distance = dist }) else -- Можно либо ничего не делать, -- либо всегда убирать -- part.Transparency = 1 end end -- Сортируем по возрастанию distance table.sort(partsInView, function(a, b) return a.distance < b.distance end) -- Перебираем по очереди от ближайшего к самому дальнему local foundBlocker = false for _, data in ipairs(partsInView) do local part = data.part if foundBlocker then -- Если уже нашли "препятствие" — дальше всё считаем скрытым part.Transparency = 1 else -- Проверяем лучом local visible = isPartVisibleSingleRay(part, origin, raycastParams) if visible then part.Transparency = 0 else part.Transparency = 1 -- Как только встретили невидимый (заблокированный) объект, -- считаем, что всё, что дальше — тоже за ним. foundBlocker = true end end end end)
Сортировка по расстоянию
Идём от ближнего к дальнему
foundBlocker = true.Наивное «окклюженное» поведение
Мигание (flicker)
Transparency.Если у мешей есть «дырки»
CollisionFidelity = Box.CollisionFidelity = PreciseConvexDecomposition. Но и там не всегда Roblox строит коллизию идеально (особенно для очень сложных моделей).Transparency = 1.Такой скрипт может уменьшить «дёрганье» и нагрузку, но из-за своей упрощённой логики может скрывать объекты, которые на самом деле должны быть видны сбоку или через проёмы.