Прозрачность объекта через Raycast

Creado el: 22 de enero de 2025

Respondido usando O1 por Chat01

Pregunta

сделай код для lua в roblox studio: если обхект не видно ( например объект находиться за объектом) то у него transparensy на 0 (ну чтобы был прозрачным). если будешь делать через Raycast ну самый нормальный способ, то сделай так чтобы считыватель(камера игрока) была спереди на 2 stud и его высота была 8 stud и ширина 8 stud. так как от 1 лица если это не сделать то аксесуары (там очки и тд) мешают ему и поэтому он плохо считывает, у меня игра только от 1 лица поэтому надо чтобы считывель был спереди игрока и поэтому надо сделать считыватель больше как я написал

Pensamiento

Achieving transparency effects

I’m working through a Lua script for Roblox Studio to adjust object transparency based on their visibility, setting it to 0 or 1 accordingly.

Crafting the approach

I’m working on a Lua script for Roblox Studio to adjust object transparency using raycasting. The script sets the object's transparency to 0 if it's behind another object.

Clarifying object visibility

I’m examining whether the user wants objects to be transparent when not visible from the camera, or if their visibility toggles the transparency state.

Tracking transparency paradox

Transparency in Roblox isn't aligning with standard, which causes confusion. It seems the user might be mixing up concepts, indicating a need for clearer communication.

Mapping out corrections

I'm aligning transparency logic with Roblox norms, aiming for precise visibility control. Considering Raycast for optimal transparency setting. Clarifying transparency settings to avoid confusion.

Securing Raycast efficiency

Ensuring maximum visibility control by positioning an invisible part with a script for multiple rays, maintaining an 8x8 Stud bounding box in front of the camera.

Enhancing transparency control

Adjusting Raycast origin ahead of the player's head and expanding its field to an 8x8 grid to avoid accessory obstructions and access blocked objects effectively.

Mapping visibility

Positioning Raycast origins around the camera, checking if they hit objects or obstructions, and adjusting transparency based on object visibility. This approach should effectively manage transparency settings.

Crafting the solution

I'm developing a local script to dynamically handle objects, ensuring it benefits the user's needs in the current execution.

Constructing the script

Implementing a continuous loop to perform Raycast checks from a dynamic point around the camera, adjusting object transparencies based on visibility, while avoiding obstructions like the player's character.

Mapping out the logic

Clarifying that in Roblox, transparency 0 means opaque and 1 means invisible, and adjusting the script to ensure objects behind obstructions are noticeable or hidden appropriately.

Balancing transparency settings

I'm considering the script's clarity on transparency and visibility, ensuring objects behind obstructions are set to be transparent, while others remain visible or opaque.

Thought about transparency and raycasting in Roblox для 57 секунд

Respuesta

Ниже приведён пример скрипта на Lua (Roblox) для локального скрипта (LocalScript), который:

  1. Берёт текущую камеру игрока.
  2. Сдвигает «точку начала лучей» (origin) вперёд на 2 stud относительно камеры (чтобы не мешали аксессуары).
  3. «Разбрасывает» несколько лучей (Raycast) в зоне 8×8 stud (ширина×высота), чтобы проверить видимость объекта.
  4. Если объект закрыт другими деталями, делаем ему 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&times;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)

Как это работает по шагам

  1. Сбор частей: В функции GetCheckableParts мы берём все детали из workspace, исключая детали персонажа игрока (чтобы аксессуары не портили нам Raycast и чтобы мы не пытались изменить прозрачность самого персонажа).
  2. Смещение точки старта лучей: Берём CurrentCamera.CFrame.Position и двигаем на 2 stud вперёд по LookVector. Это нужно, чтобы не «упираться» в голову игрока и его аксессуары при первом же луче.
  3. Множественные лучи (Raycast):
    • Мы создаём «сетку» из нескольких лучей 8×8 stud по ширине и высоте относительно направления камеры.
    • Каждый луч идёт из origin + offset в сторону координаты part.Position - (origin + offset).
    • Если луч первым делом сталкивается именно с part, значит в этом направлении часть видна.
    • Если мы вообще не получили результат (result == nil) и расстояние до объекта не слишком большое, можно считать, что препятствий нет, и объект тоже виден. (В более продвинутом варианте можно отдельно сверять расстояние.)
  4. Назначение прозрачности:
    • Если часть видна хотя бы одним лучом, part.Transparency = 0 (полностью видим).
    • Если ни один луч не «видит» часть, part.Transparency = 1 (прозрачна).

