Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local Players = game:GetService("Players")
- local ReplicatedStorage = game:GetService("ReplicatedStorage")
- local knight = script.Parent
- local knightRoot = knight:WaitForChild("Torso")
- local startPoint = workspace:WaitForChild("StartPoint")
- local caughtEvent = ReplicatedStorage:WaitForChild("CaughtEvent")
- -- =====================================================
- -- НАСТРОЙКИ ПОЛЯ ЗРЕНИЯ
- -- =====================================================
- -- На какое расстояние видит рыцарь
- local VIEW_DISTANCE = 30
- -- Полный угол обзора
- -- Например, 70 означает 35 градусов влево
- -- и 35 градусов вправо
- local VIEW_ANGLE = 70
- -- Как часто проверять игроков
- local CHECK_DELAY = 0.1
- -- Высота жёлтого поля зрения относительно HumanoidRootPart
- local VISION_HEIGHT_OFFSET = -2.7
- -- Количество полос, из которых строится жёлтый веер
- local STRIP_COUNT = 21
- -- Прозрачность жёлтого поля
- -- 0 = непрозрачное
- -- 1 = полностью невидимое
- local VISION_TRANSPARENCY = 0.84
- -- Направление поля зрения
- -- Если поле смотрит назад, поставьте 180
- local DIRECTION_OFFSET = 0
- -- Сколько секунд игрок не может быть пойман повторно
- local CATCH_COOLDOWN = 2
- -- =====================================================
- -- СОЗДАНИЕ ВИДИМОГО ЖЁЛТОГО ПОЛЯ ЗРЕНИЯ
- -- =====================================================
- local oldVisionCone = knight:FindFirstChild("VisionCone")
- if oldVisionCone then
- oldVisionCone:Destroy()
- end
- local visionFolder = Instance.new("Folder")
- visionFolder.Name = "VisionCone"
- visionFolder.Parent = knight
- -- Расстояние между соседними полосами по углу
- local angleStep = VIEW_ANGLE / (STRIP_COUNT - 1)
- -- Ширина каждой полосы
- local stripWidth =
- 2
- * VIEW_DISTANCE
- * math.tan(math.rad(angleStep / 2))
- * 1.15
- for number = 0, STRIP_COUNT - 1 do
- local angle = -VIEW_ANGLE / 2 + angleStep * number
- local strip = Instance.new("Part")
- strip.Name = "VisionStrip"
- strip.Size = Vector3.new(
- stripWidth,
- 0.08,
- VIEW_DISTANCE
- )
- strip.Color = Color3.fromRGB(255, 220, 40)
- strip.Material = Enum.Material.Neon
- strip.Transparency = VISION_TRANSPARENCY
- strip.Anchored = false
- strip.Massless = true
- strip.CanCollide = false
- strip.CanTouch = false
- strip.CanQuery = false
- strip.CastShadow = false
- -- Размещаем полосу перед рыцарем
- strip.CFrame =
- knightRoot.CFrame
- * CFrame.Angles(
- 0,
- math.rad(DIRECTION_OFFSET + angle),
- 0
- )
- * CFrame.new(
- 0,
- VISION_HEIGHT_OFFSET,
- -VIEW_DISTANCE / 2
- )
- strip.Parent = visionFolder
- -- Прикрепляем полосу к рыцарю
- local weld = Instance.new("WeldConstraint")
- weld.Part0 = knightRoot
- weld.Part1 = strip
- weld.Parent = strip
- end
- -- =====================================================
- -- НАПРАВЛЕНИЕ, КУДА СМОТРИТ РЫЦАРЬ
- -- =====================================================
- local function getKnightForwardVector()
- local directionCFrame =
- knightRoot.CFrame
- * CFrame.Angles(
- 0,
- math.rad(DIRECTION_OFFSET),
- 0
- )
- return directionCFrame.LookVector
- end
- -- =====================================================
- -- НАСТРОЙКА ЛУЧА
- -- =====================================================
- local raycastParameters = RaycastParams.new()
- raycastParameters.FilterType = Enum.RaycastFilterType.Exclude
- -- Луч не должен сталкиваться с частями самого рыцаря
- raycastParameters.FilterDescendantsInstances = {
- knight
- }
- raycastParameters.IgnoreWater = true
- -- =====================================================
- -- ПРОВЕРКА: ВИДИТ ЛИ РЫЦАРЬ ПЕРСОНАЖА
- -- =====================================================
- local function canSeeCharacter(character)
- if not character then
- return false
- end
- local characterRoot =
- character:FindFirstChild("Torso")
- local humanoid =
- character:FindFirstChildOfClass("Humanoid")
- if not characterRoot or not humanoid then
- return false
- end
- if humanoid.Health <= 0 then
- return false
- end
- -- Точка, из которой смотрит рыцарь
- local eyePosition =
- knightRoot.Position
- + Vector3.new(0, 1.5, 0)
- -- Точка на теле игрока
- local targetPosition =
- characterRoot.Position
- + Vector3.new(0, 1, 0)
- local directionToPlayer =
- targetPosition - eyePosition
- local distanceToPlayer =
- directionToPlayer.Magnitude
- -- Проверяем расстояние
- if distanceToPlayer > VIEW_DISTANCE then
- return false
- end
- if distanceToPlayer < 0.1 then
- return false
- end
- -- Убираем высоту, чтобы проверять угол только по горизонтали
- local horizontalDirectionToPlayer = Vector3.new(
- directionToPlayer.X,
- 0,
- directionToPlayer.Z
- )
- local knightForward = getKnightForwardVector()
- local horizontalKnightForward = Vector3.new(
- knightForward.X,
- 0,
- knightForward.Z
- )
- if horizontalDirectionToPlayer.Magnitude < 0.01 then
- return false
- end
- if horizontalKnightForward.Magnitude < 0.01 then
- return false
- end
- -- Вычисляем угол между взглядом рыцаря и игроком
- local dotProduct = math.clamp(
- horizontalKnightForward.Unit:Dot(
- horizontalDirectionToPlayer.Unit
- ),
- -1,
- 1
- )
- local angleToPlayer =
- math.deg(math.acos(dotProduct))
- -- Игрок находится слишком далеко в стороне
- if angleToPlayer > VIEW_ANGLE / 2 then
- return false
- end
- -- Пускаем луч от рыцаря до игрока
- local raycastResult = workspace:Raycast(
- eyePosition,
- directionToPlayer,
- raycastParameters
- )
- -- Если луч ни во что не попал,
- -- значит между рыцарем и игроком нет препятствия
- if not raycastResult then
- return true
- end
- -- Если луч попал в часть персонажа,
- -- значит рыцарь действительно видит игрока
- if raycastResult.Instance:IsDescendantOf(character) then
- return true
- end
- -- Луч попал в стену или другой объект
- return false
- end
- -- =====================================================
- -- ВОЗВРАЩЕНИЕ ИГРОКА НА СТАРТ
- -- =====================================================
- local caughtPlayers = {}
- local function catchPlayer(player)
- -- Защита от многократного срабатывания
- if caughtPlayers[player] then
- return
- end
- caughtPlayers[player] = true
- -- Показываем сообщение только пойманному игроку
- caughtEvent:FireClient(
- player,
- "Вы попались, попробуйте ещё раз"
- )
- local character = player.Character
- if character then
- local characterRoot =
- character:FindFirstChild("Torso")
- if characterRoot then
- -- Перемещаем весь персонаж на старт
- character:PivotTo(
- startPoint.CFrame
- * CFrame.new(0, 3, 0)
- )
- -- Останавливаем движение после телепортации
- characterRoot.AssemblyLinearVelocity =
- Vector3.zero
- characterRoot.AssemblyAngularVelocity =
- Vector3.zero
- end
- end
- -- Через несколько секунд игрок снова может быть замечен
- task.delay(CATCH_COOLDOWN, function()
- caughtPlayers[player] = nil
- end)
- end
- -- =====================================================
- -- ПОСТОЯННАЯ ПРОВЕРКА ВСЕХ ИГРОКОВ
- -- =====================================================
- while knight.Parent do
- for _, player in ipairs(Players:GetPlayers()) do
- if not caughtPlayers[player] then
- local character = player.Character
- if canSeeCharacter(character) then
- catchPlayer(player)
- end
- end
- end
- task.wait(CHECK_DELAY)
- end
Advertisement
Add Comment
Please, Sign In to add comment