Dans Roblox, les textures servent à donner un aspect plus réaliste aux objets du jeu et donne du relief au jeu. Au lieu d’avoir seulement une couleur simple, on peut ajouter des images pour représenter du bois, de la pierre, du métal ou encore de l’herbe. La texture principale correspond à l’image visible sur l’objet, appelée souvent “Color” ou “Albedo”, qui donne les couleurs et les détails. Il existe aussi des images spéciales comme la Normal Map : elle ne change pas la forme réelle de l’objet, mais elle crée une illusion de relief avec la lumière, par exemple des bosses, des fissures ou des briques. D’autres textures peuvent contrôler la brillance, les reflets ou les zones transparentes, ce qui permet de créer des matériaux beaucoup plus réalistes dans un jeu Roblox.
Comment affecter une texture existante
Crée un part :
Associe au part une texture existante :
Puis choisis une couleur :
Essaye les autres textures.
Comment créer une nouvelle texture
Tu peux utiliser ce site pour créer une nouvelle texture ou ce site pour trouver des textures libre de droit :
Choisir une texture sur un site :
Modifie la taille de l’image dans Paint pour une taille de 512×512 pixels :
Puis générer une image « normalMap » pour le relief avec ce site :
Clic avec le bouton gauche de ta souris pour charger ton image :
Modifie les différents paramètres pour obtenir l’image que tu souhaites :
Puis saisis le nom de ton fichier puis clic sur Download :
Maintenant que tes deux images sont prêtes, dans ROBLOX demande l’ouverture du gestionnaire des matériaux :
Crée un nouveau matériau en cliquant sur le + en haut à gauche :
Dans ce tutoriel, tu vas apprendre à programmer un système de construction comme dans Minecraft avec Roblox et le langage Lua. Le joueur pourra créer des blocs pour construire des structures de blocs et détruire des blocs. Tu découvriras comment utiliser la souris, le clavier et les scripts pour interagir avec les objets dans Roblox Studio. Ce projet permet d’apprendre les bases importantes de la programmation de jeux vidéo : événements, conditions, coordonnées et objets 3D, les scripts en local et les scripts sur le serveur. À la fin du tutoriel, tu auras créé un véritable système de construction interactif dans ton propre jeu Roblox.
Dans un premier temps, tu vas récupérer les actions du joueur comme les clics de souris et les touches du clavier dans LocalScript. Dans un deuxième temps, ces informations seront ensuite envoyées par un système ROBLOX de RemoteEvent au serveur afin de vérifier et traiter les demandes du joueur. Dans un troisième temps, tu créeras un Script sur le serveur pour créer ou détruir les blocs dans le monde du jeu pour que tous les joueurs puissent voir les modifications en même temps.
Tu découvriras ainsi comment fonctionne la communication entre le joueur et le serveur dans un vrai jeu multijoueur Roblox.
Récupération des événements souris et clavier
Sous StarterPlayer puis StarterCharacterScripts crée un Folder que tu renommes Minecraft, puis un LocalScript que tu renommes MCInput :
Le joueur interagit avec le monde du jeu comme dans Minecraft grâce à la souris et au clavier. Un clic sur le bouton gauche de la souris permet de construire un nouveau bloc dans le jeu. Un clic sur le bouton droitde la souris permet de détruire un bloc existant. Cependant, pour laisser la possibilité d’utiliser la souris pour les déplacements du joueur et de la caméra, la construction ou la destruction ne sera possible uniquement si le joueur maintient en même temps la touche Majuscule gauche (Left Shift) du clavier.
Dans MCInput, saisie cette programmation pour récupérer les événements clavier et souris :
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
RunService.RenderStepped:Connect(function()
-- Appuyer sur la touche la touche majuscule pour que la souris soit prise en compteif
if not UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) then return end
-- Touche gauche de la souris pour construire
if UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton1) then
print("Mouse left pressed")
-- Touche droite de la souris pour détruire
elseif UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton2) then
print("Mouse right pressed")
end
end)
Lance le jeu et vérifie la prise en compte des boutons gauche et droit de la souris :
Comment remonter les actions du local vers le serveur
Dans Roblox, les actions de la souris sont détectées dans le LocalScript, car elles appartiennent uniquement au joueur qui utilise le jeu. Pour que le serveur puisse créer ou détruire les blocs, nous devons lui envoyer les informations des clics de souris. Nous allons utiliser des RemoteEvent, un système de communication entre le joueur et le serveur dans Roblox. Le LocalScript enverra par exemple le clic gauche et l’action demandée (construire). Le serveur recevra ensuite ces événements et appliquera les modifications dans le monde du jeu pour que tous les joueurs puissent les voir.
Deux événements sont remontés construire et détruire.
Crée deux Folder sous ReplicatedStorage Minecraft puis Remotes.
Sous le folder Remotes crée deux RemoteEvent MCBuild et MCDestroy :
Puis sous ServerScriptService crée le Folder Maincraft puis crée un Script MCSystem afin d’exécuter les ordres reçues par le LocalScript du joueur via les RemoteEvent :
Saisie ce code dans MCSystem :
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Références
local minecraft = ReplicatedStorage:WaitForChild("Minecraft")
local remotes = minecraft:WaitForChild("Remotes")
-- Remote : poser un bloc
remotes.MCBuild.OnServerEvent:Connect(function(player, data)
print("Action Build")
end)
-- Remote : détruire un bloc
remotes.MCDestroy.OnServerEvent:Connect(function(player, data)
print("Action Destroy")
end)
Modifie ton LocalScript MCInput pour envoyer les actions :
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local minecraft = ReplicatedStorage:WaitForChild("Minecraft")
local remotes = minecraft:WaitForChild("Remotes")
RunService.RenderStepped:Connect(function()
-- Appuyer sur la touche la touche majuscule pour que la souris soit prise en compte
if not UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) then return end
-- Touche gauche de la souris pour construire
if UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton1) then
print("Mouse left pressed")
remotes.MCBuild:FireServer()
-- Touche droite de la souris pour détruire
elseif UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton2) then
print("Mouse right pressed")
remotes.MCDestroy:FireServer()
end
end)
Lance ton jeu et vérifie que le serveur reçoit bien les deux actions :
Lorsque le joueur maintient un bouton de la souris appuyé, Roblox envoie les événements plusieurs fois de suite très rapidement. Sans protection, le serveur pourrait alors créer ou détruire un très grand nombre de blocs en quelques secondes. Cela peut provoquer des bugs, ralentir le jeu ou permettre à un joueur de construire trop vite. Nous allons donc mettre en place un mécanisme de limitation, appelé souvent “cooldown” ou temporisation, pour attendre un petit délai entre deux actions. Ainsi, le serveur traitera les demandes de manière plus propre et le jeu restera fluide pour tous les joueurs.
Modifie le script serveur MCSystem :
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Debris = game:GetService("Debris")
-- Références
local minecraft = ReplicatedStorage:WaitForChild("Minecraft")
local remotes = minecraft:WaitForChild("Remotes")
-- Système anti rebond via un Folder temporaire dans le Character du player
local function withCooldown(character, actionName, callback)
if character:FindFirstChild(actionName) then return end
local tag = Instance.new("Folder")
tag.Name = actionName
tag.Parent = character
Debris:AddItem(tag, COOLDOWN_DURATION)
callback()
end
-- Remote : poser un bloc
remotes.MCBuild.OnServerEvent:Connect(function(player, data)
local character = player.Character
withCooldown(character, "Build", function()
print("Action Build")
end)
end)
-- Remote : détruire un bloc
remotes.MCDestroy.OnServerEvent:Connect(function(player, data)
local character = player.Character
withCooldown(character, "Destroying", function()
print("Action Destroy")
end)
end)
Tu remarques que dans la sortie, tu as une seule action pour plusieurs événements souris :
Récupération de la position du bloc à créer
Maintenant, nous devons récupérer la position de la souris dans le monde du jeu et non sa position sur l’écran de l’ordinateur. En effet, pour construire ou détruire des blocs, le jeu doit savoir exactement quel endroit du monde 3D le joueur vise avec sa souris. Le LocalScript va donc utiliser un système appelé “Raycast” pour projeter un rayon invisible depuis la caméra vers la scène du jeu. Grâce à cela, nous pourrons obtenir les coordonnées précises du point visé dans l’espace ainsi que l’objet touché par la souris, par exemple un bloc ou la baseplate. Ces informations seront ensuite envoyées au serveur pour créer ou supprimer les blocs au bon endroit.
Modifie le script serveur MCSystem :
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Debris = game:GetService("Debris")
-- Références
local minecraft = ReplicatedStorage:WaitForChild("Minecraft")
local remotes = minecraft:WaitForChild("Remotes")
-- Système anti rebond via un Folder temporaire dans le Character du player
local function withCooldown(character, actionName, callback)
if character:FindFirstChild(actionName) then return end
local tag = Instance.new("Folder")
tag.Name = actionName
tag.Parent = character
Debris:AddItem(tag, COOLDOWN_DURATION)
callback()
end
-- Valide les données communes aux deux remotes
local function parseData(player, data)
local character = player.Character
if not character then return nil end
if not character:FindFirstChildOfClass("Humanoid") then return nil end
local position3D = data[1]
local direction = data[2]
local partTouchee = data[3]
if not partTouchee then return nil end
print("Position3D:", position3D, "Direction:", direction, "Part touchée:", partTouchee.Name)
return character, position3D, direction, partTouchee
end
-- Remote : poser un bloc
remotes.MCBuild.OnServerEvent:Connect(function(player, data)
local character, position3D, direction = parseData(player, data)
if not character then return end
withCooldown(character, "Build", function()
print("Action Build")
end)
end)
-- Remote : détruire un bloc
remotes.MCDestroy.OnServerEvent:Connect(function(player, data)
local character, _, _, partTouchee = parseData(player, data)
if not character then return end
withCooldown(character, "Destroying", function()
print("Action Destroy")
end)
end)
Lance ton jeu et vérifie que le serveur reçoit la position pointée par la souris, le direction et la part touchée.
Création d’un bloc Minecraft
Pour créer un nouveau bloc dans le jeu, nous allons utiliser un modèle de bloc préparé dans le ReplicatedStorage. Le serveur va créer une copie de ce bloc, appelée un “clone”, puis le placer dans un dossier Minecraft créé dans le Workspace pour ranger proprement tous les blocs Minecraft. Le bloc doit être ensuite aligné automatiquement sur une grille afin que toutes les constructions restent bien organisées comme dans Minecraft. Après sa création, le bloc descend jusqu’à toucher un autre bloc ou la Baseplate. Dès qu’il entre en contact avec un support, nous activerons la propriété Anchored pour le bloquer définitivement et éviter qu’il continue à bouger.
Crée un part qui sera ta brique de base Minecraft :
Fais en sorte que le part soit cubique :
Déplace ton bloc de base sous ReplicatedStorage et renomme le MCBlock :
Puis continue à modifier le script serveur MCSystem pour créer les blocs :
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Debris = game:GetService("Debris")
-- Références
local minecraft = ReplicatedStorage:WaitForChild("Minecraft")
local remotes = minecraft:WaitForChild("Remotes")
local blockTemplate = minecraft:WaitForChild("MCBlock")
-- Configuration
local GRID_SIZE = blockTemplate.Size.X
local ANCHOR_DELAY = 0.5 -- délai avant tentative d'ancrage
local VELOCITY_THRESHOLD = 0.1 -- seuil de vitesse pour ancrage
local COOLDOWN_DURATION = 0.5 -- durée du cooldown build/destroy
local FOLDERMC = "Minecraft"
-- Système anti rebond via un Folder temporaire dans le Character du player
local function withCooldown(character, actionName, callback)
if character:FindFirstChild(actionName) then return end
local tag = Instance.new("Folder")
tag.Name = actionName
tag.Parent = character
Debris:AddItem(tag, COOLDOWN_DURATION)
callback()
end
-- Valide les données communes aux deux remotes
local function parseData(player, data)
local character = player.Character
if not character then return nil end
if not character:FindFirstChildOfClass("Humanoid") then return nil end
local position3D = data[1]
local direction = data[2]
local partTouchee = data[3]
if not partTouchee then return nil end
print("Position3D:", position3D, "Direction:", direction, "Part touchée:", partTouchee.Name)
return character, position3D, direction, partTouchee
end
-- Aligne une position sur la grille en fonction du cube de référence
local function snapToGrid(position)
local function snap(v) return math.floor(v / GRID_SIZE + 0.5) * GRID_SIZE end
return Vector3.new(snap(position.X), snap(position.Y), snap(position.Z))
end-- Ancrage progressif d'un bloc
local function anchorBlock(block)
if block.Anchored then return end
task.wait(ANCHOR_DELAY)
repeat task.wait(0.1) until
not block or
not block.Parent or
block.AssemblyLinearVelocity.Magnitude <= VELOCITY_THRESHOLD
if not block or not block.Parent then return end
block.Anchored = true
block.AssemblyLinearVelocity = Vector3.zero
block.AssemblyAngularVelocity = Vector3.zero
end-- Récupère ou crée le folder Minecraft dans le workspace
local function getOrCreateFolderMC()
local folder = workspace:FindFirstChild(FOLDERMC)
if not folder then
folder = Instance.new("Folder")
folder.Name = FOLDERMC
folder.Parent = workspace
end
return folder
endlocal folderMinecraft = getOrCreateFolderMC()
-- Remote : poser un bloc
remotes.MCBuild.OnServerEvent:Connect(function(player, data)
local character, position3D, direction = parseData(player, data)
if not character then return end
withCooldown(character, "Build", function()
local newPosition = snapToGrid(position3D + direction * GRID_SIZE)
local newBlock = blockTemplate:Clone()
newBlock.Position = newPosition
newBlock.Anchored = false
newBlock.Parent = folderMinecraft
local connection
connection = newBlock.Touched:Connect(function(otherPart)
if not otherPart.Anchored and otherPart.Name ~= "Baseplate" then return end
connection:Disconnect() -- un seul ancrage
anchorBlock(newBlock)
end)
end)
end)
-- Remote : détruire un bloc
remotes.MCDestroy.OnServerEvent:Connect(function(player, data)
local character, _, _, partTouchee = parseData(player, data)
if not character then return end
withCooldown(character, "Destroying", function()
print("Action Destroy")
end)
end)
Destruction d’un bloc
Dans le script serveur MCSystem modifie juste l’action MCDestroy :
-- Remote : détruire un bloc
remotes.MCDestroy.OnServerEvent:Connect(function(player, data)
local character, _, _, partTouchee = parseData(player, data)
if not character then return end
withCooldown(character, "Destroying", function()
if partTouchee:IsA("BasePart") and partTouchee.Name == blockTemplate.Name then
partTouchee:Destroy()
end
end)
end)
Si tout fonctionne correctement, tu peux supprimer les « print » de ton code ou les mettre en commentaire :
-- Valide les données communes aux deux remotes
local function parseData(player, data)
local character = player.Character
if not character then return nil end
if not character:FindFirstChildOfClass("Humanoid") then return nil end
local position3D = data[1]
local direction = data[2]
local partTouchee = data[3]
if not partTouchee then return nil end
--print("Position3D:", position3D, "Direction:", direction, "Part touchée:", partTouchee.Name)
return character, position3D, direction, partTouchee
end
Construis un système de portes que tu pourras répliquer partout dans ton jeu. Le joueur pourra ouvrir des portes et découvrir d’autres pièces ou d’autres mondes. Tu pourras choisir entre une fermeture manuelle de la porte ou automatique après un laps de temps. Plusieurs boutons synchronisés permettent de piloter une même porte. Le bouton peut-être installé à distance de la porte.
Construis cette arborescence dans ton workspace :
Sous un folder, insére un son pour l’ouverture de ta porte, un script, un part représentant ta porte, sous la porte un part pour représenter le bouton d’ouverture, puis un proxyprompt sous le bouton. Renomme chaque élément pour plus de clarté :
Les noms donnés n’ont pas d’importance par contre la structure de l’arborescence du folder doit être absolument respectée.
Saisie le code suivant dans le script :
-- Récupération du joueur
local Players = game:GetService("Players")
-- Récupération du dossier des portes
local doorsFolder = script.Parent
-- Initialisation de chaque porte
-- Le dossier doit comporte un part pour la porte
-- puis un part pour chaque bouton
-- puis proxymity prompt pour chaque bouton
for _, door in doorsFolder:GetChildren() do
if not door:IsA("Part") then continue end
door.Anchored = true
door.Transparency = 0
door.CanCollide = true
for _, button in door:GetChildren() do
if not button:IsA("Part") then continue end
button.Anchored = true
button.Transparency = 0
button.CanCollide = true
local proxi = button:FindFirstChildOfClass("ProximityPrompt")
if not proxi then continue end
proxi.ActionText = LABEL_OPEN
proxi.KeyboardKeyCode=Enum.KeyCode.E
local delay = button:GetAttribute("TimeoutClose") or TIMEOUT_CLOSE
proxi.Triggered:Connect(function(player)
-- Ouverture ou fermeture de la porte
print("ouverture porte")
end)
end
end
Lance ton jeu, quand tu actives le proximitry prompt, le message suivant s’affiche dans la console de sortie mais la porte ne s’ouvre pas encore :
Modifie ton code pour ouvrir la porte :
-- Récupération du joueur
local Players = game:GetService("Players")
-- Configuration
local TIMEOUT_CLOSE = 0
local LABEL_CLOSE = "Fermer"
local LABEL_OPEN = "Ouvrir"
-- Récupération du dossier des portes
local doorsFolder = script.Parent
local sound = doorsFolder:FindFirstChildOfClass("Sound")
-- Lecture du son d'ouverture de la porte
local function playSound()
if not sound then return end
pcall(function() sound:Play() end)
end
-- Ouverture ou fermeture d'une porte
local function setDoor(door, isOpen)
door.Transparency = isOpen and 1 or 0
door.CanCollide = not isOpen
end
-- Mise à jour du texte des proximity prompt des boutons
local function setActionText(door)
-- Pas de fermeture automatique
for _, button in door:GetChildren() do
if button:IsA("Part") then
local proxi = button:FindFirstChildOfClass("ProximityPrompt")
if proxi then
proxi.ActionText = door.CanCollide and LABEL_OPEN or LABEL_CLOSE
end
end
end
end-- Activation ou désactivation des proximity prompts des boutons
local function setEnabledProxi(door, isOpen)
-- Pas de fermeture automatique
for _, button in door:GetChildren() do
if button:IsA("Part") then
local proxi = button:FindFirstChildOfClass("ProximityPrompt")
proxi.Enabled = isOpen
end
end
end
-- Initialisation de chaque porte
-- Le dossier doit comporte un part pour la porte
-- puis un part pour chaque bouton
-- puis proxymity prompt pour chaque bouton
for _, door in doorsFolder:GetChildren() do
if not door:IsA("Part") then continue end
door.Anchored = true
door.Transparency = 0
door.CanCollide = true
for _, button in door:GetChildren() do
if not button:IsA("Part") then continue end
button.Anchored = true
button.Transparency = 0
button.CanCollide = true
local proxi = button:FindFirstChildOfClass("ProximityPrompt")
if not proxi then continue end
proxi.ActionText = LABEL_OPEN
proxi.KeyboardKeyCode=Enum.KeyCode.E
local delay = button:GetAttribute("TimeoutClose") or TIMEOUT_CLOSE
proxi.Triggered:Connect(function(player)
-- Ouverture ou fermeture de la porte
playSound()
setDoor(door, door.CanCollide)
if delay > 0 then
-- Décompte pour fermeture automatique
setEnabledProxi(door, false)
task.delay(delay, function()
setDoor(door, false)
setEnabledProxi(door, true)
playSound()
end)
else
-- Pas de fermeture automatique
setActionText(door)
end
end)
end
end
Lance ton jeu et regarde l’ouverture de ta porte.
Modifie dans ton script la valeur de cette constante :
local TIMEOUT_CLOSE = 3
Lance ton jeu et regarde le comportement de ta porte.
Duplique les boutons d’ouverture :
Lance ton jeu, et essaye tous les boutons d’ouverture.
Construis un décor autour de ta porte :
Crée une arborescence sous la forme d’un folder sous ta porte pour ranger la structure qui entoure celle-ci :
Tu peux dupliquer ta porte et ainsi avoir tout un système de portes dans ton jeu :
Puis différencier les portes :
Modifie ton code pour prendre en compte un ClickDetector sur un bouton :
Modifie ton script pour tester si le bouton a un promixityPrompt ou un clickDetector :
-- Récupération du joueur
local Players = game:GetService("Players")
-- Configuration
local TIMEOUT_CLOSE = 0
local LABEL_CLOSE = "Fermer"
local LABEL_OPEN = "Ouvrir"
-- Récupération du dossier des portes
local doorsFolder = script.Parent
local sound = doorsFolder:FindFirstChildOfClass("Sound")
-- Lecture du son d'ouverture de la porte
local function playSound()
if not sound then return end
pcall(function() sound:Play() end)
end
-- Ouverture ou fermeture d'une porte
local function setDoor(door, isOpen)
door.Transparency = isOpen and 1 or 0
door.CanCollide = not isOpen
end
-- Mise à jour du texte des proximity prompt des boutons
local function setActionText(door)
-- Pas de fermeture automatique
for _, button in door:GetChildren() do
if button:IsA("Part") then
local proxi = button:FindFirstChildOfClass("ProximityPrompt")
if proxi then
proxi.ActionText = door.CanCollide and LABEL_OPEN or LABEL_CLOSE
end
end
end
end
-- Activation ou désactivation des proximity prompts des boutons
local function setEnabledProxi(door, isOpen)
-- Pas de fermeture automatique
for _, button in door:GetChildren() do
if button:IsA("Part") then
local proxi = button:FindFirstChildOfClass("ProximityPrompt")
if proxi then
proxi.Enabled = isOpen
end
end
end
end
-- Logique de déclenchement commune
local function connectTrigger(door, button, onTriggered)
local delay = button:GetAttribute("TimeoutClose") or TIMEOUT_CLOSE
onTriggered(function()
-- Ouverture ou fermeture de la porte
playSound()
setDoor(door, door.CanCollide)
if delay > 0 then
-- Décompte pour fermeture automatique
setEnabledProxi(door, false)
task.delay(delay, function()
setDoor(door, false)
setEnabledProxi(door, true)
playSound()
end)
else
-- Pas de fermeture automatique
setActionText(door)
end
end)
end
-- Initialisation de chaque porte
-- Le dossier doit comporte un part pour la porte
-- puis un part pour chaque bouton
-- puis proxymity prompt pour chaque bouton
for _, door in doorsFolder:GetChildren() do
if not door:IsA("Part") then continue end
door.Anchored = true
door.Transparency = 0
door.CanCollide = true
for _, button in door:GetChildren() do
if not button:IsA("Part") then continue end
button.Anchored = true
button.Transparency = 0
button.CanCollide = true
--test si le bouton a un proximity prompt ou un click detector
local proxi = button:FindFirstChildOfClass("ProximityPrompt")
local clicker = button:FindFirstChildOfClass("ClickDetector")
if proxi then
proxi.ActionText = LABEL_OPEN
proxi.KeyboardKeyCode=Enum.KeyCode.E
connectTrigger(door, button, function(callback)
proxi.Triggered:Connect(function(player)
callback()
end)
end)
elseif clicker then
connectTrigger(door, button, function(callback)
clicker.MouseClick:Connect(function(player)
callback()
end)
end)
else
warn("[DoorManager] Aucun ProximityPrompt ni ClickDetector sur :", button:GetFullName())
end
end
end
Crée dans ton jeu des barrières invisibles que le joueur ne peut pas franchir. Dès que le joueur touche une barrière invisible, celle-ci se matérialise et déclenche une alarme sonore. Tu peux répliquer autant de barrières invisibles et infranchissables que tu auras besoin pour ton jeu. Ces barrières permettent à ton joueur d’être guider et l’oblige à passer par certaines étapes de ton monde.
Crée dans ton workspace l’arborescence suivante dans un Folder avec un fichier son en option, un script, un part représentant un mur :
Puis saisie ce code dans ton script :
local Players = game:GetService("Players")
-- Configuration
local TRANSPARENCY_VISIBLE = 0.7
local TRANSPARENCY_HIDDEN = 1
local FADE_DELAY = 2
-- Récupération des objets du folder
local wallsFolder = script.Parent
-- Fonction appelée quand un joueur touche un mur
local function onWallTouched(otherPart, wall)
-- Vérification joueur uniquement
local character = otherPart.Parent
if not Players:GetPlayerFromCharacter(character) then return end
print("Collision avec le joueur")
end
-- Lecture de tous les murs du dossier
for _, wall in wallsFolder:GetChildren() do
if not wall:IsA("Part") then continue end
wall.Anchored = true
wall.Transparency = TRANSPARENCY_HIDDEN
wall.Material = Enum.Material.Neon
wall.CanCollide = true -- mur bloquant
wall.Touched:Connect(function(otherPart)
onWallTouched(otherPart, wall)
end)
end
Lance ton jeu, le mur est invisible, si ton joueur touche le mur il reste bloqué !!! Un message dans la sortie indique que le joueur est rentré en collision avec le mur :
Afin d’indiquer au joueur qu’il ne peut pas franchir le mur invisible, on va le faire apparaître et déclencher une alarme sonore.
Modifie le script :
local Players = game:GetService("Players")
-- Configuration
local TRANSPARENCY_VISIBLE = 0.7
local TRANSPARENCY_HIDDEN = 1
local FADE_DELAY = 2
-- Récupération des objets du folder
local wallsFolder = script.Parent
-- Chargement du fichier sonore (si présent)
local sound = wallsFolder:FindFirstChildOfClass("Sound")
if not sound then
warn("[CoinManager] Aucun Sound trouvé dans ", coinsFolder.Name)
end
-- Fonction appelée quand un joueur touche un mur
local function onWallTouched(otherPart, wall)
-- Vérification joueur uniquement
local character = otherPart.Parent
if not Players:GetPlayerFromCharacter(character) then return end
-- Évite les appels multiples simultanés
if wall.Transparency == TRANSPARENCY_VISIBLE then return end
-- Affichage du mur en transparence
wall.Transparency = TRANSPARENCY_VISIBLE
-- Son d'alerte
if sound then
pcall(function() sound:Play() end)
end
task.wait(FADE_DELAY)
wall.Transparency = TRANSPARENCY_HIDDEN
end
-- Lecture de tous les murs du dossier
for _, wall in wallsFolder:GetChildren() do
if not wall:IsA("Part") then continue end
wall.Anchored = true
wall.Transparency = TRANSPARENCY_HIDDEN
wall.Material = Enum.Material.Neon
wall.CanCollide = true -- mur bloquant
wall.Touched:Connect(function(otherPart)
onWallTouched(otherPart, wall)
end)
end
Duplique les murs invisibles infranchissables et déplace les dans ton espace de jeu :
Crée un système de gain pour que ton joueur puisse gagner des points. Ce tuto montre comment programmer un système de gain en attrapant des pièces de valeurs différentes. Tu pourras répliquer autant de pièces que tu le souhaites dans ton monde.
Tu peux convertir cette proposition par un système de gains autre que par des pièces. A toi d’être imaginatif !!!
Crée la structure suivante « Coins » pour ton système de gain :
Crée un Model sous workspace que tu peux renommer Coins. Puis dessous ajoute un son, puis un script et également un part pour représenter ta pièce.
Puis modifie ton script pour générer ton système de gain, le script affiche la pièce et le gain potentiel. Pour l’instant le joueur ne peut pas récupérer les pièces donc son gain :
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
-- Configuration
local ROTATION_SPEED = 5
local MAX_GAIN = 10
local RESPAWN_DELAY = 10 -- secondes avant respawn
local FADE_SPEED = 0.05 -- vitesse de disparition
local COIN_LEADERBOARD = "Coins" --leaderstats
-- Répertoire de ton système de gains sous workspace
local coinsFolder = script.Parent
-- Chargement du fichier sonore (si présent)
local sound = coinsFolder:FindFirstChildOfClass("Sound")
if not sound then
warn("[CoinManager] Aucun Sound trouvé dans ", coinsFolder.Name)
end
-- Initialise et retourne les composants d'une pièce
-- Ajoute à tes pièces la valeur du gain et un système de particules
local function setupCoin(coin)
coin.Anchored = true
coin.Transparency = 0
coin.CanTouch = true
coin.CanCollide = false
-- Hitbox box de collision autour de la pièce
local hitbox = Instance.new("Part")
hitbox.Name = "Hitbox"
hitbox.Parent = coin
hitbox.Position = coin.Position
hitbox.Size = Vector3.new(coin.Size.X, coin.Size.Y, coin.Size.Z)
hitbox.Transparency = 0.5
hitbox.CanCollide = false
hitbox.Anchored = true
-- Particule effet lorsque le pièce est touchée
local particule = Instance.new("ParticleEmitter")
particule.Parent = coin
particule.EmissionDirection = Enum.NormalId.Back
particule.Enabled = false
particule.Speed = NumberRange.new(20, 60)
particule.Rate = 40
particule.ShapeInOut = Enum.ParticleEmitterShapeInOut.InAndOut
-- Billboard pour afficher le gain
local billboard = Instance.new("BillboardGui")
billboard.Name = "CoinLabel"
billboard.Adornee = coin
billboard.Size = UDim2.new(0, 40, 0, 20)
billboard.StudsOffset = Vector3.new(0, coin.Size.Y/2 + 1, 0)
billboard.AlwaysOnTop = false
billboard.Parent = coin
local label = Instance.new("TextLabel")
label.Parent = billboard
label.Size = UDim2.new(1, 0, 1, 0)
label.BackgroundTransparency = 1
label.Text = tostring(math.random(1, MAX_GAIN))
label.TextColor3 = Color3.fromRGB(255, 255, 0)
label.TextStrokeTransparency = 0
label.TextScaled = true
return hitbox, particule, label, billboard
end
-- Connecte la logique de collecte sur une pièce
local function connectCoin(coin)
local hitbox, particule, label, billboard = setupCoin(coin)
local gain = tonumber(label.Text)
end
-- Rotation & disparition progressive (Heartbeat)
RunService.Heartbeat:Connect(function()
for _, coin in coinsFolder:GetChildren() do
if not coin:IsA("Part") then continue end
if coin.CanTouch then
-- Rotation active
if coin.Transparency > 0 then
coin.Transparency = 0
end
coin.CFrame *= CFrame.Angles(0, math.rad(ROTATION_SPEED), 0)
else
-- Disparition progressive
coin.Transparency = math.min(coin.Transparency + FADE_SPEED, 1)
end
end
end)
-- Initialisation de toutes les pièces
for _, coin in coinsFolder:GetChildren() do
if coin:IsA("Part") then
connectCoin(coin)
end
end
Puis modifie ton script pour que le joueur puisse attraper les pièces :
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
-- Configuration
local ROTATION_SPEED = 5
local MAX_GAIN = 10
local RESPAWN_DELAY = 10 -- secondes avant respawn
local FADE_SPEED = 0.05 -- vitesse de disparition
local COIN_LEADERBOARD = "Coins" --leaderstats
-- Répertoire de ton système de gains sous workspace
local coinsFolder = script.Parent
-- Chargement du fichier sonore (si présent)
local sound = coinsFolder:FindFirstChildOfClass("Sound")
if not sound then
warn("[CoinManager] Aucun Sound trouvé dans ", coinsFolder.Name)
end
-- Initialise et retourne les composants d'une pièce
-- Ajoute à tes pièces la valeur du gain et un système de particules
local function setupCoin(coin)
coin.Anchored = true
coin.Transparency = 0
coin.CanTouch = true
coin.CanCollide = false
-- Hitbox box de collision autour de la pièce
local hitbox = Instance.new("Part")
hitbox.Name = "Hitbox"
hitbox.Parent = coin
hitbox.Position = coin.Position
hitbox.Size = Vector3.new(coin.Size.X, coin.Size.Y, coin.Size.Z)
hitbox.Transparency = 0.5
hitbox.CanCollide = false
hitbox.Anchored = true
-- Particule effet lorsque le pièce est touchée
local particule = Instance.new("ParticleEmitter")
particule.Parent = coin
particule.EmissionDirection = Enum.NormalId.Back
particule.Enabled = false
particule.Speed = NumberRange.new(20, 60)
particule.Rate = 40
particule.ShapeInOut = Enum.ParticleEmitterShapeInOut.InAndOut
-- Billboard pour afficher le gain
local billboard = Instance.new("BillboardGui")
billboard.Name = "CoinLabel"
billboard.Adornee = coin
billboard.Size = UDim2.new(0, 40, 0, 20)
billboard.StudsOffset = Vector3.new(0, coin.Size.Y/2 + 1, 0)
billboard.AlwaysOnTop = false
billboard.Parent = coin
local label = Instance.new("TextLabel")
label.Parent = billboard
label.Size = UDim2.new(1, 0, 1, 0)
label.BackgroundTransparency = 1
label.Text = tostring(math.random(1, MAX_GAIN))
label.TextColor3 = Color3.fromRGB(255, 255, 0)
label.TextStrokeTransparency = 0
label.TextScaled = true
return hitbox, particule, label, billboard
end
-- Réinitialise une pièce pour le respawn
local function resetCoin(coin)
coin.Transparency = 0
coin.CanTouch = true
end
-- Connecte la logique de collecte sur une pièce
local function connectCoin(coin)
local hitbox, particule, label, billboard = setupCoin(coin)
local gain = tonumber(label.Text)
hitbox.Touched:Connect(function(otherPart)
if not coin.CanTouch then return end
local character = otherPart.Parent
if not character then return end
local player = Players:GetPlayerFromCharacter(character)
local humanoid = character:FindFirstChildOfClass("Humanoid")
if not player or not humanoid then return end
-- Collecte
coin.CanTouch = false
particule.Enabled = true
billboard.Enabled = false
-- Lecture protégée
local soundDuration = 1 -- fallback si pas de son
if sound then
pcall(function() sound:Play() end)
soundDuration = sound.TimeLength > 0 and sound.TimeLength or 1
end
-- Ajout des points
local leaderstats = player:FindFirstChild("leaderstats")
if leaderstats and leaderstats:FindFirstChild(COIN_LEADERBOARD) then
leaderstats.Coins.Value += gain
end
-- Arrêt des particules à la fin du son
task.delay(soundDuration, function()
if not coin or not coin.Parent then return end
particule.Enabled = false
particule:Clear()
end)
-- Relance de la pièce après un délai
if RESPAWN_DELAY > 0 then
task.delay(RESPAWN_DELAY, function()
-- Attente puis respawn
if not coin or not coin.Parent then return end
resetCoin(coin)
end)
end
end)
end
-- Rotation & disparition progressive (Heartbeat)
RunService.Heartbeat:Connect(function()
for _, coin in coinsFolder:GetChildren() do
if not coin:IsA("Part") then continue end
if coin.CanTouch then
-- Rotation active
if coin.Transparency > 0 then
coin.Transparency = 0
end
coin.CFrame *= CFrame.Angles(0, math.rad(ROTATION_SPEED), 0)
else
-- Disparition progressive
coin.Transparency = math.min(coin.Transparency + FADE_SPEED, 1)
end
end
end)
-- Initialisation de toutes les pièces
for _, coin in coinsFolder:GetChildren() do
if coin:IsA("Part") then
connectCoin(coin)
end
end
Tu peux changer ces paramètres :
local ROTATION_SPEED = 5 — Modifie la vitesse de rotation de tes pièces local MAX_GAIN = 10 — Modifie le système de gain de 1 à 10 local RESPAWN_DELAY = 5 — Secondes avant respawn des pièces, 0 pas de respawn des pièces local FADE_SPEED = 0.01 — vitesse de la disparition des pièces
Duplique les pièces et place les dans ton espace de jeu :
Avec ce tuto tu peux créer un océan ou une espace d’eau confiné par exemple comme une piscine.
Par script, tu généreras un plan d’eau sur toute ta map comme pour un océan ou en fonction de la taille d’un part que tu disposeras sur ton jeu.
Dans un premier temps, crée un script sur le serveur qui va générer un espace d’eau soit un océan ou un espace contraint dans un part :
Crée un ModuleScript sous ServerScriptService :
Renomme le nom du moduleScript en waterGenerator :
Saisie le code suivant pour générer un espace d’eau comme un océan ou un espace d’eau contraint par un part comme une piscine :
local waterGenerator = {}
-- Valeurs par défaut
local TAILLE_DEFAUT = 4048
local PROFONDEUR_DEFAUT = 200
-- Privée : fonction centrale de génération
local function createWater(terrain, centreX, centreY, centreZ, tailleX, tailleY, tailleZ)
if not terrain then
warn("[waterGenerator] Terrain manquant.")
return
end
local cframe = CFrame.new(centreX, centreY, centreZ)
local size = Vector3.new(tailleX, tailleY, tailleZ)
terrain:FillBlock(cframe, size, Enum.Material.Water)
end
-- Génère une mer plate
-- hauteur : niveau Y de la surface
-- profondeur : épaisseur de l'eau vers le bas
-- taille : largeur X et Z (optionnel, défaut 4048)
function waterGenerator.createSea(terrain, hauteur, profondeur, taille)
if not hauteur or hauteur <= 0 then
warn("[waterGenerator] Hauteur invalide ou manquante.")
return
end
profondeur = profondeur or PROFONDEUR_DEFAUT
if profondeur <= 0 then
warn("[waterGenerator] Profondeur invalide.")
return
end
taille = taille or TAILLE_DEFAUT
local centreY = hauteur - profondeur / 2
createWater(terrain, 0, centreY, 0, taille, profondeur, taille)
end
-- Génère de l'eau centrée sur une Part
-- La part définit position et taille (axes intentionnellement remappés X→Z, Z→Y, Y→X)
function waterGenerator.createPart(terrain, part)
if not part then
warn("[waterGenerator] Part manquante.")
return
end
-- Préparation de la part : on la fait disparaître
part.Anchored = true
part.CanCollide = false
part.Transparency = 1
createWater(
terrain,
part.Position.X,
part.Position.Y,
part.Position.Z,
part.Size.X, -- tailleX
part.Size.y, -- tailleY
part.Size.Z -- tailleZ
)
end
return waterGenerator
Comment créer un océan :
Dans ton workpace, crée un script :
local terrain = workspace.Terrain
local waterGenerator = require(game.ServerScriptService.waterGenerator)
-- Génération de la mer
waterGenerator.createSea(terrain, 5, 200)
Si tu lances ton jeu :
Comment créer une piscine :
Crée un part qui va contenir l’eau de ta piscine :
Crée un script avec le code suivant :
local terrain = workspace.Terrain
local piscine = script.Parent
local waterGenerator = require(game.ServerScriptService.waterGenerator)
waterGenerator.createPart(terrain, piscine)
Lance ton jeu, tu obtiens une masse d’eau contenu dans ton part :
Et avec un peu de décor, tu obtiendras une belle piscine :
Dans beaucoup de jeux, les joueurs peuvent acheter des objets, des vêtements, des pouvoirs ou des accessoires. La boutique sert donc à donner plus d’objectifs au joueur : gagner de l’argent dans le jeu, choisir quoi acheter et améliorer son personnage. Une boutique rend aussi le jeu plus vivant et plus intéressant.
Dans Roblox Studio, on peut construire des décors, placer des objets et ajouter des menus à l’écran. Avec le langage de programmation Lua, nous apprendrons à donner des actions aux objets et à créer les règles du jeu.
La démarche pour la construction de ta boutique sera progressive :
D’abord, nous apprendrons à créer l’interface de la boutique : une fenêtre avec des boutons et les noms des objets à vendre.
Ensuite, nous programmerons l’argent du joueur avec une variable. Puis, nous coderons les achats : lorsqu’un joueur clique sur un bouton, le programme vérifie s’il a assez d’argent. Si oui, il paie et reçoit l’objet. Sinon, un message lui indique qu’il lui manque des pièces.
Ce projet te permettra de découvrir plusieurs notions importantes en programmation : les variables, les conditions, les événements, les fonctions et l’organisation d’un vrai projet numérique. À la fin, tu disposeras d’une boutique fonctionnelle dans Roblox.
Création de la boutique :
Pout ta boutique crée :
une fenêtre sur ta boutique avec les options à acheter
un bouton pour ouvrir et fermer la fenêtre de ta boutique
un localscript pour gérer l’ouverture de ta boutique
Création d’un écran utilisateur pour représenter la boutique, sous StaterGui demande la création d’un ScreenGui
Sous ScreenGui crée un Frame :
Déplace et agrandit le Frame pour prendre la place de ta boutique :
Modifie les propriétés, la couleur, le contour, la transparence du Frame :
Renomme les objets de ta botique :
Sous FrameStore rajoute un TextLabel pour le titre de ta boutique :
Dans le TextLabel rajoute le titre de la boutique :
Maintenant, tu vas rajouter des boutons pour chaque Item de ta boutique :
Tu dois obtenir ce résultat :
Tu peux rajouter une image pour certain élément de ta boutique :
Recherche une image dans la boîte à outil :
Récupère l’identifiant de l’image par un clic droit sur l’image :
Copie l’identifiant dans la propriété Image de ton ImageLabel :
Sous ScreenStore crée un ImageButton qui te permettra d’ouvrir ou fermer ta boutique :
Recherche dans la boîte à outil une image pour représenter ta boutique :
Par le clic droit demande à copier l’identifiant de l’image que tu colle dans la propriété Image de ton ImageButton :
Sous ScreenStore demande la création d’un LocalScript :
Renomme ton LocalScript et tu dois obtenir ce résultat :
Maintenant, tu peux modifier le script pour ouvrir ou fermer la boutique :
-- récupération du bouton pour ouvrir la boutique
local openButton = script.Parent.OpenButton
-- récupération du frame de la boutique
local frameStore = script.Parent.FrameStore
-- on cache le frame de la boutique au lancement du jeu par defaut
frameStore.Visible = false
-- on écoute l'événement de clic sur le bouton pour ouvrir ou fermer la boutique
openButton.MouseButton1Click:Connect(function()
frameStore.Visible = not frameStore.Visible
end)
Crée un tableau de points
Objectif : Créé un tableau de scores de Points
Créer un script pour afficher un tableau de score au joueur sous ServerScriptService :
Renommer le script leaderstats :
Saisir le code suivant pour un affichage d’une barre de scores de Points :
-- récupérer le service sur les joueurs
local players = game:GetService("Players")
-- écouteur d'un nouveau joueur
players.PlayerAdded:Connect(function(player)
-- créer un dossier leaderstats
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
end)
Si tu lances ton jeu une barre s’affiche :
Si le score ne s’affiche pas vérifie :
Rajoute à ton script un score à afficher :
-- récupérer le service sur les joueurs
local players = game:GetService("Players")
-- écouteur d'un nouveau joueur
players.PlayerAdded:Connect(function(player)
-- créer un dossier leaderstats
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
-- créer un score de Points
local score = Instance.new("IntValue")
score.Parent = leaderstats
score.Name = "Points"
score.Value = 10
end)
Le score s’affiche si tu lances ton jeu :
GAGNER DES POINTS
Créer un Part avec un ClickDetector afin que le joueur puisse gagner des points, exemple à reproduire sur tous les objets que tu souhaites dans ton jeu :
Exemple d’un script pour augmenter le score si le joueur click sur un ClickDetector :
Sasie le code suivant- dans le script :
-- récupérer le ClickDetector du Part
local clickDetector = script.Parent.ClickDetector
-- écouter l'événement de clic du Clickdetector
clickDetector.MouseClick:Connect(function(player)
-- ajouter 10 Points au joueur
player:WaitForChild("leaderstats").Points.Value += 10
end)
Lance ton jeu, maintenant à chaque clic le score de points augmente de 10 :
Acheter
Sur l’écran de sa boutique, le joueur clic sur l’item de son achat. Le LocalScript de la boutique demande via une RemoteFunction la possibilité de l’achat de l’item. Un script sur le serveur vérifie le nombre de point et la disponibilité de l’item.
Mis en place de l’échange entre le Localscript de la boutique et le serveur par un RemoteFunction :
Renomme RemoteFunction par RemoteBuy :
Puis modifie le script StoreScript lié à ta boutique :
-- récupération du bouton pour ouvrir la boutique
local openButton = script.Parent.OpenButton
-- récupération du frame de la boutique
local frameStore = script.Parent.FrameStore
-- on cache le frame de la boutique au lancement du jeu par defaut
frameStore.Visible = false
-- on écoute l'événement de clic sur le bouton pour ouvrir ou fermer la boutique
openButton.MouseButton1Click:Connect(function()
frameStore.Visible = not frameStore.Visible
end)
-- on récupère le bouton de la lampe dans la boutique
local lampButton = script.Parent.FrameStore.LampButton
-- on récupère le RemoteEvent pour acheter la lampe
local remoteBuy = game:GetService("ReplicatedStorage"):WaitForChild("RemoteBuy")
-- on écoute le clic sur le bouton de la lampe pour l'acheter
lampButton.MouseButton1Click:Connect(function(itemName)
-- on envoie une requête au serveur pour acheter la lampe
local success, message = remoteBuy:InvokeServer("lamp")
-- on affiche le message de retour du serveur
print(success, message)
end)
Puis demande à créer un Script sur le serveur sous ServerScriptService afin de valider l’achat :
Renomme ton Script en storeScript:
Ecris ce code dans le script storeScript :
-- récupérer le RemoteFunction RemoteBuy
local remoteBuy = game:GetService("ReplicatedStorage"):WaitForChild("RemoteBuy")
-- écouter l'événement de requête d'achat du joueur
remoteBuy.OnServerInvoke = function(player, value)
-- vérifier si le joueur a assez de points pour acheter l'objet
local points = player:WaitForChild("leaderstats").Points.Value
return true, " Achat confirmé !"
end
Lance ton jeu pour vérifier le bon fonctionnement de l’achat, tu trouveras le message de l’acquittement de l’achat dans la console.
Modifie le script storeScript pour vérifier si le joueur dispose d’assez de points pour l’achat et la disponibilité du stock :
-- récupérer le RemoteFunction RemoteBuy
local remoteBuy = game:GetService("ReplicatedStorage"):WaitForChild("RemoteBuy")
local ITEMS = {
["lamp"] = { price = 5, stock = 2 },
["run"] = { price = 10, stock = 1 },
["visible"] = { price = 15, stock = 1 }
}
-- écouter l'événement de requête d'achat du joueur
remoteBuy.OnServerInvoke = function(player, value)
-- vérifier si le joueur a assez de points pour acheter l'objet
local item = ITEMS[value]
-- Vérification stock
if not item or item.stock <= 0 then
return false, value .. " : Item indisponible "
end
-- Vérification points du joueur
local points = player:WaitForChild("leaderstats").Points.Value
if points < item.price then
return false, "Points insuffisants (" .. points .. "/" .. item.price .. ")"
end
-- Transaction confirmée
player.leaderstats.Points.Value -= item.price
item.stock -= 1
return true, "Achat confirmé !"
end
Rajoute sur l’écran de ta boutique un message pour indiquer au joueur la confirmation de son achat ou non :
Renomme ton TextLabel en MsgLabel :
Modifie le script storeScript pour afficher le message :
-- récupération du bouton pour ouvrir la boutique
local openButton = script.Parent.OpenButton
-- récupération du frame de la boutique
local frameStore = script.Parent.FrameStore
-- on cache le frame de la boutique au lancement du jeu par defaut
frameStore.Visible = false
-- on écoute l'événement de clic sur le bouton pour ouvrir ou fermer la boutique
openButton.MouseButton1Click:Connect(function()
frameStore.Visible = not frameStore.Visible
end)
-- on récupère le bouton de la lampe dans la boutique
local lampButton = script.Parent.FrameStore.LampButton
-- on récupère le RemoteEvent pour acheter la lampe
local remoteBuy = game:GetService("ReplicatedStorage"):WaitForChild("RemoteBuy")
local msg = script.Parent.FrameStore.MsgLabel
-- on écoute l'événement de clic sur le bouton de la lampe pour l'acheter
lampButton.MouseButton1Click:Connect(function(itemName)
-- on envoie une requête au serveur pour acheter la lampe
local success, message = remoteBuy:InvokeServer("lamp")
-- on affiche le message de retour du serveur sur l'acran de la boutique
msg.Text = message
end)
Lance ton jeu pour vérifier si le message s’affiche correctement après un achat :
Création d’un outil
A mettre à la main du joueur et à présenter dans la boutique
Sous Workspace créer un outil Tool :
Renommer Tool en Lamp :
Maintenant, dessine une lampe en utilisant plusieurs parts que tu assembleras :
Colorise et choisis tes matériaux pour tes parts qui composera ta lampe, puis crée un Union de tes parts et renome l’objet obtenu en Handle :
Rajoute un SpotLight à ta lampe :
Modifie les propriétés de SpotLight pour orienter correctement le faisceau lumineux de ta lampe :
Lance ton jeu, le joueur doit pouvoirt attraper la lampe et vérifie que celle-ci est correctement orientée :
Je constate que la lampe n’est pas correctement orientée, je modifie le Grip de mon Tool pour l’orienter correctement :
AFFICHER L’OUTIL DANS LA BOUTIQUE
L’objectif est de présenter la lampe dans la boutique.
Dans un premier déplace ton outil Tool lampe sous ReplicatedStorage :
Puis tu vas modifier l’écran de ta boutique.
Supprimer le ImageLabel et remplace par un ViewportFrame :
Modifie le LocalScript StoreScript de la boutique :
-- récupération du bouton pour ouvrir la boutique
local openButton = script.Parent.OpenButton
-- récupération du frame de la boutique
local frameStore = script.Parent.FrameStore
--[[
*** Rendre le viewport 3D de la lampe visible dans la boutique
]]
local replicatedStorage = game:GetService("ReplicatedStorage")
local lamp = replicatedStorage:WaitForChild("Lamp")
local viewport = script.Parent.FrameStore.ViewportFrame
-- Nettoyage
viewport:ClearAllChildren()
-- Clone de l'objet
local clone = lamp:Clone()
clone.Parent = viewport
-- Caméra
local camera = Instance.new("Camera")
viewport.CurrentCamera = camera
camera.Parent = viewport
local center
local size
-- Calcul de la position de la caméra (adapté à la taille de l'objet)
if clone:IsA("Model") then
size = clone:GetExtentsSize()
center = clone:GetPivot().Position
else
size = clone.Size
center = clone.Position
end
-- Position de la caméra (à adapter selon la taille de l'objet)
camera.CFrame = CFrame.new(
center + Vector3.new(size.X, size.Y, size.Z) * 0.8,
center
)
-- on cache le frame de la boutique au lancement du jeu par defaut
frameStore.Visible = false
-- on écoute l'événement de clic sur le bouton pour ouvrir ou fermer la boutique
openButton.MouseButton1Click:Connect(function()
frameStore.Visible = not frameStore.Visible
end)
-- on récupère le bouton de la lampe dans la boutique
local lampButton = script.Parent.FrameStore.LampButton
-- on récupère le RemoteEvent pour acheter la lampe
local remoteBuy = game:GetService("ReplicatedStorage"):WaitForChild("RemoteBuy")
local msg = script.Parent.FrameStore.MsgLabel
-- on écoute l'événement de clic sur le bouton de la lampe pour l'acheter
lampButton.MouseButton1Click:Connect(function(itemName)
-- on envoie une requête au serveur pour acheter la lampe
local success, message = remoteBuy:InvokeServer("lamp")
-- on affiche le message de retour du serveur sur l'acran de la boutique
msg.Text = message
end)
Orienter l’image de l’outil pour une présentation optimum au joueur :
Mettre l’outil dans l’inventaire du joueur
Lors de l’achat de la lampe, mettre celle-ci dans l’inventaire.
Afin d’optimiser notre code, éviter des erreurs de saisie, tu vas centraliser tout ce que on appelle constante. Ces constantes sont des valeurs fixes et utilisées dans plusieurs parties du code.
Sous ReplicatedStorage crée un ModuleScript pour stocker toutes tes constantes :
Modifie le LocalScript StoreScript de la boutique pour rajouter l’appel aux constantes :
local Constants = require(game.ReplicatedStorage.Constants)
-- récupération du bouton pour ouvrir la boutique
local openButton = script.Parent.OpenButton
-- récupération du frame de la boutique
local frameStore = script.Parent.FrameStore
--[[
*** Rendre le viewport 3D de la lampe visible dans la boutique
]]
local replicatedStorage = game:GetService("ReplicatedStorage")
local lamp = replicatedStorage:WaitForChild("Lamp")
local viewport = script.Parent.FrameStore.ViewportFrame
-- Nettoyage
viewport:ClearAllChildren()
-- Clone de l'objet
local clone = lamp:Clone()
clone.Parent = viewport
-- Caméra
local camera = Instance.new("Camera")
viewport.CurrentCamera = camera
camera.Parent = viewport
local center
local size
-- Calcul de la position de la caméra (adapté à la taille de l'objet)
if clone:IsA("Model") then
size = clone:GetExtentsSize()
center = clone:GetPivot().Position
else
size = clone.Size
center = clone.Position
end
-- Position de la caméra (à adapter selon la taille de l'objet)
camera.CFrame = CFrame.new(
center + Vector3.new(size.X, size.Y, size.Z) * 0.8,
center
)
-- on cache le frame de la boutique au lancement du jeu par defaut
frameStore.Visible = false
-- on écoute l'événement de clic sur le bouton pour ouvrir ou fermer la boutique
openButton.MouseButton1Click:Connect(function()
frameStore.Visible = not frameStore.Visible
end)
-- on récupère le bouton de la lampe dans la boutique
local lampButton = script.Parent.FrameStore.LampButton
-- on récupère le RemoteEvent pour acheter la lampe
local remoteBuy = game:GetService("ReplicatedStorage"):WaitForChild("RemoteBuy")
local msg = script.Parent.FrameStore.MsgLabel
-- on écoute l'événement de clic sur le bouton de la lampe pour l'acheter
lampButton.MouseButton1Click:Connect(function(itemName)
-- on envoie une requête au serveur pour acheter la lampe
local success, message = remoteBuy:InvokeServer("lamp")
-- on affiche le message de retour du serveur sur l'acran de la boutique
msg.Text = message
end)
Modifie le script storeScript sous ServerScriptService pour rajouter le module des constantes :
-- récupérer le module Constants
local Constants = require(game.ReplicatedStorage.Constants)
-- récupérer le RemoteFunction RemoteBuy
local remoteBuy = game:GetService("ReplicatedStorage"):WaitForChild("RemoteBuy")
-- écouter l'événement de requête d'achat du joueur
remoteBuy.OnServerInvoke = function(player, value)
-- vérifier si le joueur a assez de points pour acheter l'objet
local points = player:WaitForChild("leaderstats").Points.Value
return true, " Achat confirmé !"
end
Modifie le script storeScript sous ServerScriptService, pour rajouter le contrôle des points, rajouter un tableau de tous les item de la boutique :
-- récupérer le module Constants
local Constants = require(game.ReplicatedStorage.Constants)
-- récupérer le RemoteFunction RemoteBuy
local remoteBuy = game:GetService("ReplicatedStorage"):WaitForChild("RemoteBuy")
local ITEMS = {
[Constants.LAMP] = { price = 5, power = 10 , stock = 2 },
[Constants.POWER_RUN_MORE] = { price = 10, power = 10, stock = 2 },
[Constants.VISIBILITY] = { price = 15, power = 10, stock = 1 }
}
-- écouter l'événement de requête d'achat du joueur
remoteBuy.OnServerInvoke = function(player, value)
-- vérifier si le joueur a assez de points pour acheter l'objet
local item = ITEMS[value]
-- Vérification stock
if not item or item.stock <= 0 then
return false, value .. " : Item indisponible "
end
-- Vérification points du joueur
local points = player:WaitForChild("leaderstats").Points.Value
if points < item.price then
return false, "Points insuffisants (" .. points .. "/" .. item.price .. ")"
end -- Vérification si le joueur possède déjà l'item
local newitem = player.leaderstats:FindFirstChild(Constants.POWER_RUN_MORE)
if not newitem then
newitem = Instance.new("IntValue")
newitem.Parent = player.leaderstats
newitem.Name = value
end
newitem.Value += item.power
-- Transaction confirmée
player.leaderstats.Points.Value -= item.price
item.stock -= 1 return true, "Achat confirmé !"
end
Modifie le script pour rajouter la lampe dans l’inventaire du joueur :
local Constants = require(game.ReplicatedStorage.Constants)
-- récupération du bouton pour ouvrir la boutique
local openButton = script.Parent.OpenButton
-- récupération du frame de la boutique
local frameStore = script.Parent.FrameStore
--[[
*** Rendre le viewport 3D de la lampe visible dans la boutique
]]
local replicatedStorage = game:GetService("ReplicatedStorage")
local lamp = replicatedStorage:WaitForChild("Lamp")
local viewport = script.Parent.FrameStore.ViewportFrame
-- Nettoyage
viewport:ClearAllChildren()
-- Clone de l'objet
local clone = lamp:Clone()
clone.Parent = viewport
-- Caméra
local camera = Instance.new("Camera")
viewport.CurrentCamera = camera
camera.Parent = viewport
local center
local size
-- Calcul de la position de la caméra (adapté à la taille de l'objet)
if clone:IsA("Model") then
size = clone:GetExtentsSize()
center = clone:GetPivot().Position
else
size = clone.Size
center = clone.Position
end
-- Position de la caméra (à adapter selon la taille de l'objet)
camera.CFrame = CFrame.new(
center + Vector3.new(size.X, size.Y, size.Z) * 0.8,
center
)
-- on cache le frame de la boutique au lancement du jeu par defaut
frameStore.Visible = false
-- on écoute l'événement de clic sur le bouton pour ouvrir ou fermer la boutique
openButton.MouseButton1Click:Connect(function()
frameStore.Visible = not frameStore.Visible
end)
-- on récupère le bouton de la lampe dans la boutique
local lampButton = script.Parent.FrameStore.LampButton
-- on récupère le RemoteEvent pour acheter la lampe
local remoteBuy = game:GetService("ReplicatedStorage"):WaitForChild("RemoteBuy")
local msg = script.Parent.FrameStore.MsgLabel
local player = game.Players.LocalPlayer
-- on écoute l'événement de clic sur le bouton de la lampe pour l'acheter
lampButton.MouseButton1Click:Connect(function(itemName)
if not player then return end
if not lamp then return end
-- on envoie une requête au serveur pour acheter la lampe
local success, message = remoteBuy:InvokeServer(Constants.LAMP)
-- on affiche le message de retour du serveur sur l'acran de la boutique
msg.Text = message
-- ajouter la lampe dans l'inventaire du joueur
if success then
local backpack = player:WaitForChild("Backpack")
lamp.Parent = player:WaitForChild("Backpack")
end
end)
Courir plus vite
Modifie le localScript ScreenStore pour générer le pouvoir courir plus vite :
local Constants = require(game.ReplicatedStorage.Constants)
-- récupération du bouton pour ouvrir la boutique
local openButton = script.Parent.OpenButton
-- récupération du frame de la boutique
local frameStore = script.Parent.FrameStore
--[[
*** Rendre le viewport 3D de la lampe visible dans la boutique
]]
local replicatedStorage = game:GetService("ReplicatedStorage")
local lamp = replicatedStorage:WaitForChild("Lamp")
local viewport = script.Parent.FrameStore.ViewportFrame
-- Nettoyage
viewport:ClearAllChildren()
-- Clone de l'objet
local clone = lamp:Clone()
clone.Parent = viewport
-- Caméra
local camera = Instance.new("Camera")
viewport.CurrentCamera = camera
camera.Parent = viewport
local center
local size
-- Calcul de la position de la caméra (adapté à la taille de l'objet)
if clone:IsA("Model") then
size = clone:GetExtentsSize()
center = clone:GetPivot().Position
else
size = clone.Size
center = clone.Position
end
-- Position de la caméra (à adapter selon la taille de l'objet)
camera.CFrame = CFrame.new(
center + Vector3.new(size.X, size.Y, size.Z) * 0.8,
center
)
-- on cache le frame de la boutique au lancement du jeu par defaut
frameStore.Visible = false
-- on écoute l'événement de clic sur le bouton pour ouvrir ou fermer la boutique
openButton.MouseButton1Click:Connect(function()
frameStore.Visible = not frameStore.Visible
end)
-- on récupère le bouton de la lampe dans la boutique
local lampButton = script.Parent.FrameStore.LampButton
-- on récupère le RemoteEvent pour acheter la lampe
local remoteBuy = game:GetService("ReplicatedStorage"):WaitForChild("RemoteBuy")
local msg = script.Parent.FrameStore.MsgLabel
local player = game.Players.LocalPlayer
local humanoid
local leaderstats
local walkSpeed
local MAXSPEED = Constants.MAX_SPEED or 50
local function setup(character)
humanoid = character:WaitForChild("Humanoid")
leaderstats = player:FindFirstChild("leaderstats")
walkSpeed = humanoid.WalkSpeed
end
player.CharacterAdded:Connect(setup)
if player and player.Character then
setup(player.Character)
end
-- on écoute l'événement de clic sur le bouton de la lampe pour l'acheter
lampButton.MouseButton1Click:Connect(function(itemName)
if not player then return end
if not lamp then return end
-- on envoie une requête au serveur pour acheter la lampe
local success, message = remoteBuy:InvokeServer(Constants.LAMP)
-- on affiche le message de retour du serveur sur l'acran de la boutique
msg.Text = message
-- ajouter la lampe dans l'inventaire du joueur
if success then
local backpack = player:WaitForChild("Backpack")
lamp.Parent = player:WaitForChild("Backpack")
endend)
-- on récupère le bouton pour le pouvoir de courir plus vite
runButton.MouseButton1Click:Connect(function(itemName)
if not player then return end
if not leaderstats then return end
-- on envoie une requête au serveur pour le pouvoir courir plus vite
local success, message = remoteBuy:InvokeServer(Constants.POWER_RUN_MORE)
-- on affiche le message de retour du serveur sur l'acran de la boutique
msg.Text = message
end)
Vérifie que tu disposes bien du pouvoir de courir plus vite :
Ajouter le pouvoir de courir plus vite
Sur la boutique programmer le bouton « Courrir plus vite »
Objectif :
✔ programmer le bouton de la boutique « courir plus vite » ✔ envoyer une demande au serveur pour acheter le pouvoir ✔ recevoir une réponse succès ou pas ✔ activer un pouvoir avec le clavier par la touche R
Le principe du pouvoir :
Le joueur peut :
acheter un pouvoir “courir plus vite”
appuyer sur R pour l’activer
utiliser ce pouvoir un certain nombre de fois
Programme le clic sur le bouton « courir plus vite » :
Modifie le localScript StoreScript et ajoute en fin du script :
-- on récupère le bouton pour le pouvoir de courir plus vite
runButton.MouseButton1Click:Connect(function(itemName)
if not player then return end
if not leaderstats then return end
-- on envoie une requête au serveur pour le pouvoir courir plus vite
local success, message = remoteBuy:InvokeServer(Constants.POWER_RUN_MORE)
-- on affiche le message de retour du serveur sur l'acran de la boutique
msg.Text = message
end)
Programme pour que le joueur court plus vite sur l’appui de la touche R :
Modifie le localScript StoreScript et ajoute en fin du script :
--[[
Traitement des touches du clavier pour activer/désactiver les pouvoirs du joueur
]]
-- Connecte l'événement du clavier
local UserInputService = game:GetService("UserInputService")
local runMore = false
-- Récupère l'évènement du clavier
UserInputService.InputBegan:Connect(function(input, gameProcessed)
if not player then return end
if not humanoid then return end
-- Vérifie si l'évènement est un clavier
if input.KeyCode ~= Enum.KeyCode.Unknown then
-- Ferme l'écran de la boutique
frameStore.Visible = false
-- Vérifie si la touche appuyée est R
if input.KeyCode == Enum.KeyCode.R and not gameProcessed then
if runMore then
runMore = false
humanoid.WalkSpeed = walkSpeed
else
-- Vérifie si le leaderstats existe et le pouvoir de courir plus vite
if leaderstats and leaderstats:FindFirstChild(Constants.POWER_RUN_MORE) then
local run = leaderstats:FindFirstChild(Constants.POWER_RUN_MORE)
-- si le pouvoir est actif
if run.Value > 0 then
humanoid.WalkSpeed = MAXSPEED
run.Value -= 1
runMore = true
end
end
end
end
end
end)
A chaque utilisation de la touche R pour courir plus vite, le pouvoir diminue :
Fermeture automatique de la boutique
Fermer automatiquement la boutique en cliquant en dehors de la fen,être de la boutique ou en utilisant une touche du clavier.
Crée un TextButton dans un Frame qui couvre tout l’écran :
Renomme le TextButton en Background.
Donne une priorité à la fenêtre de la boutique et aux boutons de la boutique :
Modifie le localScript StoreScript :
-- on récupère le background de l'interface de la boutique
local background = script.Parent.FrameBackground.Background
--[[
Affichage ou non de la boutique sur l'appui de l'icone de la boutique
]]
-- on cache le frame de la boutique au lancement du jeu par defaut
frameStore.Visible = false
background.Interactable = false
-- on écoute l'événement de clic sur le bouton pour ouvrir ou fermer la boutique
openButton.MouseButton1Click:Connect(function()
frameStore.Visible = not frameStore.Visible
background.Interactable = frameStore.Visible
frameStore.MsgLabel.Text = "Choisis ton option"
end)
background.MouseButton1Click:Connect(function()
frameStore.Visible = false
-- rendre non cliquable
background.Interactable = frameStore.Visible
end)
et lors de l’appui d’une touche :
--[[
Traitement des touches du clavier pour activer/désactiver les pouvoirs du joueur
]]
-- Connecte l'événement du clavier
local UserInputService = game:GetService("UserInputService")
local runMore = false
-- Récupère l'évènement du clavier
UserInputService.InputBegan:Connect(function(input, gameProcessed)
if not player then return end
if not humanoid then return end
-- Vérifie si l'évènement est un clavier
if input.KeyCode ~= Enum.KeyCode.Unknown then
-- Ferme l'écran de la boutique
frameStore.Visible = false
background.Interactable = frameStore.Visible
-- Vérifie si la touche appuyée est R
if input.KeyCode == Enum.KeyCode.R and not gameProcessed then
if runMore then
runMore = false
humanoid.WalkSpeed = walkSpeed
else
-- Vérifie si le leaderstats existe et le pouvoir de courir plus vite
if leaderstats and leaderstats:FindFirstChild(Constants.POWER_RUN_MORE) then
local run = leaderstats:FindFirstChild(Constants.POWER_RUN_MORE)
-- si le pouvoir est actif
if run.Value > 0 then
humanoid.WalkSpeed = MAXSPEED
run.Value -= 1
runMore = true
end
end
end
end
end
end)
Plusieurs exemples de scripts LUA pour changer la couleur d’un part, mourir à petit feu, devenir tout petit, glisser, voler, se téléporter, grandir, sauter plus, courir plus, crée un menu, crée une arme fatale.
Crée un Folder à ton nom pour mettre toutes tes créations :
Changer la couleur d’un part
local part = script.Parent
part.Touched:Connect(function(hit)
part.BrickColor = BrickColor.random()
part.Material = Enum.Material.Neon
end)
Un part mortel :
local part = script.Parent
part.Touched:Connect(function(hit)
-- Récupération du joueur
local character = hit.Parent
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
if not humanoid or humanoid.Health <= 0 then return end
humanoid:TakeDamage(10)
end)
Rendre tout petit le joueur :
local part = script.Parent
part.Touched:Connect(function(hit)
-- Récupération du joueur
local character = hit.Parent
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
if not humanoid or humanoid.Health <= 0 then return end
humanoid.BodyHeightScale.Value = 0.4
humanoid.BodyWidthScale.Value = 0.4
humanoid.BodyDepthScale.Value = 0.4
humanoid.HeadScale.Value = 0.4
end)
Faire glisser un part avec un clickDetector :
local part = script.Parent
local clickDetector = part.ClickDetector
clickDetector.MouseClick:Connect(function(player)
part.Anchored = false
part.AssemblyLinearVelocity = Vector3.new(0, 0, 50)
end)
Part pour voler avec un clickDetector :
local AntiGravity = require(game.ReplicatedStorage.AntiGravity)
local part = script.Parent
local clickDetector = part.ClickDetector
clickDetector.MouseClick:Connect(function(player)
part.Anchored = false
AntiGravity.Start(part)
part.AssemblyLinearVelocity = Vector3.new(0, 10, 10)
end)
Téléportation d’un point A à un point B :
Voici l’explication du code, ligne par ligne :
Les variables de départ
local teleports = script.Parent
local teleportA = teleports.TeleportA
local teleportB = teleports.TeleportB
On récupère deux objets dans le jeu Roblox : la plateforme A (celle sur laquelle on va marcher) et la plateforme B (celle où on va arriver). Ces deux plateformes sont des enfants du script dans l’arbre du jeu.
L’événement Touched
teleportA.Touched:Connect(function(hit)
Touched est un événement Roblox : il se déclenche automatiquement dès que quelque chose touche la plateforme A. Le paramètre hit contient la pièce (la partie du corps) qui l’a touchée.
La vérification du joueur
local character = hit.Parent
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
if not humanoid or humanoid.Health <= 0 then return end
On remonte d’un niveau pour trouver le personnage entier (hit.Parent), puis on cherche s’il contient un Humanoid (ce qui prouve que c’est bien un joueur, pas un mur ou une balle). Si rien de valide n’est trouvé, ou si le joueur est mort (Health <= 0), on arrête tout avec return.
La téléportation
local offset = Vector3.new(0, 2, 0)
character:PivotTo(CFrame.new(teleportB.Position + offset))
Vector3.new(0, 2, 0) crée un décalage de 2 unités vers le haut pour que le joueur atterrisse au-dessus de la plateforme B et pas à l’intérieur. PivotTo est la fonction qui déplace instantanément tout le personnage à une nouvelle position — c’est ça la téléportation !
Le code complet de la téléportation
-- Configuration
local teleports = script.Parent
local teleportA = teleports.TeleportA
local teleportB = teleports.TeleportB
teleportA.Touched:Connect(function(hit)
-- Récupération du joueur
local character = hit.Parent
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
if not humanoid or humanoid.Health <= 0 then return end
-- Décalage légert vers le haut pour poser le joueur sur la plateforme
local offset = Vector3.new(0, 2, 0)
character:PivotTo(CFrame.new(teleportB.Position + offset))
end)
Grandir, Sauter, Courir plus vite :
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local humanoid = player.Character and player.Character:FindFirstChildOfClass("Humanoid")
local function onKeyPress(input, gameProcessed)
if not humanoid then return end
if input.KeyCode == Enum.KeyCode.F and not gameProcessed then
humanoid.JumpHeight += 8
end
if input.KeyCode == Enum.KeyCode.G and not gameProcessed then
humanoid.JumpHeight -= 8
end
print(input.KeyCode)
if input.KeyCode == Enum.KeyCode.X and not gameProcessed then
humanoid.WalkSpeed += 10
end
if input.KeyCode == Enum.KeyCode.Z and not gameProcessed then
humanoid.WalkSpeed -= 10
end
end
-- Connecter la fonction à l'événement InputBegan
UserInputService.InputBegan:Connect(onKeyPress)print("Hello world!")
Touches du clavier :
F sauter plus haut
G sauter moins haut
X courir plus vite
W courir moins vite
A grandir
E plus petit
Menu disponible pour le joueur :
local screenGui = script.Parent
local open = screenGui.OpenButton
local screen = screenGui.Screen
local close = screen.CloseButton
local scaleUpPlayer = screen.ScaleUpPlayerButton
local scaleDownPlayer = screen.ScaleDownPlayerButton
local runSlowerPlayer = screen.RunSlowerPlayerButton
local runFasterPlayer = screen.RunFasterPlayerButton
local jumpUpPlayer = screen.JumpUpPlayerButton
local jumpDownPlayer = screen.JumpDownPlayerButton
local replicatedStorage = game:GetService("ReplicatedStorage")
local runScalePlayer = replicatedStorage:WaitForChild("RunScalePlayer")
local runScalePlayer = replicatedStorage:WaitForChild("RunScalePlayer")
local HumanoidFinder = require(replicatedStorage.HumanoidFinder)
local StarterPlayer = game:GetService("StarterPlayer")
local humanoid, character = HumanoidFinder.GetLocal()
screen.Visible = false
open.MouseButton1Click:Connect(function()
screen.Visible = not screen.Visible
open.Visible = not open.Visible
end)
close.MouseButton1Click:Connect(function()
screen.Visible = not screen.Visible
open.Visible = not open.Visible
end)
scaleUpPlayer.MouseButton1Click:Connect(function()
runScalePlayer:FireServer(0.5)
end)
scaleDownPlayer.MouseButton1Click:Connect(function()
runScalePlayer:FireServer(-0.5)
end)
runSlowerPlayer.MouseButton1Click:Connect(function()
humanoid.WalkSpeed -= 10
end)
runFasterPlayer.MouseButton1Click:Connect(function()
humanoid.WalkSpeed += 10
end)
jumpUpPlayer.MouseButton1Click:Connect(function()
humanoid.JumpHeight += 8
end)
jumpDownPlayer.MouseButton1Click:Connect(function()
humanoid.JumpHeight -= 8
end)
Créer une arme fatale :
Créer un objet Tool puis un part renommer Handle :
Pour réduire à l’impuissance vos adversaires :
local part = script.Parent
part.Touched:Connect(function(hit)
if not hit then return end
-- Récupération du joueur
local character = hit.Parent
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
if not humanoid or humanoid.Health <= 0 then return end
humanoid:TakeDamage(10)
end)
Comment collaborer à plusieurs sur un même projet Roblox Studio
Travailler en équipe dans Roblox Studio permet à l’équipe de créer ensemble un même jeu, en temps réel. Vous apprendrez à vous organiser, à dialoguer, et à partager des responsabilités comme dans un vrai studio de jeux vidéo !
Pourquoi collaborer ?
Les avantages :
L’équipe travaille sur le même projet, en temps réel.
Chaque membre de l’équipe peut utiliser ses points forts : script, construction…
Tous les membres partagent ses idées et se complètent : l’équipe produit un jeu plus riche et mieux pensé.
Vous apprenez à communiquer comme de vrais développeurs.
Les risques si on ne s’organise pas :
Modifier un objet qu’un autre membre est en train de changer peut générer des conflits.
Effacer par erreur le travail d’un autre membre.
Oublier d’enregistrer ou écraser la version des autres.
Règles importantes pour bien travailler en équipe :
Communiquer avant de modifier un élément du jeu.
Ne jamais supprimer un objet sans prévenir.
Toujours sauvegarder régulièrement.
Respecter les rôles définis dans l’équipe (scripteur, designer…).
ATTENTION Un seul membre de l’équipe doit créer le jeu de base
Les deux grandes étapes
Étape 1 — Un membre crée le jeu et ajoute les collaborateurs de l’équipe
Dans chaque équipe, un seul membre crée le projet Roblox Studio.
Ouvre Roblox Studio
Se connecter avec un compte 123codage inscrit sur l’ordinateur : 123codageXX (XX entre 01 et 10 selon le numéro du PC).
Crée un nouveau projet
Choisir Baseplate pour commencer.
Active la collaboration
Dans le menu du haut : Fenêtre → Collaboration → Création en équipe
Un bouton “Sauvegarder sur Roblox” apparaît → clique dessus.
Donne un nom et une description au projet
Vous devez décider en groupe :
le nom du jeu,
une courte description (quel est le but du jeu ?).
Puis clique sur Sauvegarder.
Roblox studio se relance.
Ajoute les collaborateurs
Toujours dans : Fenêtre → Collaboration → Gérer les collaborateurs
Dans la zone de recherche, taper 123codage.
Sélectionner les comptes des membres de l’équipe (par exemple : 123codage01, 123codage05, etc.). → Le numéro du PC est marqué sur la tour.
Pour chaque membre :
Modifier ses droits
Choisir Modifier (autorise à tester et modifier le jeu)
La première étape est terminée ! Tu viens de créer un projet partagé et tu as ajouté les membres de l’équipe.
Étape 2 — Les autres membres ouvrent le projet partagé
Chaque membre de l’équipe doit maintenant accéder au projet :
Comment utiliser LinearVelocity, VectorForce et AssemblyLinearVelocity pour lancer ou faire bouger des objets. Comment utiliser HingeConstraint pour faire ouvrir une barrière. Comment construire une voiture et la piloter
LinearVelocity, VectorForce et AssemblyLinearVelocity — servent tous à faire bouger des objets dans Roblox, mais ils ne fonctionnent pas du tout de la même manière.
LinearVelocity
Ce que ça fait : ➡️ Fait bouger un objet à une vitesse constante dans une direction. ➡️ Roblox s’occupe automatiquement de calculer la force nécessaire pour maintenir cette vitesse.
C’est parfait pour :
Faire voler ou propulser un objet
Tirer des projectiles
Donner un mouvement stable et constant
VectorForce
Ce que ça fait : ➡️ Applique une force brute (comme une poussée de moteur ou la gravité). ➡️ La vitesse dépend de la masse de l’objet et des forces qui s’opposent.
L’objet est poussé mais ralentit ou accélère selon sa masse, sa friction, etc.
C’est parfait pour :
Simuler une propulsion physique réaliste
Faire un jetpack, un vent, une poussée de moteur
Ajouter une force continue (comme la gravité personnalisée)
AssemblyLinearVelocity
Ce que ça fait : ➡️ Change directement la vitesse actuelle du corps rigide complet. ➡️ C’est comme “modifier” la vitesse sans appliquer de vraie force.
L’objet prend instantanément cette vitesse. Pas de physique calculée, pas de force — juste une vitesse imposée.
📍 C’est parfait pour :
Faire bouger instantanément un objet
Lancer un objet d’un coup
Remettre à zéro sa vitesse facilement
Crée une balle à lancer
Crée une balle à partir de l’onglet Modèle puis Pièce et Sphère :
Renomme le part en « Balle » :
Change les propriétés suivantes du part Balle :
Déplace le part Balle sous ServerStorage :
ServerStorage permet de stocker des objets secrets ou internes au serveur (comme des scripts, modèles ou outils que les joueurs ne doivent pas voir directement). Donc la conséquence immédiate du déplacement de la balle : celle-ci n’est plus visible dans ton jeu, ni dans ton workspace.
Programme un lancer avec linearVelocity :
VectorVelocity fait bouger ta balle à une vitesse constante dans la direction du regard du joueur.
La balle rouge bouge sans la contrainte de la physique du jeu, notamment de la gravité.
Crée un script sous ServerScriptService et lance les balles devant le joueur :
-- je récupère la balle dans le ServerStorage
local ss = game:GetService("ServerStorage")
local balle= ss:WaitForChild("Balle")
-- je récupère la liste des jueurs
local players = game:GetService("Players")
-- je lance la blle avec linearVelocity
local function linearVelocityBall(player)
-- je récupère le corps du joueur
local char = player.Character or
player.CharacterAdded:Wait()
local hrp = char:WaitForChild("HumanoidRootPart")
-- Je génère une copie de la balle dans le workspace
local ball = balle:Clone()
ball.BrickColor = BrickColor.Red()
ball.Parent = workspace
ball.CFrame = hrp.CFrame * CFrame.new(0, 2, -2)
-- J'applique à la balle une force constante
local lv = Instance.new("LinearVelocity")
lv.MaxForce = math.huge
lv.VelocityConstraintMode =
Enum.VelocityConstraintMode.Vector
lv.VectorVelocity = hrp.CFrame.LookVector * 5
lv.Parent = ball
-- J'attache cette force à la balle
local attchment = Instance.new("Attachment")
attchment.Parent = ball
lv.Attachment0 = attchment
-- Je détruis la balle au bout de 5 secondes
game.Debris:AddItem(ball, 5)
end
-- Le joueur se connecte
players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function()
for i = 1, 5 do
task.wait(1)
linearVelocityBall(player)
end
end)
end)
VectorVelocity fait bouger ta balle à une vitesse constante dans la direction du regard du joueur.
L’objet bouge sans que tu aies besoin de gérer la physique.
Ton script instancie un Attachment. Un Attachment est un point de repère invisible dans une pièce (Part, MeshPart, etc.) C’est comme une punaise virtuelle que tu peux placer n’importe où dans la pièce.
🔹 Il sert à :
indiquer un point précis où appliquer une force, un effet, ou une jointure,
ou relier deux objets ensemble (LinearVelocity, VectorForce).
L’objet bouge sans que tu aies besoin de gérer la physique.
Programme un lancer avec vectorForce:
Applique une force brute (comme une poussée de moteur ). La vitesse dépend de la masse de l’objet et des forces qui s’opposent. La belle bleue est poussée mais ralentit ou accélère selon sa masse, sa friction et subit la gravité.
Modifie ton script pour le lancer avec une balle bleue :
local function vectorForceBall(player)
-- je récupère le corps du joueur
local char = player.Character or player.CharacterAdded:Wait()
local hrp = char:WaitForChild("HumanoidRootPart")
-- Je génère une copie de la balle dans le workspace
local ball = balle:Clone()
ball.BrickColor = BrickColor.Blue()
ball.Parent = workspace
ball.CFrame = hrp.CFrame * CFrame.new(0, 2, -2)
-- J'applique à la balle un VectorForce
local vf = Instance.new("VectorForce")
vf.RelativeTo = Enum.ActuatorRelativeTo.World
vf.ApplyAtCenterOfMass = true
vf.Force = hrp.CFrame.LookVector * ball:GetMass() * 5
vf.Parent = ball
-- J'attache cette force à la balle
local attchment = Instance.new("Attachment")
attchment.Parent = ball
vf.Attachment0 = attchment
-- Je détruis la balle au bout de 5 secondes
game.Debris:AddItem(ball, 5)
end
players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function()
for i = 1, 5 do
task.wait(1)
linearVelocityBall(player)
task.wait(1)
vectorForceBall(player)
end
end)
end)
Programme un lancer avec AssemblyLinearVelocity
La balle verte prend instantanément la vitesse appliquée. Pas de physique calculée, pas de force — juste une vitesse imposée
Modifie ton script :
local function assemblyLinearVelocityBall(player)
-- je récupère le corps du joueur
local char = player.Character or player.CharacterAdded:Wait()
local hrp = char:WaitForChild("HumanoidRootPart")
-- Je génère une copie de la balle dans le workspace
local ball = balle:Clone()
ball.BrickColor = BrickColor.Green()
ball.Parent = workspace
ball.CFrame = hrp.CFrame * CFrame.new(0, 2, -2)
-- J'applique une vitesse à l'objet
ball.AssemblyLinearVelocity = hrp.CFrame.LookVector *
ball:GetMass() * 5
-- Je détruis la balle au bout de 5 secondes
game.Debris:AddItem(ball, 5)
end
players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function()
task.wait(5)
for i = 1, 5 do
task.wait(1)
linearVelocityBall(player)
task.wait(1)
vectorForceBall(player)
task.wait(1)
assemblyLinearVelocityBall(player)
end
end)
end)
PARTIE 02 : Une barrière
Avant de commencer à construire ta barrière, choisis ces options pour ton projet :
Construction d’une barrière
Puis commence à construire ta barrière en créant un bloc :
Renomme le part en Socle :
Modifie les propriétés suivantes :
Puis un deuxième part pour la barrière :
Renomme le part en Barriere :
Avec les propriétés suivantes :
Sous ton workspace crée un Model que tu renommes en Barriere puis range les deux parts précédent sous ce Model :
Installe une charnière pour le mécanisme d’ouverture de la porte :
Avec un premier point d’appui sur la barrière et un deuxième sur le socle :
Tu dois obtenir ce résultat dans explorer :
deux points d’attachement un sur le socle et un autre sur la barrière,
une charnière HingeConstraint sur la barrière.
Tu peux régler plus précisément le point d’attachement, en sélectionnant celui-ci dans explorer et en utilisant l’outil déplacer :
Modifie les propriétés du HingeConstraint :
Si tu lances ton jeu, tu obtiens ce résultat :
Si tu changes la propriété TargetAngle :
En lançant ton jeu, tu obtiens le résultat suivant :
Automatisation d’ouverture de la barrière
Rajoute une boîte de collision transparente autour de la barrière afin d’automatiser son ouverture :
N’oublie pas d’enlever le CanCollide de la boîte de collision.
Puis crée un script dans ton Model Barriere :
Puis saisie ce script pour ouvrir et fermer automatiquement ta barrière :
-- Récupération de la barrière
local barriere = script.Parent.Barriere
-- Récupération de la boîte de collision
local boiteCollision = script.Parent.BoiteCollision
-- Quand la boîte de collision touche un un personnage
boiteCollision.Touched:Connect(function(hit)
-- On récupère le modèle parent de la pièce touchée
local character = hit.Parent
-- On vérifie si ce modèle a un "Humanoid" (seuls les personnages en ont un)
local humanoid = character:FindFirstChild("Humanoid")
if humanoid then
-- Ouverture de la barrière
barriere.HingeConstraint.TargetAngle = 90
end
end)
-- Quand la boîte de collision ne touche plus de personnage
boiteCollision.TouchEnded:Connect(function(hit)
-- On récupère le modèle parent de la pièce touchée
local character = hit.Parent
-- On vérifie si ce modèle a un "Humanoid" (seuls les personnages en ont un)
local humanoid = character:FindFirstChild("Humanoid")
if humanoid then
-- Fermeture de la barrière
barriere.HingeConstraint.TargetAngle = 0
end
end)
-- Fermeture de la barrière au début
barriere.HingeConstraint.TargetAngle = 0
Lance ton jeu pour tester ta barrière automatique :
PARTIE 03 : ROULER EN VOITURE
Choisis ces paramètres pour la construction de ton véhicule :
Joindre les surfaces
1 pour l’incrémentation pour dimensionner les objets
Choix d’un Bloc pour le châssis de la voiture
Redimensionne le châssis de ta voiture et renomme le part en « Chassis« :
Crée un nouveau bloc pour créer les moyeux des roues de la voiture :
Choisis sa demension :
Duplique en quatre blocs :
Colle les blocs sur le châssis :
Sur les quatre côtés du châssis :
Duplique deux nouveaux blocs pour la direction, modifie la couleur :
Décoche la propriété CanCollide pour ces blocs :
Sous worksapace crée cette structure et range tous les parts de ta voiture :
Par le clic droit de la souris, demande le menu suivant :
Insére l’objet VehicleSeat :
Installe cet objet sur le châssis, cet objet représente le siège du conducteur :
Déplace cet objet sous l’arborescence de ta voiture :
Lance le jeu pour venir t’assoir sur le siège de ton véhicule :
Insère des contraintes entre les pièces de ton véhicule pour transmettre la direction :
Vision de la transmission de direction à droite
Vision de la transmission de direction à gauche
Tu dois obtenir ce résultat dans ton explorer :
Sélectionne les HingeConstraint de tes deux roues de l’avant pour modifier les propriétés suivantes :
Rappel : c’est quoi un HingeConstraint ?
Un HingeConstraint = une charnière virtuelle. Il permet à un objet de tourner autour d’un axe (comme une porte, une roue, ou un bras robotique).
Mais quand on ajoute un moteur dessus, il peut :
tourner jusqu’à un certain angle → Servo
Quand ActuatorType = Servo
Le Servo agit comme un moteur intelligent : il regarde la position actuelle de la pièce, puis la fait tourner jusqu’à atteindre l’angle qu’on lui demande.
👉 On lui donne une cible (TargetAngle), et il s’arrête tout seul quand il y arrive.
🔩 3️⃣ Les propriétés importantes
Propriété
Rôle
TargetAngle
l’angle à atteindre (en degrés)
ServoMaxTorque
la force que le moteur peut exercer pour y arriver
AngularSpeed
vitesse à laquelle il tourne pour atteindre l’angle
ActuatorType
doit être mis sur Enum.ActuatorType.Servo
Tu dois obtenir la visualisation du mécanisme de la direction de tes deux roues :
Renomme les parts des roues avant droite et gauche ayant un HingeConstraint en « Direction » :
Rend invisible les deux parts des deux roues avant :
Rajoute un script sous le modèle de ta voiture :
local seat = script.Parent.VehicleSeat
local roueAvantGauche = script.Parent.RoueAvantGauche
local roueAvantDroite = script.Parent.RoueAvantDroite
local steerAngle = 30
seat:GetPropertyChangedSignal("Steer"):Connect(function()
print("Steer =", seat.Steer)
roueAvantGauche.Direction.HingeConstraint.TargetAngle =
steerAngle * seat.Steer
roueAvantDroite.Direction.HingeConstraint.TargetAngle =
steerAngle * seat.Steer
end)
Ce script fait tourner les roues avant d’une voiture quand le joueur tourne à gauche ou à droite avec le clavier (A / D ou Q / D selon ton clavier).
📘 Le code
local seat = script.Parent.VehicleSeat
➡️ On récupère le siège de conduite de la voiture (VehicleSeat). C’est lui qui détecte quand le joueur tourne à gauche ou à droite.
local roueAvantGauche = script.Parent.RoueAvantGauche
local roueAvantDroite = script.Parent.RoueAvantDroite
➡️ On récupère les deux roues avant du véhicule, pour pouvoir les faire pivoter.
RoueAvantGauche = la roue de gauche
RoueAvantDroite = la roue de droite
Ces roues ont chacune une charnière (HingeConstraint) qui permet de les faire tourner.
local steerAngle = 30
➡️ C’est l’angle maximum de rotation des roues (en degrés). Ici : les roues peuvent tourner jusqu’à 30° vers la gauche ou la droite.
➡️ Le script change l’angle cible (TargetAngle) de la charnière des roues avant. 👉 Résultat : quand le joueur tourne, les roues s’inclinent à gauche ou à droite comme une vraie voiture 🚘
🧠 En résumé
Élément
Rôle
VehicleSeat
détecte les touches du joueur
Steer
indique la direction du volant (-1, 0, 1)
HingeConstraint
permet de faire tourner les roues
TargetAngle
angle que doit atteindre la roue
steerAngle
limite maximale de rotation (ici 30°)
🎯 Ce que fait le script
➡️ À chaque fois que le joueur tourne :
Le siège envoie une valeur (Steer)
Le script multiplie cette valeur par 30
Les roues avant tournent de cet angle
💬 En bref :
Quand tu tournes le volant, les roues tournent dans la même direction, comme une vraie voiture
Lance ton jeu pour vérifier le bon fonctionnement de ta direction :
Maintenant nous allons installer les roues.
Crée une roue centrée sur la direction de ta voiture, surélève ton châssis de voiture :
Renomme le part en « roue » :
Duplique en 4 roues :
Place tes 4 roues bien centrées :
Range tes roues dans l’arborescence de ta voiture :
Pour chaque roue positionne une charnière à partir du centre de la roue sur le centre de la direction :
Sélectionne les quatre HingeConstraint des quatre roues et applique ces propriétés :
Qu’est-ce qu’un HingeConstraint ?
Un HingeConstraint (en français : contrainte de charnière) relie deux objets pour qu’ils puissent tourner autour d’un axe, comme une porte sur ses gonds ou une roue sur son axe.
Sans moteur, il laisse juste la pièce tourner librement. Avec un Motor, il fait tourner activement la pièce tout seul.
Quand ActuatorType = Motor
Alors le HingeConstraint se comporte comme un moteur qui fait tourner la pièce en continu 🌀
Les propriétés les plus importantes sont :
Propriété
Rôle
AngularVelocity
vitesse de rotation de la pièce (en radians/s)
MotorMaxTorque
force maximum du moteur (plus elle est grande, plus il peut pousser)
AngularResponsiveness
vitesse à laquelle le moteur s’adapte aux changements (réactivité)
Vérifie le sens de rotation :
Tu remarques que le sens gauche est inverse de celui du sens droit.
Vérifie le bon fonctionnement de la direction :
Modifie ton script :
local seat = script.Parent.VehicleSeat
local roueAvantGauche = script.Parent.RoueAvantGauche
local roueAvantDroite = script.Parent.RoueAvantDroite
local roueArriereGauche = script.Parent.RoueArriereGauche
local roueArriereDroite = script.Parent.RoueArriereDroite
local steerAngle = 30
local speed = 60
seat:GetPropertyChangedSignal("Steer"):Connect(function()
print("Steer =", seat.Steer)
roueAvantGauche.Direction.HingeConstraint.TargetAngle =
steerAngle * seat.Steer
roueAvantDroite.Direction.HingeConstraint.TargetAngle =
steerAngle * seat.Steer
end)
seat:GetPropertyChangedSignal("Throttle"):Connect(function()
print("Throttle =", seat.Throttle)
roueAvantGauche.Roue.HingeConstraint.AngularVelocity = speed * seat.Throttle
roueAvantDroite.Roue.HingeConstraint.AngularVelocity = speed * -seat.Throttle
roueArriereGauche.Roue.HingeConstraint.AngularVelocity = speed * seat.Throttle
roueArriereDroite.Roue.HingeConstraint.AngularVelocity = speed * -seat.Throttle
end)
Ce script permet de faire tourner les roues d’une voiture quand le joueur accélère ou recule avec le clavier (les touches Z / S ou flèche haut / flèche bas).
🧩 Étape 1 : on récupère les objets importants
local seat = script.Parent.VehicleSeat
➡️ On récupère le siège de conduite de la voiture. C’est lui qui détecte quand le joueur appuie pour avancer ou reculer.
local roueAvantGauche = script.Parent.RoueAvantGauche
local roueAvantDroite = script.Parent.RoueAvantDroite
local roueArriereGauche = script.Parent.RoueArriereGauche
local roueArriereDroite = script.Parent.RoueArriereDroite
➡️ On récupère les 4 roues du véhicule :
2 à l’avant
2 à l’arrière
Chaque roue contient un objet appelé HingeConstraint, une charnière qui permet à la roue de tourner sur elle-même.
🧩 Étape 2 : les réglages de base
local steerAngle = 30
local speed = 60
steerAngle : angle pour tourner (pas utilisé ici, mais utile si on veut tourner les roues plus tard).
speed : vitesse de rotation des roues (plus la valeur est grande, plus la voiture va vite 🚀).
🧩 Étape 3 : détecter quand le joueur accélère ou recule
Chaque roue tourne grâce à sa vitesse angulaire (AngularVelocity), c’est-à-dire la vitesse à laquelle elle tourne autour de son axe.
Mais pourquoi une roue est multipliée par -seat.Throttle ❓ 👉 Parce que les roues droite et gauche sont dans des sens opposés (comme une vraie voiture). Si elles tournaient dans le même sens, la voiture glisserait sur place au lieu d’avancer 😅.
Donc :
les roues de gauche tournent dans un sens,
celles de droite tournent dans le sens inverse, ➡️ ce qui fait avancer la voiture dans la bonne direction 🚗💨