Menu
CoddyTech

Scintillement

Leçon 11 sur 20 du cours Jeu Roblox : Échapper aux ténèbres de Coddy.

Un avertissement : les lampes près du monstre vacillent. Cela ne concerne que les yeux, donc cela doit se trouver dans DarkClient, le LocalScript dans le HUD, et voici ce qui se passe d’utile : un LocalScript qui modifie une pièce du serveur la modifie uniquement sur l’écran de ce joueur. Les lampes du serveur ne changent jamais.

Tous les dixièmes de seconde, pour chaque LampLight dans les pièces, une lampe située à une distance inférieure à Settings.FlickerDistance du monstre est allumée 40 % du temps ; toute autre lampe est allumée :

light.Enabled = not near or math.random() < 0.4
challenge icon

Défi

Moyen

Dans DarkClient, récupère Players et Settings, et démarre une boucle avec task.spawn : toutes les 0.1 secondes, trouve la racine de workspace.Shadow (si elle est présente), puis, pour chaque LampLight parmi les descendants de workspace.Rooms, règle son Enabled comme ci-dessus, « proche » signifiant à moins de Settings.FlickerDistance de la partie de la lampe.

Appuie sur Play, puis sur Check.

Essayez vous-même

local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local Lighting = game:GetService("Lighting")
local TweenService = game:GetService("TweenService")
local PathfindingService = game:GetService("PathfindingService")
local Settings = require(game.ReplicatedStorage.Settings)

local rooms = workspace.Rooms
local lastRoom = rooms.Lobby -- la salle la plus récente
local count = 0 -- nombre de salles créées jusqu’ici
local monster = nil

-- La nuit tombe : plus de soleil, un peu de lumière partout et du brouillard
Lighting.ClockTime = Settings.ClockTime
Lighting.Ambient = Settings.Ambient
Lighting.OutdoorAmbient = Settings.Ambient
Lighting.FogColor = Color3.new(0, 0, 0)
Lighting.FogEnd = Settings.FogEnd
print("Lights out")

-- Un outil que porte un joueur : dans sa main ou dans son Backpack
local function findTool(player, name)
	return (player.Character and player.Character:FindFirstChild(name)) or player.Backpack:FindFirstChild(name)
end

-- Faire pivoter une porte sur ses gonds pour l’ouvrir
local function openDoor(door)
	door:SetAttribute("Open", true)
	door.Panel.Prompt.Enabled = false
	local hinge, panel = door.Hinge, door.Panel
	local goal = hinge.CFrame * CFrame.Angles(0, math.rad(90), 0) * hinge.CFrame:ToObjectSpace(panel.CFrame)
	TweenService:Create(panel, TweenInfo.new(0.6), { CFrame = goal }):Play()
end

-- Le joueur vivant le plus proche à portée de MonsterSight, et sa distance
local function nearestCharacter(position)
	local best, bestDistance = nil, Settings.MonsterSight
	for _, player in Players:GetPlayers() do
		local character = player.Character
		local root = character and character:FindFirstChild("HumanoidRootPart")
		if root and character.Humanoid.Health > 0 then
			local distance = (root.Position - position).Magnitude
			if distance < bestDistance then
				best, bestDistance = character, distance
			end
		end
	end
	return best, bestDistance
end

-- Marcher vers un point : tout droit si le passage est libre, sinon en suivant un chemin
local function walkToward(shadow, goal, path)
	local root = shadow.HumanoidRootPart
	local params = RaycastParams.new()
	params.FilterDescendantsInstances = { shadow }
	local hit = workspace:Raycast(root.Position, goal - root.Position, params)
	if not hit or hit.Instance.Parent:FindFirstChildOfClass("Humanoid") then
		shadow.Humanoid:MoveTo(goal)
		return
	end
	path:ComputeAsync(root.Position, goal)
	local points = path:GetWaypoints()
	if path.Status == Enum.PathStatus.Success and points[2] then
		shadow.Humanoid:MoveTo(points[2].Position)
	end
end

