Penentuan Jalur

*Konten ini diterjemahkan menggunakan AI (Beta) dan mungkin mengandung kesalahan. Untuk melihat halaman ini dalam bahasa Inggris, klik di sini.

Penentuan Jalur adalah proses memindahkan karakter atau objek (agen) sepanjang jalur logis di sekitar rintangan untuk mencapai tujuan, secara opsional menghindari bahan berbahaya atau wilayah yang ditentukan.

Visualisasi navigasi

Untuk membantu dalam penataan dan debugging penentuan jalur, Studio dapat merender mesh navigasi dan label modifier. Untuk mengaktifkannya, aktifkan Mesh Navigasi dan Penentu Jalur modifiers dari widget Opsi Visualisasi di sudut kanan atas jendela tampilan 3D.

Tampilan dekat dari jendela tampilan 3D dengan tombol Opsi Visualisasi yang ditunjukkan di sudut kanan atas.

Dengan Mesh navigasi diaktifkan, area berwarna menunjukkan di mana karakter bisa berjalan atau berenang. Panah kecil menunjukkan area yang akan ditempuh karakter dengan melompat.

Mesh navigasi yang ditampilkan di Studio

Implementasi

Meskipun penentuan jalur dapat diterapkan dengan berbagai cara melalui PathfindingService dan metodenya yang terkait seperti CreatePath(), bagian ini menggunakan skrip penentuan jalur berikut untuk karakter pemain.

