Загрузка данных
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local Lighting = game:GetService("Lighting")
local GuiService = game:GetService("GuiService")
local GameSettings = UserSettings():GetService("UserGameSettings")
-- ================= НАСТРОЙКИ =================
local Settings = {
-- Позиция камеры относительно головы
CameraHeight = -0.25, -- выше / ниже
CameraBack = -1.5, -- минус = вперёд, плюс = назад
CameraSide = 0, -- влево / вправо
DownPullIn = 0.3, -- 0..1: насколько камера возвращается к голове при взгляде вниз (0 = как раньше)
-- Мышка
MouseSensitivity = 0.003, -- чувствительность (больше = быстрее)
MaxDeltaPerFrame = 120, -- ограничение рывка мышки за кадр (убирает срывы при тряске)
MaxPitchDeg = 80, -- максимум вверх / вниз в градусах
-- FOV (максимум в Roblox = 120)
IdleFOV = 90,
WalkFOV = 100,
SprintFOV = 120,
FOVSmoothing = 6,
-- Движение
WalkSpeed = 16,
SprintSpeed = 26,
SprintKey = Enum.KeyCode.LeftShift,
ToggleCameraKey = Enum.KeyCode.F,
-- Покачивание при ходьбе и беге
BobEnabled = true,
BobVertical = 0.28,
BobHorizontal = 0.2,
BobSpeed = 0.45,
BobRoll = 0.035,
BobPitch = 0.025,
SprintBobMultiplier = 1.8,
BobSmoothing = 10,
-- Дыхание в покое
IdleEnabled = true,
IdleVertical = 0.03,
IdleHorizontal = 0.015,
IdleRotation = 0.004,
IdleSpeed = 0.3,
IdleDelay = 300,
IdleFade = 5,
-- Усталость от долгого бега
FatigueEnabled = true,
BlurAfter = 30,
BlurDuration = 10,
BlurMaxSize = 8,
DimAfter = 60,
DimDuration = 30,
DimBrightness = -0.2,
DimSaturation = -0.35,
-- Третье лицо (после нажатия F)
ThirdPersonMinZoom = 10,
ThirdPersonMaxZoom = 400,
-- Видимость тела
ViewAccessories = false,
ViewModels = false,
HeadLocalTransparency = 1,
HiddenParts = { "LeftUpperArm", "RightUpperArm" },
}
-- =============================================
local player = Players.LocalPlayer
local camera = workspace.CurrentCamera
local character, humanoid, root
local firstPerson = true
local sprinting = false
local bobTime = 0
local idleTime = 0
local standTime = 0
local bobOffset = Vector3.zero
local moveAlpha = 0
local locked = {}
local yaw, pitch = 0, 0
local diedConn
-- Эффекты усталости
for _, name in ipairs({ "FatigueBlur", "FatigueDim" }) do
local old = Lighting:FindFirstChild(name)
if old then old:Destroy() end
end
local blur = Instance.new("BlurEffect")
blur.Name = "FatigueBlur"
blur.Size = 0
blur.Parent = Lighting
local dim = Instance.new("ColorCorrectionEffect")
dim.Name = "FatigueDim"
dim.Parent = Lighting
local sprintTime = 0
local blurT = nil
local dimT = nil
local blurFired = false
local function envelope(t, duration, fadeIn, fadeOut)
return math.clamp(t / fadeIn, 0, 1) * math.clamp((duration - t) / fadeOut, 0, 1)
end
local function resetFatigue()
sprintTime = 0
blurT = nil
dimT = nil
blurFired = false
blur.Size = 0
dim.Brightness = 0
dim.Saturation = 0
end
local function classify(part)
if not part:IsA("BasePart") or not character then return nil end
if part.Name == "Head" and part.Parent == character then
return Settings.HeadLocalTransparency
end
if part:FindFirstAncestorOfClass("Accessory") then
return Settings.ViewAccessories and 0 or nil
end
if part.Parent == character then
if table.find(Settings.HiddenParts, part.Name) then
return 1
end
return 0
end
if part:FindFirstAncestorOfClass("Model") ~= character and Settings.ViewModels then
return 0
end
return nil
end
local function register(part)
local value = classify(part)
if value ~= nil then
locked[part] = value
end
end
-- Отдаём камеру стандартной системе Roblox (третье лицо, смерть)
local function releaseCamera()
camera.CameraType = Enum.CameraType.Custom
if humanoid then
camera.CameraSubject = humanoid
humanoid.AutoRotate = true
humanoid.CameraOffset = Vector3.zero
end
UserInputService.MouseBehavior = Enum.MouseBehavior.Default
player.CameraMode = Enum.CameraMode.Classic
player.CameraMinZoomDistance = Settings.ThirdPersonMinZoom
player.CameraMaxZoomDistance = Settings.ThirdPersonMaxZoom
camera.FieldOfView = 70
for part in pairs(locked) do
part.LocalTransparencyModifier = 0
end
end
-- Забираем камеру себе (первое лицо)
local function takeCamera()
if not humanoid or not root then return end
local look = root.CFrame.LookVector
yaw = math.atan2(-look.X, -look.Z)
pitch = 0
humanoid.CameraOffset = Vector3.zero
humanoid.AutoRotate = false
camera.CameraType = Enum.CameraType.Scriptable
end
local function applyMode()
if firstPerson then
takeCamera()
else
releaseCamera()
end
end
local function setupCharacter(char)
character = char
humanoid = char:WaitForChild("Humanoid")
root = char:WaitForChild("HumanoidRootPart")
locked = {}
sprinting = false
bobTime = 0
standTime = 0
bobOffset = Vector3.zero
moveAlpha = 0
resetFatigue()
humanoid.WalkSpeed = Settings.WalkSpeed
for _, d in ipairs(char:GetDescendants()) do
register(d)
end
char.DescendantAdded:Connect(register)
char.DescendantRemoving:Connect(function(d)
locked[d] = nil
end)
if diedConn then diedConn:Disconnect() end
diedConn = humanoid.Died:Connect(function()
releaseCamera()
end)
applyMode()
end
-- Бег и переключение камеры
UserInputService.InputBegan:Connect(function(input, processed)
if processed or not humanoid then return end
if input.KeyCode == Settings.SprintKey then
sprinting = true
humanoid.WalkSpeed = Settings.SprintSpeed
elseif input.KeyCode == Settings.ToggleCameraKey then
firstPerson = not firstPerson
applyMode()
end
end)
UserInputService.InputEnded:Connect(function(input)
if input.KeyCode == Settings.SprintKey and humanoid then
sprinting = false
humanoid.WalkSpeed = Settings.WalkSpeed
end
end)
-- Усталость
RunService.Heartbeat:Connect(function(dt)
if not Settings.FatigueEnabled or not root or not humanoid then return end
local speed = (root.AssemblyLinearVelocity * Vector3.new(1, 0, 1)).Magnitude
local running = sprinting and speed > 10 and humanoid.Health > 0
if running then
sprintTime += dt
if sprintTime >= Settings.BlurAfter and not blurFired then
blurFired = true
blurT = 0
end
if sprintTime >= Settings.DimAfter then
dimT = 0
sprintTime = 0
blurFired = false
end
else
sprintTime = math.max(0, sprintTime - dt * 2)
if sprintTime < Settings.BlurAfter then
blurFired = false
end
end
if blurT then
blurT += dt
if blurT >= Settings.BlurDuration then
blurT = nil
blur.Size = 0
else
blur.Size = Settings.BlurMaxSize * envelope(blurT, Settings.BlurDuration, 1.5, 3)
end
end
if dimT then
dimT += dt
if dimT >= Settings.DimDuration then
dimT = nil
dim.Brightness = 0
dim.Saturation = 0
else
local e = envelope(dimT, Settings.DimDuration, 3, 6)
dim.Brightness = Settings.DimBrightness * e
dim.Saturation = Settings.DimSaturation * e
end
end
end)
-- Своя камера, каждый кадр
RunService:BindToRenderStep("FirstPersonBody", Enum.RenderPriority.Camera.Value + 1, function(dt)
if not firstPerson or not humanoid or not root or humanoid.Health <= 0 then return end
local head = character and character:FindFirstChild("Head")
if not head then return end
-- Тело (самым последним делом в кадре дублируется ниже)
for part, value in pairs(locked) do
part.LocalTransparencyModifier = value
end
-- Мышка
if GuiService.MenuIsOpen then
UserInputService.MouseBehavior = Enum.MouseBehavior.Default
else
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
local delta = UserInputService:GetMouseDelta()
local m = Settings.MaxDeltaPerFrame
local dx = math.clamp(delta.X, -m, m)
local dy = math.clamp(delta.Y, -m, m)
local sens = Settings.MouseSensitivity * GameSettings.MouseSensitivity
yaw -= dx * sens
local lim = math.rad(Settings.MaxPitchDeg)
pitch = math.clamp(pitch - dy * sens, -lim, lim)
end
-- Персонаж поворачивается туда же, куда смотрит камера
root.CFrame = CFrame.new(root.Position) * CFrame.Angles(0, yaw, 0)
-- Покачивание
local speed = (root.AssemblyLinearVelocity * Vector3.new(1, 0, 1)).Magnitude
local onGround = humanoid.FloorMaterial ~= Enum.Material.Air
local moving = onGround and speed > 1 and humanoid.MoveDirection.Magnitude > 0
moveAlpha += ((moving and 1 or 0) - moveAlpha) * math.clamp(dt * 8, 0, 1)
local mult = sprinting and Settings.SprintBobMultiplier or 1
local walkOffset = Vector3.zero
local roll, bobPitch = 0, 0
if Settings.BobEnabled and moving then
bobTime += dt * speed * Settings.BobSpeed
end
if Settings.BobEnabled then
walkOffset = Vector3.new(
math.sin(bobTime) * Settings.BobHorizontal * mult,
math.abs(math.sin(bobTime)) * Settings.BobVertical * mult - Settings.BobVertical * mult * 0.5,
0
) * moveAlpha
roll = math.sin(bobTime) * Settings.BobRoll * mult * moveAlpha
bobPitch = math.sin(bobTime * 2) * Settings.BobPitch * mult * moveAlpha
end
-- Дыхание в покое (после IdleDelay секунд стояния)
local idleOffset = Vector3.zero
local idleRoll, idlePitch = 0, 0
if Settings.IdleEnabled then
idleTime += dt * Settings.IdleSpeed
if moving then standTime = 0 else standTime += dt end
local idleAlpha = (1 - moveAlpha) * math.clamp((standTime - Settings.IdleDelay) / Settings.IdleFade, 0, 1)
idleOffset = Vector3.new(
math.sin(idleTime * 0.6) * Settings.IdleHorizontal,
math.sin(idleTime) * Settings.IdleVertical,
0
) * idleAlpha
idleRoll = math.sin(idleTime * 0.7) * Settings.IdleRotation * idleAlpha
idlePitch = math.sin(idleTime * 0.9 + 1) * Settings.IdleRotation * idleAlpha
end
bobOffset = bobOffset:Lerp(walkOffset + idleOffset, math.clamp(dt * Settings.BobSmoothing, 0, 1))
-- Позиция: от головы, смещение считается по повороту влево-вправо (не по наклону)
local downFactor = 1 - math.clamp(-math.sin(pitch), 0, 1) * Settings.DownPullIn
local localOffset = Vector3.new(Settings.CameraSide, Settings.CameraHeight, Settings.CameraBack * downFactor) + bobOffset
local yawCF = CFrame.Angles(0, yaw, 0)
local pos = head.Position + yawCF:VectorToWorldSpace(localOffset)
camera.CFrame = CFrame.new(pos)
* CFrame.fromEulerAnglesYXZ(pitch, yaw, 0)
* CFrame.Angles(bobPitch + idlePitch, 0, roll + idleRoll)
-- FOV
local targetFov = Settings.IdleFOV
if moving then
targetFov = sprinting and Settings.SprintFOV or Settings.WalkFOV
end
targetFov = math.clamp(targetFov, 1, 120)
camera.FieldOfView += (targetFov - camera.FieldOfView) * math.clamp(dt * Settings.FOVSmoothing, 0, 1)
end)
-- Прозрачность тела в самый последний момент перед отрисовкой
RunService:BindToRenderStep("LockBodyTransparency", Enum.RenderPriority.Last.Value, function()
if not firstPerson then return end
for part, value in pairs(locked) do
part.LocalTransparencyModifier = value
end
end)
player.CharacterAdded:Connect(setupCharacter)
if player.Character then
setupCharacter(player.Character)
end