Прозрачность объекта через Raycast
Создано: 22 января 2025 г.
Создано: 22 января 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
.Такой скрипт может уменьшить «дёрганье» и нагрузку, но из-за своей упрощённой логики может скрывать объекты, которые на самом деле должны быть видны сбоку или через проёмы.