Roblox · Анимации

Катсцена: камера и субтитры

Заходишь в зону — сверху и снизу выезжают чёрные полосы, камера облетает точки, внизу субтитры. Потом камера возвращается, и можно играть.

Структура

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

Workspace
├─ CutsceneTrigger (Part) — зона запуска: прозрачная, CanCollide выкл.; атрибут Repeat — показывать каждый раз
└─ CutscenePoints (Folder)
   └─ 1, 2, 3… (Part) — передняя грань — куда смотрит камера; атрибуты Text, Time, Hold
StarterGui
└─ CutsceneGui (ScreenGui) — ResetOnSpawn выкл., IgnoreGuiInset вкл.
   ├─ Top, Bottom (Frame) — чёрные полосы, спрятаны за краями экрана
   └─ Subtitle (TextLabel)
StarterPlayer → StarterPlayerScripts
└─ CutsceneLocal (LocalScript)

Шаги

  1. Точки камеры: маленькие прозрачные детали 1, 2, 3… Передняя грань (Orientation Indicator) — куда смотрит камера. Удобно: встань камерой Studio в нужный кадр, создай деталь и поверни её так же.
  2. Атрибуты точки: Text — субтитр, Time — секунд полёта к ней, Hold — секунд стоять.
  3. Зона запуска «CutsceneTrigger» — прозрачная деталь, CanCollide выкл.
  4. StarterGui → ScreenGui «CutsceneGui» (ResetOnSpawn выкл., IgnoreGuiInset вкл.): Frame «Top» (Size 1, 0, 0.12, 0, Position 0, 0, −0.12, 0) и «Bottom» (Position 0, 0, 1, 0) чёрные, TextLabel «Subtitle» внизу.
  5. StarterPlayerScripts → LocalScript «CutsceneLocal»: камера Scriptable, TweenService летит от точки к точке, управление выключено через PlayerModule. В конце камера снова Custom.

Скрипты

CutsceneLocal

Где: StarterPlayer → StarterPlayerScripts · LocalScript
Зачем: Полосы, полёт камеры, субтитры.

-- плавное движение камеры
local TS = game:GetService("TweenService")
-- этот игрок
local player = game.Players.LocalPlayer
-- его камера
local camera = workspace.CurrentCamera
-- зона, где начинается катсцена
local trigger = workspace:WaitForChild("CutsceneTrigger")
-- точки камеры 1, 2, 3…
local points = workspace:WaitForChild("CutscenePoints")
-- чёрные полосы и субтитры
local gui = player:WaitForChild("PlayerGui"):WaitForChild("CutsceneGui")
-- верхняя полоса
local top = gui:WaitForChild("Top")
-- нижняя полоса
local bottom = gui:WaitForChild("Bottom")
-- текст
local subtitle = gui:WaitForChild("Subtitle")
-- управление персонажем
local controls = require(player:WaitForChild("PlayerScripts"):WaitForChild("PlayerModule")):GetControls()
-- идёт катсцена
local playing = false
-- уже показывали
local played = false

-- показать или спрятать полосы кино
local function Bars(show)
    -- полсекунды
    local info = TweenInfo.new(0.5)
    -- верхняя выезжает сверху
    TS:Create(top, info, {Position = show and UDim2.fromScale(0, 0) or UDim2.fromScale(0, -0.12)}):Play()
    -- нижняя — снизу
    TS:Create(bottom, info, {Position = show and UDim2.fromScale(0, 0.88) or UDim2.fromScale(0, 1)}):Play()
-- конец функции
end

-- сама катсцена
local function Play()
    -- точки
    local list = points:GetChildren()
    -- по номерам
    table.sort(list, function(a, b) return (tonumber(a.Name) or 0) < (tonumber(b.Name) or 0) end)
    -- точек нет
    if #list == 0 then return end
    -- идёт
    playing = true
    -- персонаж стоит
    controls:Disable()
    -- полосы кино
    Bars(true)
    -- камерой управляет скрипт
    camera.CameraType = Enum.CameraType.Scriptable
    -- встаём в первую точку
    camera.CFrame = list[1].CFrame
    -- каждая точка
    for i, point in ipairs(list) do
        -- субтитр этой точки
        subtitle.Text = point:GetAttribute("Text") or ""
        -- к следующей точке летим
        if i > 1 then
            -- за Time секунд
            local tween = TS:Create(camera, TweenInfo.new(point:GetAttribute("Time") or 3, Enum.EasingStyle.Sine), {CFrame = point.CFrame})
            -- летим
            tween:Play()
            -- ждём
            tween.Completed:Wait()
        -- конец проверки
        end
        -- смотрим Hold секунд
        task.wait(point:GetAttribute("Hold") or 1)
    -- конец перебора
    end
    -- субтитр убираем
    subtitle.Text = ""
    -- полосы уезжают
    Bars(false)
    -- камера снова у игрока
    camera.CameraType = Enum.CameraType.Custom
    -- можно ходить
    controls:Enable()
    -- кончилась
    playing = false
    -- атрибут Repeat — показывать каждый раз
    played = not trigger:GetAttribute("Repeat")
-- конец функции
end

-- зоны что-то коснулось
trigger.Touched:Connect(function(otherPart)
    -- идёт или уже было
    if playing or played then return end
    -- только свой персонаж
    if not player.Character or not otherPart:IsDescendantOf(player.Character) then return end
    -- показываем
    Play()
-- конец обработчика
end)

Проверка

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

Собрать катсцену одной командой