Untuk menguji sambil membaca:

  1. PENTING
    Di Explorer, pilih wadah StarterPlayer. Kemudian, di jendela Properties, atur baik DevComputerMovementMode dan DevTouchMovementMode ke Scriptable.

  2. Salin kode berikut ke dalam LocalScript dalam StarterCharacterScripts, atau dapatkan paket ini dan letakkan di dalam StarterCharacterScripts.

    PlayerPathFollow (LocalScript in StarterCharacterScripts)

    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local DESTINATION = Vector3.new(20, 0.5, 20)
    local GROUND_WAIT = 0.01
    local VELOCITY_MULTIPLIER = 0.0625
    local path = PathfindingService:CreatePath({
    AgentCanClimb = true,
    Costs = {
    Water = 20
    }
    })
    local character = script.Parent
    local humanoid = character:WaitForChild("Humanoid")
    local waypoints
    local nextWaypointIndex
    local blockedConnection
    local currentWaypointReachedConnection
    local currentWaypointPlaneNormal = Vector3.zero
    local currentWaypointPlaneDistance = 0
    local pathfinderWorking = false
    local function disconnectCurrentWaypointReachedConnection()
    if not currentWaypointReachedConnection then return end
    currentWaypointReachedConnection:Disconnect()
    currentWaypointReachedConnection = nil
    end
    local function isCurrentWaypointReached()
    if humanoid.FloorMaterial == Enum.Material.Air then
    return false
    end
    local reached = false
    if currentWaypointPlaneNormal ~= Vector3.zero then
    -- Hitung jarak dari humanoid ke bidang tujuan
    local dist = currentWaypointPlaneNormal:Dot(humanoid.RootPart.Position) - currentWaypointPlaneDistance
    -- Hitung komponen kecepatan humanoid yang menuju bidang
    local velocity = -currentWaypointPlaneNormal:Dot(humanoid.RootPart.Velocity)
    -- Hitung ambang batas dari bidang tujuan berdasarkan kecepatan humanoid
    local threshold = math.max(1.0, VELOCITY_MULTIPLIER * velocity)
    -- Anggap waypoint tercapai jika kurang dari ambang batas di depan bidang
    reached = dist < threshold
    else
    reached = true
    end
    if reached then
    currentWaypointPlaneNormal = Vector3.zero
    currentWaypointPlaneDistance = 0
    moveToNextWaypoint()
    end
    end
    local function calculateNextWaypointApproach()
    nextWaypointIndex += 1
    if nextWaypointIndex > #waypoints then
    return false
    end
    local currentWaypoint = waypoints[nextWaypointIndex - 1]
    local nextWaypoint = waypoints[nextWaypointIndex]
    -- Bangun bidang tujuan dari waypoint berikutnya menuju yang saat ini
    currentWaypointPlaneNormal = currentWaypoint.Position - nextWaypoint.Position
    -- Atur normal tegak lurus terhadap bidang Y saat tidak mendaki
    if nextWaypoint.Label ~= "Climb" then
    currentWaypointPlaneNormal = Vector3.new(currentWaypointPlaneNormal.X, 0, currentWaypointPlaneNormal.Z)
    end
    if currentWaypointPlaneNormal.Magnitude > 0.000001 then
    currentWaypointPlaneNormal = currentWaypointPlaneNormal.Unit
    currentWaypointPlaneDistance = currentWaypointPlaneNormal:Dot(nextWaypoint.Position)
    end
    return true
    end
    local function resetWaypointData()
    humanoid:Move(Vector3.zero)
    currentWaypointPlaneNormal = Vector3.zero
    currentWaypointPlaneDistance = 0
    disconnectCurrentWaypointReachedConnection()
    pathfinderWorking = false
    end
    local function waitForGround()
    while humanoid.FloorMaterial == Enum.Material.Air do
    task.wait(GROUND_WAIT)
    end
    end
    function moveToNextWaypoint()
    if calculateNextWaypointApproach() then
    disconnectCurrentWaypointReachedConnection()
    currentWaypointReachedConnection = RunService.Heartbeat:Connect(isCurrentWaypointReached)
    local nextWaypointPosition = waypoints[nextWaypointIndex].Position
    local nextWaypointAction = waypoints[nextWaypointIndex].Action
    humanoid:Move(nextWaypointPosition - humanoid.RootPart.Position)
    if waypoints[nextWaypointIndex + 1] and waypoints[nextWaypointIndex + 1].Label == "UseBoat" then
    nextWaypointIndex += 1
    -- Panggil fungsi khusus untuk membuat agen menggunakan perahu
    elseif nextWaypointAction == Enum.PathWaypointAction.Jump then
    humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
    while humanoid.FloorMaterial ~= Enum.Material.Air do
    task.wait(GROUND_WAIT)
    end
    humanoid:Move(nextWaypointPosition - humanoid.RootPart.Position)
    end
    else
    resetWaypointData()
    end
    end
    local function findStartingPoint(waypoints)
    nextWaypointIndex = 1
    while nextWaypointIndex + 1 <= #waypoints do
    local dist = waypoints[nextWaypointIndex + 1].Position - humanoid.RootPart.Position
    dist = Vector3.new(dist.X, 0, dist.Z)
    if dist.magnitude >= 2 then
    return
    end
    nextWaypointIndex += 1
    end
    end
    local function followPath()
    -- Hitung jalur
    pathfinderWorking = true
    waitForGround()
    local success, errorMessage = pcall(function()
    path:ComputeAsync(character.PrimaryPart.Position, DESTINATION)
    end)
    if not success or path.Status ~= Enum.PathStatus.Success then
    warn("Jalur tidak dihitung!", errorMessage)
    return
    end
    -- Dapatkan waypoint jalur
    waypoints = path:GetWaypoints()
    -- Deteksi jika jalur terhalang
    blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
    -- Periksa jika rintangan berada lebih jauh di jalur
    if blockedWaypointIndex >= nextWaypointIndex then
    -- Hentikan deteksi rintangan jalur sampai jalur dihitung ulang
    blockedConnection:Disconnect()
    resetWaypointData()
    -- Panggil fungsi untuk menghitung ulang jalur baru
    followPath()
    end
    end)
    findStartingPoint(waypoints)
    moveToNextWaypoint()
    end
    followPath()
  3. Edit variabel DESTINATION (

    LINE 5
    ) ke tujuan Vector3 dalam dunia 3D yang dapat dicapai oleh karakter pemain.

  4. Lanjutkan melalui bagian berikut untuk belajar tentang perhitungan jalur dan gerakan karakter.

Pembuatan jalur

Penentuan jalur dimulai melalui PathfindingService dan metode CreatePath() (

LINES 9–14
). Metode ini menerima tabel parameter opsional yang menyesuaikan bagaimana karakter (agen) bergerak sepanjang jalur.