При желании можно усложнить/улучшить логику (например, разбивать пространство на большее/меньшее число лучей, уточнять дистанцию до объекта, делать объекты полупрозрачными и т.п.). Но общий принцип именно такой: мы проверяем лучами из точки (или зоны) перед камерой, чтобы аксессуары или сама голова игрока не мешали определять видимость объектов.

Pregunta

сделай так если он пропал то еще был print("yes") и надо чтобы на определенный объект который находиться в game.Workspase.Plane

Pensamiento

Crafting the script

I’m parsing the script to check if workspace.Plane is invisible, set its transparency to 1, and print "yes". This should ensure accuracy with workspace.Plane alone.

Focusing on functionality

I’m noting the function IsPartVisible to check visibility of workspace.Plane, adjusting its transparency to 0 if visible or 1 if not, and printing "yes" when it is not visible.

Thought about Raycast and Transparency logic для 24 секунд

Respuesta

Ниже приведён упрощённый пример LocalScript, который проверяет только один конкретный объект Plane (который лежит в game.Workspace.Plane). Если этот объект не виден из камеры (с учётом смещения вперёд на 2 stud и зоны 8×8), то скрипт:

  • Ставит ему Transparency = 1
  • Печатает в консоль "yes"

В противном случае (если он виден) — делает Transparency = 0.

Важно: Скрипт предназначен для работы именно в LocalScript (например, в StarterPlayerScripts или StarterCharacterScripts) так как нам нужна локальная камера игрока и Raycast от лица этого игрока.

lua
local 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 -- Сколько &laquo;лучей по каждой оси&raquo; пробрасываем (можно увеличить для точности) 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)

Краткое описание

  1. Получаем Plane: Используем workspace:WaitForChild("Plane"), чтобы дождаться его появления.
  2. RaycastParams: Настраиваем, чтобы из проверки исключался персонаж (localPlayer.Character), иначе собственные аксессуары мешают.
  3. IsPartVisible: В этой функции «рассылаем» несколько лучей (примерно сетку 3×3 в зоне 8×8) из смещённой позиции перед камерой.
    • Если хотя бы один луч напрямую упирается в Plane или вообще не встречает препятствий (и тем самым не заблокирован), считаем, что объект виден.
  4. В цикле: На каждом кадре (RenderStepped) вычисляем origin, вызываем IsPartVisible(plane, origin, ...).
    • Если visible == true, ставим Plane.Transparency = 0;
    • Иначе Transparency = 1 и выводим в консоль print("yes").

Таким образом, когда Plane оказывается полностью закрыт, он становится прозрачным, а в «Output» (в Roblox Studio) увидите "yes".

Pregunta

ДА! МОЛОДЕЦ! Так и надо было делать!! Теперь что надо чтобы не только однин объект был а много (Очень много) все объекты гарантировано находяться в model "Meshes". то есть game.Workspace.Meshes . внутри его объекты но внутри meshes еще модели поэтому тебе там надо искать по именам но если не получиться то просто у меня так что внутри meshes -> bush (это модел) а внутри его много моделей с именами bsuh каждый а внутри их уже meshpart-ы, а если вернуться в meshes и пойти в wall (модел) там уже только meshpart. если тебе нужны имена их я скажу

Pensamiento

Piecing together the structure

I’m laying out the task of recursively identifying all MeshPart objects within the "Meshes" folder, including its nested sub-models and mesh parts. This will ensure a comprehensive search.

