Загрузка данных


--// REALISTIC FIRST PERSON MOVEMENT CAMERA
--// F = переключение камеры
--// CTRL = бег
--// FOV 90 -> 120 при беге
--// Реалистичное покачивание камеры
--// Усталость через 90 секунд и затемнение после 120 секунд
--// Без приседания и автоматической остановки
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
-- НАСТРОЙКИ
local WALK_SPEED = 16
local SPRINT_SPEED = 25
local NORMAL_FOV = 90
local SPRINT_FOV = 120
local FOV_SMOOTHNESS = 5
local WALK_BOB_SPEED = 8
local SPRINT_BOB_SPEED = 11
local WALK_BOB_X = 0.018
local WALK_BOB_Y = 0.025
local SPRINT_BOB_X = 0.045
local SPRINT_BOB_Y = 0.065
local BOB_SMOOTHNESS = 9
local FATIGUE_START = 90
local FATIGUE_FULL = 120
local MAX_FATIGUE_BOB = 1.8
local MAX_DARKNESS = 0.12
-- СОСТОЯНИЕ
local character
local humanoid
local rootPart
local firstPerson = true
local sprintHeld = false
local sprintTime = 0
local bobTime = 0
local currentOffset = Vector3.zero
local currentRoll = 0
local fatigue = 0
local darkness = 0
local previousMoveDirection = Vector3.zero
local camera = workspace.CurrentCamera
-- ИНТЕРФЕЙС ЗАТЕМНЕНИЯ
local oldGui = playerGui:FindFirstChild("SprintFatigueGui")
if oldGui then
    oldGui:Destroy()
end
local fatigueGui = Instance.new("ScreenGui")
fatigueGui.Name = "SprintFatigueGui"
fatigueGui.IgnoreGuiInset = true
fatigueGui.ResetOnSpawn = false
fatigueGui.DisplayOrder = 20
fatigueGui.Parent = playerGui
local darkOverlay = Instance.new("Frame")
darkOverlay.Name = "DarkOverlay"
darkOverlay.Size = UDim2.fromScale(1, 1)
darkOverlay.Position = UDim2.fromScale(0, 0)
darkOverlay.BackgroundColor3 = Color3.new(0, 0, 0)
darkOverlay.BackgroundTransparency = 1
darkOverlay.BorderSizePixel = 0
darkOverlay.Parent = fatigueGui
-- ПЕРЕКЛЮЧЕНИЕ КАМЕРЫ
local function setCameraMode(enabled)
    firstPerson = enabled
    if enabled then
        player.CameraMode = Enum.CameraMode.LockFirstPerson
        player.CameraMinZoomDistance = 0.5
        player.CameraMaxZoomDistance = 0.5
    else
        player.CameraMode = Enum.CameraMode.Classic
        player.CameraMinZoomDistance = 0.5
        player.CameraMaxZoomDistance = 12
    end
end
-- ОБНОВЛЕНИЕ СКОРОСТИ
local function updateSprint()
    if not humanoid or humanoid.Health <= 0 then
        return
    end
    if sprintHeld then
        humanoid.WalkSpeed = SPRINT_SPEED
    else
        humanoid.WalkSpeed = WALK_SPEED
    end
end
-- НАСТРОЙКА ПЕРСОНАЖА
local function setupCharacter(newCharacter)
    character = newCharacter
    humanoid = character:WaitForChild("Humanoid")
    rootPart = character:WaitForChild("HumanoidRootPart")
    sprintHeld = false
    sprintTime = 0
    bobTime = 0
    fatigue = 0
    darkness = 0
    currentOffset = Vector3.zero
    currentRoll = 0
    previousMoveDirection = Vector3.zero
    darkOverlay.BackgroundTransparency = 1
    humanoid.WalkSpeed = WALK_SPEED
    setCameraMode(firstPerson)