KunciDeskripsiTipeDefault
AgentRadiusRadius agen, dalam stud. Berguna untuk menentukan pemisahan minimum dari rintangan.integer2
AgentHeightTinggi agen, dalam stud. Ruang kosong yang lebih kecil dari nilai ini, seperti ruang di bawah tangga, akan ditandai sebagai tidak dapat dilalui.integer5
AgentCanJumpMenentukan apakah melompat selama penentuan jalur diizinkan.booleantrue
AgentCanClimbMenentukan apakah mendaki TrussParts selama penentuan jalur diizinkan. Jalur yang bisa didaki memiliki Label bernama Climb dan biaya untuk jalur yang bisa didaki adalah 1 secara default.booleanfalse
WaypointSpacingJarak antara waypoint antara dalam jalur. Jika diatur ke math.huge, tidak akan ada waypoint antara.number4
CostsTabel bahan atau PathfindingModifiers yang ditentukan dan biaya mereka untuk dilalui. Berguna untuk membuat agen lebih suka bahan/wilayah tertentu dibandingkan yang lain. Lihat modifiers untuk rincian.tablenil

Perhitungan jalur

Setelah Anda membuat jalur yang valid dengan CreatePath(), jalur tersebut harus dihitung dengan memanggil Path:ComputeAsync() dengan Vector3 untuk titik awal dan tujuan (

LINES 133–139
).

Tanda jalur mulai/akhir di tengah dua jembatan

Setelah Path dihitung, ia akan berisi serangkaian waypoints yang melacak jalur dari awal hingga akhir. Titik-titik ini dapat dikumpulkan dengan metode Path:GetWaypoints() (

LINE 142
). Array yang dikembalikan diurutkan berdasarkan urutan waypoint dari awal jalur hingga akhir jalur.

Waypoints ditunjukkan di sepanjang jalur yang dihitung
Waypoints ditunjukkan di sepanjang jalur yang dihitung

Gerakan jalur

Setiap PathWaypoint terdiri dari Position (Vector3) dan Action (PathWaypointAction). Untuk memindahkan karakter yang memiliki Humanoid, seperti karakter Roblox yang biasa, cara terbaik adalah memanggil Humanoid:Move() dari waypoint ke waypoint dan menggunakan callback isCurrentWaypointReached() skrip (

LINES 32–56
) untuk mendeteksi saat karakter mencapai setiap waypoint.

Jalur yang terhalang

Banyak dunia Roblox bersifat dinamis; bagian mungkin bergerak atau jatuh, dan lantai mungkin runtuh. Ini dapat memblokir jalur yang dihitung dan mencegah karakter mencapai tujuannya. Untuk mengatasinya, Anda dapat menghubungkan acara Path.Blocked dan menghitung ulang jalur di sekitar apa pun yang menghalangnya (

LINES 145–154
).

Modifiers Penentuan Jalur

Secara default, Path:ComputeAsync() mengembalikan jalur terpendek antara titik awal dan tujuan, dengan pengecualian bahwa itu berusaha menghindari melompat. Ini terlihat tidak alami dalam beberapa situasi; misalnya, jalur mungkin melewati rawa daripada menghindarinya hanya karena jalur melalui air secara geometris lebih pendek.

Dua jalur ditunjukkan dengan jalur yang lebih pendek tidak selalu lebih logis

Untuk lebih mengoptimalkan penentuan jalur, Anda dapat menerapkan modifiers penentuan jalur untuk menghitung jalur yang lebih cerdas di berbagai bahan, di sekitar wilayah yang ditentukan, atau untuk mengabaikan rintangan.

Biaya Bahan

Saat bekerja dengan bahan Terrain dan BasePart, Anda dapat menyertakan tabel Costs dalam CreatePath() untuk membuat bahan tertentu lebih mudah dilalui daripada yang lain. Semua bahan memiliki biaya default 1 dan bahan mana pun dapat didefinisikan sebagai tidak dapat dilalui dengan mengatur nilainya ke math.huge.