Crafting the script

I’m working on a script to recursively locate "MeshPart" objects in "Meshes", check their visibility, and toggle transparency while logging "yes" each time a part is hidden.

Building upon previous work

I’m refining the script to locate "MeshPart" objects in "Meshes" and check their visibility. The goal is to minimize console spam while ensuring accurate visibility logging.

Thought about handling multiple objects in Meshes для 19 секунд

Respuesta

Ниже приведён пример кода LocalScript, который:

  1. Ищет все MeshPart внутри game.Workspace.Meshes (включая любые вложенные модели).
  2. На каждом кадре проверяет их видимость (с учётом смещения камеры вперёд и «зоны 8×8»).
  3. Если объект не виден, ставит ему Transparency = 1 и выводит "yes" в консоль. Если виден – делает Transparency = 0.

Обратите внимание: Если объектов очень много, то каждый кадр будет прогонять довольно много Raycast. Следите за производительностью. При необходимости уменьшайте частоту (например, проверять не каждый кадр, а раз в 0.1 секунды).

Пример скрипта

lua
local 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 -- Сколько &laquo;лучей&raquo; в каждом направлении 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)

Пояснения

  1. Сбор MeshPart:

    • Используем parent:GetDescendants(), чтобы обойти все уровни вложенных моделей в Meshes.
    • Фильтруем только объекты класса MeshPart.
  2. Проверка видимости:

    • Функция isPartVisible отправляет лучи из «рамки» 8×8 stud перед камерой.
    • Если хотя бы один луч попадает непосредственно в MeshPart (или не встречает препятствий), считаем часть видимой.
  3. Прозрачность:

    • Если объект «не виден» (закрыт другими деталями), меняем Transparency на 1 и печатаем "yes".
    • Если «виден», ставим Transparency = 0.
  4. Производительность:

    • При очень большом числе деталей и большом количестве лучей на каждый кадр – может быть заметная нагрузка.
    • При необходимости уменьшайте steps (количество лучей в сетке) или делайте проверку реже (например, раз в 0.1 сек через while wait(0.1) do вместо RenderStepped).

Таким образом, все MeshPart внутри Meshes (и во всех вложенных моделях) будут автоматически скрываться (Transparency = 1 + вывод "yes"), если окажутся за какими-то препятствиями и не видны камере, или снова становиться видимыми (Transparency = 0), если ничто их не перекрывает.

Pregunta

он слижшком тяжелый, поэтому сделай его производительнее (у него activity 100% и rate 8/s) и сделай так чтобы он работал только на экране (то есть только спереди)

Pensamiento

Refining the solution
We’re mapping out the approach of checking only objects visible to the camera, minimizing heavy computations. This involves using dot product tests or transforming bounding box corners to the camera's local or screen space.

Semi-regular checks
Considering fewer and spread-out checks, like every 0.2 or 0.5 seconds, or testing each frame to see if the object is visible through bounding sphere or rectangle checks.

Focusing in-frame objects
Ensuring checks are applied only to objects in the camera's field of view helps reduce computational load. This focuses the logic on screen-visible parts.

Refining the strategy
I'm thinking about optimizing efficiency by verifying objects on-screen or those within the camera's view, using ray or vector checks. This approach minimizes redundancy in each frame.

Enhancing checks
I’m refining the process by ensuring parts are within front-angled or near positions, then verifying visibility with a quick viewport check. Passing these criteria, a single raycast is performed.

Implementing the solution
I’m planning to use a single ray every 0.2 seconds to check visibility of parts in front of the camera, significantly reducing computational overhead and enhancing efficiency.

Crafting the solution
I’m refining the approach to use a single ray every 0.2 seconds to check visibility, skipping parts behind the camera, and printing "yes" if invisible.

Thought about optimizing raycasting performance для 35 секунд

Respuesta

Ниже пример более оптимизированного скрипта, который:

  1. Проверяет видимость объектов только раз в некий интервал (например, раз в 0.2 секунды), а не на каждом кадре.
  2. Использует единственный луч (Raycast) для каждого объекта вместо сетки из нескольких лучей.
  3. Проверяет только те объекты, которые действительно находятся впереди камеры и попадают в поле зрения (видимы на экране).
    • Для этого используется WorldToViewportPoint: если onScreen == false или объект позади камеры (Z < 0), то даже не проверяем лучом.

Таким образом, скрипт не тратит ресурсы на объекты, которые и так за спиной игрока или далеко вне экрана.

Обратите внимание:

  • Если объектов очень много, вы можете разбивать их на группы и проверять в разные кадры (дополнительно оптимизирует).
  • Если нужно более точное определение «закрыт / не закрыт», вы можете вернуть «сетку лучей», но тогда нагрузка, конечно, возрастёт.

Пример кода

lua
local 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)

Как это работает:

  1. Сбор всех MeshPart:

    • getAllMeshParts(meshesFolder) возвращает список всех MeshPart во вложенных моделях.
  2. Проверки не каждый кадр

    • Используем счётчик timeAccumulator, чтобы выполнять проверку только каждые 0.2 сек.
    • Это существенно снижает нагрузку по сравнению с проверками RenderStepped на каждом кадре.
  3. Проверяем «впереди» и «на экране»

    • WorldToViewportPoint возвращает:
      • onScreen (bool) — попадает ли точка в видимую область экрана (по X-Y).
      • screenPos.Z — положительная (Z > 0), если точка перед камерой, и отрицательная (Z < 0), если позади.
    • Если onScreen == false или Z < 0, не тратим ресурсы на Raycast.
  4. Один луч

    • Принимаем компромисс: один Raycast от смещённой вперёд камеры к центру объекта (part.Position). Это намного дешевле, чем целая «сетка» из лучей.
    • Если Raycast бьёт в сам объект, значит он не перекрыт другими деталями.
  5. Прозрачность

    • Если объект перекрыт, делаем part.Transparency = 1 и печатаем "yes".
    • Если не перекрыт, part.Transparency = 0.

Дополнительные советы по оптимизации

  • Если даже с этим скриптом нагрузка остаётся высокой при огромном числе MeshPart, можно:
    1. Разбивать список meshParts на части и проверять их по очереди (например, 1/3 списка за каждую проверку). Тогда каждый объект будет обновляться реже, но равномерно.
    2. Ограничить расстояние. Если объект слишком далеко от камеры, можно вообще не проверять (часто в играх далекие объекты либо отключают, либо делают прозрачными).
    3. Если нужно более точное определение, делать несколько лучей, но только для ближайших объектов или только для тех, что занимают большую часть экрана.

Такой скрипт уже не будет сильно грузить процессор, ведь основная нагрузка идёт, когда на каждом кадре (60 раз в секунду) делаются десятки/сотни Raycast. Теперь же мы делаем их реже (5 раз в секунду при CHECK_INTERVAL = 0.2) и только для объектов, которые действительно спереди и на экране.

Pregunta

да теперь у него activity 6 процентов, но и стал очеьн плохо работать теперь почти каждый объект пропадает сделай так чтобы activity был 30-40 процентов (наверно надо добавить не 1 луч а там 2 3 или 4 луча, ЕСЛИ ЧТО Я НЕ ЗНАЮ ПРОСТО ПРЕДПОЛОЖИЛ ТЫ ЛУЧШЕ ЗНАЕШЬ!!)

Pensamiento

I’m working on optimizing performance by suggesting a small grid of rays, like a 2x2 or 3x3 setup, only for objects visible on screen. This approach maintains efficiency with 30-40% usage.

I’m thinking about code for performance tuning, allowing users to adjust steps and check intervals. It includes options like a 4x4 grid with 16 rays or a 3x3 grid with 9 rays.

Thought about optimizing performance and accuracy для 10 секунд

Respuesta

