Roblox · Интерфейсы

Стрелка к цели квеста

На экране — значок с расстоянием до цели. Цель видна — значок над ней; за краем экрана или за спиной — стрелка у края показывает, куда повернуть.

Структура

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

Workspace
└─ Waypoints (Folder)
   └─ 1, 2, 3 (Part) — точки маршрута
ServerScriptService
└─ QuestTargetServer (Script)
Players → игрок — создаёт скрипт
└─ QuestTarget (ObjectValue) — текущая цель
StarterGui
└─ ArrowGui (ScreenGui) — ResetOnSpawn выкл., IgnoreGuiInset вкл.
   ├─ Marker (Frame) — AnchorPoint 0.5, 0.5; Size 70 × 70 px
   │  ├─ Arrow (TextLabel) — «▲»
   │  └─ Distance (TextLabel)
   └─ ArrowLocal (LocalScript)

Шаги

  1. Цель игрока — ObjectValue «QuestTarget» в самом игроке. Его ставит сервер, а стрелка показывает на любую деталь или модель, которая там лежит. Свой квест — просто меняй player.QuestTarget.Value.
  2. Пример: Workspace → Folder «Waypoints» с деталями «1», «2», «3». Script «QuestTargetServer»: коснулся текущей точки — цель переходит на следующую; после последней стрелка пропадает.
  3. ArrowGui (IgnoreGuiInset вкл. — иначе значок съезжает на высоту верхней полосы) → Frame «Marker» без фона, AnchorPoint 0.5, 0.5, Size {0,70},{0,70}. Внутри TextLabel «Arrow» с текстом «▲» и TextLabel «Distance».
  4. LocalScript «ArrowLocal»: WorldToViewportPoint переводит точку мира в точку экрана. Цель за краем — значок прижимается к краю, стрелка поворачивается в её сторону.
  5. Расстояние — в studs, для игрока это «метры».

Скрипты

QuestTargetServer

Где: ServerScriptService · Script
Зачем: Цель и маршрут по точкам.

-- игроки
local Players = game:GetService("Players")
-- точки маршрута 1, 2, 3…
local points = workspace:WaitForChild("Waypoints")

-- точка по номеру
local function Point(number)
    -- nil, если такой нет
    return points:FindFirstChild(tostring(number))
-- конец функции
end

-- игрок зашёл
local function Setup(player)
    -- текущая цель игрока
    local target = Instance.new("ObjectValue")
    -- имя — его читает стрелка
    target.Name = "QuestTarget"
    -- первая точка
    target.Value = Point(1)
    -- в игрока — клиент видит
    target.Parent = player
-- конец функции
end
-- новые игроки
Players.PlayerAdded:Connect(Setup)
-- кто зашёл раньше скрипта
for _, player in ipairs(Players:GetPlayers()) do Setup(player) end

-- каждая точка
for _, point in ipairs(points:GetChildren()) do
    -- касание
    point.Touched:Connect(function(hit)
        -- игрок?
        local player = Players:GetPlayerFromCharacter(hit.Parent)
        -- его цель
        local target = player and player:FindFirstChild("QuestTarget")
        -- это не его текущая цель
        if not target or target.Value ~= point then return end
        -- следующая точка; после последней — nil, стрелка пропадёт
        target.Value = Point(tonumber(point.Name) + 1)
    -- конец функции
    end)
-- конец перебора
end

ArrowLocal

Где: StarterGui → ArrowGui · LocalScript
Зачем: Значок и стрелка.

-- игроки
local Players = game:GetService("Players")
-- каждый кадр
local RunService = game:GetService("RunService")
-- этот игрок
local player = Players.LocalPlayer
-- камера
local camera = workspace.CurrentCamera
-- значок цели
local marker = script.Parent:WaitForChild("Marker")
-- стрелка «▲»
local arrow = marker:WaitForChild("Arrow")
-- «25 м»
local distanceLabel = marker:WaitForChild("Distance")
-- ObjectValue: на что указывать
local target = player:WaitForChild("QuestTarget")
-- отступ от края экрана, px
local EDGE = 50

-- каждый кадр
RunService.RenderStepped:Connect(function()
    -- цель
    local goal = target.Value
    -- персонаж
    local character = player.Character
    -- его центр
    local root = character and character:FindFirstChild("HumanoidRootPart")
    -- цели нет
    if not goal or not root then
        -- прячем
        marker.Visible = false
        -- выходим
        return
    -- конец проверки
    end
    -- где цель (деталь или модель)
    local position = goal:IsA("Model") and goal:GetPivot().Position or goal.Position
    -- показываем
    marker.Visible = true
    -- расстояние в studs
    distanceLabel.Text = math.floor((position - root.Position).Magnitude) .. " м"
    -- где цель на экране
    local point, onScreen = camera:WorldToViewportPoint(position)
    -- центр экрана
    local center = camera.ViewportSize / 2
    -- точка на экране
    local screen = Vector2.new(point.X, point.Y)
    -- цель за спиной — отражаем
    if point.Z < 0 then screen = center - (screen - center) end
    -- цель видна
    if onScreen then
        -- значок прямо на ней
        marker.Position = UDim2.fromOffset(screen.X, screen.Y)
        -- стрелка вниз — «вот здесь»
        arrow.Rotation = 180
    -- цель за краем экрана
    else
        -- направление от центра
        local dir = screen - center
        -- на всякий случай — вверх
        if dir.Magnitude < 1 then dir = Vector2.new(0, -1) end
        -- длина 1
        dir = dir.Unit
        -- до края с отступом
        local half = center - Vector2.new(EDGE, EDGE)
        -- сколько шагнуть до края
        local k = math.min(half.X / math.max(math.abs(dir.X), 1e-6), half.Y / math.max(math.abs(dir.Y), 1e-6))
        -- точка у края
        local pos = center + dir * k
        -- значок у края
        marker.Position = UDim2.fromOffset(pos.X, pos.Y)
        -- стрелка смотрит в сторону цели
        arrow.Rotation = math.deg(math.atan2(dir.Y, dir.X)) + 90
    -- конец проверки
    end
-- конец функции
end)

Проверка

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

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