Roblox · Аркада · урок 2
ЛКМ или пробел — очередь светящихся снарядов в курсор. По арене стоят пушки: поворачиваются к ближайшему игроку и стреляют красными снарядами.
Синим — что добавляем на этом уроке.
Workspace ├─ Projectiles (Folder) — летящие снаряды └─ Arena → Turrets (Folder) └─ Turret (Model) — Base, Head, GunEnd; TurretServer ReplicatedStorage └─ FireEvent (RemoteEvent) ServerScriptService ├─ Projectiles (ModuleScript) └─ FireServer (Script) StarterPlayer → StarterCharacterScripts └─ FireLocal (LocalScript)
Где: ServerScriptService · ModuleScript
Зачем: Снаряд и попадание.
-- удаление по таймеру
local Debris = game:GetService("Debris")
-- игроки
local Players = game:GetService("Players")
-- летящие снаряды
local folder = workspace:WaitForChild("Projectiles")
-- скорость снаряда
local SPEED = 90
-- секунд полёта
local LIFETIME = 2
-- таблица модуля
local Projectiles = {}
-- выстрел: откуда, куда, чей, урон, цвет, бьёт ли игроков
function Projectiles.Fire(from, target, owner, damage, color, hitsPlayers)
-- направление по полу
local flat = Vector3.new(target.X - from.X, 0, target.Z - from.Z)
-- цель прямо под ногами
if flat.Magnitude < 0.1 then return end
-- снаряд
local ball = Instance.new("Part")
-- шар
ball.Shape = Enum.PartType.Ball
-- размер
ball.Size = Vector3.new(0.9, 0.9, 0.9)
-- светится
ball.Material = Enum.Material.Neon
-- цвет: свои и чужие разные
ball.Color = color
-- пролетает, а не толкает
ball.CanCollide = false
-- старт
ball.CFrame = CFrame.new(from)
-- точка силы
local attachment = Instance.new("Attachment")
-- в снаряд
attachment.Parent = ball
-- постоянная скорость (в Алгоритмике — VectorForce, разгонялся)
local velocity = Instance.new("LinearVelocity")
-- за центр
velocity.Attachment0 = attachment
-- держит скорость и не падает
velocity.MaxForce = 100000
-- летит по полу к цели
velocity.VectorVelocity = flat.Unit * SPEED
-- в снаряд
velocity.Parent = ball
-- в мир
ball.Parent = folder
-- полёт считает сервер
ball:SetNetworkOwner(nil)
-- уже попал
local done = false
-- коснулся
ball.Touched:Connect(function(hit)
-- себя и другие снаряды не трогаем
if done or hit:IsDescendantOf(owner) or hit:IsDescendantOf(folder) then return end
-- модель
local model = hit:FindFirstAncestorOfClass("Model")
-- живое?
local humanoid = model and model:FindFirstChildOfClass("Humanoid")
-- да
if humanoid then
-- игрок или враг
local isPlayer = Players:GetPlayerFromCharacter(model) ~= nil
-- мёртвый или свой — пролетаем
if humanoid.Health <= 0 or isPlayer ~= hitsPlayers then return end
-- кто стрелял
local shooter = Players:GetPlayerFromCharacter(owner)
-- запоминаем — очки за убийство
if shooter then humanoid:SetAttribute("LastHit", shooter.UserId) end
-- урон
humanoid:TakeDamage(damage)
-- невидимые области
elseif not hit.CanCollide then
-- пролетаем
return
-- конец проверки
end
-- попал
done = true
-- снаряд исчез
ball:Destroy()
-- конец функции
end)
-- никуда не попал — исчезнет сам
Debris:AddItem(ball, LIFETIME)
-- конец функции
end
-- отдаём модуль
return ProjectilesГде: ServerScriptService · Script
Зачем: Выстрел игрока.
-- события
local RS = game:GetService("ReplicatedStorage")
-- модуль снарядов
local Projectiles = require(script.Parent:WaitForChild("Projectiles"))
-- клиент стреляет
local fireEvent = RS:WaitForChild("FireEvent")
-- чаще нельзя — защита от читов
local COOLDOWN = 0.18
-- урон снаряда игрока
local DAMAGE = 25
-- игрок → время прошлого выстрела
local last = {}
-- выстрел
fireEvent.OnServerEvent:Connect(function(player, target)
-- персонаж
local character = player.Character
-- центр
local root = character and character:FindFirstChild("HumanoidRootPart")
-- Humanoid
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
-- мусор или погиб
if typeof(target) ~= "Vector3" or not root or not humanoid or humanoid.Health <= 0 then return end
-- слишком часто
if os.clock() - (last[player] or 0) < COOLDOWN then return end
-- запоминаем
last[player] = os.clock()
-- чуть впереди персонажа
local from = root.Position + root.CFrame.LookVector * 2.5
-- голубой, бьёт только врагов
Projectiles.Fire(from, target, character, DAMAGE, Color3.fromRGB(90, 200, 255), false)
-- конец функции
end)
-- чистим память
game:GetService("Players").PlayerRemoving:Connect(function(player) last[player] = nil end)Где: StarterPlayer → StarterCharacterScripts · LocalScript
Зачем: Огонь.
-- клавиши и кнопки для телефона
local CAS = game:GetService("ContextActionService")
-- есть ли мышь
local UIS = game:GetService("UserInputService")
-- события
local RS = game:GetService("ReplicatedStorage")
-- игроки
local Players = game:GetService("Players")
-- выстрел клиент → сервер
local fireEvent = RS:WaitForChild("FireEvent")
-- персонаж
local character = script.Parent
-- центр
local root = character:WaitForChild("HumanoidRootPart")
-- курсор
local mouse = Players.LocalPlayer:GetMouse()
-- секунд между выстрелами
local RATE = 0.2
-- зажата кнопка огня
local holding = false
-- куда стрелять
local function Target()
-- телефон — вперёд
if UIS.TouchEnabled and not UIS.MouseEnabled then return root.Position + root.CFrame.LookVector * 50 end
-- мышь — в курсор
return mouse.Hit.Position
-- конец функции
end
-- нажали или отпустили
local function OnFire(_, state)
-- зажата?
holding = state == Enum.UserInputState.Begin
-- пробел больше не прыжок
return Enum.ContextActionResult.Sink
-- конец функции
end
-- ЛКМ, пробел, курок геймпада; true — кнопка на телефоне
CAS:BindAction("Fire", OnFire, true, Enum.UserInputType.MouseButton1, Enum.KeyCode.Space, Enum.KeyCode.ButtonR2)
-- погиб — кнопка пропадает
character:WaitForChild("Humanoid").Died:Connect(function() CAS:UnbindAction("Fire") end)
-- пока персонаж жив
while character.Parent do
-- зажато — стреляем очередью
if holding then fireEvent:FireServer(Target()) end
-- пауза
task.wait(RATE)
-- конец цикла
endГде: Workspace → Arena → Turrets → Turret · Script
Зачем: Пушка.
-- игроки
local Players = game:GetService("Players")
-- модуль снарядов
local Projectiles = require(game:GetService("ServerScriptService"):WaitForChild("Projectiles"))
-- пушка
local turret = script.Parent
-- поворотная башня
local head = turret:WaitForChild("Head")
-- конец ствола — отсюда вылетает снаряд
local gunEnd = turret:WaitForChild("GunEnd")
-- дальность
local RANGE = 60
-- секунд между выстрелами
local RATE = 1.5
-- урон
local DAMAGE = 10
-- где ствол относительно башни
local offset = head.CFrame:ToObjectSpace(gunEnd.CFrame)
-- ближайший живой игрок
local function Nearest()
-- пока никого
local best, bestDistance = nil, RANGE
-- все игроки
for _, player in ipairs(Players:GetPlayers()) do
-- персонаж
local character = player.Character
-- центр
local root = character and character:FindFirstChild("HumanoidRootPart")
-- Humanoid
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
-- жив
if root and humanoid and humanoid.Health > 0 then
-- расстояние
local distance = (root.Position - head.Position).Magnitude
-- ближе
if distance < bestDistance then best, bestDistance = root, distance end
-- конец проверки
end
-- конец перебора
end
-- центр цели или nil
return best
-- конец функции
end
-- пока пушка стоит
while turret.Parent do
-- перезарядка
task.wait(RATE)
-- кого видим
local target = Nearest()
-- есть
if target then
-- башня поворачивается к цели
head.CFrame = CFrame.lookAt(head.Position, Vector3.new(target.Position.X, head.Position.Y, target.Position.Z))
-- ствол — вместе с ней
gunEnd.CFrame = head.CFrame * offset
-- красный снаряд, бьёт игроков
Projectiles.Fire(gunEnd.Position, target.Position, turret, DAMAGE, Color3.fromRGB(255, 80, 60), true)
-- конец проверки
end
-- конец цикла
endЗапусти Play (или Run) и убедись: