Встроенный Humanoid в Roblox часто не справляется с динамичными играми: застревает на ступеньках, страдает от задержек и срывается с движущихся платформ.
Создание собственного физического контроллера дает полный контроль над управлением. С помощью левитирующей капсулы на VectorForce и проекции движения на нормали склонов через Raycast достигается отклик консольного уровня.
1. Ограничения встроенного Humanoid в экшен-играх
Почему соревновательные проекты отказываются от базового контроллера:
- Жесткий конечный автомат: Негибкие задержки при смене состояний бега и прыжков.
- Сброс с платформ: Ошибки сетевой репликации при контакте с движущимся транспортом.
- Дрожание на ступенях: Резкие прыжки высоты, вызывающие микро-дергания камеры.
- Детерминированное управление: Прямой расчет скоростей каждый кадр в цикле Heartbeat.
2. Парящая капсула и пружинная подвеска VectorForce
Бесконтактное перемещение для идеальной плавности на любом рельефе:
- Виртуальная подвеска: Нижний луч Raycast удерживает капсулу в воздухе с помощью пружинной силы.
- Оболочка без трения: Гладкая капсула с нулевым трением никогда не застревает на стыках.
- Стабилизация AlignOrientation: Удерживает вертикальное положение персонажа без опрокидывания.
- Мгновенный отклик LinearVelocity: Моментальный разгон до нужной скорости без задержек.
--!strict
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local Workspace = game:GetService("Workspace")
local Players = game:GetService("Players")
local CustomController = {}
local player = Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local hrp = char:WaitForChild("HumanoidRootPart") :: BasePart
local TARGET_HEIGHT = 4.2
local SPRING_STIFFNESS = 1800
local SPRING_DAMPING = 160
local WALK_SPEED = 24
local RAY_PARAMS = RaycastParams.new()
RAY_PARAMS.FilterType = RaycastFilterType.Exclude
local linearVelocity = Instance.new("LinearVelocity")
linearVelocity.MaxForce = 40000
linearVelocity.VelocityConstraintMode = Enum.VelocityConstraintMode.Vector
linearVelocity.RelativeTo = Enum.ActuatorRelativeTo.World
local attachment = Instance.new("Attachment")
attachment.Parent = hrp
linearVelocity.Attachment0 = attachment
linearVelocity.Parent = hrp
function CustomController.Update(dt: number)
local moveDir = Vector3.zero
local cam = Workspace.CurrentCamera
if UserInputService:IsKeyDown(Enum.KeyCode.W) then moveDir += cam.CFrame.LookVector end
if UserInputService:IsKeyDown(Enum.KeyCode.S) then moveDir -= cam.CFrame.LookVector end
if UserInputService:IsKeyDown(Enum.KeyCode.D) then moveDir += cam.CFrame.RightVector end
if UserInputService:IsKeyDown(Enum.KeyCode.A) then moveDir -= cam.CFrame.RightVector end
moveDir = Vector3.new(moveDir.X, 0, moveDir.Z)
if moveDir.Magnitude > 0.01 then moveDir = moveDir.Unit end
RAY_PARAMS.FilterDescendantsInstances = { char }
local ray = Workspace:Raycast(hrp.Position, Vector3.new(0, -TARGET_HEIGHT * 1.5, 0), RAY_PARAMS)
local vForce = 0
local targetVel = moveDir * WALK_SPEED
if ray and ray.Instance and ray.Instance.CanCollide then
local dist = (hrp.Position - ray.Position).Magnitude
targetVel = moveDir - (ray.Normal * moveDir:Dot(ray.Normal))
targetVel = targetVel.Unit * WALK_SPEED
local err = TARGET_HEIGHT - dist
vForce = (err * SPRING_STIFFNESS) - (hrp.AssemblyLinearVelocity.Y * SPRING_DAMPING)
end
linearVelocity.VectorVelocity = Vector3.new(targetVel.X, targetVel.Y + (vForce * dt), targetVel.Z)
end
RunService.Heartbeat:Connect(CustomController.Update)
return CustomController
3. Определение земли и проекция вектора скорости на склоны
Сохранение постоянной скорости на подъемах и спусках:
- Нормаль поверхности: Считывание точного наклона плоскости из параметров Raycast.
- Векторная проекция: Перенаправление вектора скорости параллельно склону.
- Предел подъема: Управляемое соскальзывание вниз на склонах круче 45 градусов.
- Обнаружение обрывов: Дополнительные лучи по краям капсулы для фиксации края пропасти.
4. Инерция, моментум и механика скольжения
Добавление динамичных приемов паркура и спринта:
- Подкат на бегу: Временное отключение сил торможения при нажатии приседания.
- Гравитационный разгон: Дополнительное ускорение при скольжении вниз по холмам.
- Сохранение импульса: Мягкий переход от скольжения к обычному шагу.
- Контроль в воздухе: Возможность маневрировать в прыжке без накопления бесконечной скорости.
5. Синхронизация процедурной анимации с IKControl
Естественное сопряжение физической скорости со скелетом 3D-модели:
- Устранение скольжения ног: Подстройка скорости анимации под реальную скорость перемещения.
- Смешивание в 8 направлениях: Бесшовный блендинг шага во все стороны.
- Точная постановка ног с IKControl: Естественное прилегание подошв к неровным камням.
- Локальные вычисления: Расчет физики на устройстве игрока для экономии трафика.
Frequently Asked Questions
Зачем подвешивать капсулу в воздухе, а не касаться земли?
Прямой контакт приводит к застреванию на неровностях; виртуальная пружинная подвеска дает идеально гладкое скольжение.
Как проекция вектора спасает от замедления на холмах?
Она перенаправляет импульс вдоль плоскости холма, исключая лобовое столкновение с рельефом.
Решает ли это проблему срыва с движущихся поездов?
Да, так как скорость платформы под ногами можно напрямую прибавить к вектору движения персонажа.