Catégories
Jeu vidéo ROBLOX

Adventure interactive rooms

Créer un jeu sur Roblox, inventer une expérience interactive pour les joueurs. tu vas construire un véritable espace de salles de cinéma interactives dans Roblox Studio.

Le joueur peut se déplacer librement d’une salle à l’autre, découvrir différents écrans et interagir directement avec eux grâce à la souris. Chaque salle pourra proposer une ambiance différente, des boutons interactifs ou des animations qui réagissent aux actions du joueur.

Par script, tu iras encore plus loin en découvrant comment modifier la caméra du joueur pour rendre l’expérience plus immersive. Selon les situations, la caméra permettra :

  • de passer automatiquement en vue First Person lorsque le joueur entre dans une salle ;
  • ou de rapprocher la caméra du personnage pour donner un effet de cinéma ou de jeu narratif ;
  • et même de changer la caméra avec une touche du clavier.

À travers ce projet, tu utilisera plusieurs outils importants de Roblox Studio :

  • la création d’interfaces et d’écrans interactifs sur des parts;
  • les scripts en langage Lua ;
  • la gestion des déplacements du joueur ;
  • les événements déclenchés par le clavier ou les collisions ;
  • et le contrôle de la caméra pour améliorer l’immersion du jeu.

A toi de réfléchir à partir de ces scripts pour programmer un jeu comme de vrais créateurs de jeux vidéo : comment guider le joueur, rendre une salle intéressante et créer une ambiance unique grâce à la caméra et aux interactions.

Crée la structure de la salle de cinéma avec une boite de collision hit box et un écran sur le lequel tu positionnes un surfaceGui. Sur le surfaceGui crée un textButton que l’on fera bouger :

Le joueur interagit sur le bouton.

Crée et modifie le script sous surfaceGui pour déplacer le bouton et interagir avec la souris :

-- récupération du frame
local frame = script.Parent.Frame
-- récupération du service de tween
local tweenService = game:GetService("TweenService")
-- récupération du bouton
local button = frame.TextButton
-- variable de bascule entre pause et reprendre le déplacement
local pause = false
-- couleur de base du bouton
local colorBase = button.BackgroundColor3

-- déplacement horizontal du bouton avec un tweenService
local tween = tweenService:Create(button, 
	TweenInfo.new(10, 
		Enum.EasingStyle.Linear, 
		Enum.EasingDirection.InOut, -1, false), 
	{Position = button.Position + UDim2.new(1, 0, 0, 0)})

tween:Play()

-- action sur le click de souris
button.MouseButton1Click:Connect(function()
	--click de la souris pour mettre en pause ou reprendre le tween
	pause = not pause
	if pause then
		tween:Pause()
		button.BackgroundColor3 = Color3.fromRGB(0, 255, 0)
	else
		tween:Play()
		button.BackgroundColor3 = colorBase
	end


end)

Sous ReplicatedStorage crée un remoteEvent, ce remoteEvent est utilisé pour prévenir un scriptLocal du joueur afin de changer son comportement :

La hitbox est une Part qui occupe tout l’intérieur de la salle. Elle est transparente et sans CanCollide. Elle sert à gérer la collision lorsque le joueur pénètre dans la salle.

Crée et modifie ce script pour gérer la collision avec le joueur quand celui ci rentre ou sort de la salle pour envoyer un signal au script local du joueur :

local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- récupération du remoteEvent
local kioskEvent = ReplicatedStorage:WaitForChild("KioskEvent")
-- récupération des services Debris
local Debris            = game:GetService("Debris")
-- récupération de la hitbox
local hitbox = script.Parent
-- nom de l'action
local actionName = "KioskLocked"
--- durée du cooldown en secondes
local COOLDOWN_DURATION = 2

-- locker du kiosk
local function KioskLocked(character)
	
	-- si le joueur est déjà dans la salle, ne rien faire
	if character:FindFirstChild(actionName) then return false end
	-- création d'un tag temporaire pour indiquer que le joueur est dans la salle		
	local tag = Instance.new("Folder")
	tag.Name  = actionName
	tag.Parent = character
	Debris:AddItem(tag, COOLDOWN_DURATION)
	
	return true
end

-- détection du joueur qui entre dans la salle 
hitbox.Touched:Connect(function(hit)
	local player = game.Players:GetPlayerFromCharacter(hit.Parent)
	if player then
		local character = player.Character
		if not character then return end
		-- Envoyer au client concerné uniquement
		if KioskLocked(character) then
			kioskEvent:FireClient(player, true)  
		end	
	end
end)

-- détection du joueur qui sort de la salle
hitbox.TouchEnded:Connect(function(hit)
	local player = game.Players:GetPlayerFromCharacter(hit.Parent)
	if player then
		local character = player.Character
		if not character then return end
		-- Envoyer au client concerné uniquement
		if KioskLocked(character) then
			kioskEvent:FireClient(player, false)  
		end		
	end
end)

Crée un localScript sous StarterPlayer pour gérer une touche pour le changement de caméra sur le joueur ou pour gérer le remoteEvent venant du serveur :

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local kioskEvent = ReplicatedStorage:WaitForChild("KioskEvent")
local CinemaEvent = ReplicatedStorage:WaitForChild("CinemaEvent")

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")

