Roblox · Шутер · урок 9
Итог курса на одной странице: полная структура шутера и финальные версии всех скриптов — пистолет, автомат, снайперка, граната и вид от первого лица, без «замени блок». Удобно сверить свою игру или собрать её заново.
Синим — что добавляем на этом уроке.
Workspace ├─ SpawnLocation (SpawnLocation) — на стартовой площадке ├─ Locations (Folder) │ ├─ Start (Model) — безопасная зона │ ├─ Battle1 (Model) — укрытия │ ├─ Cache1 (Model) — ящики с оружием │ └─ Final (Model) — арена с боссом ├─ Signs (Folder) — таблички-указатели ├─ Enemies (Folder) — монстры с Humanoid ├─ Rifle (Tool) — автомат — в тайнике │ ├─ Handle (Part) — модель; Anchored выкл., Massless вкл. │ │ └─ Shoot, Empty, Reload (Sound) — выстрел, пустой магазин, перезарядка │ ├─ Ammo, MaxAmmo (NumberValue) — патроны сейчас и вместимость │ ├─ Shot (RemoteEvent) — клиент → сервер: выстрел │ ├─ GunGui (ScreenGui) — счётчик патронов │ │ └─ Frame (Frame) │ │ └─ TextLabel (TextLabel) — «30 / 30» │ ├─ GunLocal (LocalScript) │ │ └─ Animation (Animation) — перезарядка │ └─ GunServer (Script) ├─ Sniper (Tool) — снайперка — то же устройство, что у Rifle └─ Grenade (Tool) — в тайнике ├─ Handle (Part) │ ├─ Throw, Boom (Sound) — бросок и взрыв │ ├─ Attachment (Attachment) │ └─ VectorForce (VectorForce) — Attachment0 = Attachment, Force 0 ├─ Animation (Animation) — бросок, кадр «Release» ├─ Throw (RemoteEvent) — клиент → сервер ├─ GrenadeLocal (LocalScript) └─ GrenadeServer (Script) StarterPack — выдаётся каждому игроку └─ Gun (Tool) — пистолет — с самого начала ├─ Handle (Part) — модель; Anchored выкл., Massless вкл. │ └─ Shoot, Empty, Reload (Sound) — выстрел, пустой магазин, перезарядка ├─ Ammo, MaxAmmo (NumberValue) — патроны сейчас и вместимость ├─ Shot (RemoteEvent) — клиент → сервер: выстрел ├─ GunGui (ScreenGui) — счётчик патронов │ └─ Frame (Frame) │ └─ TextLabel (TextLabel) — «30 / 30» ├─ GunLocal (LocalScript) │ └─ Animation (Animation) — перезарядка └─ GunServer (Script) StarterPlayer ├─ StarterPlayerScripts │ └─ PlayerController (LocalScript) └─ StarterCharacterScripts └─ CharacterController (LocalScript)
Где: StarterPack → Gun · LocalScript
Зачем: Выстрел по клику, патроны, перезарядка по R, счётчик, прицел на ПКМ.
-- оружие (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)Где: StarterPack → Gun, Workspace → Rifle · Script
Зачем: Пуля к точке под курсором, урон 15 один раз.
-- оружие (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 → 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)Где: Workspace → Sniper · LocalScript
Зачем: То же, но после выстрела 2 секунды на затвор.
-- оружие (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 shooting = false
-- обновляет счётчик
local function UpdateAmmo()
-- «патроны / вместимость»
ammoText.Text = ammo.Value .. " / " .. maxAmmo.Value
-- конец функции
end
-- патроны изменились — обновляем
ammo.Changed:Connect(UpdateAmmo)
-- события каждого кадра
local RunService = game:GetService("RunService")
-- в прицеле винтовку не показываем — она закрыла бы обзор
local SHOW_GUN = false
-- сейчас целимся?
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 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)
-- нажата клавиша
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)Где: Workspace → Sniper · Script
Зачем: Урон 60, пуля вдвое быстрее.
-- оружие (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 = 1000
-- урон за попадание
local damage = 60
-- клиент сообщил о выстреле и точке под курсором
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 → Grenade · LocalScript
Зачем: Замах по клику; без анимации — бросок сразу.
-- граната (Tool)
local grenade = script.Parent
-- анимация броска
local anim = grenade:WaitForChild("Animation")
-- сигнал «бросок» на сервер
local throwEvent = grenade:WaitForChild("Throw")
-- загрузим, когда возьмём гранату
local animTrack = nil
-- бросок уже начат?
local thrown = false
-- взяли гранату
local function OnEquipped()
-- анимации ещё нет — бросим без замаха
if anim.AnimationId == "" then return end
-- Humanoid персонажа
local humanoid = grenade.Parent:WaitForChild("Humanoid")
-- готовим анимацию
animTrack = humanoid.Animator:LoadAnimation(anim)
-- анимация дошла до именованного кадра
animTrack.KeyframeReached:Connect(function(kfName)
-- кадр, где рука отпускает гранату
if kfName == "Release" then
-- просим сервер бросить
throwEvent:FireServer()
-- конец проверки
end
-- конец обработчика кадра
end)
-- конец функции
end
-- каждый раз, когда берут в руки
grenade.Equipped:Connect(OnEquipped)
-- подобрали с земли: скрипт запустился уже в руках — Equipped был раньше
if grenade.Parent and grenade.Parent:FindFirstChildOfClass("Humanoid") then OnEquipped() end
-- убрали гранату
grenade.Unequipped:Connect(function()
-- бросок отменён — можно снова
thrown = false
-- конец обработчика
end)
-- клик, пока граната в руках
grenade.Activated:Connect(function()
-- уже бросаем — второй раз не надо
if thrown then return end
-- бросок начат
thrown = true
-- анимация есть
if animTrack then
-- замах — бросок на кадре Release
animTrack:Play()
-- анимации нет
else
-- бросаем сразу
throwEvent:FireServer()
-- конец проверки
end
-- конец обработчика
end)Где: Workspace → Grenade · Script
Зачем: Бросок, взрыв через 1,5 с, граната пропадает из инвентаря.
-- граната (Tool)
local grenade = script.Parent
-- модель гранаты
local handle = grenade:WaitForChild("Handle")
-- сигнал «бросок» от клиента
local throwEvent = grenade:WaitForChild("Throw")
-- удаление по таймеру
local Debris = game:GetService("Debris")
-- граната одна — бросок один
local thrown = false
-- клиент дошёл до кадра Release
throwEvent.OnServerEvent:Connect(function(player)
-- уже брошена или не в руках у этого игрока
if thrown or grenade.Parent ~= player.Character then return end
-- запоминаем
thrown = true
-- звук броска
handle.Throw:Play()
-- летящая копия гранаты
local grenadeClone = handle:Clone()
-- отскакивает от земли и стен
grenadeClone.CanCollide = true
-- в мир
grenadeClone.Parent = workspace
-- граната в руке исчезла
handle.Transparency = 1
-- корпус персонажа
local hrp = player.Character:WaitForChild("HumanoidRootPart")
-- направление: вперёд и вверх
local dir = hrp.CFrame.LookVector + hrp.CFrame.UpVector
-- толчок; учитываем массу — любая граната летит одинаково
grenadeClone.VectorForce.Force = dir * grenadeClone.AssemblyMass * 300
-- толкаем 0,2 секунды
task.wait(0.2)
-- дальше летит сама
grenadeClone.VectorForce.Enabled = false
-- запал
task.wait(1.5)
-- звук взрыва
grenadeClone.Boom:Play()
-- взрыв
local exp = Instance.new("Explosion")
-- там, где граната
exp.Position = grenadeClone.Position
-- радиус
exp.BlastRadius = 20
-- сила отбрасывания
exp.BlastPressure = 500000
-- взрываем
exp.Parent = workspace
-- граната исчезла
grenadeClone.Transparency = 1
-- стоит на месте, пока доигрывает звук
grenadeClone.Anchored = true
-- ни с чем не сталкивается
grenadeClone.CanCollide = false
-- через 5 с удаляется
Debris:AddItem(grenadeClone, 5)
-- убираем гранату из инвентаря — в самом конце: скрипт внутри неё
grenade:Destroy()
-- конец обработчика
end)Где: StarterPlayer → StarterPlayerScripts · LocalScript
Зачем: Вид от первого лица.
-- этот игрок
local player = game.Players.LocalPlayer
-- камера всегда от первого лица
player.CameraMode = Enum.CameraMode.LockFirstPersonГде: StarterPlayer → StarterCharacterScripts · LocalScript
Зачем: Свои руки видны от первого лица.
-- персонаж (скрипт кладётся в него)
local character = script.Parent
-- события каждого кадра
local RunService = game:GetService("RunService")
-- каждый кадр
RunService.RenderStepped:Connect(function()
-- все части персонажа
for _, part in pairs(character:GetChildren()) do
-- в имени есть Arm или Hand — это рука
if string.match(part.Name, "Arm") or string.match(part.Name, "Hand") then
-- показываем её этому игроку
part.LocalTransparencyModifier = 0
-- конец проверки
end
-- конец перебора
end
-- конец обработчика
end)Запусти Play (или Run) и убедись: