Roblox · Шутер · урок 6

Автомат и снайперка

Автомат стреляет очередью, пока зажата кнопка. Снайперка — один мощный выстрел и 2 секунды на затвор.

Структура

Синим — что добавляем на этом уроке.

Workspace
├─ Rifle (Tool) — копия Gun — автомат
│  └─ GunLocal (LocalScript) — версия с очередью
└─ Sniper (Tool) — копия Gun — снайперка
   ├─ GunLocal (LocalScript) — блок выстрела заменён
   └─ GunServer (Script) — damage = 60, bulletSpeed = 1000

Шаги

  1. Сделай две копии Gun со своими моделями: Rifle и Sniper.
  2. Характеристики меняются в GunServer: damage и bulletSpeed; у автомата ещё fireRate в GunLocal.
  3. Снайперке — меньше патронов (Ammo/MaxAmmo = 5).

Скрипты

GunLocal (автомат)

Где: Workspace → Rifle · 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 fireRate = 0.1
-- кнопка стрельбы зажата?
local shooting = false
-- очередь уже идёт?
local looping = false

-- обновляет счётчик
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)
    -- очередь прекращается
    shooting = false
-- конец обработчика
end)

-- кнопку стрельбы нажали
gun.Activated:Connect(function()
    -- зажата
    shooting = true
    -- очередь уже идёт — вторую не запускаем
    if looping then return end
    -- начинаем очередь
    looping = true
    -- пока кнопка зажата и оружие в руках
    while shooting and equipped do
        -- не перезаряжаемся
        if not reloading then
            -- патроны есть
            if ammo.Value > 0 then
                -- выстрел
                shotEvent:FireServer(mouse.Hit.Position)
                -- минус патрон
                ammo.Value -= 1
            -- магазин пуст
            else
                -- щелчок
                emptySound:Play()
                -- очередь прекращается
                shooting = false
            -- конец проверки патронов
            end
        -- конец проверки перезарядки
        end
        -- пауза — всегда, иначе цикл повесит игру
        task.wait(fireRate)
    -- следующий выстрел
    end
    -- очередь закончилась
    looping = false
-- конец обработчика
end)
-- кнопку отпустили
gun.Deactivated:Connect(function()
    -- очередь прекращается
    shooting = false
-- конец обработчика
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)

GunLocal (снайперка) — блок выстрела

Где: Workspace → Sniper · LocalScript
Зачем: Заменяет блок gun.Activated: после выстрела 2 секунды нельзя стрелять.

-- добавь к переменным: идёт затвор после выстрела?
local shooting = false

-- клик, пока оружие в руках
gun.Activated:Connect(function()
    -- перезарядка или затвор — не стреляем
    if reloading or shooting then return end
    -- патроны есть
    if ammo.Value > 0 then
        -- затвор занят
        shooting = true
        -- выстрел
        shotEvent:FireServer(mouse.Hit.Position)
        -- минус патрон
        ammo.Value -= 1
        -- 2 секунды на затвор
        task.wait(2)
        -- можно стрелять снова
        shooting = false
    -- магазин пуст
    else
        -- щелчок
        emptySound:Play()
    -- конец проверки
    end
-- конец обработчика
end)

Проверка

Запусти Play (или Run) и убедись: