Roblox Studio est un outil qui permet de créer des jeux vidéo en utilisant le langage Lua. Aujourd’hui, nous allons créer un mini-jeu de basket où :
- Le joueur entre dans une zone de lancer.
- Il appuie sur la touche F pour lancer un ballon.
- Si le ballon entre dans le panier, il marque des points.

Avant de coder, il faut imaginer le jeu :
- Le joueur entre dans une zone de lancer (une hitbox).
- En appuyant sur F, il lance une balle.
- Si la balle touche le panier, il gagne des points.
Objets nécessaires :
- Une hitbox (zone invisible pour détecter le joueur).
- Un ballon (modèle 3D).
- Un panier (pour marquer des points).
- Un script (pour gérer les règles du jeu).
À toi de jouer !
- Dans Roblox Studio, ajoute :
- Une Part (pour la hitbox) dans
Workspace. - Un Ball (modèle de balle) dans
ReplicatedStorage. - Un Basket (panier) dans
Workspace.
- Une Part (pour la hitbox) dans
- Teste : Déplace ton personnage dans la hitbox et imagine le gameplay.


Étape 2 : Créer la structure des objets dans l’Explorateur
But : Organiser les objets du jeu dans l’Explorateur pour faciliter la programmation.
Concepts clés : Hiérarchie des objets, organisation.
Explications
Dans Roblox, les objets sont organisés comme un arbre :
- Workspace : Contient tous les objets visibles dans le jeu (balles, paniers, etc.).
- ServerScriptService : Contient les scripts côté serveur (pour la logique du jeu).
- ReplicatedStorage : Contient les modèles réutilisables (comme la balle).
- StarterGui : Contient les interfaces utilisateur (pour afficher des messages).
Exemple :
textCopier
Workspace
├── Basket (Folder)
│ ├── HitBox (Part)
│ ├── Ball (Model)
│ └── ControlBall (Part)
ReplicatedStorage
└── FireInputEvent (RemoteEvent)


Étape 3 : Démarrer le script avec une gestion de hitbox
But : Détecter quand un joueur entre dans la zone de lancer.
Concepts clés : Événements, fonctions, détection de collision.
Explications
Une hitbox est une zone invisible qui détecte quand un joueur entre ou sort.
En Lua, on utilise l’événement .Touched pour savoir quand un objet touche la hitbox.
Exemple :
-- ==================== RÉFÉRENCES SCÈNE ====================
local basketFolder = workspace:WaitForChild("Basket")
local hitbox = basketFolder:WaitForChild("HitBox")
hitbox.Touched:Connect(function(hit)
print("Quelque chose a touché la hitbox !")
end)
Touched: Événement déclenché quand un objet entre en contact avec la hitbox.hit: L’objet qui a touché la hitbox (ici, une partie du personnage du joueur).
-- ==================== SERVICES ====================
local Players = game:GetService("Players")
-- ==================== RÉFÉRENCES SCÈNE ====================
local basketFolder = workspace:WaitForChild("Basket")
local hitbox = basketFolder:WaitForChild("HitBox")
-- ==================== ÉVÉNEMENTS : ZONE DE JEU
-- Quand un joueur touche la hitbox
hitbox.Touched:Connect(function(hit)
-- Vérifie si l'objet qui a touché est un personnage de joueur
local character = hit.Parent
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
local player = Players:GetPlayerFromCharacter(character)
if player then
print(player.Name .. " est entré dans la zone de lancer !")
end
end
end)
Explications du code
| Ligne | Explication |
|---|---|
local Players = game:GetService("Players") | Récupère le service Players pour accéder aux joueurs. |
hitbox.Touched:Connect(...) | Écoute l’événement Touched sur la hitbox. |
hit.Parent | Récupère le parent de l’objet qui a touché (ici, le personnage du joueur). |
FindFirstChildOfClass("Humanoid") | Vérifie si le parent a un Humanoid (donc c’est un personnage joueur). |
Players:GetPlayerFromCharacter(character) | Récupère le joueur associé au personnage. |
Copie ce code dans un Script (dans ServerScriptService).
Teste :
Entre dans la hitbox avec ton personnage.
Ouvre la fenêtre de sortie (Affichage > Sortie) pour voir le message.

QCM / Questions
À quoi sert hitbox.Touched:Connect(...) ?
- a) À détruire la hitbox.
- b) À exécuter du code quand un objet touche la hitbox.
- c) À changer la couleur de la hitbox.
Que fait Players:GetPlayerFromCharacter(character) ?
- a) Crée un nouveau personnage.
- b) Récupère le joueur associé à un personnage.
- c) Supprime le personnage.
Étape 4 : Changer la vision du joueur en première personne
But : Passer en vue première personne quand le joueur entre dans la zone.
Concepts clés : Propriétés des objets, fonctions utilitaires.
Explications
En Roblox, on peut modifier la caméra du joueur pour :
- LockFirstPerson : Vue à la première personne (comme dans un FPS).
- Classic : Vue classique (derrière le personnage).
Exemple :
player.CameraMode = Enum.CameraMode.LockFirstPerson
Code Lua
Ajoute ces fonctions au script précédent :
luaCopier
-- Applique une caméra first person (vue première personne verrouillée et zoomée)
local function lockFirstPersonCamera(player)
player.CameraMode = Enum.CameraMode.LockFirstPerson
player.CameraMinZoomDistance = 0.5
player.CameraMaxZoomDistance = 0.5
end
-- Restaure la caméra classique du joueur
local function resetCamera(player)
player.CameraMode = Enum.CameraMode.Classic
player.CameraMaxZoomDistance = 400
task.wait(0.1) -- Attend 0.1 seconde pour que la caméra se réinitialise
player.CameraMinZoomDistance = 10
end
Puis modifie l’événement Touched :
hitbox.Touched:Connect(function(hit)
local character = hit.Parent
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
local player = Players:GetPlayerFromCharacter(character)
if player then
print(player.Name .. " est entré dans la zone de lancer !")
lockFirstPersonCamera(player) -- Passe en première personne
end
end
end)
Explications du code
| Ligne | Explication |
|---|---|
player.CameraMode = Enum.CameraMode.LockFirstPerson | Force la vue à la première personne. |
player.CameraMinZoomDistance = 0.5 | Définit la distance minimale de zoom. |
task.wait(0.1) | Attend 0.1 seconde pour éviter les bugs. |
À toi de jouer !
- Copie le code dans ton script.
- Teste :
- Entre dans la hitbox : la vue doit passer en première personne.
- Modifie :
- Change
CameraMinZoomDistanceà1.0et observe la différence.
- Change
QCM / Questions
Que fait player.CameraMode = Enum.CameraMode.LockFirstPerson ?
a) Passe en vue à la troisième personne.
b) Passe en vue à la première personne.
c) Désactive la caméra.
Rajoute dans ton code pour modifier la caméra en FirstPerson lorsque le joueur rentre dans la zone de tir, puis modifie la caméra en normal lorsque le joueur quitte la zone de tir :
-- ==================== CONFIGURATION ====================
local EXIT_CONFIRM_DELAY = 0.5 -- Délai avant de confirmer la sortie de zone (anti faux-positif)
-- ==================== SERVICES ====================
local Players = game:GetService("Players")
-- ==================== RÉFÉRENCES SCÈNE ====================
local basketFolder = workspace:WaitForChild("Basket")
local hitbox = basketFolder:WaitForChild("HitBox")
-- ==================== ÉTAT DU JEU ====================
-- playersData[player] = { inZone = bool, nbrPoints = number }
local playersData = {}
-- Applique une caméra first person (vue première personne verrouillée et zoomée)
local function lockFirstPersonCamera(player)
player.CameraMode = Enum.CameraMode.LockFirstPerson
player.CameraMinZoomDistance = 0.5
player.CameraMaxZoomDistance = 0.5
end
-- Restaure la caméra classique du joueur
local function resetCamera(player)
player.CameraMode = Enum.CameraMode.Classic
player.CameraMaxZoomDistance = 400
task.wait(0.1) -- Attend 0.1 seconde pour que la caméra se réinitialise
player.CameraMinZoomDistance = 10
end
-- Fait sortir proprement un joueur de la zone : reset caméra + pyramide + état
local function exitZone(player)
local data = playersData[player]
if not data or not data.inZone then return end
data.inZone = false
resetCamera(player)
end
-- ==================== ÉVÉNEMENTS : ZONE DE JEU
-- Quand un joueur touche la hitbox
hitbox.Touched:Connect(function(hit)
-- Vérifie si l'objet qui a touché est un personnage de joueur
local character = hit.Parent
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
local player = Players:GetPlayerFromCharacter(character)
if player then
-- Anti-spam : on ignore si le joueur est déjà comptabilisé dans la zone
local data = playersData[player]
if data and data.inZone then return end
playersData[player] = { inZone = true, fire = 0 }
print(player.Name .. " est entré dans la zone de lancer !")
lockFirstPersonCamera(player) -- Passe en première personne
end
end
end)
-- Quand une partie du personnage quitte la hitbox, on vérifie si c'est un joueur
hitbox.TouchEnded:Connect(function(hit)
-- Vérifie si l'objet qui a touché est un personnage de joueur
local character = hit.Parent
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
local player = Players:GetPlayerFromCharacter(character)
if player then
task.wait(EXIT_CONFIRM_DELAY)
-- On vérifie qu'aucune partie du personnage ne touche encore la hitbox
-- (utile car Touched/TouchEnded se déclenchent par membre du corps)
local character = player.Character
local stillTouching = false
if character then
for _, part in pairs(hitbox:GetTouchingParts()) do
if part.Parent == character then
stillTouching = true
break
end
end
end
if stillTouching then return end
exitZone(player)
end
end
end)
Pourquoi utilise-t-on task.wait(0.1) dans resetCamera ?
a) Pour attendre que la caméra se réinitialise correctement.
b) Pour ralentir le jeu.
c) Pour afficher un message.
Documentation Roblox Lua :
Étape 5 : Écrire une interface utilisateur pour indiquer d’utiliser la touche F
But : Afficher un message à l’écran pour guider le joueur.
Concepts clés : LocalScript, ** interfaces utilisateur (GUI)**, TextLabel.
Explications
Pour afficher un message à l’écran, on utilise :
- Un ScreenGui (interface à l’écran).
- Un TextLabel (texte affiché).
- Un LocalScript (script côté client, car l’interface est visible uniquement par le joueur).
Exemple :
local player = game.Players.LocalPlayer
local gui = Instance.new("ScreenGui")
local label = Instance.new("TextLabel")
label.Text = "Appuie sur F pour lancer !"
label.Parent = gui
gui.Parent = player:WaitForChild("PlayerGui")
Code Lua
- Crée un LocalScript dans
StarterPlayerScripts. - Copie ce code :

-- ==================== SERVICES ====================
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- ==================== RÉFÉRENCES ====================
local player = Players.LocalPlayer
local readyEvent = ReplicatedStorage:WaitForChild("ReadyEvent")
-- ==================== INTERFACE ====================
local gui = Instance.new("ScreenGui")
local label = Instance.new("TextLabel")
-- Configure le TextLabel
label.Text = "Appuie sur F pour lancer !"
label.Size = UDim2.new(0, 200, 0, 50) -- Largeur: 200, Hauteur: 50
label.Position = UDim2.new(0.5, -100, 0.9, -25) -- Centre en bas
label.BackgroundColor3 = Color3.fromRGB(0, 0, 0) -- Fond noir
label.TextColor3 = Color3.fromRGB(255, 255, 255) -- Texte blanc
label.TextScaled = true -- Le texte s'adapte à la taille
label.Parent = gui
gui.Parent = player:WaitForChild("PlayerGui")
-- Désactive l'affichage au début
gui.Enabled = false
-- Écoute l'événement ReadyEvent pour activer/désactiver l'interface
readyEvent.OnClientEvent:Connect(function(isReady)
gui.Enabled = isReady
end)
Modifie le script sur le serveur pour envoyer l’évènement readyevent avec l’instruction readyEvent:FireClient(player, true) :
-- ==================== CONFIGURATION ====================
local EXIT_CONFIRM_DELAY = 0.5 -- Délai avant de confirmer la sortie de zone (anti faux-positif)
-- ==================== SERVICES ====================
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local fireInputEvent = ReplicatedStorage:WaitForChild("FireInputEvent")
local readyEvent = ReplicatedStorage:WaitForChild("ReadyEvent")
-- ==================== RÉFÉRENCES SCÈNE ====================
local basketFolder = workspace:WaitForChild("Basket")
local hitbox = basketFolder:WaitForChild("HitBox")
-- ==================== ÉTAT DU JEU ====================
-- playersData[player] = { inZone = bool, nbrPoints = number }
local playersData = {}
-- Applique une caméra first person (vue première personne verrouillée et zoomée)
local function lockFirstPersonCamera(player)
player.CameraMode = Enum.CameraMode.LockFirstPerson
player.CameraMinZoomDistance = 0.5
player.CameraMaxZoomDistance = 0.5
end
-- Restaure la caméra classique du joueur
local function resetCamera(player)
player.CameraMode = Enum.CameraMode.Classic
player.CameraMaxZoomDistance = 400
task.wait(0.1) -- Attend 0.1 seconde pour que la caméra se réinitialise
player.CameraMinZoomDistance = 10
end
-- Fait sortir proprement un joueur de la zone : reset caméra + pyramide + état
local function exitZone(player)
local data = playersData[player]
if not data or not data.inZone then return end
data.inZone = false
resetCamera(player)
end
-- ==================== ÉVÉNEMENTS : ZONE DE JEU
-- Quand un joueur touche la hitbox
hitbox.Touched:Connect(function(hit)
-- Vérifie si l'objet qui a touché est un personnage de joueur
local character = hit.Parent
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
local player = Players:GetPlayerFromCharacter(character)
if player then
-- Anti-spam : on ignore si le joueur est déjà comptabilisé dans la zone
local data = playersData[player]
if data and data.inZone then return end
playersData[player] = { inZone = true, fire = 0 }
print(player.Name .. " est entré dans la zone de lancer !")
lockFirstPersonCamera(player) -- Passe en première personne
-- Envoie d'un évènement au client pour lui dire qu'il peut lancer le ballon
readyEvent:FireClient(player, true) -- prêt à lancer le ballon
end
end
end)
-- Quand une partie du personnage quitte la hitbox, on vérifie si c'est un joueur
hitbox.TouchEnded:Connect(function(hit)
-- Vérifie si l'objet qui a touché est un personnage de joueur
local character = hit.Parent
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
local player = Players:GetPlayerFromCharacter(character)
if player then
task.wait(EXIT_CONFIRM_DELAY)
-- On vérifie qu'aucune partie du personnage ne touche encore la hitbox
-- (utile car Touched/TouchEnded se déclenchent par membre du corps)
local character = player.Character
local stillTouching = false
if character then
for _, part in pairs(hitbox:GetTouchingParts()) do
if part.Parent == character then
stillTouching = true
break
end
end
end
if stillTouching then return end
exitZone(player)
-- Envoie d'un évènement au client pour lui dire qu'il ne peut plus lancer le ballon
readyEvent:FireClient(player, false)
end
end
end)
Explications du code
| Ligne | Explication |
|---|---|
local player = Players.LocalPlayer | Récupère le joueur local (celui qui exécute le script). |
Instance.new("ScreenGui") | Crée une interface à l’écran. |
label.Text = "Appuie sur F pour lancer !" | Définit le texte affiché. |
readyEvent.OnClientEvent:Connect(...) | Écoute un événement distant pour activer/désactiver l’interface. |
À toi de jouer !
- Crée un RemoteEvent nommé
ReadyEventdansReplicatedStorage. - Copie le code du LocalScript dans
StarterPlayerScripts. - Teste :
- Entre dans la hitbox : le message doit s’afficher.
- Modifie :
- Change le texte en
"Prêt ? Appuie sur F !". - Change la couleur du fond en rouge (
Color3.fromRGB(255, 0, 0)).
- Change le texte en

