Le contrôleur par défaut de Roblox (Humanoid) est limité pour les jeux d'action nerveux : saccades dans les escaliers, inertie absente et éjections brutales sur les plateformes mobiles.
En concevant un contrôleur basé sur des forces physiques réelles, les créateurs obtiennent une jouabilité ultra-réactive. Grâce à une capsule en lévitation amortie par VectorForce et une projection de trajectoire sur le sol via raycast, le déplacement devient fluide et organique.
1. Limitations du Humanoid par défaut
Pourquoi les jeux compétitifs s'affranchissent du système natif :
- Machine à états fermée : Transitions rigides bloquant les changements de direction immédiats.
- Éjections sur plateformes : Conflits de réplication projetant le joueur dans le vide.
- Micro-saccades sur marches : Hausses brutales de position perturbant la caméra.
- Contrôle prévisible : Calcul mathématique exact de la vitesse à chaque image (Heartbeat).
2. Capsule flottante et suspension VectorForce
Lévitation sans contact pour effacer les irrégularités du sol :
- Suspension virtuelle : Amortisseur calculé par raycast vertical maintenant une garde au sol constante.
- Enveloppe sans frottement : Cylindre extérieur lisse empêchant tout accrochage aux arêtes.
- Stabilisation AlignOrientation : Maintien vertical empêchant le personnage de basculer.
- Réactivité LinearVelocity : Atteinte instantanée de la vitesse voulue sans inertie forcée.
--!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. Détection par raycast et projection sur pentes
Conserver une vitesse uniforme en montée comme en descente :
- Vecteur normal : Extraction de la pente exacte via les normales du raycast.
- Projection vectorielle : Réorientation du mouvement parallèlement au relief.
- Angle limite : Glissade incontrôlée au-delà de 45 degrés d'inclinaison.
- Anticipation du vide : Rayons périphériques pour détecter les chutes.
4. Gestion de l'inertie et glissades dynamiques
Mécaniques modernes pour jeux de parkour et plateformes :
- Glissade accroupie : Chute temporaire de friction en pleine course.
- Accélération gravitaire : Gain de vitesse dans les descentes pentues.
- Conservation d'énergie : Amorti progressif à la fin des glissades.
- Contrôle aérien : Maniabilité en saut sans gain de vitesse infini.
5. Synchronisation des animations et ancrage IK
Harmonie totale entre déplacement physique et squelette 3D :
- Suppression du patinage : Ajustement du tempo d'animation à la vitesse réelle.
- Arbre directionnel : Mélange fluide dans les 8 directions.
- Pieds au sol avec IKControl : Adaptation naturelle aux marches et cailloux.
- Exécution côté client : Réactivité maximale sans encombrement réseau.
Frequently Asked Questions
Pourquoi surélever la capsule avec un ressort virtuel ?
Le contact physique direct accroche les micro-imperfections du décor ; la suspension assure une glisse parfaite.
Comment la projection évite-t-elle de ralentir en côte ?
Elle redirige la force vers le haut de la pente au lieu de buter contre le relief.
Les bugs de plateformes mobiles sont-ils résolus ?
Oui, car le contrôleur peut hériter directement de la vitesse mesurée sur le support.