Roblox · Шутер · урок 5
Пуля ранит всё живое (кроме стрелка) и исчезает. На экране — патроны «28 / 30». ПКМ — прицел с приближением.
Синим — что добавляем на этом уроке.
Workspace └─ Gun (Tool) ├─ Handle (Part) ├─ GunGui (ScreenGui) — появится на экране, когда оружие в руках │ └─ Frame (Frame) — подложка │ └─ TextLabel (TextLabel) — «30 / 30» ├─ GunLocal (LocalScript) └─ GunServer (Script)
Где: Workspace → Gun · Script
Зачем: Пуля один раз ранит того, в кого попала, и исчезает.
-- оружие (Tool)
local gun = script.Parent
-- модель оружия
local handle = gun:WaitForChild("Handle")
-- сигнал «выстрел» от клиента
local shotEvent = gun:WaitForChild("Shot")
-- звук выстрела
local shootSound = handle:WaitForChild("Shoot")
-- удаление по таймеру
local Debris = game:GetService("Debris")
-- скорость пули
local bulletSpeed = 500
-- урон за попадание
local damage = 15
-- клиент сообщил о выстреле и точке под курсором
shotEvent.OnServerEvent:Connect(function(player, mousePosition)
-- стреляет только тот, у кого оружие в руках
if gun.Parent ~= player.Character then return end
-- новая пуля
local bullet = Instance.new("Part")
-- шарик
bullet.Shape = Enum.PartType.Ball
-- маленький
bullet.Size = Vector3.new(0.3, 0.3, 0.3)
-- чёрный
bullet.BrickColor = BrickColor.new("Black")
-- не отскакивает от своего же оружия
bullet.CanCollide = false
-- у ствола, смотрит на курсор
bullet.CFrame = CFrame.new(handle.Position, mousePosition)
-- в мир
bullet.Parent = workspace
-- физику пули считает сервер — попадания честные
bullet:SetNetworkOwner(nil)
-- летит вперёд
bullet.AssemblyLinearVelocity = bullet.CFrame.LookVector * bulletSpeed
-- звук выстрела
shootSound:Play()
-- пуля ещё никого не задела
local hit = false
-- пуля чего-то коснулась
bullet.Touched:Connect(function(otherPart)
-- уже попала или задела стрелка — пропускаем
if hit or otherPart:IsDescendantOf(player.Character) then return end
-- запоминаем попадание
hit = true
-- модель, которой принадлежит деталь
local model = otherPart:FindFirstAncestorOfClass("Model")
-- есть ли у неё Humanoid (живой ли)
local humanoid = model and model:FindFirstChildOfClass("Humanoid")
-- попали в живого
if humanoid then
-- урон
humanoid:TakeDamage(damage)
-- конец проверки
end
-- пуля исчезает при любом попадании
bullet:Destroy()
-- конец обработчика касания
end)
-- промах — пуля удалится через 10 секунд
Debris:AddItem(bullet, 10)
-- конец обработчика
end)Где: Workspace → Gun · LocalScript
Зачем: Счётчик патронов на экране, курсор-перекрестье, прицел на ПКМ.
-- оружие (Tool)
local gun = script.Parent
-- сигнал «выстрел» на сервер
local shotEvent = gun:WaitForChild("Shot")
-- этот игрок
local player = game.Players.LocalPlayer
-- его мышь: mouse.Hit — точка под курсором
local mouse = player:GetMouse()
-- оружие в руках?
local equipped = false
-- нажатия клавиш
local UIS = game:GetService("UserInputService")
-- модель оружия
local handle = gun:WaitForChild("Handle")
-- щелчок пустого магазина
local emptySound = handle:WaitForChild("Empty")
-- звук перезарядки
local reloadSound = handle:WaitForChild("Reload")
-- патроны в магазине сейчас
local ammo = gun:WaitForChild("Ammo")
-- вместимость магазина
local maxAmmo = gun:WaitForChild("MaxAmmo")
-- анимация перезарядки
local reloadAnimation = script:WaitForChild("Animation")
-- загрузим, когда возьмём оружие
local animTrack = nil
-- идёт перезарядка?
local reloading = false
-- счётчик патронов
local gunGui = gun:WaitForChild("GunGui")
-- надпись «30 / 30»
local ammoText = gunGui.Frame.TextLabel
-- камера — для прицела
local camera = workspace.CurrentCamera
-- обновляет счётчик
local function UpdateAmmo()
-- «патроны / вместимость»
ammoText.Text = ammo.Value .. " / " .. maxAmmo.Value
-- конец функции
end
-- патроны изменились — обновляем
ammo.Changed:Connect(UpdateAmmo)
-- события каждого кадра
local RunService = game:GetService("RunService")
-- показывать оружие в виде от первого лица
local SHOW_GUN = true
-- сейчас целимся?
local aiming = false
-- режим камеры до прицеливания
local normalMode = nil
-- минимальное приближение до прицеливания
local normalMinZoom = nil
-- подписка «оружие видно»
local showConnection = nil
-- включить или выключить вид от первого лица
local function FirstPerson(on)
-- уже так — ничего не делаем
if aiming == on then return end
-- запоминаем
aiming = on
-- начали целиться
if on then
-- запоминаем режим камеры
normalMode = player.CameraMode
-- и минимальное приближение
normalMinZoom = player.CameraMinZoomDistance
-- камера в голове персонажа
player.CameraMode = Enum.CameraMode.LockFirstPerson
-- оружие должно быть видно
if SHOW_GUN then
-- каждый кадр
showConnection = RunService.RenderStepped:Connect(function()
-- все детали оружия
for _, part in ipairs(gun:GetDescendants()) do
-- Roblox прячет их от первого лица — возвращаем
if part:IsA("BasePart") then part.LocalTransparencyModifier = 0 end
-- конец перебора
end
-- конец обработчика
end)
-- конец проверки
end
-- перестали целиться
else
-- оружие снова прячется как обычно
if showConnection then showConnection:Disconnect() end
-- подписки нет
showConnection = nil
-- режим камеры, который был
player.CameraMode = normalMode or Enum.CameraMode.Classic
-- камера обычная
if player.CameraMode == Enum.CameraMode.Classic then
-- отодвигаем камеру назад — иначе останется в голове
player.CameraMinZoomDistance = 10
-- один кадр
task.wait()
-- возвращаем приближение, как было
player.CameraMinZoomDistance = normalMinZoom or 0.5
-- конец проверки
end
-- конец проверки
end
-- конец функции
end
-- взяли оружие в руки
local function OnEquipped()
-- запоминаем
equipped = true
-- в руках — значит, родитель оружия персонаж
local humanoid = gun.Parent:FindFirstChild("Humanoid")
-- анимация задана
if reloadAnimation.AnimationId ~= "" then
-- готовим анимацию перезарядки
animTrack = humanoid:LoadAnimation(reloadAnimation)
-- конец проверки
end
-- показываем счётчик на экране
gunGui.Parent = player.PlayerGui
-- с актуальным числом
UpdateAmmo()
-- курсор-перекрестье
mouse.Icon = "rbxassetid://6884107931"
-- конец функции
end
-- каждый раз, когда берут в руки
gun.Equipped:Connect(OnEquipped)
-- подобрали с земли: скрипт запустился, когда оружие уже в руках, — Equipped был раньше
if gun.Parent and gun.Parent:FindFirstChildOfClass("Humanoid") then OnEquipped() end
-- убрали оружие
gun.Unequipped:Connect(function()
-- запоминаем
equipped = false
-- прячем счётчик обратно в оружие
gunGui.Parent = gun
-- обычный курсор
mouse.Icon = ""
-- выходим из прицела
camera.FieldOfView = 70
-- и из вида от первого лица
FirstPerson(false)
-- конец обработчика
end)
-- клик, пока оружие в руках
gun.Activated:Connect(function()
-- во время перезарядки не стреляем
if reloading then return end
-- патроны есть
if ammo.Value > 0 then
-- выстрел
shotEvent:FireServer(mouse.Hit.Position)
-- минус патрон
ammo.Value -= 1
-- магазин пуст
else
-- щелчок
emptySound:Play()
-- конец проверки
end
-- конец обработчика
end)
-- нажата клавиша
UIS.InputBegan:Connect(function(input, gp)
-- печатает в чат / оружие не в руках / уже перезаряжаемся
if gp or not equipped or reloading then return end
-- R, и магазин не полный
if input.KeyCode == Enum.KeyCode.R and ammo.Value < maxAmmo.Value then
-- началась перезарядка
reloading = true
-- звук
reloadSound:Play()
-- анимация, если задана
if animTrack then animTrack:Play() end
-- ждём длину звука (минимум 1 с — даже если звук не загрузился)
task.wait(math.max(reloadSound.TimeLength, 1))
-- полный магазин
ammo.Value = maxAmmo.Value
-- перезарядка закончилась
reloading = false
-- конец проверки
end
-- конец обработчика
end)
-- нажата ПКМ
mouse.Button2Down:Connect(function()
-- приближаем камеру — прицел
if equipped then camera.FieldOfView = 30 end
-- смотрим от первого лица
if equipped then FirstPerson(true) end
-- конец обработчика
end)
-- ПКМ отпущена
mouse.Button2Up:Connect(function()
-- обычный обзор
camera.FieldOfView = 70
-- камера снова за спиной
FirstPerson(false)
-- конец обработчика
end)Запусти Play (или Run) и убедись: