Menu
CoddyTech

لوحة البرج

الدرس 13 من 22 في دورة لعبة Roblox: الدفاع عن الأبراج على Coddy.

يزداد البرج قوةً مع المستويات. المستوى سمة في نموذج البرج، Level، ويبدأ عند 1. تكلف كل ترقية حصة من سعر البرج، Settings.UpgradeCost، مضروبةً في مستواه الحالي، وتحافظ math.floor على العدد صحيحًا. تكلفة الترقية التالية لبرج Frost من المستوى 2 هي:

math.floor(90 * 0.75 * 2) --> 135

يؤدي النقر على برج إلى فتح Panel في HUD، حيث يظهر اسمه ومستواه وسعر ترقيته. تصيب النقرة أحد أجزاء البرج، لذا تعثر target:FindFirstAncestorOfClass("Model") على البرج الذي ينتمي إليه ذلك الجزء.

challenge icon

التحدي

متوسط

انقر على برج لفتح لوحته.

  1. في placeTower، اضبط السمة Level للبرج على 1.
  2. في DefenseClient، اكتب showPanel(): بالنسبة إلى البرج chosen، اعرض النصين Archer level 1 في hud.Panel.Title وUpgrade $45 في hud.Panel.Upgrade؛ ثم أظهر اللوحة.
  3. في Button1Down، يؤدي النقر على برج في workspace.Towers إلى اختياره.

اضغط على «العب» وانقر على برج Archer بنيته: ستفتح لوحته. ثم اضغط على «تحقق».

جرّب بنفسك

local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local Debris = game:GetService("Debris")
local Settings = require(game.ReplicatedStorage.Settings)

local remotes = game.ReplicatedStorage.Remotes
local enemies = workspace.Enemies
local towers = workspace.Towers

print("The base can take " .. Settings.BaseHealth .. " hits")

-- نقاط المسار بالترتيب (أسماؤها 1، 2، 3...)
local waypoints = workspace.Path.Waypoints:GetChildren()
table.sort(waypoints, function(a, b)
	return tonumber(a.Name) < tonumber(b.Name)
end)

-- صحة القاعدة، بوصفها خاصية في workspace: يمكن لكل عميل قراءتها
workspace:SetAttribute("BaseHealth", Settings.BaseHealth)

-- يحصل كل لاعب على Money في لوحة المتصدرين
Players.PlayerAdded:Connect(function(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	local money = Instance.new("IntValue")
	money.Name = "Money"
	money.Value = Settings.StartMoney
	money.Parent = leaderstats
end)

-- وصل عدو إلى القاعدة
local function damageBase(amount)
	local health = math.max(0, workspace:GetAttribute("BaseHealth") - amount)
	workspace:SetAttribute("BaseHealth", health)
	print("Base: " .. health)
end

-- حرّك عدوًا على طول المسار. يتوقف MoveTo عن المحاولة بعد 8 ثوانٍ ويُبلغ عن ذلك
-- (MoveToFinished بقيمة false)، لذا تُعاد محاولة تحريك العدو البطيء حتى يصل.
local function walkPath(enemy)
	local humanoid = enemy.Humanoid
	for i = 2, #waypoints do
		repeat
			humanoid:MoveTo(waypoints[i].Position)
		until humanoid.MoveToFinished:Wait() or not enemy.Parent
		if not enemy.Parent then
			return
		end
	end
	-- وصل إلى القاعدة
	damageBase(Settings.Enemies[enemy.Name].Damage)
	enemy:Destroy()
end

-- عدو جديد عند البوابة يسير على طول المسار
local function spawnEnemy(kind)
	local enemy = ServerStorage.Enemies[kind]:Clone()
	-- يوضع فوق أول نقطة مسار مباشرةً
	enemy:PivotTo(waypoints[1].CFrame + Vector3.new(0, 5, 0))
	local info = Settings.Enemies[kind]
	enemy.Humanoid.MaxHealth = info.Health
	enemy.Humanoid.Health = info.Health
	enemy.Humanoid.WalkSpeed = info.Speed
	enemy.Parent = enemies
	enemy.Humanoid.Died:Connect(function()
		-- يحصل كل لاعب على المكافأة
		for _, player in Players:GetPlayers() do
			player.leaderstats.Money.Value += info.Reward
		end
		enemy:Destroy()
	end)
	task.spawn(walkPath, enemy)
	return enemy
end

-- أقرب عدو حي ضمن نطاق نقطة معيّنة، أو nil
local function nearestEnemy(position, range)
	local best, bestDistance = nil, range
	for _, enemy in enemies:GetChildren() do
		local root = enemy:FindFirstChild("HumanoidRootPart")
		if root and enemy.Humanoid.Health > 0 then
			local distance = (root.Position - position).Magnitude
			if distance <= bestDistance then
				best, bestDistance = enemy, distance
			end
		end
	end
	return best
end

-- وميض ضوء من برج إلى هدفه
local function beam(from, to)
	local ray = Instance.new("Part")
	ray.Anchored = true
	ray.CanCollide = false
	ray.CanTouch = false
	ray.Material = Enum.Material.Neon
	ray.Color = Color3.fromRGB(255, 240, 150)
	ray.Size = Vector3.new(0.2, 0.2, (to - from).Magnitude)
	ray.CFrame = CFrame.lookAt((from + to) / 2, to)
	ray.Parent = workspace
	Debris:AddItem(ray, 0.1)
end

-- طلقة: ألحق الضرر بالهدف
local function shoot(tower, target, info)
	local damage = info.Damage
	local at = target.HumanoidRootPart.Position
	target.Humanoid:TakeDamage(damage)
	beam(tower.Head.Position, at)
end

-- دورة عمل البرج: صوّب نحو أقرب عدو ضمن المدى، وأطلق النار، ثم انتظر انتهاء فترة التهدئة
local function runTower(tower)
	local info = Settings.Towers[tower.Name]
	while tower.Parent do
		local target = nearestEnemy(tower.Head.Position, info.Range)
		if target then
			tower.Head.CFrame = CFrame.lookAt(tower.Head.Position, target.HumanoidRootPart.Position)
			shoot(tower, target, info)
			task.wait(info.Cooldown)
		else
			task.wait(0.1)
		end
	end
end

-- ابنِ برجًا على منصة
local function placeTower(pad, kind)
	local tower = ServerStorage.Towers[kind]:Clone()
	tower:PivotTo(pad.CFrame + Vector3.new(0, pad.Size.Y / 2 + 1, 0))
	tower.Parent = towers
	pad:SetAttribute("Used", true)
	task.spawn(runTower, tower)
end

-- يطلب العميل وضع برج على منصة. تحقّق من كل شيء: أن تكون المنصة موجودة وغير مستخدمة،
-- وأن يكون البرج موجودًا، وأن يتوفر المال لدفع ثمنه.
remotes.Place.OnServerEvent:Connect(function(player, pad, kind)
	if typeof(pad) ~= "Instance" or pad.Parent ~= workspace.Pads or pad:GetAttribute("Used") then
		return
	end
	local info = typeof(kind) == "string" and Settings.Towers[kind]
	if not info then
		return
	end
	local money = player.leaderstats.Money
	if money.Value < info.Price then
		return
	end
	money.Value -= info.Price
	placeTower(pad, kind)
end)

-- موجة واحدة: أطلِق أعداءها واحدًا تلو الآخر، ثم انتظر حتى يختفوا جميعًا
local function runWave(list)
	for _, kind in list do
		spawnEnemy(kind)
		task.wait(Settings.SpawnGap)
	end
	while #enemies:GetChildren() > 0 do
		task.wait(0.2)
	end
end

-- الموجات، تأتي كل منها بعد استراحة
for i, list in Settings.Waves do
	task.wait(Settings.WaveBreak)
	runWave(list)
end

جميع دروس لعبة Roblox: الدفاع عن الأبراج

تدرّب بنفسك: Playground لـ Roblox