Ниже приведён компромиссный вариант скрипта, который:

  1. Работает реже (например, каждые 0.1 секунды).
  2. Проверяет только объекты спереди и в границах экрана (через WorldToViewportPoint).
  3. Использует небольшую «сетку лучей» (например, 3×3 или 4×4 лучей) вместо одного луча или слишком подробной сетки.

Таким образом, скрипт не будет загружать CPU так сильно, как при проверке на каждом кадре и/или большой сеткой, но в то же время будет проверять видимость точнее, чем одним лучом.


Пример кода

lua
local 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&times;3) -------------------------------------------------------------------------------- -- boxSize = радиус области в stud вокруг центра камеры, из которой "стреляем" лучами -- steps = количество сегментов (steps=2 даёт сетку 3&times;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&times;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&times;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)

Как настроить баланс между производительностью и точностью

  1. Частота проверок:

    • CHECK_INTERVAL = 0.1 — это значит 10 проверок в секунду.
    • Уменьшите до 0.05, если хотите чаще (но выше нагрузка). Увеличьте до 0.2 или 0.3, если хотите ниже нагрузку.
  2. Количество лучей:

    • SMALL_GRID_STEPS = 2 даёт сетку 3×3 = 9 лучей на объект.
    • Если поставить 3, будет 4×4 = 16 лучей (точнее, но тяжелее).
    • Если поставить 1, будет 2×2 = 4 луча (легче, но может чаще ошибаться).
  3. Размер «коробки» (boxSize):

    • BOX_SIZE = 6 (или 8) означает, что мы «смещаем» начало лучей на ±3 (или ±4) stud по горизонтали/вертикали перед камерой.
    • Если сделать слишком маленьким, аксессуары игрока могут перекрыть объект. Слишком большим — лучи будут уходить в сторону, и может быть более «щадяще» к проверке (зависит от вашей логики).
  4. Обработка «вне экрана»:

    • Если объект «за спиной» (Z<0) или вообще не на экране (onScreen == false), можно либо ничего не делать (оставляем Transparency как есть), либо всегда делать объект прозрачным.

Попробуйте подобрать значения так, чтобы получить целевые 30–40% загрузки (Activity). Если всё ещё мало/много — меняйте CHECK_INTERVAL и SMALL_GRID_STEPS.


Дополнительные улучшения

  • Разделение списка: Если объектов очень много, можно разбить их на порции и проверять порцию 1 в этом цикле, порцию 2 — в следующем, и т.д.
  • Дистанция: Если объект слишком далеко, можно сразу делать его прозрачным или вообще не проверять (чтобы не расходовать лучи).

Pregunta

все равно очень плохо и если смотришь на большое кол-во обхектов все равно начинает тормозить жутко и он на 100 работат и еще он почемуто meshpart видет как тупо блок пез дырок и поэтому он непоявляеться так как они не видны ему почени это может и поэтому плохо сработал может вернуть 1 луч и починить чтобы через дырки тоже появлялся

Pensamiento

Clarifying collision settings

I'm looking into Roblox collision settings for raycasting and mesh parts. Considerations include bounding box, mesh collider, and "PreciseConvexDecomposition" or "Default" for gaps. The user prefers a single ray to avoid performance issues.

Adjusting collision fidelity

I'm noting that to truly detect "through holes," Raycast's behavior needs refinement. Enhancing collision fidelity beyond "PreciseConvexDecomposition" to "DoubleSided" or "Precise" is crucial.

Enhancing collision fidelity

I'm incorporating the ability to dynamically adjust MeshPart.CollisionFidelity to "PreciseConvexDecomposition" in the script, aiming to improve the collision accuracy for parts with visible holes behind them.

Tackling collision configs

I'm mapping out the user's needs by adjusting collision fidelity to "PreciseConvexDecomposition" or exploring CSG unions and Collision Groups for precise identification of geometry behind the mesh part.

Refining collision settings

