Dans ce tutoriel sur Roblox, tu vas pouvoir apprendre à créer un parcours de plateformes dynamique et dangereux. Le principe est simple : plusieurs chemins sont proposés au joueur pour atteindre la sortie du niveau. Mais attention, les différents chemins ne restent pas visibles éternellement ! Grâce à un script en Lua, certains chemins disparaissent régulièrement au fil du temps. Le joueur doit donc observer, réfléchir rapidement et changer de direction avant de tomber dans le vide.
Création de la structure des différents chemins
Dans un premier temps, créer une structure de différents chemins composés de plateformes.
Créer un Folder principal nommé PathsFinding puis des sous Folder qui représenteront tes différents chemins possibles Path01, Path02 … puis sous les différents chemins créer tes plateformes, puis un Script :
Programmation de l’animation des différents chemins
Saisie ce code dans ton script, ce script en Lua pour Roblox permet de créer plusieurs chemins de plateformes qui apparaissent et disparaissent automatiquement.
Au début, le programme récupère le service TweenService, utilisé pour créer des animations fluides comme les effets de disparition et d’apparition.
Les variables de configuration définissent combien de temps un chemin reste visible, le temps d’attente entre les transitions et la durée de l’animation :
local DISPLAY_DURATION = 3 — secondes d’affichage du chemin
local WAIT_TIME = 3 — secondes entre chaque transition
local FADE_DURATION = 0.8 — secondes pour le fade in/out
Le script récupère ensuite tous les dossiers contenant les chemins et les trie dans l’ordre (path1, path2, etc.).
Chaque plateforme est préparée : elle devient fixe (Anchored = true), invisible (Transparency = 1) et sans collision (CanCollide = false).
Les informations importantes des chemins sont stockées dans des tableaux pour rendre le script plus rapide et éviter des calculs inutiles. La fonction fadeParts() sert à rendre les plateformes visibles ou invisibles grâce à une animation progressive appelée “fade”. Au démarrage, seul le premier chemin apparaît et devient solide pour que le joueur puisse marcher dessus.
Ensuite, une boucle infinie fait apparaître le chemin suivant pendant que l’ancien disparaît progressivement. Grâce à ce système, le joueur doit constamment changer de chemin pour éviter de tomber et réussir à trouver la sortie du niveau dans Roblox Studio.
local TweenService = game:GetService("TweenService")
-- Configuration
local DISPLAY_DURATION = 3 -- secondes d'affichage du chemin
local WAIT_TIME = 3 -- secondes entre chaque transition
local FADE_DURATION = 0.8 -- secondes pour le fade in/out
-- Récupérer tous les chemins du dossier
local pathsFinding = script.Parent
-- trie des chemins par leur nom : path1, path2, path3, ...
local children = pathsFinding:GetChildren()
table.sort(children, function(a, b) return a.Name < b.Name end)
local maxPaths = 0
-- Cache des parts par niveau (évite GetChildren() en boucle)
local pathFinding = {}
-- Cache des données par niveau (évite GetAttribute à chaque frame)
local pathData = {}
-- Prépare les données des différents chemins
for i, path in ipairs(children) do
if not path:IsA("Folder") then continue end
local parts = {}
for _, part in ipairs(path:GetChildren()) do
if not part:IsA("BasePart") then continue end
part.Anchored = true
part.CanCollide = false
part.Transparency = 1
table.insert(parts, part)
end
maxPaths += 1
pathFinding[maxPaths] = parts
-- Stocke les attibuts en mémoire
pathData[maxPaths] = {
displayDuration = path:GetAttribute("DisplayDuration") or DISPLAY_DURATION,
waitTime = path:GetAttribute("WaitTime") or WAIT_TIME,
}
end
-- TweenInfo créé une seule fois
local tweenInfo = TweenInfo.new(FADE_DURATION, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut)
-- Rend visible ou invisible tous les les parts d'un chemin
local function fadeParts(parts, targetTransparency, canCollide)
for _, part in ipairs(parts) do
TweenService:Create(part, tweenInfo, {
Transparency = targetTransparency
}):Play()
if canCollide ~= nil then
part.CanCollide = canCollide
end
end
end
-- Affiche le premier chemin au démarrage
fadeParts(pathFinding[1], 0, true)
local currentIndex = 1
while true do
-- Attendre avant de rendre in le chemin actuel
task.wait(pathData[currentIndex].displayDuration)
-- Calcule l'index du prochain chemin (boucle)
local nextIndex = (currentIndex % maxPaths) + 1
-- Transition simultanée : fade out ancien + fade in nouveau
fadeParts(pathFinding[nextIndex], 0, true)
-- Attendre avant de rendre invisible le chemin actuel
task.wait(pathData[currentIndex].waitTime)
fadeParts(pathFinding[currentIndex], 1, false)
-- Attendre le temps du fadout avant de continuer
task.wait(FADE_DURATION)
-- Mise à jour l'index pour la prochaine itération
currentIndex = nextIndex
end
Dans ce tutoriel, tu vas apprendre à programmer des ennemis très simples dans Roblox. Les ennemis sont représentés par des blocs. Les ennemies détectent un joueur proche, avancent vers lui, et lui infligent des dégâts lorsqu’ils le touchent. Tu vas construire ton script pour gérer plusieurs ennemis qui pourront avoir des comportements différents : distance proche différence, vitesse de déplacement différente, niveau de dommage différent.
Les ennemis s’approchent
Dans un premier temps construis l’arborescence suivante avec un Folder qui va regrouper tous tes ennemis, un script, des Part qui vont représenter tes ennemis :
Renomme les éléments de ton arborescence :
Folder : Enemies
Script : EnemiesScript
Part : Enemy
Puis saisie le code suivant dans ton script pour que tous les blocs ennemis s’approchent du joueur à partir d’une certaine distance entre le joueur et le bloc :
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local Debris = game:GetService("Debris")
-- Configuration
local DETECTION_DISTANCE = 30 -- distance de détection du joueur (studs)
local SPEED = 10 -- vitesse de déplacement (studs/s)
local STOP_DISTANCE = 3 -- distance à laquelle la part s'arrête
local enemies = script.Parent
-- Cache des données par platform (évite GetAttribute à chaque frame)
local enemiesData = {}
-- Initialisation
for _, enemy in enemies:GetChildren() do
if not enemy:IsA("BasePart") then continue end
enemy.Anchored = true
-- Stocke les données en mémoire
enemiesData[enemy] = {
detectionDistance = enemy:GetAttribute("DetectionDistance") or DETECTION_DISTANCE,
speed = enemy:GetAttribute("Speed") or SPEED,
stopDistance = enemy:GetAttribute("StopDistance") or STOP_DISTANCE,
}
end
RunService.Heartbeat:Connect(function(deltaTime)
-- Récupère le joueur local (ou le premier joueur connecté côté serveur)
local player = Players:GetPlayers()[1]
if not player then return end
local character = player.Character
if not character then return end
-- Récupère la position du joueur
local root = character:FindFirstChild("HumanoidRootPart")
if not root then return end
for enemy, data in pairs(enemiesData) do
-- Vérifie que la plateforme existe encore
if not enemy or not enemy.Parent then
enemyData[enemy] = nil
continue
end
-- Si la part est déjà touchée, on ne fait rien
if enemy:FindFirstChild("Touched") then return end
-- Vérifie que le joueur est à portée
local distance = (root.Position - enemy.Position).Magnitude
-- Trop loin : la part attend
if distance > data.detectionDistance then return end
-- Assez proche : arrêt
if data.stopDistance > 0 and distance <= data.stopDistance then
enemy.AssemblyLinearVelocity = Vector3.zero
return
end
-- Calcul de la direction vers le joueur
local direction = (root.Position - enemy.Position).Unit
local newPosition = enemy.Position + direction * SPEED * deltaTime
-- Mise à jour de la force à appliquer en fonction de la vitesse
enemy.AssemblyLinearVelocity = direction * data.speed
-- Mise à jour de la position de la part et son orientation
enemy.CFrame = CFrame.lookAt(enemy.Position, root.Position) + direction * data.speed * deltaTime
end
end)
Lance le jeu : les blocs s’approchent du joueur mais restent à une certaine distance du joueur.
Les ennemis infligent des dommages au joueur
Dans le script suivant les blocs s’approchent à toucher le joueur, et lui infligent des dommages :
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local Debris = game:GetService("Debris")
-- Configuration
local DETECTION_DISTANCE = 30 -- distance de détection du joueur (studs)
local SPEED = 10 -- vitesse de déplacement (studs/s)
local STOP_DISTANCE = 0 -- distance à laquelle la part s'arrête
local DAMAGE = 5 -- dégâts infligés aux joueurs
local RESET_DELAY = 0.5 -- délai avant de pouvoir toucher à nouveau (secondes)
local enemies = script.Parent
-- Cache des données par platform (évite GetAttribute à chaque frame)
local enemiesData = {}
-- Initialisation
for _, enemy in enemies:GetChildren() do
if not enemy:IsA("BasePart") then continue end
enemy.Anchored = true
enemy.CanCollide = true
-- Stocke les données en mémoire
enemiesData[enemy] = {
detectionDistance = enemy:GetAttribute("DetectionDistance") or DETECTION_DISTANCE,
speed = enemy:GetAttribute("Speed") or SPEED,
stopDistance = enemy:GetAttribute("StopDistance") or STOP_DISTANCE,
damage = enemy:GetAttribute("Damage") or DAMAGE,
resetDelay = enemy:GetAttribute("ResetDelay") or RESET_DELAY,
}
enemy.Touched:Connect(function(otherPart)
-- Vérifie que l'objet touché est un personnage
local character = otherPart.Parent
if not Players:GetPlayerFromCharacter(character) then return end
local humanoid = character:FindFirstChildOfClass("Humanoid")
if not humanoid or humanoid.Health <= 0 then return end
-- Vérifie que la part n'a pas déjà été touchée
if enemy:FindFirstChild("Touched") then return end
-- Applique les dégâts et empêche la répétition
humanoid:TakeDamage((enemiesData[enemy].damage))
enemy.AssemblyLinearVelocity = Vector3.zero
-- Marque la part comme touchée
local tag = Instance.new("Folder")
tag.Name = "Touched"
tag.Parent = enemy
Debris:AddItem(tag, enemiesData[enemy].resetDelay)
end)
end
RunService.Heartbeat:Connect(function(deltaTime)
-- Récupère le joueur local (ou le premier joueur connecté côté serveur)
local player = Players:GetPlayers()[1]
if not player then return end
local character = player.Character
if not character then return end
-- Récupère la position du joueur
local root = character:FindFirstChild("HumanoidRootPart")
if not root then return end
for enemy, data in pairs(enemiesData) do
-- Vérifie que la plateforme existe encore
if not enemy or not enemy.Parent then
enemyData[enemy] = nil
continue
end
-- Si la part est déja touchée, on ne fait rien
if enemy:FindFirstChild("Touched") then return end
-- Vérifie que le joueur est à portée
local distance = (root.Position - enemy.Position).Magnitude
-- Trop loin : la part attend
if distance > data.detectionDistance then return end
-- Assez proche : arrêt
if data.stopDistance > 0 and distance <= data.stopDistance then
enemy.AssemblyLinearVelocity = Vector3.zero
return
end
-- Calcul de la direction vers le joueur
local direction = (root.Position - enemy.Position).Unit
local newPosition = enemy.Position + direction * SPEED * deltaTime
-- Mise à jour de la force à appliquer en fonction de la vitesse
enemy.AssemblyLinearVelocity = direction * data.speed
-- Mise à jour de la position de la part et son orientation
enemy.CFrame = CFrame.lookAt(enemy.Position, root.Position) + direction * data.speed * deltaTime
end
end)
Paramétrer chaque ennemis
Ajoute aux propriétés de chaque ennemi les attributs suivants avec des valeurs différentes :
DetectionDistance : distance de détection du joueur (studs)
Speed : vitesse de déplacement (studs/s)
StopDistance : distance à laquelle la part s’arrête si 0 l’ennemi vient toucher le joueur
Damage : dégâts infligés aux joueurs
ResetDelay : délai avant de pouvoir toucher à nouveau (secondes)
Puis saisie le Nom de l’attribut ainsi que son type number :
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 = 5 -- secondes avant respawn
local FADE_SPEED = 0.01 -- vitesse de disparition
local COIN_LEADERBOARD = "Coins"
-- 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
-- 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(2, 4, 2)
hitbox.Transparency = 1
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
-- 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, 1.5, 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.CanCollide 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 = 5 -- secondes avant respawn
local FADE_SPEED = 0.01 -- vitesse de disparition
local COIN_LEADERBOARD = "Coins"
-- 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
-- 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(2, 4, 2)
hitbox.Transparency = 1
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
-- 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, 1.5, 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.CanCollide = 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.CanCollide 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.CanCollide = 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)
billboard.Enabled = true -- réaffiche le label au respawn
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.CanCollide 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)