QCM / Questions
À quoi sert un LocalScript ?
- a) À exécuter du code côté serveur.
- b) À créer des objets dans le jeu.
- c) À exécuter du code côté client (pour un joueur spécifique).
Que fait gui.Enabled = false ?
- a) Supprime l’interface.
- b) Désactive l’interface (elle n’est plus visible).
- c) Change la taille de l’interface.
Documentation Roblox Lua :
Étape 6 : Coder un LocalScript pour détecter l’appui sur la touche F
But : Détecter quand le joueur appuie sur F et envoyer un signal au serveur.
Concepts clés : InputBegan, RemoteEvent, UserInputService.
Explications
Pour détecter une touche, on utilise :
UserInputService: Service qui gère les entrées clavier/souris.InputBegan: Événement déclenché quand une touche est pressée.RemoteEvent: Pour envoyer un signal du client au serveur.
Exemple :
luaCopier
local UserInputService = game:GetService("UserInputService")
UserInputService.InputBegan:Connect(function(input, gameProcessed)
if input.KeyCode == Enum.KeyCode.F then
print("F a été pressée !")
end
end)
Code Lua
Ajoute ce code au LocalScript de l’étape 5 :
luaCopier
-- ==================== SERVICES ====================
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- ==================== RÉFÉRENCES ====================
local player = Players.LocalPlayer
local readyEvent = ReplicatedStorage:WaitForChild("ReadyEvent")
local fireInputEvent = ReplicatedStorage:WaitForChild("FireInputEvent")
-- ==================== INTERFACE ====================
local gui = Instance.new("ScreenGui")
local label = Instance.new("TextLabel")
-- Configure le TextLabel
label.Text = "Appuie sur F pour lancer !"
label.Size = UDim2.new(0, 200, 0, 50) -- Largeur: 200, Hauteur: 50
label.Position = UDim2.new(0.5, -100, 0.9, -25) -- Centre en bas
label.BackgroundColor3 = Color3.fromRGB(0, 0, 0) -- Fond noir
label.TextColor3 = Color3.fromRGB(255, 255, 255) -- Texte blanc
label.TextScaled = true -- Le texte s'adapte à la taille
label.Parent = gui
gui.Parent = player:WaitForChild("PlayerGui")
-- Désactive l'affichage au début
gui.Enabled = false
-- Écoute l'événement ReadyEvent pour activer/désactiver l'interface
readyEvent.OnClientEvent:Connect(function(isReady)
gui.Enabled = isReady
end)
-- ==================== DÉTECTION TOUCHE F ====================
UserInputService.InputBegan:Connect(function(input, gameProcessed)
if input.KeyCode == Enum.KeyCode.F then
-- Envoie un événement au serveur avec la direction du regard du joueur
local character = player.Character or player.CharacterAdded:Wait()
local humanoidRootPart = character:WaitForChild("HumanoidRootPart")
local lookDirection = humanoidRootPart.CFrame.LookVector
fireInputEvent:FireServer(lookDirection)
end
end)
Puis modifie le script serveur pour recevoir l’évènement de la demande de TIR :
Rajoute EventRemote : fireInputEvent :
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local fireInputEvent = ReplicatedStorage:WaitForChild("FireInputEvent")
local readyEvent = ReplicatedStorage:WaitForChild("ReadyEvent")
Puis la réception de l’évènement dans hitbox.Touched :
-- ==================== ÉVÉNEMENTS : ZONE DE JEU
-- Quand un joueur touche la hitbox
hitbox.Touched:Connect(function(hit)
-- Vérifie si l'objet qui a touché est un personnage de joueur
local character = hit.Parent
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
local player = Players:GetPlayerFromCharacter(character)
if player then
-- Anti-spam : on ignore si le joueur est déjà comptabilisé dans la zone
local data = playersData[player]
if data and data.inZone then return end
playersData[player] = { inZone = true, fire = 0 }
print(player.Name .. " est entré dans la zone de lancer !")
lockFirstPersonCamera(player) -- Passe en première personne
-- Envoie d'un évènement au client pour lui dire qu'il peut lancer le ballon
readyEvent:FireClient(player, true) -- prêt à lancer le ballon
-- Réception véènment pour le lancer du ballon
fireInputEvent.OnServerEvent:Connect(function(player, lookDirection)
print("Lancement du ballon par " .. player.Name)
end)
end
end
end)
Explications du code
| Ligne | Explication |
|---|---|
UserInputService.InputBegan:Connect(...) | Écoute quand une touche est pressée. |
input.KeyCode == Enum.KeyCode.F | Vérifie si la touche F a été pressée. |
humanoidRootPart.CFrame.LookVector | Récupère la direction dans laquelle le joueur regarde. |
fireInputEvent:FireServer(lookDirection) | Envoie la direction au serveur via un RemoteEvent. |
À toi de jouer !
- Crée un RemoteEvent nommé
FireInputEventdansReplicatedStorage. - Copie le code dans le LocalScript.
- Teste :
- Entre dans la hitbox.
- Appuie sur F : rien ne se passe encore (le serveur ne gère pas encore l’événement).
- Modifie :
- Change la touche en E (
Enum.KeyCode.E).
- Change la touche en E (
QCM / Questions
À quoi sert UserInputService.InputBegan ?
- a) À détecter quand une touche est relâchée.
- b) À détecter quand une touche est pressée.
- c) À changer la touche par défaut.
Que fait fireInputEvent:FireServer(lookDirection) ?
- a) Affiche un message à l’écran.
- b) Envoie la direction du regard au serveur.
- c) Détruit le ballon.
Pour aller plus loin
- Ajoute un cooldown (délai entre deux lancers) pour éviter le spam.
Documentation Roblox Lua :
Étape 7 : Gérer la réception du RemoteEvent et lancer la balle
But : Recevoir le signal du client et lancer une balle.
Concepts clés : RemoteEvent.OnServerEvent, clonage d’objets, physique.
Explications
Quand le serveur reçoit un RemoteEvent, il peut :
- Vérifier que le joueur a le droit de lancer.
- Cloner la balle depuis
ReplicatedStorage. - Appliquer une force pour la lancer.
Modifie le Script (côté serveur) :
-- ==================== CONFIGURATION ====================
local EXIT_CONFIRM_DELAY = 0.5 -- Délai avant de confirmer la sortie de zone (anti faux-positif)
local PUSH_FORCE = 150 -- Force de lancement en fonction de l'angle
local BALL_LIFETIME = 2 -- Durée de vie de la balle avant destruction (secondes)
-- ==================== SERVICES ====================
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local fireInputEvent = ReplicatedStorage:WaitForChild("FireInputEvent")
local readyEvent = ReplicatedStorage:WaitForChild("ReadyEvent")
local ServerStorage = game:GetService("ServerStorage")
local Debris = game:GetService("Debris")
-- ==================== RÉFÉRENCES SCÈNE ====================
local basketFolder = workspace:WaitForChild("Basket")
local hitbox = basketFolder:WaitForChild("HitBox")
local ballTemplate = basketFolder:WaitForChild("Ball")
ballTemplate.Parent = ServerStorage -- Déplace le modèle dans ServerStorage
-- ==================== ÉTAT DU JEU ====================
-- playersData[player] = { inZone = bool, nbrPoints = number }
local playersData = {}
-- Applique une caméra first person (vue première personne verrouillée et zoomée)
local function lockFirstPersonCamera(player)
player.CameraMode = Enum.CameraMode.LockFirstPerson
player.CameraMinZoomDistance = 0.5
player.CameraMaxZoomDistance = 0.5
end
-- Restaure la caméra classique du joueur
local function resetCamera(player)
player.CameraMode = Enum.CameraMode.Classic
player.CameraMaxZoomDistance = 400
task.wait(0.1) -- Attend 0.1 seconde pour que la caméra se réinitialise
player.CameraMinZoomDistance = 10
end
-- Fait sortir proprement un joueur de la zone : reset caméra + pyramide + état
local function exitZone(player)
local data = playersData[player]
if not data or not data.inZone then return end
data.inZone = false
resetCamera(player)
end
-- Fait apparaître une balle devant le joueur et lui appliquer une impulsion
local function fireBall(player, force)
local character = player.Character or player.CharacterAdded:Wait()
local hrp = character:WaitForChild("HumanoidRootPart")
local ball = ballTemplate:Clone()
ball.CustomPhysicalProperties = PhysicalProperties.new(
1, -- Density
0, -- Friction
0.1, -- Elasticity
0, -- FrictionWeight
0.1 -- ElasticityWeight
)
ball.Parent = workspace
ball.CFrame = hrp.CFrame * CFrame.new(-2, 2, 0) -- Position devant le joueur
ball.Name = player.Name
-- Applique une vitesse initiale vers l'avant + vers le haut
ball.AssemblyLinearVelocity = hrp.CFrame.LookVector * (force - math.random(38, 42)) + Vector3.new(0, force + math.random(28, 32), 0)
-- Détruit la balle après 2 secondes
Debris:AddItem(ball, BALL_LIFETIME)
end
local function throwBall(player, lookDirection)
local data = playersData[player]
if tick() - data.fire < 2 then return end -- Cooldown de 2 secondes
readyEvent:FireClient(player, false)
data.fire = tick()
local force = ((lookDirection.Y + 1) / 2) * PUSH_FORCE -- Force de lancement en fonction de l'angle
fireBall(player, force)
task.wait(2)
readyEvent:FireClient(player, true)
end
-- ==================== ÉVÉNEMENTS : ZONE DE JEU
-- Quand un joueur touche la hitbox
hitbox.Touched:Connect(function(hit)
-- Vérifie si l'objet qui a touché est un personnage de joueur
local character = hit.Parent
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
local player = Players:GetPlayerFromCharacter(character)
if player then
-- Anti-spam : on ignore si le joueur est déjà comptabilisé dans la zone
local data = playersData[player]
if data and data.inZone then return end
playersData[player] = { inZone = true, fire = 0 }
print(player.Name .. " est entré dans la zone de lancer !")
lockFirstPersonCamera(player) -- Passe en première personne
-- Envoie d'un évènement au client pour lui dire qu'il peut lancer le ballon
readyEvent:FireClient(player, true) -- prêt à lancer le ballon
-- Réception véènment pour le lancer du ballon
fireInputEvent.OnServerEvent:Connect(function(player, lookDirection)
throwBall(player, lookDirection)
end)
end
end
end)
-- Quand une partie du personnage quitte la hitbox, on vérifie si c'est un joueur
hitbox.TouchEnded:Connect(function(hit)
-- Vérifie si l'objet qui a touché est un personnage de joueur
local character = hit.Parent
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
local player = Players:GetPlayerFromCharacter(character)
if player then
task.wait(EXIT_CONFIRM_DELAY)
-- On vérifie qu'aucune partie du personnage ne touche encore la hitbox
-- (utile car Touched/TouchEnded se déclenchent par membre du corps)
local character = player.Character
local stillTouching = false
if character then
for _, part in pairs(hitbox:GetTouchingParts()) do
if part.Parent == character then
stillTouching = true
break
end
end
end
if stillTouching then return end
exitZone(player)
-- Envoie d'un évènement au client pour lui dire qu'il ne peut plus lancer le ballon
readyEvent:FireClient(player, false)
end
end
end)
Explications du code
| Ligne | Explication |
|---|---|
fireInputEvent.OnServerEvent:Connect(...) | Écoute l’événement envoyé par le client. |
ballTemplate:Clone() | Crée une copie de la balle. |
ball.CFrame = hrp.CFrame * CFrame.new(-2, 2, 0) | Positionne la balle devant le joueur. |
ball.AssemblyLinearVelocity = ... | Applique une vitesse à la balle (physique). |
Debris:AddItem(ball, 2) | Détruit la balle après 2 secondes. |
tick() - data.fire < 2 | Vérifie si 2 secondes se sont écoulées depuis le dernier lancer. |
À toi de jouer !
- Copie le code dans le Script côté serveur.
- Teste :
- Entre dans la hitbox.
- Appuie sur F : une balle doit être lancée !
- Modifie :
- Change
BALL_LIFETIMEà5pour que la balle reste plus longtemps. - Change la force du lancer en modifiant
local PUSH_FORCE = 150 — Force de lancement en fonction de l’angleforce = ((lookDirection.Y + 1) / 2) * PUSH_FORCE.
- Change
QCM / Questions
À quoi sert ballTemplate:Clone() ?
- a) À supprimer la balle.
- b) À créer une copie de la balle.
- c) À changer la couleur de la balle.
Que fait Debris:AddItem(ball, 2) ?
- a) Détruit la balle après 2 secondes.
- b) Affiche la balle pendant 2 secondes.
- c) Fait rebondir la balle 2 fois.
Pour aller plus loin
- Ajoute un effet de particule quand la balle est lancée.
Documentation Roblox Lua :
Étape 8 : Détecter quand la balle touche le panier
But : Marquer des points quand la balle entre dans le panier.
Concepts clés : Détection de collision, gestion de score, sons.

