Pencarian Jalur

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

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.

Tampilan dekat viewport 3D dengan tombol Opsi Visualisasi ditunjukkan di sudut kanan atas.

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.

Mesh navigasi ditampilkan di Studio

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:

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

  2. 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.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 memanjat
    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 kustom Anda 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 apakah rintangan lebih jauh di jalur
    if blockedWaypointIndex >= nextWaypointIndex then
    -- Hentikan deteksi pemblokiran 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 (

    BARIS 5
    ) ke tujuan Vector3 dalam dunia 3D yang dapat dijangkau oleh karakter pemain.

  4. Lanjutkan melalui bagian berikut untuk mempelajari tentang perhitungan jalur dan pergerakan karakter.

Pembuatan jalur

Pencarian jalur dimulai melalui PathfindingService dan metode CreatePath() (

BARIS 9–14
). Metode ini menerima tabel parameter opsional yang memperhalus cara karakter (agen) bergerak sepanjang jalur.

KunciDeskripsiTipeDefault
AgentRadiusJari-jari 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 pencarian jalur diizinkan.booleantrue
AgentCanClimbMenentukan 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.booleanfalse
WaypointSpacingJarak antara waypoint sementara dalam jalur. Jika diatur ke math.huge, tidak akan ada waypoint sementara.number4
CostsTabel 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.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 (

BARIS 133–139
).

Jalur mulai/akhir ditandai di 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() (

BARIS 142
). Array yang dikembalikan diatur dalam urutan waypoint dari awal jalur hingga akhir jalur.

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

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 (

BARIS 32–56
) untuk mendeteksi kapan karakter mencapai setiap waypoint.

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 (

BARIS 145–154
).

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.

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

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.

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
}
})

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.

  1. Buat bagian Anchored di sekitar daerah dan atur properti CanCollide ke false.

    Bagian yang ditambatkan mendefinisikan daerah untuk menerapkan modifier pencarian jalur.
  2. Sisipkan instance PathfindingModifier ke bagian tersebut, temukan properti Label dan berikan nama yang berarti seperti DangerZone.

    Instance PathfindingModifier dengan properti Label diatur ke DangerZone.
  3. 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.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 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.

  1. Buat bagian Anchored di sekitar objek dan atur properti CanCollide ke false.

    Bagian yang ditambatkan mendefinisikan daerah untuk menerapkan modifier pencarian jalur.
  2. Sisipkan instance PathfindingModifier ke bagian tersebut dan aktifkan properti PassThrough.

    Instance PathfindingModifier dengan properti PassThrough diaktifkan.

    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.

    Jalur NPC zombie melewati pintu yang sebelumnya menghalangi.

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.

PathfindingLink menunjukkan bagaimana agen dapat menggunakan perahu.

Untuk membuat PathfindingLink menggunakan contoh ini:

  1. OPSIONAL
    Aktifkan Tautan Pencarian Jalur dari widget Opsi Visualisasi di sudut kanan atas viewport 3D. Ini membantu dengan visualisasi dan debugging saat menerapkan tautan pencarian jalur.

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

    Lampiran dibuat untuk awal dan akhir tautan pencarian jalur.
  3. Buat objek PathfindingLink di workspace, lalu tetapkan properti Attachment0 dan Attachment1 ke lampiran awal dan akhir masing-masing.

    Properti Attachment0/Attachment1 dari PathfindingLink. PathfindingLink divisualisasikan di dunia 3D.
  4. 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.

    Properti Label yang ditentukan untuk PathfindingLink.
  5. 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.01
    local VELOCITY_MULTIPLIER = 0.0625
    local path = PathfindingService:CreatePath({
    AgentCanClimb = true,
    Costs = {
    UseBoat = 2, Water = 20
    }
    })
  6. 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.

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