Kunci dalam tabel Costs harus nama string yang mewakili nama Enum.Material, misalnya Water untuk Enum.Material.Water atau CrackedLava untuk Enum.Material.CrackedLava.

PlayerPathFollow (LocalScript)

local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local DESTINATION = Vector3.new(20, 0.5, 20)
local GROUND_WAIT = 0.01
local VELOCITY_MULTIPLIER = 0.0625
local path = PathfindingService:CreatePath({
AgentCanClimb = true,
Costs = {
Water = 20, CrackedLava = 100, Slate = 20
}
})

Mengonfigurasi Wilayah

Dalam beberapa kasus, preferensi bahan tidak cukup. Misalnya, Anda mungkin ingin karakter menghindari wilayah tertentu, terlepas dari bahan di bawah kaki. Ini dapat dicapai dengan menambahkan objek PathfindingModifier ke bagian.

  1. Buat sebuah bagian Anchored di sekitar wilayah tersebut dan atur propertinya CanCollide menjadi false.

    Bagian yang diikat mendefinisikan wilayah untuk menerapkan modifier penentu jalur.
  2. Masukkan instance PathfindingModifier ke bagian tersebut, cari propertinya Label, dan berikan nama yang berarti seperti DangerZone.

    Instance PathfindingModifier dengan properti Label disetel ke DangerZone.
  3. Sertakan kunci yang cocok DangerZone dan nilai numerik yang terkait dalam tabel Costs dari CreatePath(). Modifier dapat didefinisikan sebagai tidak dapat dilalui dengan mengatur nilainya ke math.huge.

    PlayerPathFollow (LocalScript)

    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local DESTINATION = Vector3.new(20, 0.5, 20)
    local GROUND_WAIT = 0.01
    local VELOCITY_MULTIPLIER = 0.0625
    local path = PathfindingService:CreatePath({
    AgentCanClimb = true,
    Costs = {
    DangerZone = math.huge, Water = 20, CrackedLava = 20, Slate = 20
    }
    })

Mengabaikan Rintangan

Dalam beberapa kasus, berguna untuk mencari jalan melalui rintangan solid seolah-olah mereka tidak ada. Ini memungkinkan Anda menghitung jalur melalui pemblokir fisik tertentu, daripada perhitungan gagal sepenuhnya.

  1. Buat sebuah bagian Anchored di sekitar objek dan atur propertinya CanCollide menjadi false.

    Bagian yang diikat mendefinisikan wilayah untuk menerapkan modifier penentu jalur.
  2. Masukkan instance PathfindingModifier ke bagian tersebut dan aktifkan propertinya PassThrough.

    Instance PathfindingModifier dengan properti PassThrough diaktifkan.

    Sekarang, saat jalur dihitung dari NPC zombie ke karakter pemain, jalur tersebut memperluas di luar pintu dan Anda dapat memaksa zombie untuk melintasinya. Bahkan jika zombie tidak dapat membuka pintu, ia bereaksi seolah-olah "mendengar" karakter di balik pintu.

    Jalur NPC zombie melewati pintu yang sebelumnya menghalangi.

Tautan Penentuan Jalur

Terkadang perlu menemukan jalur di ruang yang tidak dapat dilalui secara normal, seperti melintasi jurang, dan melakukan tindakan khusus untuk mencapai waypoint berikutnya. Ini dapat dicapai melalui objek PathfindingLink.

Menggunakan contoh dari atas, Anda dapat membuat agen menggunakan perahu.

PathfindingLink menunjukkan bagaimana agen dapat menggunakan perahu.