I’m focusing on part.CollisionFidelity = Enum.CollisionFidelity.PreciseConvexDecomposition for detailed collision analysis. For performance concerns, I’m also considering part.CanQuery = false to enhance rayblocking precision.

Refining shape representation

Considering part.CollisionFidelity = Enum.CollisionFidelity.PreciseConvexDecomposition to ensure collision shapes closely match the actual geometry, potentially addressing the issue of the script seeing holes as solid blocks.

Pinpointing the issue

OK, let me see. The challenge involves setting the mesh part collision method to PreciseConvexDecomposition to accurately detect obstacles with holes, while also noting performance implications.

Thinking through performance

I'm planning to implement a single-ray approach for performance but note that PreciseConvexDecomposition might impact it negatively. I'll also suggest setting "CanCollide = false" for decorative geometry.

Thought about optimization and mesh transparency для 45 секунд

Respuesta

Ниже приведён пример, в котором мы:

  1. Используем только один луч (Raycast) на объект для максимальной скорости.
  2. Проверяем, что объект действительно находится перед камерой и в пределах экрана (через WorldToViewportPoint).
  3. Используем интервал (например, раз в 0.1 секунды), чтобы не перегружать игру.
  4. Совет по поводу «дыр» в MeshPart: в Roblox по умолчанию коллизия MeshPart может быть «коробкой» (Box) или «упрощённой оболочкой» (Hull). Если вам нужно, чтобы Ray проходил сквозь настоящие отверстия в модели, нужно изменить свойство CollisionFidelity на PreciseConvexDecomposition (или «PreciseConvexDecomp» в скрипте). Тогда коллизия будет ближе к реальной форме модели, а не к сплошной коробке. Однако это может нагрузить физику при очень сложных/больших мешах.

Важно: Если в модели есть действительно «дырки», но при этом даже «PreciseConvexDecomposition» их не воспринимает (особенно если модель слишком сложная или не «водонепроницаемая»), то Roblox может всё равно частично заливать их при разбиении. В таком случае, Ray не пройдёт. Это ограничение движка.

Также убедитесь, что свойство part.CanQuery (по умолчанию true) не сброшено в false, иначе Raycast не будет учитывать такой объект.

Шаги, чтобы включить «точную» коллизию для мешей

Если хотите, чтобы лучи проходили сквозь реальные отверстия в MeshPart:

  1. В Roblox Studio: Выделите нужный MeshPart в Explorer.
  2. Перейдите в Properties -> CollisionFidelity и выберите PreciseConvexDecomposition.
  3. Также можно сделать это на лету в скрипте, если надо (но обычно задают в Studio).

Пример кода

lua
local 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)

Разбор основных моментов

  1. Один луч:

    • isPartVisibleSingleRay отправляет всего один луч от камеры к центру part.Position. Если у меша есть настоящая коллизия с отверстиями (при CollisionFidelity = PreciseConvexDecomposition), и луч попадает сквозь отверстие, то мы сможем «видеть» меш за ним. Если же коллизия у меша упрощённая (Box/Hull), луч упрётся в «стенку», даже если в реальном 3D-моделе там дырка.
  2. Интервал CHECK_INTERVAL:

    • 0.1 секунды означает, что скрипт делается 10 раз в секунду (вместо 30–60). Если всё ещё лагает, можно сделать 0.2–0.3. Если наоборот слишком «лениво» проверяет — уменьшить до 0.05.
  3. Проверка, что объект «впереди»:

    • WorldToViewportPoint даёт screenPos.Z. Если Z <= 0, значит точка за спиной. Если onScreen == false, значит объект за пределами экрана (слева, справа, сверху или снизу). Тогда мы можем пропустить Raycast.
  4. Установка CollisionFidelity:

    • Код, который в цикле getAllMeshParts, пытается сделать part.CollisionFidelity = Enum.CollisionFidelity.PreciseConvexDecomposition. Не всегда это сработает — в зависимости от версии движка, а также если часть слишком сложная. Но обычно в новых версиях Roblox это доступно.
    • Если ваша модель действительно имеет сквозные дыры, и вы хотите, чтобы Ray мог через них проходить, — нужна «максимально точная» коллизия. Но это может нагружать физику.

Дополнительные советы по оптимизации

  • Разбивать объекты на группы и проверять по очереди (например, часть мешей в одном кадре, часть — в другом), чтобы не пытаться проверить все объекты одновременно.
  • Ограничить дистанцию: если объект далеко (например, distance > 200 studs), можно сразу сделать его прозрачным или не проверять.
  • Убрать коллизию (CanCollide = false) и/или CanQuery = false для чисто декоративных объектов, через которые мы хотим свободно «видеть». Тогда эти детали вообще не будут мешать Raycast.

Таким образом, вы получите более-менее приемлемую производительность и сможете пускать луч сквозь реальные дыры в мешах, если коллизия настроена правильно.

Pregunta

смотри все во 1 я заметил что работает как будто не навсе а на часть небольшую, во 2 надо добавить две проверки как раз спереди чтобы он только работал (Что ты уже сделал вроде) и если он не видит дальше то дальше проверять не надо так он только сильнее нагружает, и все равно они то появляються то исчезают

Pensamiento

Crafting targeted object visibility

I’m crafting a method to check only objects in front of the camera. This involves sorting objects by distance and skipping those blocked by nearer objects.

Prioritizing visibility

I’m prioritizing objects by distance, checking their visibility to ensure only the nearest, unobstructed objects are evaluated, effectively filtering out obscured items.

Assessing visibility intricacies

I’m evaluating a bounding sphere method to stabilize visibility, which involves checking from the camera outward to determine if objects should be visible or invisible due to occlusion.

Implementing stability measures

I’m exploring solutions like adding delay or hysteresis to confirm visibility changes, using a bounding region approach, or multiple rays to stabilize visibility and reduce flickering.

Managing occlusion

I’m pondering the efficiency of avoiding raycasting for objects behind a visible one, as they likely remain hidden. However, this might not hold true in cases with holes.

Implementing orderly checks

I’m suggesting a distance-based sorting approach, mapping out objects by their proximity from the camera. This method aims to reduce redundancy by skipping objects obscured by others in line of sight.

Ensuring visibility

I’m working on a method to ensure every object is checked. Objects ahead of the camera receive a raycast to confirm visibility, set transparency, and skip further checks if they are shown.

Streamlining visibility checks

I’m refining the code by sorting objects by distance, checking visibility from near to far, and skipping those blocked by visible objects, even considering edge cases like multiple angles and partial occlusions.

Testing visibility options

I'm working through the simplest approach to check visibility by sorting objects by distance, examining each object in sequence, and skipping those blocked by visible objects, even considering new angles.

Enhancing visibility checks

I’m proposing a method to optimize visibility testing by sorting objects by distance, using raycasts to confirm visibility, and skipping objects blocked by visible ones, mindful of partial or multiple angle complexity.

Thought about performance and raycast optimizations для 57 секунд

Respuesta

Ниже — более «жёсткий» и упрощённый подход, который:

  1. Использует один луч на каждый объект (минимум вычислений, меньше шансов «моргать»).
  2. Сортирует объекты по расстоянию от камеры (от ближних к дальним).
  3. Проверяет видимость только для тех, кто действительно спереди (Z>0) и в пределах экрана.
  4. Если мы нашли один «непрозрачный» объект, мы считаем, что всё, что дальше по прямой, уже не видимо и не проверяем их (очень грубая, но быстрая «окклюзия»).

Важно понимать: этот метод наивный, и годится только если у вас сцена устроена так, что объекты, стоящие дальше «по линии взгляда», будут всегда перекрыты объектами ближе к камере. Например, «коридор» или «стена за стеной». Если у вас есть сцена, где справа/слева или в щелях могут быть видны дальние объекты, данный упрощённый подход «пропустит» их, потому что «посчитает» всё дальнее перекрытым.

