Загрузка данных
--// 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