Untuk membuat PathfindingLink menggunakan contoh ini:

  1. OPSIONAL
    Aktifkan Tautan Penentuan Jalur dari widget Opsi Visualisasi di sudut kanan atas jendela tampilan 3D. Ini membantu dengan visualisasi dan debugging saat menerapkan tautan penentuan jalur.

  2. Buat dua Attachments, satu di kursi perahu dan satu dekat titik pendaratan perahu.

    Lampiran dibuat untuk tautan penentuan jalur awal dan akhir.
  3. Buat objek PathfindingLink dalam workspace, lalu tetapkan propertinya Attachment0 dan Attachment1 ke lampiran awal dan akhir masing-masing.

    Properti Attachment0/Attachment1 dari PathfindingLink. PathfindingLink divisualisasikan dalam dunia 3D.
  4. Berikan nama yang berarti seperti UseBoat pada propertinya Label. Nama ini digunakan sebagai tanda dalam skrip penentuan jalur untuk memicu tindakan khusus saat agen mencapai titik tautan awal.

    Properti Label ditentukan untuk PathfindingLink.
  5. Sertakan tabel Costs dalam CreatePath() yang berisi kunci Water dan kunci khusus yang cocok dengan nama properti Label. Tetapkan nilai kunci khusus lebih rendah daripada biaya Water.

    PlayerPathFollow (LocalScript)

    local PathfindingService = game:GetService("PathfindingService")
    local Players = game:GetService("Players")
    local RunService = game:GetService("RunService")
    local DESTINATION = Vector3.new(20, 0.5, 20)
    local GROUND_WAIT = 0.01
    local VELOCITY_MULTIPLIER = 0.0625
    local path = PathfindingService:CreatePath({
    AgentCanClimb = true,
    Costs = {
    UseBoat = 2, Water = 20
    }
    })
  6. Dalam fungsi moveToNextWaypoint() (

    LINES 93–114
    ), pemeriksaan khusus untuk nama modifier Label dapat digunakan untuk mengambil tindakan yang berbeda dari Humanoid:Move(); dalam hal ini, Anda mungkin memanggil fungsi untuk menempatkan agen di dalam perahu, memindahkan perahu melintasi air, mengeluarkan agen di titik pendaratan perahu, dan kemudian melanjutkan perjalanan agen ke tujuan akhir.

Kompatibilitas Streaming

Dalam-game streaming instance adalah fitur kuat yang secara dinamis memuat dan mengeluarkan konten 3D saat karakter pemain bergerak di sekitar dunia. Ketika mereka menjelajahi ruang 3D, subset baru dari ruang tersebut akan di-stream ke perangkat mereka dan beberapa subset yang ada mungkin di-stream keluar.

Pertimbangkan praktik terbaik berikut untuk menggunakan PathfindingService dalam permainan yang mendukung streaming:

  • Streaming dapat memblokir atau membuka blokir jalur tertentu saat karakter bergerak di sepanjangnya. Misalnya, saat seorang karakter berlari melalui hutan, sebuah pohon mungkin di-stream di depan mereka dan menghalangi jalur. Untuk membuat penentuan jalur bekerja dengan lancar dengan streaming, sangat disarankan untuk menggunakan teknik menangani jalur yang terhalang dan menghitung ulang jalur saat diperlukan.

  • Pendekatan umum dalam penentuan jalur adalah menggunakan koordinat objek yang ada untuk perhitungan, seperti mengatur tujuan jalur ke posisi model TreasureChest yang ada di dunia. Pendekatan ini sepenuhnya kompatibel dengan Scripts sisi server karena server memiliki pandangan penuh dunia setiap saat, tetapi LocalScripts dan ModuleScripts yang dijalankan di klien mungkin gagal jika mereka berusaha menghitung jalur ke objek yang tidak di-stream.

    Untuk mengatasi masalah ini, pertimbangkan untuk mengatur tujuan ke posisi BasePart dalam model persisten. Model persistent dimuat segera setelah pemain bergabung dan mereka tidak pernah di-stream keluar, jadi skrip sisi klien dapat terhubung ke acara PersistentLoaded dan dengan aman mengakses model untuk membuat waypoint setelah acara dipicu.

Batasan dan faktor kegagalan

Mesin penentuan jalur memiliki batasan tertentu untuk memastikan pemrosesan yang efisien dan kinerja optimal. Selain itu, perhitungan penentuan jalur perhitungan dapat gagal karena berbagai alasan yang diuraikan di bawah ini.

©2026 Roblox Corporation. Roblox, logo Roblox, dan Powering Imagination termasuk dalam merek dagang kami yang terdaftar dan tidak terdaftar di AS dan negara lainnya.