-- La logique du monstre : toutes les quarts de seconde, il poursuit quelqu’un
local function runMonster(shadow)
	local humanoid = shadow.Humanoid
	local root = shadow.HumanoidRootPart
	local path = PathfindingService:CreatePath()
	while shadow.Parent do
		local character = nearestCharacter(root.Position)
		if character then
			walkToward(shadow, character.HumanoidRootPart.Position, path)
		else
			humanoid:MoveTo(root.Position)
		end
		task.wait(0.25)
	end
end

-- Le monstre arrive derrière vous : à l’entrée de la salle située deux salles avant la plus récente
local function spawnMonster()
	local behind = nil
	for _, room in rooms:GetChildren() do
		if room:GetAttribute("Number") == count - 2 then
			behind = room
		end
	end
	if not behind then
		return
	end
	monster = ServerStorage.Shadow:Clone()
	monster:PivotTo(behind.Entrance.CFrame + Vector3.new(0, 3.1, 0))
	monster.Humanoid.WalkSpeed = Settings.MonsterSpeed
	monster.Parent = workspace
	task.spawn(runMonster, monster)
end

local addRoom -- défini plus bas : une porte ajoute une salle, et une salle a une porte

-- Ce qui se passe lorsqu’un joueur ouvre une porte
local function setupDoor(door)
	door.Panel.Prompt.Triggered:Connect(function(player)
		if door:GetAttribute("Open") then
			return
		end
		if door:GetAttribute("Locked") then
			local key = findTool(player, "Key")
			if not key then
				return
			end
			key:Destroy()
			door:SetAttribute("Locked", nil)
		end
		if door.Parent == lastRoom then
			addRoom()
		end
		openDoor(door)
	end)
end
setupDoor(workspace.Rooms.Lobby.Door)

-- Entrer dans une salle : elle devient la Room du joueur et son point de réapparition
local function setupEntry(room)
	room.Entry.Touched:Connect(function(hit)
		local player = Players:GetPlayerFromCharacter(hit.Parent)
		if player then
			local number = room:GetAttribute("Number")
			player.leaderstats.Room.Value = number
			player.RespawnLocation = room.Checkpoint
		end
	end)
end
setupEntry(workspace.Rooms.Lobby)

-- Une nouvelle salle à la sortie de la plus récente
function addRoom()
	count += 1
	local name = Settings.Order[(count - 1) % #Settings.Order + 1]
	if count >= Settings.RoomsToEscape then
		name = "Exit"
	end
	local room = ServerStorage.Rooms[name]:Clone()
	room:PivotTo(lastRoom.Exit.CFrame)
	room:SetAttribute("Number", count)
	room.Sign.SurfaceGui.Number.Text = "Room " .. count
	room.Parent = rooms
	lastRoom = room
	setupDoor(room.Door)
	setupEntry(room)
	-- une salle verrouillée garde sa clé à l’intérieur
	local keySpot = room:FindFirstChild("KeySpot")
	if keySpot then
		local key = ServerStorage.Key:Clone()
		key.Handle.CFrame = keySpot.CFrame
		key.Parent = room
		key.Handle.Prompt.Triggered:Connect(function(player)
			key.Handle.Prompt.Enabled = false
			key.Parent = player.Backpack
		end)
	end
	-- conserver la salle la plus récente et KeepRooms salles derrière elle
	for _, old in rooms:GetChildren() do
		if old:GetAttribute("Number") < count - Settings.KeepRooms then
			old:Destroy()
		end
	end
	if count == Settings.MonsterFrom then
		spawnMonster()
	end
	return room
end

-- Chaque joueur : Room dans le classement et une lampe torche à chaque nouvelle vie
Players.PlayerAdded:Connect(function(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	local room = Instance.new("IntValue")
	room.Name = "Room"
	room.Parent = leaderstats

	player.CharacterAdded:Connect(function()
		local flashlight = ServerStorage.Flashlight:Clone()
		flashlight.Parent = player.Backpack
		local beam = flashlight.Handle.Beam
		flashlight.Activated:Connect(function()
			beam.Enabled = not beam.Enabled
		end)
	end)
end)
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Jeu Roblox : Échapper aux ténèbres