À quoi sert ce script ?
Ce script permet au joueur d’appuyer sur une touche (F) pour obtenir un boost temporaire de vitesse et de saut pendant quelques secondes, avec une énergie limitée qui se réduit à chaque utilisation, affichée sous forme de barre.
Pourquoi ce script doit être un LocalScript et pas un script serveur
1. Détection des touches du clavier
UserInputService.InputBegan (qui détecte l’appui sur F) ne fonctionne que côté client. Le serveur ne « voit » pas directement quelle touche un joueur appuie sur son clavier — seul l’ordinateur du joueur (le client) peut détecter ça. Si ce script était sur le serveur, la détection de la touche ne fonctionnerait tout simplement pas.
2. Réactivité et fluidité
Un LocalScript s’exécute directement sur l’ordinateur du joueur, sans attendre d’aller-retour réseau avec le serveur. Le chrono et le boost réagissent donc instantanément, sans latence perceptible.
3. Affichage de l’interface (GUI)
Modifier un TextLabel ou la taille d’une barre (energy.Size) dans la PlayerGui doit se faire depuis un LocalScript : c’est l’interface personnelle de ce joueur, elle ne concerne que son écran à lui.
⚠️ Attention cependant : comme ce script est entièrement côté client, un joueur malveillant pourrait potentiellement le modifier pour tricher (boost infini, énergie illimitée…). Dans un vrai jeu, il faudrait que le serveur vérifie et valide réellement les changements de vitesse/saut, plutôt que de faire une confiance totale au client. Ce script convient pour l’affichage et le confort, mais une version « anti-triche » demanderait une communication avec un script serveur.
Pourquoi utiliser des CONSTANTES
EN MAJUSCULES (TIMELAPS, SPEEDMAX, JUMPMAX…)
- Convention de lisibilité : en programmation, écrire un nom de variable entièrement en majuscules est une convention largement utilisée pour signaler « ceci est une constante de configuration, une valeur qui ne doit jamais changer pendant l’exécution du script ». Ça permet, en un coup d’œil, de distinguer les réglages (en haut du script) des variables qui évoluent pendant le jeu (
energyLevel,startime…). - Facilité de modification : toutes les valeurs importantes sont regroupées au même endroit, en haut du script. Si tu veux changer la durée du boost ou la touche utilisée, tu modifies une seule ligne, sans devoir chercher dans tout le code où cette valeur est utilisée.
- Évite les « nombres magiques » : sans constante, on verrait des
50,70,6disséminés dans le code sans savoir à quoi ils correspondent. AvecSPEEDMAX = 50, le nom explique immédiatement le rôle du nombre.
Pourquoi RunService.Heartbeat plutôt qu’une boucle while ... do wait() end
Le problème d’une boucle while avec wait()
-- ❌ Mauvaise pratique
while true do
wait()
-- code du chrono
end
- Bloque le thread : une boucle infinie s’exécute en continu et occupe le script en permanence, même quand il n’y a rien à faire.
- Timing imprécis :
wait()ne garantit pas un délai exact — il peut varier légèrement selon la charge du jeu, ce qui rend le chrono moins précis. - Difficile à arrêter proprement : il faut gérer soi-même une condition de sortie, et un oubli peut bloquer le script indéfiniment.
Pourquoi Heartbeat est meilleur
- Synchronisé avec le moteur du jeu :
RunService.Heartbeatse déclenche automatiquement à chaque frame, juste après les calculs de physique — c’est le rythme naturel du jeu (environ 60 fois par seconde), donc le chrono reste fluide et précis. - Pas de blocage : contrairement à une boucle
while,Heartbeat:Connect(...)ne bloque jamais le reste du script — le code continue de s’exécuter normalement en parallèle. - Gestion automatique : pas besoin de gérer soi-même l’arrêt de la boucle ; la fonction ne fait simplement rien (
returnimmédiat) tant questartimeestnil, sans gaspiller de ressources inutiles. - Standard de l’industrie : c’est la méthode recommandée par Roblox pour tout ce qui doit se mettre à jour en continu (chronos, animations personnalisées, barres de vie…).
QCM pour vérifier la compréhension
1. Pourquoi ce script doit-il être un LocalScript et non un script serveur ?
A. Parce que les LocalScripts sont plus rapides à écrire
B. Parce que la détection des touches du clavier (InputBegan) ne fonctionne que côté client
C. Parce que Roblox interdit les scripts serveur sur les touches
D. Ça n’a aucune importance, les deux fonctionneraient pareil
2. Que signifie écrire une variable EN MAJUSCULES comme SPEEDMAX ?
A. Que la variable est plus rapide à lire par l’ordinateur
B. Que c’est une convention indiquant qu’il s’agit d’une constante de configuration
C. Que la variable est obligatoirement un nombre
D. Que la variable est visible par tous les scripts du jeu
3. Quel est le principal problème d’une boucle while true do wait() end pour gérer un chrono ?
A. Elle ne fonctionne pas du tout sur Roblox
B. Elle est difficile à écrire
C. Son timing est moins précis et elle bloque le thread en continu
D. Elle ne peut pas afficher de texte
4. Que fait RunService.Heartbeat ?
A. Il vérifie le rythme cardiaque du joueur
B. Il exécute une fonction automatiquement à chaque frame du jeu
C. Il redémarre le script à chaque respawn
D. Il détecte les touches du clavier
5. Pourquoi regrouper tous les réglages (TIMELAPS, SPEEDMAX, JUMPMAX…) en haut du script ?
A. Pour que le script soit plus court
B. Pour faciliter la modification des paramètres à un seul endroit, sans chercher dans tout le code
C. Parce que Roblox l’exige
D. Pour que le jeu se charge plus vite
6. Quel est le risque de sécurité mentionné concernant ce LocalScript ?
A. Il pourrait faire planter le jeu
B. Un joueur malveillant pourrait le modifier pour tricher (énergie infinie, boost permanent)
C. Il consomme trop de mémoire
D. Il ne fonctionne que sur mobile
7. Que se passe-t-il concrètement si on remplace Heartbeat par une boucle while wait() do dans ce script ?
A. Rien ne change, c’est strictement identique
B. Le chrono pourrait être légèrement moins précis et le code moins optimisé, bien que fonctionnellement proche
C. Le boost ne fonctionnerait plus du tout
D. Le jeu planterait immédiatement


Crée cette structure d’UI pour la barre d’énergie et le compteur du boost :


Le ScreenGui possède une propriété ResetOnSpawn, qui vaut true par défaut. Quand elle est activée :
- À chaque respawn, Roblox détruit le
ScreenGuiactuel dans laPlayerGuiet le recrée à neuf depuis sa version originale dansStarterGui. - Toutes les modifications faites par script sont donc perdues, et la barre d’énergie revient à sa taille définie dans les Properties de Studio.
Solution
Dans l’Explorer, sélectionne ton ScreenGui et décoche/mets à false la propriété ResetOnSpawn :

Crée un LocalScript sous StarterPlayerScripts

Sous StarterPlayerScripts
Le script s’exécute dès la connexion du joueur, souvent avant même que le personnage n’existe dans le jeu (player.Character vaut alors nil, car le perso n’a pas encore spawn).
Sous StarterCharacterScripts
Le script est recréé et réexécuté à chaque respawn du personnage.
Dans notre cas, nous avons besoin de garder les informations sur le joueur après un respawn, crée donc un LocalScript sous StarterPlayerScripts :
-- Récupération des services
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
-- Paramètrage du service
local TIMELAPS = 6
local SPEEDMAX = 50
local JUMPMAX = 70
local TOUCHBOOST = Enum.KeyCode.F
local ENERGY_COST = 0.1
-- Récupération du player et des écrans
local player = Players.LocalPlayer
-- La PlayerGui et son contenu ne sont PAS détruits au respawn : on les récupère une seule fois
local playerGui = player:WaitForChild("PlayerGui")
local screenGui = playerGui:WaitForChild("ScreenGui")
-- Penser à modifier la propriété suivante dans ROBLOX STUDIO screenGui.ResetOnSpawn = false
local chrono = playerGui:WaitForChild("ScreenGui"):WaitForChild("Frame"):WaitForChild("ChronoBoost")
local energy = playerGui:WaitForChild("ScreenGui"):WaitForChild("Frame"):WaitForChild("Energy")
local info = energy:WaitForChild("Frame"):WaitForChild("InfoLabel")
-- Initialisation des variables au lancement du script
local humanoid, jumpHeight, walkSpeed
local energyLevel = 1
local startime = nil
-- Initialisation de l'affichage de la barre de puissance
info.Text = "Press " .. TOUCHBOOST.Name .. " to boost for 3 seconds (Energy " .. math.floor(ENERGY_COST * 100) .. "%)"
chrono.Visible = false
-- Formatage du temps restant en seconde et dixième de seconde
local function formatTime(t)
local seconds = math.floor(t % 60)
local tenths = math.floor((t * 10) % 10)
return string.format("%02d.%d", seconds, tenths)
end
-- Chargement du player
local function onCharacterAdded(character)
humanoid = character:WaitForChild("Humanoid")
jumpHeight = humanoid.JumpHeight
walkSpeed = humanoid.WalkSpeed
startime = nil
end
player.CharacterAdded:Connect(onCharacterAdded)
if player.Character then
onCharacterAdded(player.Character)
end
-- Récupération de la touche du clavier pour actionner le boost
local function onKeyPress(input, gameProcessed)
if gameProcessed or startime or not humanoid then return end
if input.KeyCode ~= TOUCHBOOST then return end
if energyLevel < ENERGY_COST then return end
humanoid.JumpHeight = JUMPMAX
humanoid.WalkSpeed = SPEEDMAX
--energyLevel = math.clamp(energyLevel - ENERGY_COST, 0, 1)
startime = tick()
--energy.Size = UDim2.new(energyLevel, 0, energy.Size.Y.Scale, 0)
end
UserInputService.InputBegan:Connect(onKeyPress)
-- Affichage du temps restant du boost
RunService.Heartbeat:Connect(function()
if not startime or not humanoid then return end
local elapsed = TIMELAPS - (tick() - startime)
if elapsed > 0 then
chrono.Visible = true
chrono.Text = formatTime(elapsed)
else
startime = nil
humanoid.JumpHeight = jumpHeight
humanoid.WalkSpeed = walkSpeed
chrono.Visible = false
energyLevel = math.clamp(energyLevel - ENERGY_COST, 0, 1)
energy.Size = UDim2.new(energyLevel, 0, energy.Size.Y.Scale, 0)
end
end)
