As-tu déjà rêvé de faire bondir les joueurs à plusieurs mètres de hauteur dans ton jeu Roblox ? Elles peuvent servir à franchir un obstacle, atteindre une plateforme cachée ou simplement ajouter une touche amusante à un niveau. Dans ce tutoriel, tu vas découvrir comment transformer un simple part en véritable trampoline interactif. Tu verras également comment régler la puissance du rebond pour obtenir différents effets de jeu.
Rebond du joueur sur la plaque
Crée un part et dessous un script :
Saisie ce code dans le script :
local TweenService = game:GetService("TweenService")
local BOUNCE_SPEED = 120 -- vitesse verticale imposée (studs/s)
local COOLDOWN = 0.3 -- évite les rebonds multiples par contact
local trampoline = script.Parent
local lastBounce = {} -- cooldown par joueur
local function squish(trampoline, bounceSpeed, squishTime)
if bounceSpeed==nil then bounceSpeed = BOUNCE_SPEED end
-- Remplace directement la vélocité verticale
local vel = trampoline.AssemblyLinearVelocity
trampoline.AssemblyLinearVelocity = Vector3.new(vel.X, bounceSpeed, vel.Z)
end
-- Appliquer des propriétés physiques pour que la part soit glissante
trampoline.CustomPhysicalProperties = PhysicalProperties.new(
0.7, -- Density
1, -- Friction
1, -- Elasticity
1, -- FrictionWeight
1 -- ElasticityWeight
)
-- Appliquer un matériau glissant comme de la glace
trampoline.Material = Enum.Material.Carpet
trampoline.Touched:Connect(function(hit)
local character = hit.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if not humanoid or humanoid.Health <= 0 then return end
local rootPart = character:FindFirstChild("HumanoidRootPart")
if not rootPart then return end
-- Cooldown par personnage
local now = tick()
if lastBounce[character] and (now - lastBounce[character]) < COOLDOWN then return end
lastBounce[character] = now
squish(trampoline)
end)
Ce script transforme une simple pièce Roblox en trampoline capable de faire rebondir les joueurs lorsqu’ils la touchent. Les variables BOUNCE_SPEEDet COOLDOWNdéfinissent respectivement la puissance du rebond et le temps d’attente avant qu’un même joueur puisse rebondir à nouveau. Les propriétés physiques et le matériau de la pièce sont modifiés pour lui donner l’apparence et le comportement d’une surface adaptée au rebond.
La fonction squish() applique une nouvelle vitesse verticale à la pièce afin de créer l’effet de propulsion vers le haut.
L’événement Touched détecte lorsqu’un objet entre en contact avec le trampoline et vérifie qu’il s’agit bien d’un personnage vivant possédant un Humanoid. Un système de cooldown mémorise l’instant du dernier rebond de chaque joueur afin d’éviter plusieurs déclenchements successifs lorsque le personnage reste en contact avec le trampoline.
Animation de la plaque comme un trampoline
Par rapport à la version précédente, le script suivant ajoute un effet visuel d’écrasement et de retour à la forme initiale, ce qui donne davantage l’impression qu’il s’agit d’un véritable trampoline.
Une nouvelle variable originalSizemémorise la taille initiale de la plaque afin de pouvoir la restaurer après l’animation. La constante SQUISH_TIME définit la durée de l’animation de compression et de retour à la normale. La table isSquishingempêche que plusieurs animations soient lancées en même temps sur la même plaque, ce qui éviterait des déformations incohérentes. Le service TweenServiceest utilisé pour créer une animation fluide plutôt qu’un changement instantané de taille.
Lorsqu’un joueur rebondit, la plaque s’aplatit : sa hauteur diminue tandis que sa largeur et sa longueur augmentent légèrement. Cela reproduit visuellement le comportement d’un trampoline qui se comprime sous le poids du joueur. Après un court délai, une seconde animation redonne progressivement à la plaque sa taille d’origine, donnant l’impression qu’elle reprend sa forme après avoir propulsé le joueur.
L’effet visuel est synchronisé avec le rebond du joueur, ce qui rend l’interaction plus réaliste et plus agréable à observer.
local TweenService = game:GetService("TweenService")
local BOUNCE_SPEED = 120 -- vitesse verticale imposée (studs/s)
local COOLDOWN = 0.3 -- évite les rebonds multiples par contact
local SQUISH_TIME = 0.08 -- durée de l'étirement en secondes
local trampoline = script.Parent
local lastBounce = {} -- cooldown par joueur
local isSquishing = {} -- marquage de la part en état d'étirement
local function squish(trampoline, bounceSpeed, squishTime)
if bounceSpeed==nil then bounceSpeed = BOUNCE_SPEED end
if squishTime==nil then squishTime = SQUISH_TIME end
local originalSize = trampoline.Size
-- Remplace directement la vélocité verticale
local vel = trampoline.AssemblyLinearVelocity
trampoline.AssemblyLinearVelocity = Vector3.new(vel.X, bounceSpeed, vel.Z)
if isSquishing[trampoline] then return end
isSquishing[trampoline] = true
local tweenInfo = TweenInfo.new(squishTime, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
TweenService:Create(trampoline, tweenInfo, {
Size = Vector3.new(originalSize.X * 1.05, originalSize.Y * 0.4, originalSize.Z * 1.05)
}):Play()
task.delay(SQUISH_TIME, function()
TweenService:Create(trampoline, tweenInfo, { Size = originalSize }):Play()
task.delay(SQUISH_TIME, function() isSquishing[trampoline] = false end)
end)
end
-- Appliquer des propriétés physiques pour que la part soit glissante
trampoline.CustomPhysicalProperties = PhysicalProperties.new(
0.7, -- Density
1, -- Friction
1, -- Elasticity
1, -- FrictionWeight
1 -- ElasticityWeight
)
-- Appliquer un matériau glissant comme de la glace
trampoline.Material = Enum.Material.Carpet
trampoline.Touched:Connect(function(hit)
local character = hit.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if not humanoid or humanoid.Health <= 0 then return end
local rootPart = character:FindFirstChild("HumanoidRootPart")
if not rootPart then return end
-- Cooldown par personnage
local now = tick()
if lastBounce[character] and (now - lastBounce[character]) < COOLDOWN then return end
lastBounce[character] = now
squish(trampoline)
end)
Système de plaques rebondissantes
Modifie la structure en créant un Folder, sous le folder ton script et la part, puis duplique la plaque rebondissante plusieurs fois :
Modifie ton script pour lire tous les parts rebondissants :
local TweenService = game:GetService("TweenService")
local BOUNCE_SPEED = 120 -- vitesse verticale imposée (studs/s)
local COOLDOWN = 0.3 -- évite les rebonds multiples par contact
local SQUISH_TIME = 0.08 -- durée de l'étirement en secondes
local trampolines = script.Parent
local lastBounce = {} -- cooldown par joueur
local isSquishing = {} -- marquage de la part en état d'étirement
local function squish(trampoline, bounceSpeed, squishTime)
if bounceSpeed==nil then bounceSpeed = BOUNCE_SPEED end
if squishTime==nil then squishTime = SQUISH_TIME end
local originalSize = trampoline.Size
-- Remplace directement la vélocité verticale
local vel = trampoline.AssemblyLinearVelocity
trampoline.AssemblyLinearVelocity = Vector3.new(vel.X, bounceSpeed, vel.Z)
if isSquishing[trampoline] then return end
isSquishing[trampoline] = true
local tweenInfo = TweenInfo.new(squishTime, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
TweenService:Create(trampoline, tweenInfo, {
Size = Vector3.new(originalSize.X * 1.05, originalSize.Y * 0.4, originalSize.Z * 1.05)
}):Play()
task.delay(SQUISH_TIME, function()
TweenService:Create(trampoline, tweenInfo, { Size = originalSize }):Play()
task.delay(SQUISH_TIME, function() isSquishing[trampoline] = false end)
end)
end
for _, trampoline in ipairs(trampolines:GetChildren()) do
if not trampoline:IsA("BasePart") then continue end
-- Appliquer des propriétés physiques pour que la part soit glissante
trampoline.CustomPhysicalProperties = PhysicalProperties.new(
0.7, -- Density
1, -- Friction
1, -- Elasticity
1, -- FrictionWeight
1 -- ElasticityWeight
)
-- Appliquer un matériau glissant comme de la glace
trampoline.Material = Enum.Material.Carpet
trampoline.Touched:Connect(function(hit)
local character = hit.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if not humanoid or humanoid.Health <= 0 then return end
local rootPart = character:FindFirstChild("HumanoidRootPart")
if not rootPart then return end
-- Cooldown par personnage
local now = tick()
if lastBounce[character] and (now - lastBounce[character]) < COOLDOWN then return end
lastBounce[character] = now
squish(trampoline)
end)
end
Dans beaucoup de jeux Roblox, il existe des zones spéciales qui changent complètement la manière de jouer. Imagine un sol où ton personnage se met à glisser tout seul, comme sur de la glace, sans pouvoir s’arrêter immédiatement. Ou encore une pente qui accélère ton déplacement, te propulsant vers une nouvelle plateforme. Ces effets simples transforment un niveau classique en un vrai parcours d’obstacles amusant et dynamique. Dans ce tutoriel, tu vas apprendre à créer ce type de mécanismes toi-même avec Lua dans Roblox Studio. Tu vas découvrir comment contrôler le comportement d’un joueur lorsqu’il touche une zone spéciale. Tu vas progresser étape par étape pour comprendre comment la physique du jeu peut être modifiée. À la fin, tu seras capable de concevoir tes propres zones de glisse comme dans les vrais jeux d’obby.
Ce tutoriel est organisé en trois parties :
La première te montrera comment créer une plaque glissante simple pour faire “patiner” un joueur.
La deuxième expliquera comment gérer une plaque en pente pour influencer la direction du déplacement.
Enfin, la troisième partie te permettra de combiner plusieurs plaques glissantes pour créer un parcours complet et dynamique.
Plaque glissante
Crée un part puis dessous un script :
Ce script transforme une Part en surface glissante grâce à des propriétés physiques qui réduisent les frottements et lui donnent l’apparence de la glace. Lorsqu’un joueur touche cette Part, le script vérifie qu’il s’agit bien d’un personnage et récupère son HumanoidRootPart, qui représente le centre de son déplacement. Il calcule ensuite la direction dans laquelle regarde le joueur à l’aide du LookVector. La Part reçoit alors une vitesse SPEED qui la fait glisser dans la direction choisie pendant une durée définie SLIDER_LENGTH. Enfin, après ce délai, la vitesse est remise à zéro pour arrêter la glissade.
-- Script pour une part qui glisse sur un joueur lorsqu'il la touche
local part = script.Parent
-- Appliquer des propriétés physiques pour que la part soit glissante
part.CustomPhysicalProperties = PhysicalProperties.new(
0.7, -- Density
0, -- Friction
0, -- Elasticity
0, -- FrictionWeight
1 -- ElasticityWeight
)
-- Appliquer un matériau glissant comme de la glace
part.Material = Enum.Material.Ice
-- Vitesse de déplacement de la glissade
local SPEED = 15
local SLIDER_LENGTH = 1
-- Fonction appelée lorsque la part est touchée
part.Touched:Connect(function(hit)
-- Vérifier si l'objet touché est un personnage
local character = hit:FindFirstAncestorOfClass("Model")
if not character then return end
-- Récupérer le joueur et sa racine
local rootPart = character:FindFirstChild("HumanoidRootPart")
if not rootPart then return end
-- Récupérer la direction de la caméra du joueur
local dir = rootPart.CFrame.LookVector
-- Appliquer une impulsion sur la part pour faire glisser le joueur dans la direction de la caméra
part.AssemblyLinearVelocity = dir * SPEED
-- Arrêter la glissade après un délai
task.spawn(function()
task.wait(SLIDER_LENGTH)
part.AssemblyLinearVelocity = Vector3.zero
end)
end)
Pente glissante
Modifie le code pour permettre à la plaque glissante de tenir compte de son inclinaison.
Une nouvelle fonction, getSurfaceInclination, calcule la direction naturelle de la pente à partir de la gravité et de la normale de la surface. La gravité indique toujours le bas, tandis que la normale renseigne sur l’orientation de la plaque. En combinant ces deux informations, le script détermine la direction dans laquelle un objet glisserait naturellement sur la pente.
Lorsque le joueur touche la plaque, le script calcule cette direction de glissade et vérifie si elle est suffisamment importante. Si la plaque est inclinée, la vitesse est appliquée dans le sens de la pente afin de simuler une descente naturelle. Si la plaque est horizontale, il n’y a pas de direction de pente significative : le joueur glisse alors dans la direction vers laquelle il regarde. Grâce à cet ajout, le même script peut gérer aussi bien une plaque plate qu’une plaque inclinée, ce qui rend le comportement plus réaliste et permet de créer des parcours plus variés.
-- Script pour une part qui glisse sur un joueur lorsqu'il la touche
local part = script.Parent
-- Appliquer des propriétés physiques pour que la part soit glissante
part.CustomPhysicalProperties = PhysicalProperties.new(
0.7, -- Density
0, -- Friction
0, -- Elasticity
0, -- FrictionWeight
1 -- ElasticityWeight
)
-- Appliquer un matériau glissant comme de la glace
part.Material = Enum.Material.Ice
-- Vitesse de déplacement de la glissade
local SPEED = 15
local SLIDER_LENGTH = 1
local function getSurfaceInclination(hit)
-- Récupérer la direction de la gravité
local gravity = Vector3.new(0, -1, 0)
-- Récupérer la normale de la surface de la part
-- la normale est un vecteur unitaire perpendiculaire à la surface
-- qui indique “dans quel sens la surface est orientée"
local normal = part.CFrame.UpVector
-- enlève la composante perpendiculaire à la surface
local slopeDirection = gravity - normal * gravity:Dot(normal)
return slopeDirection.Unit
end
-- Fonction appelée lorsque la part est touchée
part.Touched:Connect(function(hit)
-- Vérifier si l'objet touché est un personnage
local character = hit:FindFirstAncestorOfClass("Model")
if not character then return end
-- Récupérer le joueur et sa racine
local rootPart = character:FindFirstChild("HumanoidRootPart")
if not rootPart then return end
-- Récupérer la direction de la caméra du joueur
local dir = rootPart.CFrame.LookVector
-- Récuoération du sens de la pente de la part par rapport à la gravité
local slopeDirection = getSurfaceInclination(part)
-- Glissade en fonction si pente de la part ou part horizontale
if slopeDirection.Magnitude > 0.1 then
-- Appliquer une impulsion sur la part pour faire glisser le joueur dans la direction de la pente
part.AssemblyLinearVelocity = slopeDirection * SPEED
else
-- Appliquer une impulsion sur la part pour faire glisser le joueur dans la direction de la caméra
part.AssemblyLinearVelocity = dir * SPEED
-- Arrêter la glissade après un délai
task.spawn(function()
task.wait(SLIDER_LENGTH)
part.AssemblyLinearVelocity = Vector3.zero
end)
end
end)
Systèmes de plaques et pentes glissantes
Dans cette nouvelle version, le script ne gère plus une seule plaque glissante mais un ensemble de plaques regroupées dans un dossier (Folder). Au démarrage, le script récupère tous les objets contenus dans ce dossier puis les parcourt un par un grâce à une boucle.
local folder = script.Parent
local parts = folder:GetChildren()for _, part in pairs(parts) do
end
Une vérification est effectuée pour s’assurer que chaque objet est bien une BasePart afin d’éviter les erreurs avec d’autres types d’objets.
if not part:IsA("BasePart") then continue end
Pour chaque plaque trouvée, les propriétés physiques et le matériau de glace sont appliqués automatiquement. Cela évite de dupliquer le même script sur chaque plaque du parcours. La fonction de calcul de la pente est ensuite associée à chaque plaque afin que son inclinaison soit prise en compte individuellement. Enfin, un événement Touched est créé pour chacune d’elles : lorsqu’un joueur touche une plaque, le script utilise les caractéristiques propres à cette plaque (orientation, pente, vitesse de glisse) pour déterminer son déplacement.
Grâce à cette modification, il devient très simple de créer un parcours complet composé de nombreuses surfaces glissantes. Il suffit d’ajouter ou de supprimer des plaques dans le dossier pour que le script les prenne automatiquement en charge, sans aucune modification supplémentaire du code.
-- Script pour une part qui glisse sur un joueur lorsqu'il la touche
local folder = script.Parent
local parts = folder:GetChildren()for _, part in pairs(parts) doif not part:IsA("BasePart") then continue end
-- Appliquer des propriétés physiques pour que la part soit glissante
part.CustomPhysicalProperties = PhysicalProperties.new(
0.7, -- Density
0, -- Friction
0, -- Elasticity
0, -- FrictionWeight
1 -- ElasticityWeight
)
-- Appliquer un matériau glissant comme de la glace
part.Material = Enum.Material.Ice
-- Vitesse de déplacement de la glissade
local SPEED = 15
local SLIDER_LENGTH = 3
local function getSurfaceInclination(hit)
-- Récupérer la direction de la gravité
local gravity = Vector3.new(0, -1, 0)
-- Récupérer la normale de la surface de la part
-- la normale est un vecteur unitaire perpendiculaire à la surface
-- qui indique “dans quel sens la surface est orientée"
local normal = part.CFrame.UpVector
-- enlève la composante perpendiculaire à la surface
local slopeDirection = gravity - normal * gravity:Dot(normal)
return slopeDirection.Unit
end
-- Fonction appelée lorsque la part est touchée
part.Touched:Connect(function(hit)
-- Vérifier si l'objet touché est un personnage
local character = hit:FindFirstAncestorOfClass("Model")
if not character then return end
-- Récupérer le joueur et sa racine
local rootPart = character:FindFirstChild("HumanoidRootPart")
if not rootPart then return end
-- Récupérer la direction de la caméra du joueur
local dir = rootPart.CFrame.LookVector
-- Récuoération du sens de la pente de la part par rapport à la gravité
local slopeDirection = getSurfaceInclination(part)
-- Glissade en fonction si pente de la part ou part horizontale
if slopeDirection.Magnitude > 0.1 then
-- Appliquer une impulsion sur la part pour faire glisser le joueur dans la direction de la pente
part.AssemblyLinearVelocity = slopeDirection * SPEED
else
-- Appliquer une impulsion sur la part pour faire glisser le joueur dans la direction de la caméra
part.AssemblyLinearVelocity = dir * SPEED
-- Arrêter la glissade après un délai
task.spawn(function()
task.wait(SLIDER_LENGTH)
part.AssemblyLinearVelocity = Vector3.zero
end)
end
end)
end
Dans ce tutoriel, tu vas créer une plaque spéciale qui modifie la gravité lorsque le joueur marche dessus.
Grâce à cette plaque, les personnages pourront :
sauter beaucoup plus haut ;
retomber plus lentement ;
atteindre des plateformes normalement inaccessibles.
Ce type de mécanisme est souvent utilisé dans les jeux de science-fiction, les parcours d’obstacles (Obby) ou les bases spatiales.
Comprendre la gravité
La gravité est la force qui attire les objets vers le sol.
Sur Terre, lorsque tu sautes :
tu montes ;
tu ralentis ;
tu redescends.
Dans Roblox, la gravité agit exactement de la même manière.
Si on la diminue :
les sauts deviennent plus grands ;
les chutes sont plus lentes ;
les déplacements semblent plus légers.
Solution 1 : Modifier la gravité pour tout le monde
Dans cette version, lorsqu’un joueur active la plaque, la gravité du jeu entier est modifiée.
Conséquences
tous les joueurs sont affectés ;
tous les sauts deviennent plus grands ;
tous les objets physiques réagissent également à la nouvelle gravité.
Cette solution est simple à mettre en place mais peut rendre le jeu imprévisible si plusieurs joueurs jouent en même temps.
Exemple de situation
Imaginons :
Paul marche sur la plaque ;
la gravité est divisée par deux.
Même les autres joueurs situés à l’autre bout de la carte profiteront immédiatement de cet effet.
Créer une plaque de non gravité
Commence par construire une Part qui servira de plaque spéciale.
Tu peux lui donner :
une couleur vive ;
un matériau futuriste ;
un effet lumineux.
Les joueurs comprendront ainsi qu’il s’agit d’une zone particulière du parcours.
Détecter l’entrée du joueur
Lorsque le personnage marche sur la plaque, le jeu doit détecter le contact.
Cette détection permet de savoir :
quel joueur est entré dans la zone ;
quand appliquer l’effet de faible gravité.
C’est le même principe que pour une zone de téléportation ou un checkpoint.
Puis un script :
-- référence au part pour le changement de gravité
local part = script.Parent
-- détection du joueur qui touche la part
part.Touched:Connect(function(hit)
local character = hit.Parent
local humanoid = character and character:FindFirstChild("Humanoid")
if not humanoid then return end
-- changement de gravité à 10 au moment de la collision
workspace.Gravity = 10
end)
Créer une plaque pour retrouver la gravité
Commence par construire une Part qui servira de plaque spéciale .
Puis un script :
-- référence de la part pour retourner la gravité à 196.2
local part = script.Parent
-- détection du joueur qui touche la part
part.Touched:Connect(function(hit)
local character = hit.Parent
local humanoid = character and character:FindFirstChild("Humanoid")
if not humanoid then return end
-- retour de la gravité à 196.2 au moment de la collision
workspace.Gravity = 196.2
end)
Solution 2 : Modifier la gravité pour un seul joueur
Dans cette version, seul le joueur qui touche la plaque bénéficie de l’effet.
Les autres joueurs continuent à jouer normalement.
Cette méthode est souvent préférable dans un jeu multijoueur car chacun peut vivre sa propre expérience.
Comment simuler une faible gravité pour un joueur ?
Comme la gravité du monde est partagée par tous, on ne peut pas simplement changer la gravité globale pour un seul joueur.
On utilise donc une autre technique :
augmenter sa puissance de saut ;
ralentir sa descente ;
modifier certaines propriétés de déplacement.
Le résultat ressemble à une gravité plus faible sans affecter les autres joueurs.
Script à modifier pour la plaque de changement de gravité pour un seul joueur :
-- référence au part pour le changement de gravité
local part = script.Parent
-- détection du joueur qui touche la part
part.Touched:Connect(function(hit)
local character = hit.Parent
local hrp = character and character:WaitForChild("HumanoidRootPart")
if not hrp then return end
if hrp:FindFirstChild("GravityAttachment") then return end -- évite les doublons
-- 1. Créer une pièce jointe (Attachment) requise pour VectorForce
local attachment = Instance.new("Attachment")
attachment.Name = "GravityAttachment"
attachment.Parent = hrp
-- 2. Créer la force physique (VectorForce)
local vectorForce = Instance.new("VectorForce")
vectorForce.Name = "CustomGravityForce"
vectorForce.Attachment0 = attachment
vectorForce.ApplyAtCenterOfMass = true
vectorForce.RelativeTo = Enum.ActuatorRelativeTo.World
-- 3. Calculer la masse totale du personnage
local totalMass = 0
for _, part in ipairs(character:GetDescendants()) do
if part:IsA("BasePart") then
totalMass = totalMass + part:GetMass()
end
end
-- 4. Appliquer une force vers le haut pour réduire la gravité ressentie
-- Exemple : contrer 60% de la gravité (le joueur se sentira plus léger)
local gravityCounterFactor = 0.6
local upwardForce = totalMass * workspace.Gravity * gravityCounterFactor
vectorForce.Force = Vector3.new(0, upwardForce, 0)
vectorForce.Parent = hrp
end)
Script à modifier pour retrouver la gravité normal pour un seul joueur :
-- référence de la part pour retourner la gravité à 196.2
local part = script.Parent
-- détection du joueur qui touche la part
part.Touched:Connect(function(hit)
local character = hit.Parent
local hrp = character and character:FindFirstChild("HumanoidRootPart")
if not hrp then return end
-- Trouver et supprimer l'Attachment
local attachment = hrp:FindFirstChild("GravityAttachment")
if attachment then
attachment:Destroy()
end
-- Trouver et supprimer le VectorForce
local vectorForce = hrp:FindFirstChild("CustomGravityForce")
if vectorForce then
vectorForce:Destroy()
end
end)
Dans un couloir géant, des portes colorées s’ouvrent ou se ferment en fonction d’une condition secrète à définir au lancement de la partie (nombre pair, tous les x, couleur, niveau de transparence, type de matériaux de la porte …). La mauvaise porte téléporte en début de partie.
Règles du jeu
2 à 6 joueurs simultanés sur le même serveur
Chaque manche dure 90 secondes — le premier à passer 5 portes gagne
Une mauvaise porte = retour en début de partie
Si possible les indices changent aléatoirement à chaque manche et l’accès aux bonnes portes
Un tableau de score (Leaderboard) est visible de tous et montre le nombre de bonnes portes ouvertes et le nombre de parties gagnées
Techniques Lua abordées
Variables locales/globales : Stocker la couleur de la porte et le score du joueur
Instruction if/elseif/else : Tester la condition de la bonne porte
Gérer dynamiquement et aléatoirement les propriétés des Parts
RemoteEvents : Communiquer entre le serveur et les clients
Leaderboard (Leaderstats) : Afficher les scores de tous les joueurs
ClickDetector pour l’ouverture des portes
Gérer les ressources du workspace (script.Parent, :GetChildren(), if part:IsA(« BasePart »)
Boucle
for _, enfant in ipairs(dossier:GetChildren()) do
print(enfant.Name)
end
Compétences acquises
Déclarer et utiliser des variables
Écrire des conditions simples et imbriquées
Comprendre la différence Server/Client
Créer un système de score multijoueur
QCM de validation — 4 questions
1. En Lua, quelle instruction permet de tester une condition ?
A. check
B. if
C. test
D. when
2. Comment déclare-t-on une variable locale en Lua ?
A. var score = 0
B. let score = 0
C. local score = 0
D. int score = 0
3. Quel objet Roblox permet d’afficher le score de tous les joueurs ?
A. ScoreBoard
B. Leaderstats
C. PlayerGUI
D. DataStore
4. Que se passe-t-il si la condition d’un if est fausse et qu’il y a un else ?
A. Le script s’arrête
B. La condition est retestée
C. Le bloc else est exécuté
D. Une erreur apparaît
Défi 02 : Le Labyrinthe Fantôme
Des murs du labyrinthe, ou un chemin de plateformes apparaissent et disparaissent par cycles réguliers, comme des fantômes. Les joueurs doivent mémoriser et anticiper. Un joueur « Fantôme » (désigné aléatoirement) voit tous les murs en transparence et peut guider les autres via un chat.
Règles du jeu
1 Fantôme (voit tout) + 1 à 5 Chasseurs (voient un brouillard)
Les murs disparaissent ou réapparaissent toutes les 3 secondes
Le Fantôme doit guider les Chasseurs jusqu’à la sortie en 2 minutes
Si un Chasseur touche un mur plein, il revient au départ
Bonus si toute l’équipe sort ensemble
Techniques Lua abordées
Boucle while true do : Faire clignoter les murs indéfiniment
task.wait(n) : Contrôler la durée entre chaque clignotement
Propriété Transparency : Rendre les murs visibles/invisibles
CanCollide : Activer/désactiver la collision des murs
Lire tous les joueurs local Players = game:GetService(« Players ») Players.PlayerAdded:Connect(function(player)
Compétences acquises
Comprendre le fonctionnement client/serveur
Gérer les RemoteEvent
Créer des boucles infinies contrôlées
Manipuler les propriétés des Parts Roblox
Concevoir un mécanisme de jeu temporisé
Comprendre CanCollide et Transparency
QCM de validation — 4 questions
1. Quelle boucle Lua tourne indéfiniment jusqu’à ce qu’on l’arrête ?
A. for i=1,10
B. repeat until
C. while true do
D. loop forever
2. Que fait wait(3) dans un script Roblox ?
A. Attend l’appui d’une touche
B. Pause l’exécution 3 secondes
C. Répète une action 3 fois
D. Crée un délai de 0,3 s
3. Quelle propriété rend un objet traversable sans le rendre invisible ?
A. Transparency = 1
B. CanCollide = false
C. Anchored = true
D. Locked = true
4. Comment accède-t-on à la propriété d’un objet Roblox en Lua ?
A. objet->propriete
B. objet.propriete
C. objet[propriete]
D. get(objet, propriete)
Défi 03 : Roi de la Plateforme
Une grande plateforme centrale se rétrécit progressivement grâce au TweenService. Des blocs se déplacent ou tournent sur cette plateforme et poussent les joueurs. Ceux qui tombent sont éliminés. Le dernier joueur debout devient le Roi et gagne des points. Les joueurs éliminés doivent attendre en spectateur, qu’il ne reste plus qu’un seul joueur présent sur la plateforme, pour recommencer une nouvelle partie.
La difficulté augmente : les blocs se déplacent de plus en plus vite.
Règles du jeu
2 à 8 joueurs par partie
La plateforme perd 10% de sa taille toutes les 15 secondes
Un joueur éliminé peut regarder la partie en spectateur
Victoire = survivre jusqu’à la fin et être le dernier avec le plus de points
3 manches par session, le total des Points compte pour la victoire finale
Techniques Lua abordées
Fonctions nommées : Organiser le code en blocs réutilisables
TweenService : Animer la réduction de la plateforme
Touched event : Détecter quand un joueur tombe dans le vide
Boucle for numérique : Compter les manches et les cycles de rétrécissement
Compétences acquises
Déclarer et appeler des fonctions
Utiliser TweenService pour des animations
Connecter des événements (Touched)
Structurer un jeu en manches avec boucle for
QCM de validation — 4 questions
1. Comment appelle-t-on une fonction nommée ‘demarrer’ en Lua ?
A. call demarrer()
B. run:demarrer()
C. demarrer()
D. execute(demarrer)
2. Quel service Roblox permet de créer des animations fluides ?
A. AnimationService
B. TweenService
C. SmoothService
D. TransitionService
3. Comment s’écrit une boucle for qui compte de 1 à 5 en Lua ?
A. for(i=0; i<5; i++)
B. for i in range(5)
C. for i = 1, 5 do
D. repeat 5 times
4. Quel événement détecte qu’un objet touche une Part Roblox ?
A. .OnTouch
B. .Touched:Connect
C. .Hit:Bind
D. Contact:Link
Défi 04 : La Bombe Logique
Une bombe virtuelle affiche un chrono avant son explosion. Elle raccordée par une séquence de fils de couleurs tirée au hasard. Les joueurs doivent couper les fils dans la bonne séquence. Chaque joueur reçoit une partie du code sur son écran (GUI), et doit communiquer avec les autres pour coordonner la séquence exacte de câbles à couper avant que le chrono n’atteigne zéro.
Règles du jeu
3 à 5 joueurs requis (chaque joueur voit 1/3 du code)
La bombe explose si le mauvais câble est coupé
30 secondes maximum pour désamorcer
Code différent à chaque partie (aléatoire)
Techniques Lua abordées
Tables (tableaux) : Stocker la séquence de couleurs de la bombe
math.random() : Générer la séquence aléatoire à chaque partie
ScreenGui / Frame : Afficher les indices à chaque joueur
ipairs / pairs : Parcourir la table des couleurs
Compétences acquises
Créer et manipuler des tables Lua
Utiliser math.random pour l’aléatoire
Créer une interface joueur (GUI)
Itérer avec ipairs sur une liste
QCM de validation — 4 questions
1. Comment crée-t-on une table vide en Lua ?
A. table = []
B. table = new Table()
C. table = {}
D. table = list()
2. Quelle fonction ajoute un élément à la fin d’une table ?
A. table.push()
B. table.add()
C. table.append()
D. table.insert()
3. Que retourne math.random(1, 6) ?
A. Toujours 1
B. Un nombre décimal entre 1 et 6
C. Un entier aléatoire entre 1 et 6
D. La somme 1+6
4. Quelle boucle est faite pour parcourir une table indexée en Lua ?
A. while
B. for k,v in pairs
C. for i,v in ipairs
D. foreach
Défi 05 : Ascenseur Infernal
Les joueurs fabriquent, dans un espace dédié, un ascenseur fonctionnel qui monte et descend entre différents étages. Chaque étage contient des obstacles différents. Le joueur programme lui-même la vitesse, les arrêts et les comportements des obstacles à chaque palier et un objet à trouver. Un mini concours final compare les ascenseurs de tous les participants.
Règles du jeu
Travail individuel avec présentation finale en groupe
L’ascenseur doit desservir au minimum 3 étages
Chaque étage doit avoir un obstacle scripté différent
Le joueur doit pouvoir appuyer sur un bouton pour appeler l’ascenseur
Bonus : ajouter une porte qui s’ouvre à l’arrivée
Compétences acquises
Manipuler CFrame et Vector3 pour le déplacement
Utiliser les Welds pour solidariser des objets
Créer des interactions ProximityPrompt
Organiser des données dans une table d’étages
QCM de validation — 4 questions
1. Qu’est-ce que CFrame représente dans Roblox ?
A. La couleur d’un objet
B. La position ET l’orientation d’un objet
C. La taille d’une Part
D. Le nom d’un script
2. À quoi sert un WeldConstraint entre deux Parts ?
A. Les rendre invisibles
B. Les coller ensemble pour qu’elles bougent solidairement
C. Les faire tomber en même temps
D. Les fusionner en une seule Part
3. Comment crée-t-on un point 3D à X=5, Y=10, Z=0 en Lua Roblox ?
A. Point(5,10,0)
B. Vector3.new(5,10,0)
C. CFrame.new(5,10,0)
D. Pos3D(5,10,0)
4. Quel objet Roblox crée une interaction ‘Appuyer sur E’ dans le monde ?
A. ButtonPart
B. ClickDetector
C. ProximityPrompt
D. TouchButton
Défi 06 : Sauve-qui-peut !
Un joueur est désigné « Monstre » avec une vitesse boostée. Les autres doivent traverser un OBBY classique tout en étant poursuivis. Des zones de ralentissement, de vitesse boost, et de gravité réduite sont scriptées dans l’arène. Si le Monstre te touche, tu deviens Monstre à ton tour ! Le jeu s’arrête lorsque les joueurs sont tous des monstres ou au bout d’un certain temps.
Règles du jeu
Monstre pour commencer, désigné aléatoirement
Les survivants gagnent 1 point par checkpoint franchi
Un survivant tagué devient immédiatement Monstre
Le dernier survivant gagne un bonus x3
Partie de 3 minutes max
Techniques Lua abordées
Humanoid.WalkSpeed : Modifier la vitesse du Monstre et des joueurs
Humanoid.JumpPower : Ajuster la hauteur de saut dans les zones spéciales
workspace.Gravity : Créer des zones de gravité réduite
GetPlayers() : Récupérer tous les joueurs pour désigner le Monstre
Compétences acquises
Modifier les propriétés du Humanoid
Sélectionner un joueur aléatoire depuis GetPlayers()
Créer des zones d’effet avec Touched
Comprendre la physique Roblox (vitesse, gravité)
QCM de validation — 4 questions
1. Quelle propriété du Humanoid contrôle la vitesse de marche ?
A. Humanoid.Speed
B. Humanoid.WalkSpeed
C. Character.Velocity
D. Player.RunSpeed
2. Que retourne Players:GetPlayers() ?
A. Le nombre de joueurs
B. Le premier joueur connecté
C. Une table de tous les joueurs connectés
D. Le joueur local
3. Comment accède-t-on au personnage d’un joueur ?
A. player.Model
B. player.Character
C. player.Avatar
D. player.Body
4. Qu’est-ce que workspace.Gravity contrôle ?
A. La couleur du ciel
B. La force de gravité sur tous les objets
C. La vitesse du vent
D. La taille des joueurs
Défi 07 : Le Voleur de Flag
Deux équipes (Rouge et Bleue) s’affrontent dans un OBBY en miroir. Chaque équipe doit traverser l’OBBY adverse, voler le flag ennemi et le ramener à sa base. Des pièges scriptés bloquent les chemins selon l’équipe du joueur. Le flag est un objet physique que le personnage « porte » grâce à un Weld. Le jeu se termine quand une équipe ramène le flag à un endroit déterminé.
Règles du jeu
2 équipes de 2 à 4 joueurs chacune
Pour marquer, le joueur doit ramener le flag sans mourir
Les pièges changent d’état toutes les 10 secondes
Techniques Lua abordées
Teams Service :; Créer et assigner des équipes Rouge/Bleue
ObjectValue / BoolValue : Savoir qui porte le flag (valeur partagée)
RemoteEvent (FireAllClients) : Annoncer une capture à tous les joueurs
Weld dynamique : Attacher le flag au personnage qui le ramasse
Compétences acquises
Configurer le système Teams de Roblox
Partager des données entre Server et Clients
Créer des Welds dynamiques par script
Utiliser FireAllClients pour des annonces globales
QCM de validation — 4 questions
1. Comment vérifier l’équipe d’un joueur en Lua Roblox ?
A. player.Color
B. player.Group.Name
C. player.Team.Name
D. player.Side
2. Quelle valeur Roblox permet de stocker une référence à un objet ?
A. StringValue
B. ObjectValue
C. NumberValue
D. ReferenceValue
3. Que fait RemoteEvent:FireAllClients() ?
A. Envoie un signal au serveur
B. Envoie un signal à un seul joueur
C. Envoie un signal à tous les joueurs connectés
D. Crée un nouvel événement
4. Comment créer un nouvel objet Roblox par script ?
A. new Instance(‘WeldConstraint’)
B. Instance.new(‘WeldConstraint’)
C. create(‘WeldConstraint’)
D. Roblox.new(‘WeldConstraint’)
Défi 08 : Puzzle Coopératif
Un OBBY avec des portes géantes qui ne s’ouvrent que si PLUSIEURS joueurs appuient simultanément sur des boutons répartis dans des zones différentes. Le niveau est impossible à finir seul. Des scripts de détection comptent les joueurs sur chaque bouton et déclenchent l’ouverture seulement quand le bon nombre est atteint.
Règles du jeu
3 à 5 joueurs requis (portes à 2, 3 joueurs simultanés)
Les boutons restent actifs 5 secondes maximum
Si un joueur lâche son bouton, tout le monde doit recommencer
Des checkpoints communs sauvegardent la progression collective
Fin = tous les joueurs arrivent ensemble à la salle finale
Techniques Lua abordées
BindableEvent : Communiquer entre scripts locaux du serveur
Compteur de joueurs : Détecter combien de joueurs sont sur un bouton
Fonctions modulaires : Organiser le code en modules réutilisables
BoolValue partagée : Synchroniser l’état ouvert/fermé des portes
Compétences acquises
Utiliser des compteurs et variables d’état
Coordonner plusieurs scripts avec BindableEvent
Concevoir une logique coopérative
Structurer du code modulaire et réutilisable
QCM de validation — 4 questions
1. Quelle est la différence entre BindableEvent et RemoteEvent ?
A. Aucune différence
B. BindableEvent = serveur→serveur, RemoteEvent = serveur↔client
C. BindableEvent = client→client uniquement
D. RemoteEvent ne fonctionne qu’en studio
2. Comment incrémenter un compteur de 1 en Lua ?
A. compteur++
B. compteur += 1
C. compteur = compteur + 1
D. increment(compteur)
3. Que retourne hit.Parent:FindFirstChild(‘Humanoid’) si absent ?
A. Une erreur
B. false
C. nil
D. 0
4. Pour qu’une porte détecte l’événement d’un autre script, on utilise ?
A. .OnServerEvent
B. .Event:Connect
C. .Listen()
D. .Subscribe()
Défi : 09 Arène des pièges
Chaque joueur crée un piège dans l’arène (lame rotative, boule de feu, zone électrique) puis tous jouent ensemble sur la carte collective. Le créateur du piège qui élimine le plus d’adversaires gagne. Les pièges doivent utiliser des patterns de mouvement différents (oscillation, rotation, trajectoire). Chaque joueur crée une zone bonus protégée par les pièges pour permettre la sortie de l’arène.
Règles du jeu
Phase 1 (45 min) : chaque joueur code son piège dans sa zone
Phase 2 : les pièges sont assemblés dans une arène commune
Chaque élimination rapporte 2 points au créateur du piège
Atteindre une une zone de bonus 10 points
Obtenir plus de 30 points permet de sortir de l’arène
Techniques Lua abordées
TakeDamage() : Infliger des dégâts au Humanoid au contact
CFrame rotation : Faire tourner un piège sur lui-même
math.sin() oscillation : Créer un mouvement de va-et-vient fluide
Respawn personnalisé : Réapparaître à un spawn aléatoire
Compétences acquises
Infliger et gérer les points de vie (TakeDamage)
Animer avec RunService.Heartbeat
Créer des mouvements avec math.sin et CFrame
Concevoir et partager ses propres mécaniques
QCM de validation — 4 questions
1. Quelle méthode inflige des dégâts à un Humanoid ?
A. Humanoid.Health -= 10
B. Humanoid:Hurt(10)
C. Humanoid:TakeDamage(10)
D. Humanoid.DealDamage(10)
2. Que fait RunService.Heartbeat:Connect(function(dt) … end) ?
A. Lance le jeu au démarrage
B. Exécute la fonction à chaque frame
C. Attend 1 seconde puis exécute
D. Connecte deux serveurs
3. math.sin(t) retourne des valeurs entre :
A. 0 et 1
B. -1 et 1
C. -180 et 180
D. 0 et 360
4. CFrame.Angles(0, math.pi, 0) représente une rotation de :
A. 90° sur l’axe Y
B. 360° sur l’axe X
C. 180° sur l’axe Y
D. 45° sur tous les axe
Défi 10 : Le Grand OBBY Final
Projet final d’atelier : les joueurs assemblent tous les mécanismes appris dans un OBBY. Chaque stage reprend une technique (portes, labyrinthes, plateformes, pièges). Les progrès sont sauvegardés dans un DataStore pour qu’un joueur puisse reprendre où il s’est arrêté. Un leaderboard global classe tous les participants de la session.
Règles du jeu
OBBY solo mais visible par tous les autres joueurs en temps réel
Reprise de tous les 9 scénarios + un boss final
Chaque stage complété est sauvegardé en DataStore
Timer global affiché pour tout le serveur
Cérémonie de fin : podium animé avec les 3 meilleurs temps
Techniques Lua abordées
DataStoreService : Sauvegarder la progression du joueur entre sessions
Checkpoint system : Respawn au dernier stage sauvegardé
ModuleScript : Centraliser le code réutilisé dans des modules
Intégration complète : Combiner toutes les techniques précédentes
Compétences acquises
Utiliser DataStoreService pour la persistance
Architecturer un projet Roblox complet
Utiliser des ModuleScripts pour organiser le code
Combiner toutes les compétences Lua acquises
QCM de validation — 4 questions
1. À quoi sert le DataStoreService ?
A. Sauvegarder des données entre les sessions de jeu
B. Stocker les scripts du jeu
C. Gérer les animations des personnages
D. Envoyer des données au client
2. Quelle est la clé utilisée pour identifier un joueur de façon unique ?
A. player.Name
B. player.DisplayName
C. player.UserId
D. player.AccountId
3. Qu’est-ce qu’un ModuleScript dans Roblox ?
A. Un script qui se lance automatiquement
B. Un script qui retourne des fonctions réutilisables
Dans ce tutoriel, tu vas apprendre à créer une arme que le joueur pourra récupérer dans le monde du jeu puis utiliser pour combattre des ennemis.
L’objectif est de réaliser un système complet comprenant :
une arme posée au sol ;
le ramassage automatique par le joueur ;
l’utilisation de l’arme depuis l’inventaire ;
une animation de frappe ;
des effets visuels lors de l’attaque ;
la détection des ennemis touchés ;
l’application de dégâts.
À la fin du projet, ton personnage pourra se défendre contre des adversaires comme dans de nombreux jeux d’aventure ou de rôle.
Crée une arme avec l’objet Tool puis un part renommer « Handle » de roblox :
Choisis la matière et la couleur de ton arme :
Positionne l’arme dans la main du joueur :
Saisie ce code dans le script :
-- récupération de l'arme
local arm = script.Parent
-- récupération des services pour animer
local Debris = game:GetService("Debris")
local TweenService = game:GetService("TweenService")
local Debris = game:GetService("Debris")
--
-- CONFIGURATION DES ANIMATIONS
--
local CONFIG = {
-- Particules
nbEtincelles = 50,
-- Onde de choc
ondeActive = true,
ondeCouleur = Color3.fromRGB(255, 150, 0),
-- Dégâts (rayon)
rayonDegats = 40,
-- Flash
flashActif = true,
sizeFlash = Vector3.new(5, 5, 5),
colorFlash = Color3.fromRGB(255, 0, 0),
}
--
-- EFFET FLASH ET ONDE DE CHOC
--
local function creerFlash(position)
local flash = Instance.new("Part")
flash.Shape = Enum.PartType.Ball
flash.Size = Vector3.new(0.1, 0.1, 0.1)
flash.Position = position
flash.Anchored = true
flash.CanCollide = false
flash.CastShadow = false
flash.Material = Enum.Material.Neon
flash.Color = CONFIG.colorFlash
flash.Parent = workspace
local tween = TweenService:Create(flash,
TweenInfo.new(0.15, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
{ Size = CONFIG.sizeFlash, Transparency = 1 }
)
tween:Play()
Debris:AddItem(flash, 0.2)
end
local function creerOndeChoc(position)
local onde = Instance.new("Part")
onde.Shape = Enum.PartType.Ball
onde.Size = Vector3.new(1, 1, 1)
onde.Position = position
onde.Anchored = true
onde.CanCollide = false
onde.CastShadow = false
onde.Material = Enum.Material.Neon
onde.Color = CONFIG.ondeCouleur
onde.Transparency = 0.8
onde.Parent = workspace
local tween = TweenService:Create(onde,
TweenInfo.new(0.4, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
{ Size = Vector3.new(CONFIG.rayonDegats * 2, CONFIG.rayonDegats * 2, CONFIG.rayonDegats * 2), Transparency = 1 }
)
tween:Play()
Debris:AddItem(onde, 0.5)
end
--
-- EFFET ETINCELLES
--
local function creerEtincelles(position)
for i = 1, CONFIG.nbEtincelles do
local etincelle = Instance.new("Part")
etincelle.Shape = Enum.PartType.Ball
-- taille des etincelles
etincelle.Size = Vector3.new(0.2, 0.2, 0.2)
etincelle.Position = position
etincelle.CanCollide = false
etincelle.CastShadow = false
etincelle.Material = Enum.Material.Neon
-- Couleur aléatoire entre orange et jaune
etincelle.Color = Color3.fromRGB(255, math.random(100, 255), 0)
etincelle.Parent = workspace
-- Direction aléatoire
local direction = Vector3.new(
math.random(-100, 100) / 100,
math.random(20, 100) / 100,
math.random(-100, 100) / 100
).Unit
-- vitesse aléatoire de propagation des étincelles
local vitesse = math.random(20, 200)
local vb = Instance.new("BodyVelocity")
vb.Velocity = direction * vitesse
vb.MaxForce = Vector3.new(1e4, 1e4, 1e4)
vb.Parent = etincelle
-- Disparition progressive
local duree = math.random(5, 12) / 10
local tween = TweenService:Create(etincelle,
TweenInfo.new(duree, Enum.EasingStyle.Quad, Enum.EasingDirection.In),
{ Transparency = 1, Size = Vector3.new(0.05, 0.05, 0.05) }
)
tween:Play()
Debris:AddItem(etincelle, duree)
end
end
--
-- limite le nombre de dégâts pendant un certain temps
--
local function withCooldown(hit)
local tag = Instance.new("Folder")
tag.Name = "ArmTag"
tag.Parent = hit.Parent
Debris:AddItem(tag, 0.4)
task.wait(0.4)
end
-- si le joueur est en cooldown, ne pas appliquer les dégâts
local function isCooldown(hit)
return hit.Parent:FindFirstChild("ArmTag")
end
-- mouvement coup d'épée
local function swordStroke(arm)
local tween = TweenService:Create(arm.Parent,
TweenInfo.new(1, Enum.EasingStyle.Elastic, Enum.EasingDirection.Out),
{ Grip = arm.Parent.Grip * CFrame.Angles(0, 0, math.rad(90)) }
)
tween:Play()
end
-- l'arme touche un humanoid
arm.Touched:Connect(function(hit)
local humanoid = hit and hit.Parent:FindFirstChild("Humanoid")
local player = game.Players:GetPlayerFromCharacter(hit.Parent)
if player then return end
if not humanoid then return end
if isCooldown(hit) then return end
-- coup d'épée
swordStroke(arm)
-- dégâts
humanoid:TakeDamage(10)
-- effets
if CONFIG.flashActif then creerFlash(hit.Position) end
if CONFIG.ondeActive then creerOndeChoc(hit.Position) end
creerEtincelles(hit.Position)
-- cooldown pendant un certain temps
withCooldown(hit)
end)
Rajoute un son laser
Insère un son dans l’arborescence du tool :
Rajoute dans ton script la référence du son :
local soundLaser = arm.Laser
Puis ajoute dans ton script la fonction pour jouer le son :
local function playSound(sound)
if not sound then return end
pcall(function() sound:Play() end)
end
Puis modifie la fonction arm.Touched pour jouer le son :
-- l'arme touche un humanoid
arm.Touched:Connect(function(hit)
local humanoid = hit and hit.Parent:FindFirstChild("Humanoid")
local player = game.Players:GetPlayerFromCharacter(hit.Parent)
if player then return end
if not humanoid then return end
if isCooldown(hit) then return end
-- coup d'épée
playSound(soundLaser)
swordStroke(arm)
-- dégâts
humanoid:TakeDamage(10)
-- effets
if CONFIG.flashActif then creerFlash(hit.Position) end
if CONFIG.ondeActive then creerOndeChoc(hit.Position) end
creerEtincelles(hit.Position)
-- cooldown pendant un certain temps
withCooldown(hit)
end)
Tu es le maître d‘un jeu d’un parcours d’obstacles (un Obby). Ton rôle est de surveiller les joueurs, de noter leur progression, et de les renvoyer au bon endroit s’ils tombent dans le vide.
Crée dans ton jeu des CheckPoints sur ton parcours, les checkpoints seront visibles ou non, puis programme un script qui reconnaitre tous les checkpoints et notera pour chaque joueur sa progression. En cas de respawn le joueur reviendra sur le dernier checkpoint validé.
Au tout début du script, on trouve une boîte appelée CONFIG. C’est le « panneau de contrôle » du jeu. C’est ici que tu peux changer facilement les règles sans toucher au reste du code.
Quand un joueur rejoint le jeu (PlayerAdded) :
On initialise ses données (il commence au Checkpoint 1).
On écoute quand son personnage apparaît (CharacterAdded) pour le téléporter au bon checkpoint (au cas où il meurt et respawn).
Quand un joueur quitte le jeu (PlayerRemoving) :
On supprime ses données pour libérer de la mémoire.
Au démarrage du script :
On initialise tous les joueurs déjà présents.
On connecte tous les checkpoints.
Tu places 5 checkpoints dans un dossier appelé « Checkpoints » dans Workspace :
Saisie ce script CheckpointsService sous ServerScriptService :
-- Script : GestionCheckpoints
local Players = game:GetService("Players")
local DialogueModule = require(game.ReplicatedStorage.DialogueModule)
-- ═══════════════════════════════════════
-- CONFIGURATION
-- ═══════════════════════════════════════
local CONFIG = {
dossierCheckpoints = "Checkpoints", -- nom du dossier dans Workspace
offsetRespawn = Vector3.new(0, 3, 0), -- hauteur au-dessus du checkpoint
afficherMessages = true,
sauvegarderOrdre = true, -- respecte l'ordre numérique des checkpoints
checkpointValidated = BrickColor.Green(),
checkpointInitial = BrickColor.DarkGray()
}
-- ═══════════════════════════════════════
-- RÉCUPÉRATION DES CHECKPOINTS
-- ═══════════════════════════════════════
local dossier = workspace:WaitForChild(CONFIG.dossierCheckpoints)
local function getCheckpointsTries()
local liste = {}
for _, cp in ipairs(dossier:GetChildren()) do
if cp:IsA("BasePart") or cp:IsA("Model") then
table.insert(liste, cp)
end
end
-- Trier par nom numérique (Checkpoint1, Checkpoint2...)
table.sort(liste, function(a, b)
local numA = tonumber(a.Name:match("%d+")) or 0
local numB = tonumber(b.Name:match("%d+")) or 0
return numA < numB
end)
return liste
end
local checkpoints = getCheckpointsTries()
-- ═══════════════════════════════════════
-- DONNÉES PAR JOUEUR
-- ═══════════════════════════════════════
local progressionJoueurs = {}
-- { [userId] = { indexActuel = 1, checkpointPart = part } }
local function getPositionCheckpoint(cp)
if cp:IsA("Model") then
local primary = cp.PrimaryPart or cp:FindFirstChildOfClass("BasePart")
return primary and primary.Position or Vector3.new(0, 0, 0)
end
return cp.Position
end
-- ═══════════════════════════════════════
-- RESPAWN AU DERNIER CHECKPOINT
-- ═══════════════════════════════════════
local function respawnAuCheckpoint(player)
local data = progressionJoueurs[player.UserId]
if not data then return end
local character = player.Character
if not character then return end
local rootPart = character:FindFirstChild("HumanoidRootPart")
if not rootPart then return end
local position = getPositionCheckpoint(data.checkpointPart)
rootPart.CFrame = CFrame.new(position + CONFIG.offsetRespawn)
end
-- ═══════════════════════════════════════
-- VALIDATION D'UN CHECKPOINT
-- ═══════════════════════════════════════
local function validerCheckpoint(player, indexCP)
local data = progressionJoueurs[player.UserId]
if not data then return false end
-- Vérifier l'ordre si activé
if CONFIG.sauvegarderOrdre and indexCP ~= data.indexActuel + 1 then
return false -- ignore si ce n'est pas le suivant
end
if indexCP > #checkpoints or indexCP <= data.indexActuel then return false end
data.indexActuel = indexCP
data.checkpointPart = checkpoints[indexCP]
if CONFIG.afficherMessages then
local character = player.Character
DialogueModule.creerDialogue(character, "🏁 " .. player.Name .. " a atteint le checkpoint " .. indexCP .. "/" .. #checkpoints)
end
-- Dernier checkpoint = fin du parcours
if indexCP == #checkpoints then
if CONFIG.afficherMessages then
local character = player.Character
DialogueModule.creerDialogue(character, "🎉 " .. player.Name .. " a terminé le parcours !")
end
-- Ajoute ici ta logique de fin (récompense, téléportation...)
end
return true
end
-- ═══════════════════════════════════════
-- CONNEXION DES TOUCHES DE CHAQUE CHECKPOINT
-- ═══════════════════════════════════════
local function connecterCheckpoints()
for index, cp in ipairs(checkpoints) do
local part = cp:IsA("Model") and (cp.PrimaryPart or cp:FindFirstChildOfClass("BasePart")) or cp
if part then
-- Couleur initiale pour visualiser l'ordre
if CONFIG.checkpointInitial then
part.BrickColor = CONFIG.checkpointInitial
end
part.Touched:Connect(function(hit)
local character = hit.Parent
local player = Players:GetPlayerFromCharacter(character)
if not player then return end
local data = progressionJoueurs[player.UserId]
if not data then return end
-- Déjà validé
if data.indexActuel >= index then return end
if validerCheckpoint(player, index) then
-- Allumer le checkpoint pour ce joueur (visuel global simplifié)
if CONFIG.checkpointValidated then
part.BrickColor = CONFIG.checkpointValidated
end
end
end)
end
end
end
--
-- GESTION DES JOUEURS
--
local function initialiserJoueur(player)
progressionJoueurs[player.UserId] = {
indexActuel = 0,
checkpointPart = checkpoints[1], -- spawn initial = checkpoint 1
}
player.CharacterAdded:Connect(function(character)
task.wait(0.2) -- laisser le personnage se charger
respawnAuCheckpoint(player)
end)
end
-- supprimer la progression du joueur
local function nettoyerJoueur(player)
progressionJoueurs[player.UserId] = nil
end
--
-- LANCEMENT
--
Players.PlayerAdded:Connect(initialiserJoueur)
Players.PlayerRemoving:Connect(nettoyerJoueur)
for _, player in ipairs(Players:GetPlayers()) do
initialiserJoueur(player)
end
connecterCheckpoints()
Rangement des Checkpoints (getCheckpointsTries)
Dans Roblox, quand le jeu se lance, les objets ne s’installent pas toujours dans l’ordre. Le script doit donc faire le tri.
Il va chercher tous les blocs dans le dossier « Checkpoints ».
Il regarde leur nom (ex: « Checkpoint1 », « Checkpoint2 », « Checkpoint3 »).
Il extrait les chiffres grâce à une formule magique (%d+) et les trie du plus petit au plus grand dans une liste ordonnée
Le carnet de notes du jeu (progressionJoueurs)
Le jeu a besoin d’une mémoire pour savoir où en est chaque joueur. Pour cela, il utilise un tableau (comme une liste de classe) :
lualocal progressionJoueurs = {}
Quand un joueur se connecte, le script lui crée une fiche :
Le bloc de réapparition (le tout premier checkpoint).
Son niveau actuel (commence à 0).
Le Respawn (La réapparition après une chute)
Quand ton personnage meurt ou réapparaît, la fonction respawnAuCheckpoint se déclenche :
Le script cherche le nom du joueur dans son carnet de notes.
Il trouve la position 3D (X, Y, Z) du dernier checkpoint validé.
Il téléporte le joueur à cette position, en le surélevant légèrement (grâce à l’offset de 3 blocs défini dans la configuration) pour qu’il ne s’enfonce pas dans le bloc.
Toucher et Valider un Checkpoint (validerCheckpoint)
C’est ici que la magie opère quand tes pieds touchent une plaque :
Le détecteur (Touched) : Chaque checkpoint possède un capteur invisible. Dès qu’un joueur marche dessus, le script se réveille.
La vérification anti-triche : Si sauvegarderOrdre est activé, le script vérifie si le checkpoint touché est bien le suivant dans la liste (par exemple, passer du 2 au 3). Si tu essaies de sauter directement du 1 au 3, le jeu t’ignore !
La mise à jour : Si tout est bon :
Le jeu met à jour ta fiche dans le carnet.
Le checkpoint change de couleur (il devient vert pour te montrer visuellement que c’est bon).
Un message s’affiche dans le chat grâce à un module de dialogue (ex: » Joueur1 a atteint le checkpoint 3/5″).
Si c’est le tout dernier checkpoint, une fête se déclenche ( » Joueur1 a terminé le parcours ! »).
L’arrivée et le départ des joueurs
Tout en bas du script, le jeu surveille les connexions :
Un joueur arrive (PlayerAdded) : On crée sa fiche de suivi et on prépare son personnage pour qu’il soit téléporté à son dernier checkpoint à chaque fois qu’il réapparaît.
Un joueur s’en va (PlayerRemoving) : On efface sa fiche du carnet de notes pour ne pas encombrer inutilement la mémoire du serveur du jeu (c’est le nettoyage).
Ce qu’il faut retenir (les concepts clés de programmation) :
Les Variables (local ...) : Des boîtes pour ranger des informations (la couleur des blocs, les coordonnées…).
Les Tableaux ({}) : Des listes pour ranger des groupes d’objets (les checkpoints dans l’ordre) ou des informations complexes (la liste des joueurs et leur niveau).
Les Événements (.Touched, .PlayerAdded) : Des déclencheurs qui disent au script : « Hé ! Quelque chose s’est passé, exécute le code maintenant ! »
Voici une explication simple et étape par étape pour créer un module Lua sur Roblox qui affiche une boîte de dialogue au-dessus de la tête des joueurs. C’est comme une bulle de BD qui apparaît quand un personnage parle !
Ce module permet d’afficher un texte (comme « Bonjour ! ») au-dessus de la tête d’un personnage dans Roblox, avec un design personnalisable (couleurs, taille, durée d’affichage, etc.). Idéal pour :
Des quêtes ou dialogues dans ton jeu.
Des messages temporaires (ex : « Bravo ! »).
Des indications pour les joueurs.
Créer un module pour générer les boîtes de dialogue
Un module, c’est comme un générateur unique pour créer tes boîtes de dialogue :
Tu gagnes du temps (pas besoin de tout réécrire).
Tu évites les erreurs (tout est centralisé).
Tu peux améliorer ton jeu facilement (ajouter des animations, des sons, etc.).
« Un bon développeur est un développeur fainéant : il écrit le code une fois et le réutilise 1000 fois ! »
Où placer le ModuleScript pour la génération des boîtes de dialogue
Emplacement
Usage
ReplicatedStorage
Accessible côté client et serveur recommandé
ServerScriptService
Serveur uniquement
StarterPlayerScripts
Client uniquement
ReplicatedStorage est le meilleur choix pour une utilisation coté client et serveur. Placer un module dans ReplicatedStorage dans Roblox peut être pratique dans ce cas, mais cela comporte aussi des risques importants à connaître, surtout si ton module contient des fonctions sensibles ou des données critiques. Tout ce qui est dans ReplicatedStorage est téléchargé et exécutable côté client (par les joueurs). Un joueur malveillant peut :
Exploiter des failles si ton module contient des appels non sécurisés
Lire tout le code de ton module (même si tu le caches, il peut être extrait avec des outils comme Roblox Studio ou des exploits).
Modifier localement le comportement du module (en utilisant des exploits ou des scripts clients).
Crée un ModuleScript :
local DialogueModule = {}
local TweenService = game:GetService("TweenService")
-- CONFIGURATION
DialogueModule.CONFIG = {
largeur = 250,
hauteur = 80,
offsetY = 3,
couleurFond = Color3.fromRGB(0, 0, 0),
transparenceFond = 0.4,
couleurTexte = Color3.fromRGB(255, 255, 255),
couleurBordure = Color3.fromRGB(255, 255, 255),
taillePolicee = 16,
dureAffichage = 4,
}
function DialogueModule.creerDialogue(personnage, texte)
local CONFIG = DialogueModule.CONFIG
local head = personnage:WaitForChild("Head", 5)
if not head then return end
-- Supprimer l'ancienne boîte
local ancien = head:FindFirstChild("DialogueGui")
if ancien then ancien:Destroy() end
-- Créer le BillboardGui
local billboard = Instance.new("BillboardGui")
billboard.Name = "DialogueGui"
billboard.Adornee = head
billboard.Size = UDim2.new(0, CONFIG.largeur, 0, CONFIG.hauteur)
-- Pour le rebond
billboard.StudsOffset = Vector3.new(0, CONFIG.offsetY - 2, 0)
billboard.AlwaysOnTop = true
billboard.ResetOnSpawn = false
billboard.Parent = head
-- Fond
local fond = Instance.new("Frame")
fond.Size = UDim2.new(1, 0, 1, 0)
fond.BackgroundColor3 = CONFIG.couleurFond
fond.BackgroundTransparency = 1 -- Commence invisible
fond.BorderSizePixel = 0
fond.Parent = billboard
-- Coins arrondis
local coin = Instance.new("UICorner")
coin.CornerRadius = UDim.new(0, 10)
coin.Parent = fond
-- Bordure
local bordure = Instance.new("UIStroke")
bordure.Color = CONFIG.couleurBordure
bordure.Thickness = 1.5
bordure.Transparency = 0.5
bordure.Parent = fond
-- Texte
local label = Instance.new("TextLabel")
label.Size = UDim2.new(1, -16, 1, -10)
label.Position = UDim2.new(0, 8, 0, 5)
label.BackgroundTransparency = 1
label.TextColor3 = CONFIG.couleurTexte
label.TextSize = CONFIG.taillePolicee
label.Font = Enum.Font.GothamMedium
label.Text = texte
label.TextWrapped = true
label.TextXAlignment = Enum.TextXAlignment.Center
label.Parent = fond
-- Flèche
local fleche = Instance.new("Frame")
fleche.Size = UDim2.new(0, 14, 0, 14)
fleche.Position = UDim2.new(0.5, -7, 1, -7)
fleche.BackgroundColor3 = CONFIG.couleurFond
fleche.BackgroundTransparency = CONFIG.transparenceFond
fleche.BorderSizePixel = 0
fleche.Rotation = 45
fleche.Parent = fond
-- Suppression après délai
if CONFIG.dureAffichage > 0 then
task.delay(CONFIG.dureAffichage, function()
if billboard and billboard.Parent then
billboard:Destroy()
end
end)
end
return billboard
end
return DialogueModule
Génère la boîte de dialogue soit dans un localScript :
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local DialogueModule = require(game.ReplicatedStorage.DialogueModule)
-- Afficher
local character = game.Players.LocalPlayer.Character
DialogueModule.creerDialogue(character, "Bonjour !")
Ou dans un script sur le serveur par exemple lors d’une collision avec un part :
local part = script.Parent
local Players = game:GetService("Players")
local DialogueModule = require(game.ReplicatedStorage.DialogueModule)
part.Touched:Connect(function(hit)
local player = Players:GetPlayerFromCharacter(hit.Parent)
if player then
-- Afficher
local character = player.Character
DialogueModule.creerDialogue(character, "Bonjour !")
end
end)
Comment modifier les paramètres de la boîte de dialogue :
-- Modifier la config si besoin
DialogueModule.CONFIG.dureAffichage = 6
DialogueModule.CONFIG.couleurFond = Color3.fromRGB(30, 30, 80)
Rajoute dans ton code une animation de rebonds avant la suppression :
-- 1. Rebond de la boîte
local tweenRebond = TweenService:Create(
billboard,
TweenInfo.new(0.3, Enum.EasingStyle.Bounce, Enum.EasingDirection.Out),
{StudsOffset = Vector3.new(0, CONFIG.offsetY, 0)}
)
tweenRebond:Play()
Animation transparence du fond :
-- 2. Fondu du fond
local tweenFondu = TweenService:Create(
fond,
TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
{BackgroundTransparency = CONFIG.transparenceFond}
)
tweenFondu:Play()
Dans ce tutoriel, tu vas réaliser une animation où une boule descend une pente, disparaît lorsqu’elle arrive en bas, puis réapparaît automatiquement en haut pour recommencer son parcours.
Cette technique est très utilisée dans les jeux pour créer des décors animés ou des parcours d’obstacles.
Tu vas apprendre à :
construire une boule avec un Part sphérique ;
créer une pente avec un Part Coin ;
détecter l’arrivée de la boule grâce à une boîte de collision ;
utiliser un script serveur pour replacer la boule ;
organiser correctement les objets dans l’Explorateur ;
utiliser ReplicatedStorage pour conserver un modèle de référence.
Elle servira uniquement à détecter l’arrivée de la boule.
On l’appelle souvent une HitBox ou boîte de collision.
Pourquoi utiliser une boîte de collision ?
Au lieu de vérifier en permanence la position de la boule, Roblox peut détecter automatiquement lorsqu’un objet touche une zone.
Les avantages :
moins de calculs ;
code plus simple ;
meilleure précision.
Faire disparaître et réapparaître la boule
Où placer le script ?
Deux solutions sont possibles.
Solution 1 : ServerScriptService
ServerScriptService └─ GestionBoule
C’est la méthode recommandée.
Les avantages :
tous les scripts sont regroupés ;
plus facile à maintenir ;
meilleure sécurité.
Solution 2 : Sous le Folder
Workspace └─ Boules └─ Script
Cela fonctionne également.
Cette solution est parfois utilisée lorsque le script ne gère qu’un seul système.
Pour un petit projet comme celui-ci, cela reste acceptable, crée un script sous folder :
Saisie le code suivant :
-- récupération des ressources graphiques
local folder = script.Parent
local hitBox = folder.HitBox
local ball = folder.Ball
-- récupération du folder replicatedStorage
local replicatedStorage = game:GetService("ReplicatedStorage")
-- récupération de la position initiale de la balle
local positionInitial = ball.Position
-- modification des propriété de la ball
ball.Anchored = false
ball.CanCollide = true
ball.Name = "Ball"
-- clonage de la balle
local newBall = ball:Clone()
newBall.Parent = folder
-- rangement de la balle en modèle replicatedStorage
ball.Parent = replicatedStorage
-- détection de la collision de la balle
hitBox.Touched:Connect(function(hit)
if hit.Name ~= "Ball" then return end
newBall:Destroy()
task.wait(1)
newBall = ball:Clone()
newBall.Parent = folder
newBall.Anchored = false
end)
Pourquoi utiliser un Script serveur ?
Nous allons utiliser un Script et non un LocalScript.
Le serveur est responsable :
des objets du Workspace ;
de la physique ;
des collisions ;
des déplacements des Parts.
Comme la boule existe pour tous les joueurs, c’est le serveur qui doit la gérer.
Mémoriser la position de départ
Le script va enregistrer la position initiale de la boule :
local positionInitial = ball.Position
Cette variable contient les coordonnées exactes du point de départ.
Plus tard, le script pourra replacer la boule exactement au même endroit.
Sauvegarder un modèle dans ReplicatedStorage
Crée une copie parfaite de la boule.
Place cette copie dans :
-- récupération du folder replicatedStorage
local replicatedStorage = game:GetService("ReplicatedStorage")
ball.Parent = replicatedStorage
Cette boule ne sera jamais utilisée directement dans le jeu.
Elle sert de modèle de référence.
Pourquoi utiliser ReplicatedStorage ?
ReplicatedStorage permet de stocker des objets qui pourront être utilisés plus tard.
Les avantages :
conserver un modèle intact ;
recréer facilement un objet détruit ;
éviter de reconstruire l’objet à la main ;
faciliter les systèmes complexes.
Pourquoi cloner la boule ?
Lorsque la boule disparaît, il est parfois plus simple de :
détruire l’ancienne ;
recréer une nouvelle boule.
Pour cela :
local newBall = ball:Clone()
Le clonage produit une copie identique :
même taille ;
même couleur ;
mêmes propriétés ;
mêmes scripts éventuels.
Cette méthode est très utilisée dans les jeux Roblox.
Créer un flash lumineux
Ce script sert à créer un effet de flash lumineux à un endroit précis dans le jeu. Par exemple, il peut être utilisé lorsqu’un joueur clique, lorsqu’une explosion se produit ou lorsqu’un objet apparaît ou disparaît.
Le flash est créé sous la forme d’une petite sphère lumineuse qui grandit rapidement avant de disparaître.
Quand la fonction est appelée :
une petite boule lumineuse est créée ;
elle apparaît à la position demandée ;
elle grossit rapidement ;
elle devient transparente ;
elle disparaît ;
Roblox la supprime automatiquement.
Le résultat ressemble à une petite explosion lumineuse ou à un effet magique très utilisé dans les jeux Roblox.
Rajoute ce code dans ton script :
local Debris = game:GetService("Debris")
local TweenService = game:GetService("TweenService")
--
-- CONFIGURATION
--
local CONFIG = {
-- Flash
flashActif = true,
sizeFlash = Vector3.new(20, 20, 20),
colorFlash = Color3.fromRGB(255, 0, 0),
}
--
-- EFFETS Flash
--
local function creerFlash(position)
local flash = Instance.new("Part")
flash.Shape = Enum.PartType.Ball
flash.Size = Vector3.new(0.1, 0.1, 0.1)
flash.Position = position
flash.Anchored = true
flash.CanCollide = false
flash.CastShadow = false
flash.Material = Enum.Material.Neon
flash.Color = CONFIG.colorFlash
flash.Parent = workspace
local tween = TweenService:Create(flash,
TweenInfo.new(0.15, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
{ Size = CONFIG.sizeFlash, Transparency = 1 }
)
tween:Play()
Debris:AddItem(flash, 0.2)
end
--
-- EXPLOSION PRINCIPALE
--
local function exploser(ball)
local position = ball.Position
ball.CanCollide = false
ball.Transparency = 1
if CONFIG.flashActif then creerFlash(position) end
ball:Destroy()
end
Puis modifie ton script pour appeler le flash lorsque la balle touche la HitBox :
-- détection de la collision de la balle
hitBox.Touched:Connect(function(hit)
if hit.Name ~= "Ball" then return end
exploser(newBall)
task.wait(1)
newBall = ball:Clone()
newBall.Parent = folder
newBall.Anchored = false
end)
Crée une onde de choc
Ce script crée une onde de choc visuelle qui se propage autour d’un point précis.
Tu peux imaginer ce qui se passe lorsqu’une pierre tombe dans l’eau : une vague se forme puis s’agrandit progressivement. Ici, c’est le même principe, mais sous la forme d’une sphère lumineuse qui grandit avant de disparaître.
Ce type d’effet est souvent utilisé pour :
une explosion ;
un sort magique ;
un impact puissant ;
l’arrivée d’un boss ;
une compétence spéciale.
-- Script dans la boule (Part) ou un Script serveur
local Debris = game:GetService("Debris")
local TweenService = game:GetService("TweenService")
--
-- CONFIGURATION
--
local CONFIG = {
-- Onde de choc
ondeActive = true,
ondeCouleur = Color3.fromRGB(255, 150, 0),
-- Dégâts (rayon)
rayonDegats = 40,
forceDegats = 80,
-- Flash
flashActif = true,
sizeFlash = Vector3.new(20, 20, 20),
colorFlash = Color3.fromRGB(255, 0, 0),
}
--
-- EFFETS ONDE DE CHOC
--
local function creerOndeChoc(position)
local onde = Instance.new("Part")
onde.Shape = Enum.PartType.Ball
onde.Size = Vector3.new(1, 1, 1)
onde.Position = position
onde.Anchored = true
onde.CanCollide = false
onde.CastShadow = false
onde.Material = Enum.Material.Neon
onde.Color = CONFIG.ondeCouleur
onde.Transparency = 0.3
onde.Parent = workspace
local tween = TweenService:Create(onde,
TweenInfo.new(0.4, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
{ Size = Vector3.new(CONFIG.rayonDegats * 2, CONFIG.rayonDegats * 2, CONFIG.rayonDegats * 2), Transparency = 1 }
)
tween:Play()
Debris:AddItem(onde, 0.5)
end
local function appliquerForce(position)
for _, part in ipairs(workspace:GetPartBoundsInRadius(position, CONFIG.rayonDegats)) do
if part.Anchored then continue end
local direction = (part.Position - position)
local distance = direction.Magnitude
if distance < 0.1 then continue end
local force = (1 - distance / CONFIG.rayonDegats) * CONFIG.forceDegats
part:ApplyImpulse(direction.Unit * force * part.AssemblyMass)
end
end
--
-- EXPLOSION PRINCIPALE
--
local function exploser(ball)
local position = ball.Position
ball.CanCollide = false
ball.Transparency = 1
if CONFIG.flashActif then creerFlash(position) end
if CONFIG.ondeActive then creerOndeChoc(position) endappliquerForce(position)
ball:Destroy()
end
Lorsque la fonction creerOndeChoc() est appelée :
une petite sphère lumineuse apparaît ;
elle est placée au centre de l’explosion ;
elle s’agrandit jusqu’au rayon défini dans la configuration ;
elle devient progressivement transparente ;
elle disparaît ;
Roblox la supprime automatiquement.
Cette technique permet de créer facilement des effets spectaculaires pour des explosions, des sorts magiques ou des impacts puissants dans un jeu Roblox.
Créer de la fumée
Ce script permet de créer un effet de fumée à un endroit précis dans le jeu.
Cet effet peut être utilisé pour représenter :
une explosion ;
un moteur ;
un incendie ;
une cheminée ;
l’impact d’un projectile.
La fumée apparaît pendant un court instant, puis disparaît automatiquement.
-- ═══════════════════════════════════════
-- CONFIGURATION
-- ═══════════════════════════════════════
local CONFIG = {
-- Fumée
fumeeActive = true,
-- Onde de choc
ondeActive = true,
ondeCouleur = Color3.fromRGB(255, 150, 0),
-- Dégâts (rayon)
rayonDegats = 40,
forceDegats = 80,
-- Flash
flashActif = true,
sizeFlash = Vector3.new(20, 20, 20),
colorFlash = Color3.fromRGB(255, 0, 0),
}
-- ═══════════════════════════════════════
-- EFFETS FUMEE
-- ═══════════════════════════════════════
l
local function creerFumee(position)
local anchor = Instance.new("Part")
anchor.Size = Vector3.new(1, 1, 1)
anchor.Position = position
anchor.Anchored = true
anchor.CanCollide = false
anchor.Transparency = 1
anchor.Parent = workspace
local fumee = Instance.new("Smoke")
fumee.Color = Color3.fromRGB(80, 80, 80)
fumee.Opacity = 0.5
fumee.RiseVelocity = 10
fumee.Size = 12
fumee.Parent = anchor
-- Couper la fumée puis supprimer
task.delay(0.3, function()
fumee.Enabled = false
end)
Debris:AddItem(anchor, 3)
end
-- ═══════════════════════════════════════
-- EXPLOSION PRINCIPALE
-- ═══════════════════════════════════════
local function exploser(ball)
local position = ball.Position
ball.CanCollide = false
ball.Transparency = 1
if CONFIG.flashActif then creerFlash(position) end
if CONFIG.ondeActive then creerOndeChoc(position) end
if CONFIG.fumeeActive then creerFumee(position) end
appliquerForce(position)
ball:Destroy()
end
L’objet Smoke ne peut pas exister seul.
Il doit être attaché à un objet Roblox.
La Part invisible sert donc de :
support ;
point d’émission ;
repère dans l’espace.
Le joueur ne la voit jamais, mais elle permet à la fumée de fonctionner correctement.
Lorsque la fonction creerFumee() est appelée :
un support invisible est créé ;
un effet Smoke est ajouté dessus ;
la fumée grise est émise ;
elle monte rapidement ;
l’émission s’arrête après 0,3 seconde ;
Roblox supprime automatiquement l’ensemble après 3 secondes.
Cette technique permet d’ajouter facilement des effets visuels réalistes à tes explosions et animations dans Roblox.
Création d’étincelles
Ce script permet de créer un effet d’étincelles semblable à celui d’une explosion, d’un feu d’artifice ou d’un impact métallique.
Lorsqu’il est exécuté, plusieurs petites sphères lumineuses sont créées au même endroit. Elles partent ensuite dans différentes directions à des vitesses variées avant de disparaître progressivement.
Création de plusieurs étincelles
La fonction utilise une boucle qui se répète autant de fois que la valeur définie dans :
CONFIG.nbEtincelles
Si cette valeur est égale à 20, alors 20 étincelles seront créées.
Plus cette valeur est grande, plus l’effet sera spectaculaire, mais aussi plus il demandera de calculs à Roblox.
-- Script dans la boule (Part) ou un Script serveur
local Debris = game:GetService("Debris")
local TweenService = game:GetService("TweenService")
-- ═══════════════════════════════════════
-- CONFIGURATION
-- ═══════════════════════════════════════
local CONFIG = {
-- Particules
nbEtincelles = 50,
-- Fumée
fumeeActive = true,
-- Onde de choc
ondeActive = true,
ondeCouleur = Color3.fromRGB(255, 150, 0),
-- Dégâts (rayon)
rayonDegats = 40,
forceDegats = 80,
-- Flash
flashActif = true,
sizeFlash = Vector3.new(20, 20, 20),
colorFlash = Color3.fromRGB(255, 0, 0),
}
-- ═══════════════════════════════════════
-- EFFETS ETINCELLES
-- ═══════════════════════════════════════
local function creerEtincelles(position)
for i = 1, CONFIG.nbEtincelles do
local etincelle = Instance.new("Part")
etincelle.Shape = Enum.PartType.Ball
-- taille des etincelles
etincelle.Size = Vector3.new(0.2, 0.2, 0.2)
etincelle.Position = position
etincelle.CanCollide = false
etincelle.CastShadow = false
etincelle.Material = Enum.Material.Neon
-- Couleur aléatoire entre orange et jaune
etincelle.Color = Color3.fromRGB(255, math.random(100, 255), 0)
etincelle.Parent = workspace
-- Direction aléatoire
local direction = Vector3.new(
math.random(-100, 100) / 100,
math.random(20, 100) / 100,
math.random(-100, 100) / 100
).Unit
-- vitesse aléatoire de propagation des étincelles
local vitesse = math.random(20, 200)
local vb = Instance.new("BodyVelocity")
vb.Velocity = direction * vitesse
-- Autorise une poussée très forte sur les axes X, Y et Z pour propulser l'étincelle 1e4 = 10000
vb.MaxForce = Vector3.new(1e4, 1e4, 1e4)
vb.Parent = etincelle
-- Disparition progressive
local duree = math.random(5, 12) / 10
local tween = TweenService:Create(etincelle,
TweenInfo.new(duree, Enum.EasingStyle.Quad, Enum.EasingDirection.In),
{ Transparency = 1, Size = Vector3.new(0.05, 0.05, 0.05) }
)
tween:Play()
Debris:AddItem(etincelle, duree)
end
end
-- ═══════════════════════════════════════
-- EXPLOSION PRINCIPALE
-- ═══════════════════════════════════════
local function exploser(ball)
local position = ball.Position
ball.CanCollide = false
ball.Transparency = 1
if CONFIG.flashActif then creerFlash(position) end
if CONFIG.ondeActive then creerOndeChoc(position) end
if CONFIG.fumeeActive then creerFumee(position) end
creerEtincelles(position)
appliquerForce(position)
ball:Destroy()
end
Apparence des étincelles
Chaque étincelle est représentée par une très petite sphère lumineuse.
Le matériau Neon lui donne un aspect brillant qui ressemble à une particule incandescente.
Pour rendre l’effet plus réaliste, la couleur n’est pas toujours la même. Le rouge reste au maximum tandis que le vert varie aléatoirement. Cela produit différentes nuances allant :
du jaune vif ;
à l’orange ;
jusqu’à l’orange foncé.
Comme dans une véritable explosion, toutes les étincelles n’ont donc pas exactement la même couleur.
Une direction différente pour chaque étincelle
Après leur création, les étincelles doivent se disperser.
Le script génère donc une direction aléatoire pour chacune d’elles.
Certaines partent :
vers la gauche ;
vers la droite ;
vers l’avant ;
vers l’arrière ;
vers le haut.
Cette dispersion donne l’impression d’une véritable projection de particules.
Si toutes les étincelles allaient dans la même direction, l’effet serait beaucoup moins naturel.
Une vitesse différente pour chaque étincelle
Le script choisit également une vitesse aléatoire.
Certaines étincelles sont éjectées très rapidement tandis que d’autres se déplacent plus lentement.
C’est ce mélange de vitesses qui rend l’explosion crédible.
Dans la réalité, les fragments d’une explosion ne se déplacent jamais tous à la même vitesse.
Utilisation de BodyVelocity
Pour propulser les étincelles, le script utilise un objet appelé BodyVelocity.
On peut le comparer à une petite poussée invisible qui agit sur chaque étincelle. Cette poussée envoie immédiatement la particule dans la direction choisie avec la vitesse calculée. Le résultat est un mouvement rapide qui donne l’impression que les étincelles jaillissent du centre de l’explosion.
Disparition progressive
Une étincelle ne reste pas visible indéfiniment. Le script lui attribue une durée de vie aléatoire. Certaines disparaissent rapidement alors que d’autres restent visibles un peu plus longtemps.
Pendant cette durée :
elles deviennent progressivement transparentes ;
leur taille diminue petit à petit.
L’effet obtenu ressemble à une braise qui refroidit et s’éteint.
Animation avec TweenService
La disparition est réalisée grâce à TweenService.
Au lieu de rendre l’étincelle invisible d’un seul coup, Roblox anime progressivement :
la transparence ;
la taille.
L’étincelle semble donc se consumer naturellement avant de disparaître.
Nettoyage automatique
Une fois son animation terminée, chaque étincelle est supprimée grâce au service Debris. Cela évite d’accumuler des centaines de petits objets invisibles dans le jeu. C’est une étape importante pour conserver de bonnes performances.
Ce que voit le joueur
Lorsqu’une explosion se produit :
plusieurs petites boules lumineuses apparaissent ;
elles partent dans toutes les directions ;
certaines vont plus vite que d’autres ;
elles deviennent progressivement transparentes ;
elles rétrécissent ;
elles disparaissent complètement.
L’ensemble crée un effet très proche des étincelles d’un feu d’artifice, d’une explosion ou d’un choc métallique.
Créer un effet de projection de débris
Ce script sert à simuler une explosion de petits morceaux de matière, comme des cailloux, des fragments de mur ou des débris projetés au sol.
L’idée est simple : au lieu d’avoir un seul effet, on génère plusieurs petits objets qui partent dans toutes les directions avec des tailles et des vitesses différentes.
-- ═══════════════════════════════════════
-- CONFIGURATION
-- ═══════════════════════════════════════
local CONFIG = {
-- Particules
nbEtincelles = 50,
nbDebris = 10,
-- Fumée
fumeeActive = true,
-- Onde de choc
ondeActive = true,
ondeCouleur = Color3.fromRGB(255, 150, 0),
-- Dégâts (rayon)
rayonDegats = 40,
forceDegats = 80,
-- Flash
flashActif = true,
sizeFlash = Vector3.new(20, 20, 20),
colorFlash = Color3.fromRGB(255, 0, 0),
}
-- ═══════════════════════════════════════
-- EFFETS DEBRIS
-- ═══════════════════════════════════════
local function creerDebris(position)
for i = 1, CONFIG.nbDebris do
local morceau = Instance.new("Part")
morceau.Shape = Enum.PartType.CornerWedge
morceau.Size = Vector3.new(
math.random(1, 4) / 10,
math.random(1, 4) / 10,
math.random(1, 4) / 10
)
morceau.Position = position + Vector3.new(
math.random(-2, 2),
math.random(0, 2),
math.random(-2, 2)
)
morceau.Material = Enum.Material.SmoothPlastic
morceau.Color = Color3.fromRGB(80, 80, 80)
morceau.Parent = workspace
local direction = Vector3.new(
math.random(-100, 100) / 100,
math.random(30, 100) / 100,
math.random(-100, 100) / 100
).Unit
local vb = Instance.new("BodyVelocity")
vb.Velocity = direction * math.random(10, 30)
vb.MaxForce = Vector3.new(1e4, 1e4, 1e4)
vb.Parent = morceau
Debris:AddItem(morceau, math.random(2, 4))
end
end
-- ═══════════════════════════════════════
-- EXPLOSION PRINCIPALE
-- ═══════════════════════════════════════
local function exploser(ball)
local position = ball.Position
ball.CanCollide = false
ball.Transparency = 1
if CONFIG.flashActif then creerFlash(position) end
if CONFIG.ondeActive then creerOndeChoc(position) end
if CONFIG.fumeeActive then creerFumee(position) end
creerEtincelles(position)
creerDebris(position)
appliquerForce(position)
ball:Destroy()
end
Création de plusieurs morceaux
La fonction utilise une boucle qui se répète un nombre de fois défini par :
CONFIG.nbDebris
Plus cette valeur est grande, plus l’explosion est impressionnante. Chaque boucle crée donc un nouveau morceau de débris.
Apparence des débris
Chaque fragment est une petite Part avec :
une taille différente à chaque fois (petits ou un peu plus gros morceaux) ;
une couleur gris foncé pour simuler de la pierre ou du béton ;
un matériau simple pour rester léger et réaliste.
L’objectif est d’éviter un rendu trop uniforme : dans une vraie explosion, tous les morceaux ne sont jamais identiques.
Position initiale aléatoire
Les débris ne sont pas tous créés exactement au même point.
Ils apparaissent légèrement autour de la position d’origine, avec un petit décalage :
certains un peu à gauche ;
d’autres un peu à droite ;
certains légèrement plus haut.
Cela donne l’impression que l’explosion a un vrai volume, et pas juste un point fixe.
Direction de projection
Chaque débris reçoit une direction aléatoire dans l’espace. Cette direction est calculée pour permettre :
une dispersion horizontale (gauche / droite / avant / arrière) ;
une poussée vers le haut pour simuler l’explosion.
Ensuite, cette direction est normalisée, ce qui signifie qu’on garde uniquement le sens, sans influencer la vitesse.
Mise en mouvement
Pour propulser les morceaux, le script utilise un BodyVelocity.
Ce système agit comme une poussée instantanée appliquée à chaque débris. La vitesse est également aléatoire :
certains morceaux partent doucement ;
d’autres sont projetés rapidement.
Cela rend l’effet beaucoup plus naturel et dynamique.
Durée de vie limitée
Chaque débris n’est pas permanent. Le service Debris s’occupe de supprimer automatiquement chaque morceau après un temps aléatoire.
Cela évite :
d’encombrer le jeu avec trop d’objets ;
de ralentir les performances ;
de laisser des débris inutiles dans le monde.
Résultat visuel
Quand la fonction est appelée, on observe :
plusieurs petits morceaux apparaissent au point d’impact ;
ils sont légèrement dispersés autour du centre ;
ils sont projetés dans toutes les directions ;
certains montent haut, d’autres restent proches du sol ;
ils disparaissent progressivement.
L’ensemble donne un effet très proche :
d’une explosion de mur ;
d’un impact de balle lourde ;
ou d’un objet qui se casse violemment.
Rajouter un son d’explosion
Trouve et insère le dans l’arborescence :
Puis ajoute à ton script la gestion du son le son :
local soundExplosion = folder.Explosion
local function playSound(sound)
if not sound then return end
pcall(function() sound:Play() end)
end
Puis modifie la fonction Exploser de ton script pour ajouter le son de l’explosion :
--
-- EXPLOSION PRINCIPALE
--
local function exploser(ball)
local position = ball.Position
ball.CanCollide = false
ball.Transparency = 1
playSound(soundExplosion)
if CONFIG.flashActif then creerFlash(position) end
if CONFIG.ondeActive then creerOndeChoc(position) end
if CONFIG.fumeeActive then creerFumee(position) end
creerEtincelles(position)
creerDebris(position)
appliquerForce(position)
ball:Destroy()
end
Dans ce tutoriel, tu vas apprendre à déplacer une image vers le bas sur une interface du joueur en utilisant TweenService dans Roblox Studio.
Les animations sont très utiles pour rendre un jeu plus agréable et plus vivant. Par exemple, tu peux faire apparaître un panneau d’information, faire glisser un bouton ou afficher une récompense avec une animation fluide.
Pour réaliser cela, nous allons utiliser un LocalScript et le service TweenService
Dans Roblox, les éléments affichés à l’écran du joueur sont appelés des interfaces graphiques ou GUI.
Une interface est généralement composée de plusieurs objets :
ScreenGui : le conteneur principal affiché à l’écran.
Frame : une fenêtre ou une zone de l’interface.
ImageLabel : un objet qui affiche une image.
TextLabel : un objet qui affiche du texte.
Button : un bouton sur lequel le joueur peut cliquer.
Exemple :
Pour positionner ta fenêtre (Frame) au milieu de l’écran :
Crée un LocalScript
Les interfaces appartiennent à chaque joueur.
Si tu modifies l’interface avec un script serveur classique (Script), cela peut entraîner des problèmes car le serveur gère tous les joueurs.
Un LocalScript s’exécute uniquement sur l’ordinateur du joueur :
il est plus rapide ;
il permet de gérer l’interface personnelle du joueur ;
il évite d’envoyer des informations inutiles au serveur.
C’est donc le meilleur choix pour animer une interface graphique.
Tu pourrais déplacer une image en modifiant sa position directement :
image.Position = UDim2.new(0,0,0.5,0)
Mais l’image apparaîtrait immédiatement à sa nouvelle position.
Avec TweenService, Roblox crée automatiquement une animation fluide entre la position de départ et la position d’arrivée.
Les avantages sont nombreux :
animation plus agréable ;
code plus simple ;
contrôle de la durée ;
possibilité d’ajouter différents effets de mouvement.
-- récupérer le service TweenService
local TweenService = game:GetService("TweenService")
-- récupérer l' du joueur
local screen = script.Parent
-- récupérer l'image
local image = screen.Frame.ImageLabel
-- créer l'animation
local tweenInfo = TweenInfo.new(3, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, -1, true)
-- définir l'objectif de l'animation
local objectif = {Position = UDim2.new(image.Position.X.Scale,0 , 0.8, 0)}
-- lancer l'animation
local tween = TweenService:Create(image, tweenInfo, objectif)
tween:Play()