Pour détecter quand la balle touche le panier :
- Ajoute une Part (nommée
ControlBall) dans le panier. - Utilise
.Touchedpour détecter la collision. - Ajoute des points au joueur si la balle est la sienne.
Exemple :
ballDetector.Touched:Connect(function(hit)
local player = Players:FindFirstChild(hit.Name)
if player then
ScoreManager.addPoints(player, 3) -- Ajoute 3 points
end
end)
Voici le script final avec toutes les fonctionnalités intégrées :
-- ==================== CONFIGURATION ====================
local EXIT_CONFIRM_DELAY = 0.5 -- Délai avant de confirmer la sortie de zone (anti faux-positif)
local PUSH_FORCE = 150 -- Force de lancement en fonction de l'angle
local BALL_LIFETIME = 2 -- Durée de vie de la balle avant destruction (secondes)
-- ==================== SERVICES ====================
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local fireInputEvent = ReplicatedStorage:WaitForChild("FireInputEvent")
local readyEvent = ReplicatedStorage:WaitForChild("ReadyEvent")
local ServerStorage = game:GetService("ServerStorage")
local Debris = game:GetService("Debris")
-- ==================== RÉFÉRENCES SCÈNE ====================
local basketFolder = workspace:WaitForChild("Basket")
local hitbox = basketFolder:WaitForChild("HitBox")
local ballTemplate = basketFolder:WaitForChild("Ball")
local ballDetector = basketFolder:WaitForChild("ControlBall") -- zone qui détecte les balles dans le panier
ballTemplate.Parent = ServerStorage -- Déplace le modèle dans ServerStorage
local clapSound = basketFolder:WaitForChild("claps")
-- ==================== ÉTAT DU JEU ====================
-- playersData[player] = { inZone = bool, nbrPoints = number }
local playersData = {}
-- Joue un son
local function playSound(sound)
if not sound then return end
pcall(function() sound:Play() end)
end
-- Applique une caméra first person (vue première personne verrouillée et zoomée)
local function lockFirstPersonCamera(player)
player.CameraMode = Enum.CameraMode.LockFirstPerson
player.CameraMinZoomDistance = 0.5
player.CameraMaxZoomDistance = 0.5
end
-- Restaure la caméra classique du joueur
local function resetCamera(player)
player.CameraMode = Enum.CameraMode.Classic
player.CameraMaxZoomDistance = 400
task.wait(0.1) -- Attend 0.1 seconde pour que la caméra se réinitialise
player.CameraMinZoomDistance = 10
end
-- Fait sortir proprement un joueur de la zone : reset caméra + pyramide + état
local function exitZone(player)
local data = playersData[player]
if not data or not data.inZone then return end
data.inZone = false
resetCamera(player)
end
-- Fait apparaître une balle devant le joueur et lui appliquer une impulsion
local function fireBall(player, force)
local character = player.Character or player.CharacterAdded:Wait()
local hrp = character:WaitForChild("HumanoidRootPart")
local ball = ballTemplate:Clone()
ball.CustomPhysicalProperties = PhysicalProperties.new(
1, -- Density
0, -- Friction
0.1, -- Elasticity
0, -- FrictionWeight
0.1 -- ElasticityWeight
)
ball.Parent = workspace
ball.CFrame = hrp.CFrame * CFrame.new(-2, 2, 0) -- Position devant le joueur
ball.Name = player.Name
-- Applique une vitesse initiale vers l'avant + vers le haut
ball.AssemblyLinearVelocity = hrp.CFrame.LookVector * (force - math.random(38, 42)) + Vector3.new(0, force + math.random(28, 32), 0)
-- Détruit la balle après 2 secondes
Debris:AddItem(ball, BALL_LIFETIME)
end
local function throwBall(player, lookDirection)
local data = playersData[player]
if tick() - data.fire < 2 then return end -- Cooldown de 2 secondes
readyEvent:FireClient(player, false)
data.fire = tick()
local force = ((lookDirection.Y + 1) / 2) * PUSH_FORCE -- Force de lancement en fonction de l'angle
fireBall(player, force)
task.wait(2)
readyEvent:FireClient(player, true)
end
-- ==================== ÉVÉNEMENTS : ZONE DE JEU
-- Quand un joueur touche la hitbox
hitbox.Touched:Connect(function(hit)
-- Vérifie si l'objet qui a touché est un personnage de joueur
local character = hit.Parent
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
local player = Players:GetPlayerFromCharacter(character)
if player then
-- Anti-spam : on ignore si le joueur est déjà comptabilisé dans la zone
local data = playersData[player]
if data and data.inZone then return end
playersData[player] = { inZone = true, fire = 0 }
print(player.Name .. " est entré dans la zone de lancer !")
lockFirstPersonCamera(player) -- Passe en première personne
-- Envoie d'un évènement au client pour lui dire qu'il peut lancer le ballon
readyEvent:FireClient(player, true) -- prêt à lancer le ballon
-- Réception véènment pour le lancer du ballon
fireInputEvent.OnServerEvent:Connect(function(player, lookDirection)
print("Lancement du ballon par " .. player.Name)
throwBall(player, lookDirection)
end)
end
end
end)
-- Quand une partie du personnage quitte la hitbox, on vérifie si c'est un joueur
hitbox.TouchEnded:Connect(function(hit)
-- Vérifie si l'objet qui a touché est un personnage de joueur
local character = hit.Parent
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
local player = Players:GetPlayerFromCharacter(character)
if player then
task.wait(EXIT_CONFIRM_DELAY)
-- On vérifie qu'aucune partie du personnage ne touche encore la hitbox
-- (utile car Touched/TouchEnded se déclenchent par membre du corps)
local character = player.Character
local stillTouching = false
if character then
for _, part in pairs(hitbox:GetTouchingParts()) do
if part.Parent == character then
stillTouching = true
break
end
end
end
if stillTouching then return end
exitZone(player)
-- Envoie d'un évènement au client pour lui dire qu'il ne peut plus lancer le ballon
readyEvent:FireClient(player, false)
end
end
end)
-- Détection du ballon dans le panier
ballDetector.Touched:Connect(function(hit)
local player = Players:FindFirstChild(hit.Name)
if player then
playSound(clapSound)
print(player.Name .. " a marqué 3 points !")
end
end)
-- Nettoyage quand un joueur quitte
Players.PlayerRemoving:Connect(function(player)
playersData[player] = nil
end)
Explications du code
| Ligne | Explication |
|---|---|
ballDetector.Touched:Connect(...) | Écoute quand un objet touche le détecteur du panier. |
Players:FindFirstChild(hit.Name) | Vérifie si l’objet qui a touché est une balle lancée par un joueur. |
playSound(clapSound) | Joue un son de applaudissements. |
print(player.Name .. " a marqué 3 points !") | Affiche un message dans la sortie. |
À toi de jouer !
- Ajoute une Part nommée
ControlBalldans le panier (dansBasket). - Ajoute un son nommé
clapsdansBasket(tu peux importer un son depuis Roblox Library). - Copie le code dans le Script.
- Teste :
- Lance une balle dans le panier : tu seras applaudi.
QCM / Questions
À quoi sert ballDetector.Touched:Connect(...) ?
- a) À détruire le panier.
- b) À changer la couleur du panier.
- c) À détecter quand une balle touche le panier.
Que fait Players:FindFirstChild(hit.Name) ?
- a) Crée un nouveau joueur.
- b) Trouve un joueur dont le nom correspond à celui de l’objet qui a touché.
- c) Supprime le joueur.
Pour aller plus loin
- Ajoute un système de score avec un Leaderboard (tableau des scores).
- Affiche les points à l’écran avec un TextLabel.
Documentation Roblox Lua :