local player = Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local humanoid = char:FindFirstChildOfClass("Humanoid")
local head = char:WaitForChild("Head")
local hrp = char:WaitForChild("HumanoidRootPart")
local cam = workspace.CurrentCamera

local angleY = 0
local angleX = 0
local DISTANCE = -0.5
local HEIGHT = 0

local KEY_ROTATION_SPEED = 2

local isLocked = false
local connection = nil  -- stocker la connexion pour pouvoir la couper

local function enableLockedCam(firstPerson, viewAngle, distance, height)
	-- Paramètres par défaut
	if firstPerson == nil then
		firstPerson = true
	end

	viewAngle = viewAngle or {0,0}
	distance    = distance    or DISTANCE
	height = height or HEIGHT

	cam.CameraType = Enum.CameraType.Custom
	if firstPerson then
		UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
	else
		UserInputService.MouseBehavior = Enum.MouseBehavior.Default
	end
	-- Initialiser l'angle sur la rotation actuelle du personnage
	-- Correction : ToEulerAnglesYXZ retourne (X, Y, Z), l'angle horizontal est le 2ème
	local currentAngleX, currentAngleY, _ = hrp.CFrame:ToEulerAnglesYXZ()
	angleY = math.deg(currentAngleY)
	angleX = math.deg(currentAngleX)
	
	local initangleY = angleY
	local initangleX = angleX
	
	connection = RunService.RenderStepped:Connect(function()
		local delta = UserInputService:GetMouseDelta()
		
		-- Rotation clavier (flèches ou Q/D)
		local keyDeltaX = 0
		if UserInputService:IsKeyDown(Enum.KeyCode.Left) or UserInputService:IsKeyDown(Enum.KeyCode.Q) then
			keyDeltaX = -KEY_ROTATION_SPEED
		elseif UserInputService:IsKeyDown(Enum.KeyCode.Right) or UserInputService:IsKeyDown(Enum.KeyCode.D) then
			keyDeltaX = KEY_ROTATION_SPEED
		end

		local keyDeltaY = 0
		if UserInputService:IsKeyDown(Enum.KeyCode.Up) then
			keyDeltaY = KEY_ROTATION_SPEED
		elseif UserInputService:IsKeyDown(Enum.KeyCode.Down) then
			keyDeltaY = -KEY_ROTATION_SPEED
		end

		-- Fusionner souris + clavier
		local totalDeltaX = delta.X + keyDeltaX
		local totalDeltaY = delta.Y + keyDeltaY
		
		
		if viewAngle[1] > 0 then
			angleY = math.clamp(angleY - totalDeltaX, -viewAngle[1]+initangleY, viewAngle[1]+initangleY)
		else
			angleY = angleY - totalDeltaX * 0.3
		end
		
		if viewAngle[2] > 0 then
			angleX = math.clamp(angleX - totalDeltaY, -viewAngle[2]+initangleX, viewAngle[2]+initangleX)
		else
			angleX = 0
		end

		-- Caméra placée dans la tête, regardant vers l'avant
		local rotation = CFrame.new(head.Position) * CFrame.Angles(math.rad(angleX), math.rad(angleY), 0)
		
		-- Reculer la caméra derrière la tête (ajuste la valeur selon le rendu)
		local camCFrame = rotation * CFrame.new(0,  height, distance)
		cam.CFrame = camCFrame

		-- Tourner le personnage avec la caméra
		hrp.CFrame = CFrame.new(hrp.Position) * CFrame.Angles(0, math.rad(angleY), 0)
		
	end)
end

local function disableLockedCam()
	-- Couper le RenderStepped
	if connection then
		task.spawn(function()
			RunService.RenderStepped:Wait()
			connection:Disconnect()
			connection = nil
		end)

	end
	-- Restaurer le comportement normal de Roblox
	local cam = workspace.CurrentCamera
	cam.CameraType = Enum.CameraType.Custom
	
	-- 2. Reassigner le sujet (crucial !)
	if humanoid then
		cam.CameraSubject = humanoid
	end
	-- 3. Laisser Roblox reprendre le contrôle au prochain frame
	RunService.RenderStepped:Wait()
	UserInputService.MouseBehavior = Enum.MouseBehavior.Default
	
end

-- Toggle avec F
UserInputService.InputBegan:Connect(function(input, gameProcessed)
	if gameProcessed then return end
	if input.KeyCode == Enum.KeyCode.F then
		print("F",isLocked)
		isLocked = not isLocked
		if isLocked then
			enableLockedCam(false, {120,10},5, 2)
		else
			disableLockedCam()
		end
	end
end)

screen.Enabled = false

-- Écouter l'événement envoyé par le serveur
kioskEvent.OnClientEvent:Connect(function(entered)
	print("KioskEvent reçu :", entered)

	if entered then
		enableLockedCam(false, {0,0},-1, 1)
	else
		disableLockedCam()
	end
end)

Vue derrière le joueur :

enableLockedCam(false, {120,10},5, 2)
{angle de vision horizontal, angle de vision vertical, 5 studs en arrière, 2 au dessus de la tête

Vue devant le joueur :

enableLockedCam(false, {120,0},-1, 0)
{angle de vision horizontal, angle de vision vertical, 1 stud en avant de la tête, 0 au dessus de la tête

Vue firstperson :

enableLockedCam(true)