end
-- УПРАВЛЕНИЕ КЛАВИАТУРОЙ
UserInputService.InputBegan:Connect(function(input, processed)
    if processed then
        return
    end
    -- F: переключение камеры
    if input.KeyCode == Enum.KeyCode.F then
        setCameraMode(not firstPerson)
    end
    -- CTRL: бег
    if input.KeyCode == Enum.KeyCode.LeftControl
        or input.KeyCode == Enum.KeyCode.RightControl then
        sprintHeld = true
        updateSprint()
    end
end)
UserInputService.InputEnded:Connect(function(input)
    if input.KeyCode == Enum.KeyCode.LeftControl
        or input.KeyCode == Enum.KeyCode.RightControl then
        sprintHeld = false
        updateSprint()
    end
end)
-- КАМЕРА, ПОКАЧИВАНИЕ И УСТАЛОСТЬ
RunService:BindToRenderStep(
    "RealisticMovementCamera",
    Enum.RenderPriority.Camera.Value + 1,
    function(dt)
        camera = workspace.CurrentCamera
        if not camera then
            return
        end
        local alive = humanoid and humanoid.Health > 0
        local moving = alive
            and humanoid.MoveDirection.Magnitude > 0.05
        local activelySprinting = alive
            and sprintHeld
            and moving
        -- ВРЕМЯ НЕПРЕРЫВНОГО БЕГА
        if activelySprinting then
            sprintTime = sprintTime + dt
        else
            sprintTime = 0
        end
        -- УСТАЛОСТЬ ПОСЛЕ 90 СЕКУНД
        local targetFatigue = 0
        if activelySprinting and sprintTime > FATIGUE_START then
            targetFatigue = math.clamp(
                (sprintTime - FATIGUE_START)
                    / (FATIGUE_FULL - FATIGUE_START),
                0,
                1
            )
        end
        local fatigueAlpha = 1 - math.exp(-1.4 * dt)
        fatigue = fatigue
            + (targetFatigue - fatigue) * fatigueAlpha
        -- FOV ПРИ БЕГЕ
        local targetFOV
        if sprintHeld then
            targetFOV = SPRINT_FOV
        else
            targetFOV = NORMAL_FOV
        end
        local fovAlpha = 1 - math.exp(-FOV_SMOOTHNESS * dt)
        camera.FieldOfView = camera.FieldOfView
            + (targetFOV - camera.FieldOfView) * fovAlpha
        -- ПОКАЧИВАНИЕ КАМЕРЫ
        local targetOffset = Vector3.zero
        local targetRoll = 0
        if firstPerson and moving then
            local running = sprintHeld
            local bobSpeed
            local bobX
            local bobY
            if running then
                bobSpeed = SPRINT_BOB_SPEED
                bobX = SPRINT_BOB_X
                bobY = SPRINT_BOB_Y
            else
                bobSpeed = WALK_BOB_SPEED
                bobX = WALK_BOB_X
                bobY = WALK_BOB_Y
            end
            bobTime = bobTime + dt * bobSpeed
            local fatigueScale = 1
            if running then
                fatigueScale = 1 + fatigue * MAX_FATIGUE_BOB
            end
            -- ДВИЖЕНИЯ ГОЛОВЫ
            local x = math.cos(bobTime * 0.5)
                * bobX * fatigueScale
            local y = math.abs(math.sin(bobTime))
                * bobY * fatigueScale
            -- ИМИТАЦИЯ ТЯЖЁЛОГО ДЫХАНИЯ
            local breathing = 0
            if running then
                breathing = math.sin(bobTime * 0.42)
                    * fatigue * 0.035
            end
            targetOffset = Vector3.new(
                x,
                y + breathing,
                0
            )
            -- НАКЛОН КАМЕРЫ ПРИ ПОВОРОТАХ
            local moveDirection = humanoid.MoveDirection
            local turn = 0
            if previousMoveDirection.Magnitude > 0.05 then
                turn = previousMoveDirection:Cross(
                    moveDirection
                ).Y
            end
            targetRoll = math.clamp(
                -turn * 0.035,
                -math.rad(1.2),
                math.rad(1.2)
            )
            -- ДОПОЛНИТЕЛЬНОЕ ПОКАЧИВАНИЕ ПРИ УСТАЛОСТИ
            if running then
                targetRoll = targetRoll
                    + math.sin(bobTime * 0.42)
                    * math.rad(1.1)
                    * fatigue
            end
            previousMoveDirection = moveDirection
        else
            bobTime = 0
            if alive then
                previousMoveDirection = humanoid.MoveDirection
            else
                previousMoveDirection = Vector3.zero
            end
        end
        -- ПЛАВНОСТЬ ДВИЖЕНИЙ
        local bobAlpha = 1 - math.exp(-BOB_SMOOTHNESS * dt)
        currentOffset = currentOffset:Lerp(
            targetOffset,
            bobAlpha
        )
        currentRoll = currentRoll
            + (targetRoll - currentRoll) * bobAlpha
        -- ЗАТЕМНЕНИЕ ПРИ СИЛЬНОЙ УСТАЛОСТИ
        local targetDarkness = 0
        if activelySprinting then
            targetDarkness = fatigue * MAX_DARKNESS
        end
        local darkAlpha = 1 - math.exp(-1.2 * dt)
        darkness = darkness
            + (targetDarkness - darkness) * darkAlpha
        darkOverlay.BackgroundTransparency = 1 - darkness
        -- ПРИМЕНЕНИЕ ЭФФЕКТОВ К КАМЕРЕ
        camera.CFrame = camera.CFrame
            * CFrame.new(currentOffset)
            * CFrame.Angles(0, 0, currentRoll)
    end
)
-- ПЕРЕРОЖДЕНИЕ ПЕРСОНАЖА
player.CharacterAdded:Connect(setupCharacter)
if player.Character then
    setupCharacter(player.Character)
end