Если у вас сложная геометрия (дырки в мешах, объекты рядом, не по одной линии), то придётся делать более сложную логику (или смириться с тем, что объекты могут «моргать»).

Общий алгоритм

  1. Раз в 0.1 секунды (или другой интервал) мы:

    • Собираем список всех MeshPart, которые:
      • Ещё существуют в игре (Parent не nil).
      • Перед камерой (Z>0) и попадают на экран (onScreen==true).
    • Считаем для каждого расстояние до камеры distance = (part.Position - cameraPos).Magnitude.
    • Сортируем их по возрастанию distance.
  2. Идём по списку от ближнего к дальнему:

    • Делаем один Raycast от камеры к центру объекта.
    • Если луч упирается именно в этот объект — считаем его видимым (Transparency=0).
    • Иначе (Transparency=1) и (по желанию) запоминаем, что «на этом расстоянии» теперь всё дальше точно «закрыто» — и прекращаем цикл (не проверяем следующие объекты).
      • Это значит: как только мы наткнулись на один объект, который «блокирует» луч, всё за ним тоже не проверяем.

Такой «коридорный» принцип наиболее агрессивен в плане «экономии вычислений» и не даст «глубоко» видеть объекты, которые стоят сбоку. Но если вам важно просто отключать всё позади первого препятствия в прямом луче камеры, это будет работать быстро.


Пример кода

lua
local 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)

Пояснения

  1. Сортировка по расстоянию

    • Сначала мы отсекаем все объекты, которые позади камеры или вне экрана.
    • Оставшиеся сортируем по дистанции.
  2. Идём от ближнего к дальнему

    • Для первого объекта, который «перекрыт» (Raycast показывает, что луч упирается не в него), мы считаем его невидимым и делаем foundBlocker = true.
    • Все остальные объекты, которые дальше по списку (то есть физически дальше в ту же сторону), автоматически ставим прозрачными — без доп. Raycast.
  3. Наивное «окклюженное» поведение

    • Этот алгоритм предполагает, что если мы «упёрлись» в какой-то объект, то всё, что идёт за ним (по прямой), уже смотреть не нужно (делаем их прозрачными).
    • Это хорошо для простых случаев (например, дверь/стена закрыла коридор). Но если у вас сложная сцена, где что-то может быть видно «из-за угла», этот код его скрывает из-за упрощения.
  4. Мигание (flicker)

    • При таком подходе «резкая» смена состояния (виден / не виден) может происходить, если камера чуть сдвинулась и Ray вдруг попал / не попал в объект. Можно смягчить, вводя задержку или «порог» (гистерезис): например, если объект только что перешёл из «виден» в «не виден», подождать пару кадров или проверить повторно, прежде чем менять Transparency.
  5. Если у мешей есть «дырки»

    • Один луч может «промахнуться» в эти дырки или наоборот «упереться» в невидимую коллизию, если CollisionFidelity = Box.
    • Чтобы сквозь дырки можно было «увидеть» что-то сзади, придётся делать CollisionFidelity = PreciseConvexDecomposition. Но и там не всегда Roblox строит коллизию идеально (особенно для очень сложных моделей).

Итог

  • Скрипт быстрый, потому что мы используем по одному Raycast на те объекты, что оказались на экране.
  • Как только нашли один «заблокированный» объект (и, следовательно, «видимую стену»), мы не проверяем остальные, а ставим им Transparency = 1.
  • Этот метод подходит, если сцена действительно устроена «по прямой»: то есть вы не ожидаете видеть дальние объекты, если есть ближняя стена.
  • Если вы всё же хотите видеть объекты за «дырками», углами, арками — нужен более продвинутый подход или «несколько лучей» (что всегда дороже).

Такой скрипт может уменьшить «дёрганье» и нагрузку, но из-за своей упрощённой логики может скрывать объекты, которые на самом деле должны быть видны сбоку или через проёмы.

Compartir este Q&A