Roblox · Движение

Плавание и запас воздуха

Вода из ландшафта: плавать Roblox умеет сам. Добавляем воздух: голова под водой — внизу экрана голубая полоска тает, кончилась — персонаж получает урон. Вынырнул — воздух быстро восстанавливается.

Структура

Синим — что нужно создать.

Workspace
└─ Terrain — вода — Terrain Editor → Fill / Paint → Water
StarterPlayer
└─ StarterCharacterScripts
   └─ AirServer (Script) — серверный: воздух и урон
StarterGui
└─ AirGui (ScreenGui) — ResetOnSpawn выкл.
   ├─ Frame (Frame) — полоска внизу, Visible выкл.
   │  └─ Bar (Frame) — голубая, Size 1, 0, 1, 0
   └─ AirLocal (LocalScript)

Шаги

  1. Вода: Terrain Editor → Edit → Fill (или Generate с водой) — выбери материал Water.
  2. StarterPlayer → StarterCharacterScripts → Script «AirServer» (обычный Script: он копируется в каждого персонажа и работает на сервере — урон честный).
  3. Как проверяется вода: ландшафт — клетки 4 × 4 × 4. Скрипт читает клетку, где голова (Terrain:ReadVoxels): вода и её больше половины — голова под водой.
  4. Воздух хранится в атрибуте Air у Humanoid — его видит и сервер, и игрок.
  5. StarterGui → ScreenGui «AirGui» (ResetOnSpawn выкл.) → Frame внизу по центру (Visible выкл.) → Frame «Bar» (голубой). В AirGui — LocalScript «AirLocal»: полоска видна, только пока воздух не полный.

Скрипты

AirServer

Где: StarterPlayer → StarterCharacterScripts · Script
Зачем: Воздух под водой и урон от удушья.

-- персонаж (скрипт в StarterCharacterScripts)
local character = script.Parent
-- его Humanoid
local humanoid = character:WaitForChild("Humanoid")
-- голова — под водой ли она
local head = character:WaitForChild("Head")
-- ландшафт с водой
local terrain = workspace.Terrain
-- секунд под водой без вреда
local MAX_AIR = 10
-- урон в секунду, когда воздух кончился
local DAMAGE = 10
-- максимум — для полоски
humanoid:SetAttribute("MaxAir", MAX_AIR)
-- воздух полный
humanoid:SetAttribute("Air", MAX_AIR)

-- голова в воде?
local function HeadUnderWater()
    -- где голова
    local p = head.Position
    -- угол клетки ландшафта 4 × 4 × 4
    local low = Vector3.new(math.floor(p.X / 4) * 4, math.floor(p.Y / 4) * 4, math.floor(p.Z / 4) * 4)
    -- читаем одну клетку
    local materials, occupancies = terrain:ReadVoxels(Region3.new(low, low + Vector3.new(4, 4, 4)), 4)
    -- вода и её больше половины
    return materials[1][1][1] == Enum.Material.Water and occupancies[1][1][1] > 0.5
-- конец функции
end

-- пока жив
while humanoid.Health > 0 do
    -- 4 раза в секунду
    task.wait(0.25)
    -- воздух сейчас
    local air = humanoid:GetAttribute("Air")
    -- под водой
    if HeadUnderWater() then
        -- воздух тратится
        air = math.max(air - 0.25, 0)
        -- кончился — урон
        if air == 0 then humanoid:TakeDamage(DAMAGE * 0.25) end
    -- над водой
    else
        -- быстро восстанавливается
        air = math.min(air + 1, MAX_AIR)
    -- конец проверки
    end
    -- записываем — полоска у игрока обновится
    humanoid:SetAttribute("Air", air)
-- конец цикла
end

AirLocal

Где: StarterGui → AirGui · LocalScript
Зачем: Полоска воздуха.

-- этот игрок
local player = game.Players.LocalPlayer
-- AirGui
local gui = script.Parent
-- полоска
local frame = gui:WaitForChild("Frame")
-- воздух
local bar = frame:WaitForChild("Bar")
-- подписка на атрибут
local connection = nil

-- новый персонаж
local function Watch(character)
    -- его Humanoid
    local humanoid = character:WaitForChild("Humanoid")
    -- старая подписка
    if connection then connection:Disconnect() end
    -- обновить полоску
    local function Show()
        -- воздух
        local air = humanoid:GetAttribute("Air") or 10
        -- максимум
        local max = humanoid:GetAttribute("MaxAir") or 10
        -- длина полосы
        bar.Size = UDim2.fromScale(air / max, 1)
        -- полный воздух — полоска не нужна
        frame.Visible = air < max
    -- конец функции
    end
    -- сразу
    Show()
    -- воздух изменился
    connection = humanoid:GetAttributeChangedSignal("Air"):Connect(Show)
-- конец функции
end

-- каждый персонаж
player.CharacterAdded:Connect(Watch)
-- уже появился
if player.Character then Watch(player.Character) end

Проверка

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

Собрать бассейн с воздухом одной командой