스플래시
Coddy의 Roblox 게임: 타워 디펜스 코스 레슨. 18개 중 13번째.
대포는 느리지만 포탄이 폭발합니다. 포탄이 맞힌 적을 기준으로 Splash 스터드 이내의 모든 적에게 피해를 줍니다. 적들은 한 줄로 이동하므로 한 발로 여러 적을 맞힐 수 있습니다.
설정에서 대포의 행에만 Splash가 있으므로 다른 적의 경우 info.Splash는 nil이며, 하나의 if문으로 공격 유형을 선택합니다.
if info.Splash then
-- target의 info.Splash 범위 안에 있는 모든 대상
else
target.Humanoid:TakeDamage(damage)
end챌린지
중급shoot에서 info.Splash가 설정되어 있으면, 대상만 공격하는 대신 HumanoidRootPart가 대상에서 info.Splash 스터드 이내에 있는 모든 적에게 피해를 줍니다. 다른 타워는 여전히 대상만 공격합니다.
Play를 누르고 Cannon을 건설한 다음 Check를 누르세요.
직접 해보기
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
-- 발사: 목표물에 피해를 준다
local function shoot(tower, target, info)
local damage = info.Damage * tower:GetAttribute("Level")
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: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
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.