احفظ عند المغادرة
الدرس 20 من 21 في دورة لعبة Roblox: البرج اللانهائي على Coddy.
يجب أن تظل مرات الفوز وأفضل الأوقات موجودة في المرة القادمة. يحتفظ Roblox بالبيانات بين الزيارات في DataStore، وهو جدول على خوادم Roblox يبقى بعد إغلاق اللعبة. احفظ البيانات عندما يغادر لاعب، ولجميع اللاعبين الذين ما زالوا يلعبون عند إيقاف تشغيل الخادم:
local coinStore = DataStoreService:GetDataStore("Coins")
local function save(player)
pcall(function()
coinStore:SetAsync(player.UserId, { Coins = player.leaderstats.Coins.Value })
end)
end
Players.PlayerRemoving:Connect(save)
game:BindToClose(function()
for _, player in Players:GetPlayers() do
save(player)
end
end)قد يفشل الحفظ من جهة Roblox، وقد يؤدي الفشل إلى إيقاف البرنامج النصي، لذا يوضع داخل pcall. المفتاح هو UserId الخاص باللاعب، وهو لا يتغير أبدًا، بخلاف اسمه.
التحدي
صعباحفظ عدد مرات فوز كل لاعب وأفضل وقت له عند مغادرته.
- بالقرب من الأعلى:
local DataStoreService = game:GetService("DataStoreService")وlocal towerStore = DataStoreService:GetDataStore("Tower"). - اكتب
saveProgress(player): داخلpcall، استدعِtowerStore:SetAsync(player.UserId, { Wins = ..., BestTime = ... })مستخدمًا بيانات لوحة المتصدرين الخاصة به. - استدعِها عند
Players.PlayerRemoving، ولكل لاعب داخلgame:BindToClose.
اضغط «العب»، ثم افز واضغط «إيقاف»: سيُحفَظ عدد مرات فوزك لتتمكن من تحميله في الدرس التالي. ثم اضغط «تحقق».
جرّب بنفسك
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local CollectionService = game:GetService("CollectionService")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local Settings = require(game.ReplicatedStorage.Settings)
local stages = ServerStorage.Stages
local lobbySpawn = workspace.Lobby.SpawnLocation
local tower = nil
local towerStages = {}
local lava = nil
local roundStart = 0
local finished = {}
print("Each tower has " .. Settings.StageCount .. " stages")
-- يحصل كل لاعب على Wins وStage وBestTime في لوحة الصدارة
Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local wins = Instance.new("IntValue")
wins.Name = "Wins"
wins.Parent = leaderstats
local stage = Instance.new("IntValue")
stage.Name = "Stage"
stage.Parent = leaderstats
local best = Instance.new("NumberValue")
best.Name = "BestTime"
best.Parent = leaderstats
-- يبدأ اللاعب الذي يسقط مجددًا من آخر مرحلة وصل إليها،
-- إلا إذا غطّتها الحمم البركانية: عندها ينتظر في الردهة
player.CharacterAdded:Connect(function(character)
task.wait()
local checkpoint = towerStages[stage.Value]
local covered = checkpoint and lava and checkpoint.Start.Position.Y < lava.Position.Y + lava.Size.Y / 2
if checkpoint and not covered and not finished[player] then
character:PivotTo(checkpoint.Start.CFrame + Vector3.new(0, 4, 0))
end
end)
end)
-- اللوحة الموجودة في الردهة
local gui = Instance.new("SurfaceGui")
gui.Parent = workspace.Lobby.Board
local boardLabel = Instance.new("TextLabel")
boardLabel.Name = "Status"
boardLabel.Size = UDim2.fromScale(1, 1)
boardLabel.BackgroundColor3 = Color3.fromRGB(25, 25, 35)
boardLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
boardLabel.TextScaled = true
boardLabel.Text = "Endless Tower"
boardLabel.Parent = gui
local function setBoard(text)
boardLabel.Text = text
end
-- أي شيء يحمل الوسم Kill ينهي حياة اللاعب
local function killOnTouch(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid then
humanoid.Health = 0
end
end
-- تتلاشى المنصة المختفية بعد وقت قصير من هبوط أحد عليها، ثم تعود
local function makeVanish(part)
part.Touched:Connect(function(hit)
if part:GetAttribute("Gone") or not hit.Parent:FindFirstChild("Humanoid") then
return
end
part:SetAttribute("Gone", true)
task.wait(Settings.VanishDelay)
part.Transparency = 0.8
part.CanCollide = false
task.wait(Settings.VanishTime)
part.Transparency = 0
part.CanCollide = true
part:SetAttribute("Gone", nil)
end)
end
-- ينزلق الليزر إلى الموضع المحدد بإزاحة Slide ثم يعود، بلا توقف
local function makeSlide(part)
local info = TweenInfo.new(Settings.SlideTime, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, -1, true)
local goal = { Position = part.Position + part:GetAttribute("Slide") }
TweenService:Create(part, info, goal):Play()
end
-- تقذف منصة القفز من يهبط عليها إلى أعلى في الهواء
local function bounce(hit)
local root = hit.Parent:FindFirstChild("HumanoidRootPart")
if root then
root.AssemblyLinearVelocity = Vector3.new(0, Settings.BouncePower, 0)
end
end
-- تدور العناصر الدوّارة قليلًا في كل إطار
RunService.Heartbeat:Connect(function(dt)
for _, spinner in CollectionService:GetTagged("Spinner") do
if spinner:IsDescendantOf(workspace) then
spinner:PivotTo(spinner:GetPivot() * CFrame.Angles(0, math.rad(Settings.SpinSpeed) * dt, 0))
end
end
end)
-- فعّل كل عقبة في المرحلة وفقًا لوسومها
local function setupStage(stage, index)
for _, part in stage:GetDescendants() do
if part:HasTag("Kill") then
part.Touched:Connect(killOnTouch)
end
if part:HasTag("Vanish") then
makeVanish(part)
end
if part:HasTag("Conveyor") then
part.AssemblyLinearVelocity = part.CFrame.LookVector * Settings.BeltSpeed
end
if part:HasTag("Slider") then
makeSlide(part)
end
if part:HasTag("Bounce") then
part.Touched:Connect(bounce)
end
end
-- يُحتسب الوصول إلى Start الخاص بالمرحلة في لوحة الصدارة
stage.Start.Touched:Connect(function(hit)
local player = Players:GetPlayerFromCharacter(hit.Parent)
if player and player.leaderstats.Stage.Value < index then
player.leaderstats.Stage.Value = index
end
end)
end
-- تزداد صعوبة المراحل كلما ارتفعنا: الثلث السفلي سهل، والثلث العلوي صعب
local function pickStage(index)
local difficulty = math.ceil(index / Settings.StageCount * 3)
local choices = {}
for _, stage in stages:GetChildren() do
if stage:GetAttribute("Difficulty") == difficulty then
table.insert(choices, stage)
end
end
return choices[math.random(1, #choices)]
end
-- ضع نسخة من نموذج فوق البرج؛ تُرجع النسخة والارتفاع الجديد لقمة البرج
local function stack(template, bottom)
local copy = template:Clone()
local box, size = copy:GetBoundingBox()
local lift = bottom - (box.Position.Y - size.Y / 2)
copy:PivotTo(copy:GetPivot() + Vector3.new(0, lift, 0))
copy.Parent = tower
return copy, bottom + size.Y
end
-- قمة البرج: فوز ثم عودة إلى ساحة الانتظار
local function reachTop(hit)
local player = Players:GetPlayerFromCharacter(hit.Parent)
if not player or finished[player] then
return
end
finished[player] = true
player.leaderstats.Wins.Value += 1
local seconds = math.floor((time() - roundStart) * 10) / 10
local best = player.leaderstats.BestTime
if best.Value == 0 or seconds < best.Value then
best.Value = seconds
end
print(player.Name .. " reached the top in " .. seconds .. " seconds")
hit.Parent:PivotTo(lobbySpawn.CFrame + Vector3.new(0, 4, 0))
end
-- ابنِ برجًا عشوائيًا جديدًا من الأسفل إلى الأعلى
local function buildTower()
tower = Instance.new("Model")
tower.Name = "Tower"
tower.Parent = workspace
towerStages = {}
local top = 0
for i = 1, Settings.StageCount do
local stage, newTop = stack(pickStage(i), top)
top = newTop
setupStage(stage, i)
towerStages[i] = stage
end
local finish = stack(ServerStorage.Finish, top)
finish.FinishPad.Touched:Connect(reachTop)
return top
end
-- ترتفع الحمم البركانية من الأرض حتى أسفل قمة البرج مباشرةً
local function startLava(top)
lava = Instance.new("Part")
lava.Name = "Lava"
lava.Anchored = true
lava.CanCollide = false
lava.Size = Vector3.new(30, 2, 30)
lava.Position = Vector3.new(0, -2, 0)
lava.Color = Color3.fromRGB(255, 90, 20)
lava.Material = Enum.Material.Neon
lava.Parent = workspace
lava.Touched:Connect(killOnTouch)
local info = TweenInfo.new(top / Settings.LavaSpeed, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, 0, false, Settings.LavaDelay)
TweenService:Create(lava, info, { Position = Vector3.new(0, top - 2, 0) }):Play()
end
local function startRound()
finished = {}
for _, player in Players:GetPlayers() do
player.leaderstats.Stage.Value = 0
end
local top = buildTower()
startLava(top)
roundStart = time()
for _, player in Players:GetPlayers() do
if player.Character then
player.Character:PivotTo(towerStages[1].Start.CFrame + Vector3.new(0, 4, 0))
end
end
end
local function endRound()
for _, player in Players:GetPlayers() do
if player.Character and not finished[player] then
player.Character:PivotTo(lobbySpawn.CFrame + Vector3.new(0, 4, 0))
end
player.leaderstats.Stage.Value = 0
end
tower:Destroy()
lava:Destroy()
towerStages = {}
end
-- الدرس 20: يُضاف هنا حفظ التقدم عند المغادرة
-- حلقة الجولات
while true do
for i = Settings.Intermission, 1, -1 do
setBoard("Next tower in " .. i)
task.wait(1)
end
startRound()
for i = Settings.RoundLength, 1, -1 do
setBoard("Climb! " .. i .. " seconds left")
task.wait(1)
end
endRound()
end
جميع دروس لعبة Roblox: البرج اللانهائي
4حوّله إلى لعبة
أضف مرحلة إلى لوحة المتصدرينتدرّب بنفسك: Playground لـ Roblox