Menu
CoddyTech

서리

Coddy의 Roblox 게임: 타워 디펜스 코스 레슨. 18개 중 14번째.

프로스트 타워는 피해를 거의 주지 않습니다. 발사체가 적의 속도를 info.Slow만큼 낮춰 2초 동안 느리게 하므로 다른 타워들이 적을 더 많이 공격할 수 있습니다.

감속이 잘못 작동하는 데에는 두 가지 이유가 있습니다. 감속된 적이 다시 공격받더라도 점점 더 느려져서는 안 되므로, 적에게 속성을 표시하고 표시가 있는 동안은 건너뜁니다. 그리고 적의 속도를 되돌릴 때는 현재 걷는 속도가 아니라 설정에 지정된 적 자체의 속도로 되돌려야 합니다. task.delay는 타워를 멈추지 않고 나중에 함수를 실행합니다:

task.delay(2, function()
	-- 2초 후
end)
challenge icon

챌린지

중급
  1. slow(enemy, factor)를 작성하세요. 적에게 Slowed 속성이 있으면 반환하고, 그렇지 않으면 해당 속성을 설정하고 WalkSpeed를 Settings 속도에 factor를 곱한 값으로 설정하세요. 2초 후에도 게임에 남아 있다면 속도를 원래대로 되돌리고 속성을 지우세요.
  2. shoot에서 info.Slow가 설정되어 있고 대상이 아직 게임에 남아 있다면, 대상의 속도를 늦추세요.

플레이를 누른 다음 확인을 누르세요.

직접 해보기

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
local towerPad = {} -- 각 타워 -> 타워가 서 있는 패드

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)
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

-- 공격: 목표물에 피해를 입힌다(Cannon이라면 주변의 모든 적에게도 피해를 입힌다)
local function shoot(tower, target, info)
	local damage = info.Damage * tower:GetAttribute("Level")
	local at = target.HumanoidRootPart.Position
	if info.Splash then
		for _, enemy in enemies:GetChildren() do
			local root = enemy:FindFirstChild("HumanoidRootPart")
			if root and (root.Position - at).Magnitude <= info.Splash then
				enemy.Humanoid:TakeDamage(damage)
			end
		end
	else
		target.Humanoid:TakeDamage(damage)
	end
	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:SetAttribute("Level", 1)
	tower.Parent = towers
	pad:SetAttribute("Used", true)
	towerPad[tower] = pad
	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)

-- 업그레이드: 다음 레벨로 올리는 비용은 매번 증가하고, 레벨이 오를 때마다
-- 타워의 공격력만큼 다시 증가한다
remotes.Upgrade.OnServerEvent:Connect(function(player, tower)
	if typeof(tower) ~= "Instance" or tower.Parent ~= towers then
		return
	end
	local level = tower:GetAttribute("Level")
	local cost = math.floor(Settings.Towers[tower.Name].Price * Settings.UpgradeCost * level)
	local money = player.leaderstats.Money
	if money.Value < cost then
		return
	end
	money.Value -= cost
	tower:SetAttribute("Level", level + 1)
end)

-- 판매: 가격의 절반을 돌려받고 패드를 다시 사용할 수 있게 된다
remotes.Sell.OnServerEvent:Connect(function(player, tower)
	if typeof(tower) ~= "Instance" or tower.Parent ~= towers then
		return
	end
	player.leaderstats.Money.Value += math.floor(Settings.Towers[tower.Name].Price / 2)
	towerPad[tower]:SetAttribute("Used", nil)
	towerPad[tower] = nil
	tower:Destroy()
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
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

Roblox 게임: 타워 디펜스의 모든 레슨