Roblox · Захват флага · урок 4

Флаги: захват и доставка

Один серверный скрипт управляет всеми флагами: схватил чужой — он за спиной; донёс до своей базы — очко команде; погиб — флаг вернулся домой.

Структура

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

Workspace
└─ Bases (Folder)
ServerScriptService
└─ FlagServer (Script)
Players → игрок — создаёт скрипт
└─ leaderstats (Folder)
   └─ Flags (IntValue) — принесённые флаги

Шаги

  1. ServerScriptService → Script «FlagServer». В методичке было три скрипта в каждом флаге — один общий проще и надёжнее.
  2. Свой флаг взять нельзя, чужой — можно, если у игрока ещё нет другого флага.
  3. Флаг не переносится внутрь персонажа, как в методичке, а приваривается к спине (Weld с C0). Если положить флаг в персонажа, он удалится вместе с ним при выходе игрока из игры.
  4. Атрибут HasFlag у игрока — имя базы, чей флаг он несёт.
  5. Погиб, вышел или возродился — флаг сам возвращается на подставку, все подписки снимаются: флаг не «двоится».
  6. Очко засчитывается, когда носитель касается GivePart своей команды. Флаг возвращается домой, у игрока +1 Flags.
  7. Проверка вдвоём: Test → Clients and Servers → 2 Players, выбери разные команды.

Скрипты

FlagServer

Где: ServerScriptService · Script
Зачем: Захват, перенос, возврат флагов и очки.

-- игроки
local Players = game:GetService("Players")
-- команды
local Teams = game:GetService("Teams")
-- базы; имя базы = имя команды
local bases = workspace:WaitForChild("Bases")
-- имя базы → функция «вернуть её флаг»
local returns = {}

-- игрок зашёл
Players.PlayerAdded:Connect(function(player)
    -- таблица по Tab
    local leaderstats = Instance.new("Folder")
    -- имя строго такое
    leaderstats.Name = "leaderstats"
    -- в игрока
    leaderstats.Parent = player
    -- принесённые флаги
    local flags = Instance.new("IntValue")
    -- столбец
    flags.Name = "Flags"
    -- в таблицу
    flags.Parent = leaderstats
-- конец функции
end)

-- живой игрок, чья деталь коснулась
local function GetPlayer(hit)
    -- модель персонажа
    local character = hit.Parent
    -- Humanoid
    local humanoid = character and character:FindFirstChildOfClass("Humanoid")
    -- не персонаж или погиб
    if not humanoid or humanoid.Health <= 0 then return nil end
    -- игрок
    return Players:GetPlayerFromCharacter(character)
-- конец функции
end

-- настраиваем одну базу
local function Setup(base)
    -- команда этой базы
    local team = Teams:WaitForChild(base.Name)
    -- модель флага
    local flag = base:WaitForChild("Flag")
    -- главная деталь флага
    local hitbox = flag:WaitForChild("Hitbox")
    -- куда приносить чужой флаг
    local givePart = base:WaitForChild("GivePart")
    -- место флага на подставке
    local home = hitbox.CFrame
    -- кто несёт флаг
    local carrier = nil
    -- подписки на смерть носителя
    local connections = {}

    -- флаг — обратно на подставку
    local function ReturnFlag()
        -- отписываемся
        for _, connection in ipairs(connections) do connection:Disconnect() end
        -- список пуст
        table.clear(connections)
        -- у игрока больше нет флага
        if carrier then carrier:SetAttribute("HasFlag", nil) end
        -- никто не несёт
        carrier = nil
        -- сварка со спиной
        local weld = hitbox:FindFirstChild("CarryWeld")
        -- убираем
        if weld then weld:Destroy() end
        -- закрепляем
        hitbox.Anchored = true
        -- на подставку
        hitbox.CFrame = home
    -- конец функции
    end
    -- чтобы другая база могла вернуть этот флаг
    returns[base.Name] = ReturnFlag

    -- кто-то коснулся флага
    hitbox.Touched:Connect(function(hit)
        -- флаг уже несут
        if carrier then return end
        -- игрок
        local player = GetPlayer(hit)
        -- свой флаг не берут
        if not player or not player.Team or player.Team == team then return end
        -- два флага сразу нельзя
        if player:GetAttribute("HasFlag") then return end
        -- Humanoid носителя
        local humanoid = player.Character:FindFirstChildOfClass("Humanoid")
        -- к нему крепим
        local root = player.Character:FindFirstChild("HumanoidRootPart")
        -- персонаж не собран
        if not root then return end
        -- запоминаем носителя
        carrier = player
        -- чей флаг у игрока
        player:SetAttribute("HasFlag", base.Name)
        -- сварка со смещением
        local weld = Instance.new("Weld")
        -- имя — чтобы найти и убрать
        weld.Name = "CarryWeld"
        -- спина игрока
        weld.Part0 = root
        -- флаг
        weld.Part1 = hitbox
        -- чуть выше и за спиной
        weld.C0 = CFrame.new(0, 1.5, 1.2)
        -- в флаг
        weld.Parent = hitbox
        -- флаг едет за игроком
        hitbox.Anchored = false
        -- погиб — флаг домой
        table.insert(connections, humanoid.Died:Connect(ReturnFlag))
        -- вышел или возродился — тоже
        table.insert(connections, player.CharacterRemoving:Connect(ReturnFlag))
    -- конец функции
    end)

    -- кто-то пришёл на свою базу
    givePart.Touched:Connect(function(hit)
        -- игрок
        local player = GetPlayer(hit)
        -- только игроки этой команды
        if not player or player.Team ~= team then return end
        -- чей флаг принёс
        local enemyName = player:GetAttribute("HasFlag")
        -- ничего не несёт
        if not enemyName or not returns[enemyName] then return end
        -- чужой флаг возвращается на свою подставку
        returns[enemyName]()
        -- таблица игрока
        local stats = player:FindFirstChild("leaderstats")
        -- +1 флаг
        if stats then stats.Flags.Value += 1 end
    -- конец функции
    end)
-- конец функции
end

-- все базы
for _, base in ipairs(bases:GetChildren()) do
    -- настраиваем
    task.spawn(Setup, base)
-- конец перебора
end

Проверка

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