
Ce système affiche un chrono qui mesure le temps mis par un joueur pour aller d’un bloc de départ à un bloc d’arrivée.

Le script serveur est placé sur les blocs (départ/arrivée) : il détecte quand le joueur les touche (Touched) et décide quand le chrono doit démarrer ou s’arrêter.

Le LocalScript, lui, tourne uniquement sur l’ordinateur du joueur : il gère l’affichage du chrono à l’écran (le TextLabel).

Le serveur ne peut pas modifier directement l’écran d’un joueur : il doit envoyer un message au client via un RemoteEvent.
Le RemoteEvent fonctionne comme une messagerie entre le serveur et le client : quand le joueur touche le bloc de départ, le serveur « fire » (envoie) l’événement StartEvent.

Le LocalScript écoute cet événement avec OnClientEvent:Connect(...) et, dès qu’il le reçoit, enregistre l’heure de départ (tick()) et active running = true.
De la même façon, quand le joueur touche le bloc d’arrivée, le serveur envoie WinEvent, et le LocalScript reçoit ce signal pour arrêter le chrono (running = false).
Pendant que running est actif, la fonction RunService.Heartbeat recalcule et affiche le temps écoulé à chaque frame (60 fois par seconde environ).

La fonction formatTime transforme le nombre de secondes brut en un affichage lisible du type 01:23 (minutes:secondes).

En résumé : le serveur décide (quand démarrer/arrêter), les RemoteEvents transmettent l’info, et le LocalScript affiche le résultat à l’écran du joueur.
Petit rappel pour bien comprendre
- Script serveur : tourne sur l’ordinateur qui héberge la partie. Il contrôle les règles du jeu, mais ne peut pas modifier ce qui s’affiche à l’écran d’un joueur précis.
- LocalScript : tourne uniquement sur l’ordinateur du joueur. Il gère l’affichage (interface, sons, caméra…) mais ne peut pas être utilisé pour des règles importantes du jeu (sinon un joueur pourrait tricher).
- RemoteEvent : le « pont » entre les deux. Le serveur envoie une info avec
FireClient(), et le LocalScript la reçoit avecOnClientEvent:Connect().
QCM pour bien comprendre
1. Où s’exécute un LocalScript ?
A. Sur le serveur du jeu
B. Sur l’ordinateur du joueur uniquement
C. Sur tous les ordinateurs en même temps
D. Nulle part, c’est juste un fichier texte
2. Pourquoi ne peut-on pas modifier directement le TextLabel depuis le script serveur ?
A. Parce que c’est interdit par Roblox
B. Parce que le serveur n’a pas accès à l’affichage local de chaque joueur
C. Parce que le TextLabel n’existe pas sur le serveur
D. Parce que ça coûte trop cher en mémoire
3. À quoi sert un RemoteEvent ?
A. À supprimer un joueur du jeu
B. À transmettre une information entre le serveur et un client
C. À changer la couleur d’un bloc automatiquement
D. À sauvegarder les données du joueur
4. Que fait startEvent:FireClient(player) côté serveur ?
A. Il supprime le joueur
B. Il envoie un signal au client précis pour déclencher une action
C. Il démarre le jeu pour tous les joueurs
D. Il crée un nouveau RemoteEvent
5. Que fait RunService.Heartbeat dans ce script ?
A. Il vérifie les battements de cœur du joueur
B. Il exécute du code à chaque frame, ici pour actualiser l’affichage du chrono
C. Il redémarre le serveur régulièrement
D. Il compte le nombre de joueurs connectés
6. Que se passe-t-il si running vaut false ?
A. Le chrono continue de s’incrémenter
B. Le texte du chrono arrête de se mettre à jour
C. Le joueur est téléporté au départ
D. Le RemoteEvent est détruit
Crée un part pour déterminer le départ du chrono et un part pour déterminer le stop du chrono.


Puis deux RemoteEvent pour gérer le démarrage et l’arrêt du chrono :

Puis script sur le serveur pour gérer les collisions entre le player et les parts de départ et d’arrivée :

local replicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = replicatedStorage:WaitForChild("Coursetimecheck")
local startEvent = remoteEvent.StartEvent
local winEvent = remoteEvent.WinEvent
local start = script.Parent.StartPart
local succes = script.Parent.SuccessPart
succes.SurfaceGui.Enabled = false
succes.ParticleEmitter.Enabled = false
succes.Anchored = true
succes.CanCollide = true
succes.CanTouch = true
start.Anchored = true
start.CanCollide = false
start.CanTouch = true
start.Transparency = 1
succes.Touched:Connect(function(hit)
local character = hit.Parent
local humanoid = character and character:FindFirstChild("Humanoid")
if not humanoid then return end
if succes.SurfaceGui.Enabled then return end
remoteEvent.WinEvent:FireClient(game.Players:GetPlayerFromCharacter(character))
succes.SurfaceGui.Enabled = true
succes.ParticleEmitter.Enabled = true
end)
start.Touched:Connect(function(hit)
local character = hit.Parent
local humanoid = character and character:FindFirstChild("Humanoid")
if not humanoid then return end
local player = game.Players:GetPlayerFromCharacter(character)
if not player then return end -- sécurité si ce n'est pas un joueur (NPC par ex.)
print("StartEvent déclenché pour " .. player.Name)
startEvent:FireClient(player)
end)
Et également un localScript pour l’affichage du chrono pour le joueur :

-- Récupération des services
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
-- Récupération du joueur actuel et de la PlayerGui
local player = Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
-- Récupération de l'UI du chrono
local chrono = playerGui:WaitForChild("ScreenGui"):WaitForChild("Frame"):WaitForChild("ChronoLabel")
-- Récupération des RemoteEvents start et stop du chrono
local remoteEventsFolder = ReplicatedStorage:WaitForChild("Coursetimecheck")
local startEvent = remoteEventsFolder:WaitForChild("StartEvent")
local stopEvent = remoteEventsFolder:WaitForChild("WinEvent")
-- Fonction pour formater le temps en minutes:secondes
local function formatTime(t)
local minutes = math.floor(t / 60)
local seconds = math.floor(t % 60)
return string.format("%02d:%02d", minutes, seconds)
end
-- Variables globales pour suivre le temps et l'état du chrono
local startTime = nil
chrono.Text = "00:00"
local running = false
-- Connexion des événements serveur au client
startEvent.OnClientEvent:Connect(function()
startTime = tick()
running = true
end)
stopEvent.OnClientEvent:Connect(function()
running = false
end)
-- Mise à jour continue de l'affichage du chrono
RunService.Heartbeat:Connect(function()
if not running or not startTime then return end
chrono.Text = formatTime(tick() - startTime)
end)
