Pencarian Jalur adalah proses memindahkan karakter atau objek (agen) sepanjang jalur logis di sekitar rintangan untuk mencapai tujuan, dengan opsi untuk menghindari bahan berbahaya atau daerah yang ditentukan.
Visualisasi navigasi
Untuk membantu dengan tata letak dan debugging pencarian jalur, Studio dapat merender mesh navigasi dan label modifier. Untuk mengaktifkannya, aktifkan Mesh Navigasi dan Modifier Pencarian Jalur dari widget Opsi Visualisasi di sudut kanan atas viewport 3D.

Dengan Mesh navigasi diaktifkan, area berwarna menunjukkan di mana karakter mungkin berjalan atau berenang. Panah kecil menunjukkan area yang akan dicoba dijangkau oleh karakter dengan melompat.

Implementasi
Meskipun pencarian jalur dapat diimplementasikan dengan berbagai cara melalui PathfindingService dan metode terkaitnya seperti CreatePath(), bagian ini menggunakan skrip pencarian jalur berikut untuk karakter pemain.
Untuk menguji sambil membaca:
- PENTINGDi Explorer, pilih kontainer StarterPlayer. Kemudian, di jendela Properties, atur DevComputerMovementMode dan DevTouchMovementMode ke Scriptable.


Salin kode berikut ke dalam LocalScript di dalam StarterCharacterScripts, atau dapatkan paket ini dan letakkan di dalam StarterCharacterScripts.
PlayerPathFollow (LocalScript di 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.01local VELOCITY_MULTIPLIER = 0.0625local path = PathfindingService:CreatePath({AgentCanClimb = true,Costs = {Water = 20}})local character = script.Parentlocal humanoid = character:WaitForChild("Humanoid")local waypointslocal nextWaypointIndexlocal blockedConnectionlocal currentWaypointReachedConnectionlocal currentWaypointPlaneNormal = Vector3.zerolocal currentWaypointPlaneDistance = 0local pathfinderWorking = falselocal function disconnectCurrentWaypointReachedConnection()if not currentWaypointReachedConnection then return endcurrentWaypointReachedConnection:Disconnect()currentWaypointReachedConnection = nilendlocal function isCurrentWaypointReached()if humanoid.FloorMaterial == Enum.Material.Air thenreturn falseendlocal reached = falseif currentWaypointPlaneNormal ~= Vector3.zero then-- Hitung jarak dari humanoid ke bidang tujuanlocal dist = currentWaypointPlaneNormal:Dot(humanoid.RootPart.Position) - currentWaypointPlaneDistance-- Hitung komponen kecepatan humanoid yang menuju bidanglocal velocity = -currentWaypointPlaneNormal:Dot(humanoid.RootPart.Velocity)-- Hitung ambang batas dari bidang tujuan berdasarkan kecepatan humanoidlocal threshold = math.max(1.0, VELOCITY_MULTIPLIER * velocity)-- Anggap waypoint tercapai jika kurang dari ambang batas di depan bidangreached = dist < thresholdelsereached = trueendif reached thencurrentWaypointPlaneNormal = Vector3.zerocurrentWaypointPlaneDistance = 0moveToNextWaypoint()endendlocal function calculateNextWaypointApproach()nextWaypointIndex += 1if nextWaypointIndex > #waypoints thenreturn falseendlocal currentWaypoint = waypoints[nextWaypointIndex - 1]local nextWaypoint = waypoints[nextWaypointIndex]-- Bangun bidang tujuan dari waypoint berikutnya menuju yang saat inicurrentWaypointPlaneNormal = currentWaypoint.Position - nextWaypoint.Position-- Atur normal tegak lurus terhadap bidang Y saat tidak memanjatif nextWaypoint.Label ~= "Climb" thencurrentWaypointPlaneNormal = Vector3.new(currentWaypointPlaneNormal.X, 0, currentWaypointPlaneNormal.Z)endif currentWaypointPlaneNormal.Magnitude > 0.000001 thencurrentWaypointPlaneNormal = currentWaypointPlaneNormal.UnitcurrentWaypointPlaneDistance = currentWaypointPlaneNormal:Dot(nextWaypoint.Position)endreturn trueendlocal function resetWaypointData()humanoid:Move(Vector3.zero)currentWaypointPlaneNormal = Vector3.zerocurrentWaypointPlaneDistance = 0disconnectCurrentWaypointReachedConnection()pathfinderWorking = falseendlocal function waitForGround()while humanoid.FloorMaterial == Enum.Material.Air dotask.wait(GROUND_WAIT)endendfunction moveToNextWaypoint()if calculateNextWaypointApproach() thendisconnectCurrentWaypointReachedConnection()currentWaypointReachedConnection = RunService.Heartbeat:Connect(isCurrentWaypointReached)local nextWaypointPosition = waypoints[nextWaypointIndex].Positionlocal nextWaypointAction = waypoints[nextWaypointIndex].Actionhumanoid:Move(nextWaypointPosition - humanoid.RootPart.Position)if waypoints[nextWaypointIndex + 1] and waypoints[nextWaypointIndex + 1].Label == "UseBoat" thennextWaypointIndex += 1-- Panggil fungsi kustom Anda untuk membuat agen menggunakan perahuelseif nextWaypointAction == Enum.PathWaypointAction.Jump thenhumanoid:ChangeState(Enum.HumanoidStateType.Jumping)while humanoid.FloorMaterial ~= Enum.Material.Air dotask.wait(GROUND_WAIT)endhumanoid:Move(nextWaypointPosition - humanoid.RootPart.Position)endelseresetWaypointData()endendlocal function findStartingPoint(waypoints)nextWaypointIndex = 1while nextWaypointIndex + 1 <= #waypoints dolocal dist = waypoints[nextWaypointIndex + 1].Position - humanoid.RootPart.Positiondist = Vector3.new(dist.X, 0, dist.Z)if dist.magnitude >= 2 thenreturnendnextWaypointIndex += 1endendlocal function followPath()-- Hitung jalurpathfinderWorking = truewaitForGround()local success, errorMessage = pcall(function()path:ComputeAsync(character.PrimaryPart.Position, DESTINATION)end)if not success or path.Status ~= Enum.PathStatus.Success thenwarn("Jalur tidak dihitung!", errorMessage)returnend-- Dapatkan waypoint jalurwaypoints = path:GetWaypoints()-- Deteksi jika jalur terhalangblockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)-- Periksa apakah rintangan lebih jauh di jalurif blockedWaypointIndex >= nextWaypointIndex then-- Hentikan deteksi pemblokiran jalur sampai jalur dihitung ulangblockedConnection:Disconnect()resetWaypointData()-- Panggil fungsi untuk menghitung ulang jalur barufollowPath()endend)findStartingPoint(waypoints)moveToNextWaypoint()endfollowPath()Edit variabel DESTINATION (
BARIS 5) ke tujuan Vector3 dalam dunia 3D yang dapat dijangkau oleh karakter pemain.Lanjutkan melalui bagian berikut untuk mempelajari tentang perhitungan jalur dan pergerakan karakter.
Pembuatan jalur
Pencarian jalur dimulai melalui PathfindingService dan metode CreatePath() (
| Kunci | Deskripsi | Tipe | Default |
|---|---|---|---|
| AgentRadius | Jari-jari agen, dalam stud. Berguna untuk menentukan pemisahan minimum dari rintangan. | integer | 2 |
| AgentHeight | Tinggi agen, dalam stud. Ruang kosong yang lebih kecil dari nilai ini, seperti ruang di bawah tangga, akan ditandai sebagai tidak dapat dilalui. | integer | 5 |
| AgentCanJump | Menentukan apakah melompat selama pencarian jalur diizinkan. | boolean | true |
| AgentCanClimb | Menentukan apakah memanjat TrussParts selama pencarian jalur diizinkan. Jalur yang dapat dipanjat memiliki Label bernama Climb dan biaya untuk jalur yang dapat dipanjat adalah 1 secara default. | boolean | false |
| WaypointSpacing | Jarak antara waypoint sementara dalam jalur. Jika diatur ke math.huge, tidak akan ada waypoint sementara. | number | 4 |
| Costs | Tabel bahan atau PathfindingModifiers yang ditentukan dan biaya mereka untuk dilalui. Berguna untuk membuat agen lebih menyukai bahan/daerah tertentu dibandingkan yang lain. Lihat modifier untuk detail. | table | nil |
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 (

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

Pergerakan jalur
Setiap PathWaypoint terdiri dari Position (Vector3) dan Action (PathWaypointAction). Untuk memindahkan karakter yang memiliki Humanoid, seperti karakter Roblox pada umumnya, cara terbaik adalah memanggil Humanoid:Move() dari waypoint ke waypoint dan menggunakan callback isCurrentWaypointReached() dari skrip (
Jalur 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 menangani ini, Anda dapat menghubungkan acara Path.Blocked dan menghitung ulang jalur di sekitar apa pun yang menghalanginya (
Modifier pencarian jalur
Secara default, Path:ComputeAsync() mengembalikan jalur terpendek antara titik awal dan tujuan, dengan pengecualian bahwa ia berusaha menghindari lompatan. Ini terlihat tidak alami dalam beberapa situasi; misalnya, jalur mungkin melewati air rawa daripada mengelilinginya hanya karena jalur melalui air secara geometris lebih pendek.

Untuk mengoptimalkan pencarian jalur lebih lanjut, Anda dapat menerapkan modifier pencarian jalur untuk menghitung jalur yang lebih cerdas di berbagai bahan, di sekitar daerah 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 apa pun dapat didefinisikan sebagai tidak dapat dilalui dengan mengatur nilainya ke math.huge.
Kunci dalam tabel Costs harus berupa nama string yang mewakili nama Enum.Material, misalnya Water untuk Enum.Material.Water atau CrackedLava untuk Enum.Material.CrackedLava.
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
}
})Konfigurasi daerah
Dalam beberapa kasus, preferensi bahan tidak cukup. Misalnya, Anda mungkin ingin karakter menghindari daerah yang ditentukan, terlepas dari bahan di bawah kaki. Ini dapat dicapai dengan menambahkan objek PathfindingModifier ke sebuah bagian.
Buat bagian Anchored di sekitar daerah dan atur properti CanCollide ke false.

Sisipkan instance PathfindingModifier ke bagian tersebut, temukan properti Label dan berikan nama yang berarti seperti DangerZone.

Sertakan kunci DangerZone yang cocok dan nilai numerik 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.01local VELOCITY_MULTIPLIER = 0.0625local path = PathfindingService:CreatePath({AgentCanClimb = true,Costs = {DangerZone = math.huge, Water = 20, CrackedLava = 20, Slate = 20}})
Mengabaikan rintangan
Dalam beberapa kasus, berguna untuk melakukan pencarian jalur melalui rintangan padat seolah-olah mereka tidak ada. Ini memungkinkan Anda menghitung jalur melalui penghalang fisik tertentu, dibandingkan dengan perhitungan yang gagal secara langsung.
Buat bagian Anchored di sekitar objek dan atur properti CanCollide ke false.

Sisipkan instance PathfindingModifier ke bagian tersebut dan aktifkan properti PassThrough.

Sekarang, ketika jalur dihitung dari NPC zombie ke karakter pemain, jalur meluas melewati pintu dan Anda dapat meminta zombie untuk melaluinya. Bahkan jika zombie tidak dapat membuka pintu, ia bereaksi seolah-olah "mendengar" karakter di balik pintu.

Tautan pencarian jalur
Terkadang perlu untuk menemukan jalur di ruang yang tidak dapat dilalui secara normal, seperti melintasi jurang, dan melakukan tindakan kustom untuk mencapai waypoint berikutnya. Ini dapat dicapai melalui objek PathfindingLink.
Menggunakan contoh dari atas, Anda dapat membuat agen menggunakan perahu.

Untuk membuat PathfindingLink menggunakan contoh ini:
- OPSIONALAktifkan Tautan Pencarian Jalur dari widget Opsi Visualisasi di sudut kanan atas viewport 3D. Ini membantu dengan visualisasi dan debugging saat menerapkan tautan pencarian jalur.
Buat dua Attachments, satu di kursi perahu dan satu di dekat titik pendaratan perahu.

Buat objek PathfindingLink di workspace, lalu tetapkan properti Attachment0 dan Attachment1 ke lampiran awal dan akhir masing-masing.


Berikan nama yang berarti seperti UseBoat pada properti Label. Nama ini digunakan sebagai tanda dalam skrip pencarian jalur untuk memicu tindakan kustom ketika agen mencapai titik tautan awal.

Sertakan tabel Costs dalam CreatePath() yang berisi kunci Water dan kunci kustom yang cocok dengan nama properti Label. Tetapkan nilai kunci kustom lebih rendah dari nilai 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.01local VELOCITY_MULTIPLIER = 0.0625local path = PathfindingService:CreatePath({AgentCanClimb = true,Costs = {UseBoat = 2, Water = 20}})Dalam fungsi moveToNextWaypoint() (
BARIS 93–114), pemeriksaan kustom 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 perahu, memindahkan perahu melintasi air, mengeluarkan agen di titik pendaratan perahu, dan kemudian melanjutkan jalur agen ke tujuan akhirnya.
Kompatibilitas streaming
Streaming instansi streaming dalam permainan adalah fitur yang kuat yang memuat dan membongkar konten 3D secara dinamis saat karakter pemain bergerak di sekitar dunia. Saat mereka menjelajahi ruang 3D, subset baru dari ruang tersebut akan dialirkan ke perangkat mereka dan beberapa subset yang ada mungkin akan dialirkan 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 karakter berlari melalui hutan, sebuah pohon mungkin akan dialirkan di suatu tempat di depan mereka dan menghalangi jalur. Untuk membuat pencarian jalur bekerja dengan mulus dengan streaming, sangat disarankan agar Anda menggunakan teknik menangani jalur terhalang dan menghitung ulang jalur saat diperlukan.
Pendekatan umum dalam pencarian 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 tentang dunia setiap saat, tetapi LocalScripts dan ModuleScripts yang berjalan di klien mungkin gagal jika mereka mencoba menghitung jalur ke objek yang tidak dialirkan.
Untuk mengatasi masalah ini, pertimbangkan untuk mengatur tujuan ke posisi BasePart dalam model persisten. Model persisten dimuat segera setelah pemain bergabung dan tidak pernah dialirkan keluar, sehingga skrip sisi klien dapat terhubung ke acara PersistentLoaded dan dengan aman mengakses model untuk membuat waypoint setelah acara tersebut terjadi.
Batasan dan faktor kegagalan
Mesin pencarian jalur memiliki batasan tertentu untuk memastikan pemrosesan yang efisien dan kinerja optimal. Selain itu, perhitungan pencarian jalur dapat gagal karena berbagai alasan seperti yang dijelaskan di bawah ini.