Panduan ini menguraikan beberapa teknik untuk menggunakan streaming instance dalam permainan secara efisien dan efektif. Meskipun tidak ada solusi "satu ukuran untuk semua" dalam merancang permainan streaming atau mengonversi permainan non-streaming menjadi streaming, mengikuti langkah-langkah tingkat tinggi ini akan membawa Anda sebagian besar ke sana.
Properti streaming
Setelah StreamingEnabled diaktifkan untuk objek Workspace di Studio, atur properti terkaitnya ke nilai yang disarankan berikut:
| Properti | Rekomendasi |
|---|---|
| EnableSLIMAvatars | Gunakan Enabled untuk merender avatar rig standar sebagai stand-in yang ringan dan teranimasi ketika sesuai. Lihat avatar SLIM untuk info lebih lanjut. |
| ModelStreamingBehavior | Gunakan Improved untuk mengaktifkan streaming yang paling efisien untuk Models dengan keturunan BasePart. |
| StreamingIntegrityMode | Gunakan PauseOutsideLoadedArea untuk menyeimbangkan integritas permainan tanpa berhenti secara tidak perlu atau terlalu sering. |
| StreamingMinRadius | Gunakan default 64 untuk memaksimalkan seberapa banyak mesin dapat mengecilkan permainan untuk perangkat low-end. |
| StreamingTargetRadius | Gunakan default 1024 untuk mencapai keseimbangan yang baik antara visibilitas untuk pemain di perangkat high-end dan jejak memori yang wajar. |
| StreamOutBehavior | Gunakan Opportunistic untuk memungkinkan klien mengumpulkan konten secara agresif, secara signifikan mengurangi penggunaan memori dan membantu mencegah suara habis memori. |
Level-of-detail model
Model.LevelOfDetail membantu mengisi konten Model yang tidak di-stream dengan mesh komposit atau imposter yang ringan, membuat dunia terlihat visual lengkap. SLIM (Model Interaktif Ringan yang Dapat Diskalakan) mesh komposit sangat efektif, karena pemain sering kali tidak dapat membedakan mesh SLIM dari yang asli yang sepenuhnya di-stream.
Untuk hasil terbaik:
- Kelompokkan bagian yang terkait secara spasial dan logis, misalnya semua bagian dari sebuah mobil.
- Atur LevelOfDetail menjadi SLIM pada model yang berisi mesh dan bagian statis. Model yang berisi mesh yang tidak terikat, dimodifikasi saat runtime, atau memainkan animasi tidak didukung.
- Pertahankan jangkauan spasial masing-masing model di bawah ~64 stud kubik untuk meningkatkan kemungkinan bahwa seluruh model asli di-stream bersama. Jika sebuah model memiliki jangkauan yang sangat besar, pisahkan menjadi model modular yang lebih kecil dan terapkan LevelOfDetail yang sesuai untuk masing-masing.
Struktur model
Di luar pengaturan level-of-detail model, struktur dan pengaturan Models Anda memiliki dampak signifikan pada seberapa baik kinerja streaming. Saat Anda membangun atau mengonversi permainan yang ada:
Gunakan model atomik untuk grup logis — Ketika sebuah skrip membutuhkan akses ke semua bagian di dalam model, atur ModelStreamingMode menjadi Atomic. Ini memungkinkan skrip sisi klien untuk mengakses instance di dalam model tanpa penggunaan berlebih dari WaitForChild() (meskipun skrip semacam itu harus tetap menggunakan WaitForChild() untuk model atomik secara keseluruhan).
Minimalkan model persisten — Model persisten dimuat setelah bergabung dan tidak pernah di-stream keluar, secara permanen mengoccupy memori. Atur ModelStreamingMode model menjadi Persistent hanya jika model harus tetap tersedia dan dapat diakses oleh skrip pada semua waktu.
Pecah model kontainer — Pola non-streaming umum adalah sebuah Model besar tunggal yang berisi banyak NPC, propertis, atau pengelompokan serupa. Di bawah streaming, model kontainer mengurangi efisiensi streaming dan tidak optimal untuk level-of-detail model yang bekerja paling baik dengan instance yang dikelompokkan dengan erat. Pecahkan model kontainer menjadi model yang lebih kecil dengan bagian yang dekat fisik atau terkait secara logis.
Avatar SLIM
Avatar platform di luar area yang sedang di-stream tidak terlihat secara default, tetapi mengaktifkan Workspace.EnableSLIMAvatars merender avatar rig standar sebagai stand-in yang ringan dan teranimasi jika sesuai. Secara efektif, mesin:
- Merender versi SLIM ketika model avatar yang sebenarnya di-stream keluar.
- Menukar SLIM/yang sebenarnya berdasarkan sumber daya yang tersedia di dalam radius streaming, karena model SLIM bisa jauh lebih murah untuk dirender dibandingkan model resolusi penuh.
- Mengontrol animasi SLIM berdasarkan pentingnya adegan dan bandwidth.
Sistem level-of-detail avatar memiliki optimasi khusus untuk avatar. Berikut ini menggambarkan apa yang akan atau tidak akan diproses oleh SLIM:
Pola skrip
Pola skrip berikut ini paling sering dipengaruhi oleh streaming. Strategi yang benar tergantung pada maksud dari kode, jadi setiap pola mencantumkan beberapa opsi yang sesuai.
Indeks langsung ke keturunan
Mengindeks ke keturunan Workspace dengan operator . menghasilkan error jika ada instance dalam jalur yang saat ini tidak di-stream. Hal yang sama berlaku untuk FindFirstChild(), FindFirstChildWhichIsA(), dan FindFirstChildOfClass() yang mengembalikan nil jika anak tersebut belum di-stream.
Pencarian Keturunanlocal house1 = workspace:FindFirstChild("House1") -- nil jika "House1" belum di-streamlocal door = workspace.House1.Door -- Rusak jika "House1" atau "Door" belum di-stream
Pola serupa adalah mengakses Humanoid atau keturunan karakter lainnya langsung di dalam koneksi Player.CharacterAdded. Di bawah streaming, model karakter diparent ke Workspace sebelum semua keturunannya ter-replikasi, sehingga pengindeksan langsung gagal.
Keturunan Karakter
local Players = game:GetService("Players")
local player = Players.LocalPlayer
player.CharacterAdded:Connect(function(character)
local humanoid = character.Humanoid
end)
Jika skrip tidak dapat melanjutkan tanpa instance, tunggu untuk itu dengan WaitForChild():
Pencarian Keturunanlocal house1 = workspace:WaitForChild("House1")local door = house1:WaitForChild("Door")
Instance dikirim secara jarak jauh
Sinyal RemoteEvent/RemoteFunction dan instance yang dirujuknya berpindah secara independen, jadi sinyal dapat tiba di klien sebelum instance tersedia — atau instance mungkin tidak pernah ada sama sekali. Dua penyebab yang mungkin meliputi:
Di bawah streaming, mungkin ada sedikit keterlambatan antara saat bagian/model dibuat di server dan saat direplikasi ke klien. Secara efektif, sebuah bagian yang dirujuk oleh RemoteEvent/RemoteFunction mungkin belum ada, bahkan di dalam area yang di-stream.
Mengirim referensi bagian/model dari server ke klien melalui RemoteEvent atau RemoteFunction mengharuskan bahwa instance direplikasi ke klien yang menerima. Mengirim jalur instance sebagai string memiliki masalah yang sama, karena jalur tersebut mungkin mengarah ke lokasi yang tidak ada di klien:
Skrip Klienlocal ReplicatedStorage = game:GetService("ReplicatedStorage")local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")remoteEvent.OnClientEvent:Connect(function(data)local checkpoint = data.checkpoint -- Error jika "checkpoint" tidak di-streamlocal level = workspace.Levels[data.levelPath] -- Error jika jalur tidak di-streamend)
Jika skrip klien yang menerima memerlukan instance untuk melanjutkan, sertakan WaitForChild() sebelum menggunakannya. Perhatikan bahwa ini dapat menunggu tanpa batas jika instance tidak pernah di-stream, jadi pertimbangkan menambahkan batas waktu sebagai parameter kedua dari WaitForChild().
Desinkronisasi klien
Desinkronisasi sisi klien harus diperlakukan sebagai pengecualian, bukan pola desain standar. Memperkenalkan salinan klien-saja atau mengubah posisi instance secara lokal dapat menimbulkan masalah serius. Audit kode Anda untuk tempat-tempat yang mengandalkan perubahan jenis ini bertahan pada klien.
Sebagai contoh, memindahkan instance secara lokal dari ReplicatedStorage ke Workspace dapat membuat instance tersebut layak untuk di-stream keluar. Demikian pula, mengkloning instance secara lokal (Instance:Clone()) dari ReplicatedStorage ke dalam Workspace menciptakan salinan yang hanya ada di klien yang tidak lagi menjadi bagian dari jalur replikasi server dan tidak akan menerima pembaruan properti dari instance asli yang dimiliki server.
Konsep yang sama berlaku ketika memanggil Instance:Destroy() di klien untuk objek yang dimiliki server. Ini menghapus instance secara lokal tetapi server masih memilikinya, jadi itu akan di-stream kembali dengan keadaan aslinya saat layak.
Streaming proaktif
Ketika tujuan berikutnya seorang pemain dapat diprediksi, lakukan panggilan sisi server ke Player:RequestStreamAroundAsync() untuk streaming area sementara, atau gunakan Player:AddReplicationFocus() pada basis terbatas untuk area yang harus tetap dimuat sampai secara eksplisit dilepaskan.
Sebagai contoh, ketika karakter pemain akan teleportasi dengan perubahan CFrame ke rumah pemain lain di lokasi yang jauh, Anda dapat melakukan pre-fetch area tujuan untuk meminimalkan pop-in dan memberikan transisi yang lebih halus. Skrip berikut menunjukkan bagaimana sebuah peristiwa jarak jauh dari klien ke server dapat dipicu untuk memindahkan karakter pemain menggunakan metode pre-fetching. Jika permintaan pre-fetch berhasil saat fungsi mengembalikan, radius minimum di sekitar lokasi target harus tersedia di klien.
Skrip Server - Teleportasi Karakter Pemain
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local teleportEvent = ReplicatedStorage:WaitForChild("TeleportEvent")
local function teleportPlayer(player, teleportTarget)
-- Permintaan streaming di sekitar lokasi target
player:RequestStreamAroundAsync(teleportTarget)
-- Teleportasi karakter
local character = player.Character
if character and character.Parent then
local currentPivot = character:GetPivot()
character:PivotTo(currentPivot * CFrame.new(teleportTarget))
end
end
-- Panggil fungsi teleport saat klien memicu peristiwa jarak jauh
teleportEvent.OnServerEvent:Connect(teleportPlayer)
Membaca properti instance
Setelah sebuah instance di-stream keluar, pembaruan propertinya tidak lagi direplikasi ke klien tersebut. Membaca properti seperti BasePart.Position terus berhasil tetapi mengembalikan nilai terakhir yang direplikasi yang bisa sangat usang.
local Players = game:GetService("Players")local player = Players.LocalPlayer-- Posisi mungkin usang jika "target" telah di-stream keluarlocal dist = (target.Position - player.Character.HumanoidRootPart.Position).Magnitude
Pindahkan logika ke server, karena skrip sisi server melihat semua instance pada semua waktu. Ini umumnya merupakan opsi yang paling andal untuk cek jarak dan logika sensitif posisi lainnya.
Menunggu di jalur kritis
Beberapa permainan non-streaming memuat peta mereka dengan mengkloningnya dari ReplicatedStorage ke dalam Workspace, kemudian menunggu di klien sebelum menghilangkan layar pemuatan dan memberi sinyal kesiapan. Di bawah streaming, ini menggantung tanpa batas — karakter klien belum muncul, jadi tidak ada fokus replikasi, dan instance peta spasial tidak pernah di-stream.
Pindahkan logika layar pemuatan sehingga tidak bergantung pada instance spasial tertentu yang hadir, misalnya dengan memberi sinyal kesiapan setelah karakter muncul dan area sekitarnya di-stream.
Penanganan perubahan sinyal
Sinyal seperti Instance.ChildAdded/Instance.ChildRemoved dan sinyal CollectionService seperti GetInstanceAddedSignal() atau GetInstanceRemovedSignal() juga aktif pada streaming masuk/keluar, tidak dapat dibedakan dari spawns/removals yang nyata. Skrip penerima tidak dapat membedakan perbedaan dari sinyal itu sendiri, jadi logika yang mengasumsikan sinyal berkaitan dengan acara "nyata" perlu diperbarui.
Audit skrip untuk setiap pendengar sinyal yang bisa rusak atau berubah secara signifikan ketika dipicu oleh streaming masuk dan/atau streaming keluar. Misalnya, jika Anda memainkan efek audio atau visual ketika NPC musuh muncul ke dunia, berikan setiap musuh atribut atribut seperti Spawned pada spawn pertama, dan lewati pemutaran efek audio/visual yang sama pada streaming masuk musuh di masa depan.
Pelacakan Atribut
local CollectionService = game:GetService("CollectionService")
local TAG_NAME = "Enemy"
CollectionService:GetInstanceAddedSignal(TAG_NAME):Connect(function(enemy)
if not enemy:GetAttribute("Spawned") then
-- Set atribut "Spawned" pada musuh untuk spawn awal
enemy:SetAttribute("Spawned", true)
-- Putar efek audio/visual untuk spawn awal ini
playSpawnEffects(enemy)
end
end)
Iterasi koleksi
Iterasi koleksi sisi klien seperti Instance:GetChildren() dan Instance:GetDescendants() hanya mengembalikan sub-keturunan yang di-stream. Ini berlaku bahkan ketika induknya sendiri selalu direplikasi, seperti Folder yang langsung di bawah Workspace yang keturunannya bersifat spasial mengalir masuk dan keluar.
local Players = game:GetService("Players")local player = Players.LocalPlayer-- Folder "Homes" selalu direplikasi tetapi anak-anaknya mengalir masuk dan keluar-- Loop ini mungkin melewatkan rumah yang tidak di-streamfor _, home in workspace.Homes:GetChildren() doif home.Settings.Owner.Value == player.Name thenreturn homeendend
Jika enumerasi lengkap diperlukan, lakukan pemindaian di server dan kirim hasilnya kepada pemain melalui RemoteEvent jika perlu.
Kuery spasial
Kuery spasial sisi klien seperti WorldRoot:Raycast(), WorldRoot:GetPartBoundsInBox(), dan Model:GetBoundingBox() hanya mencerminkan konten yang di-stream. Apakah ini menjadi masalah tergantung pada penggunaan kuery tersebut.
Gunakan server untuk kuery yang hasilnya harus mencerminkan seluruh dunia, misalnya raycast yang memeriksa apakah pemain memiliki garis pandang ke target yang jauh.
Pola lainnya
Pola berikut ini juga mungkin berlaku dan harus dipertimbangkan dengan cermat:
Sebuah Sound atau AudioPlayer diparent ke objek 3D berhenti saat objek itu di-stream keluar. Untuk audio ambien yang harus terus berlanjut terlepas dari streaming, ibu emiter harus diparent ke model persisten atau ke kontainer non-streaming.
Dalam-game objek UI seperti BillboardGui atau SurfaceGui serta efek visual seperti Beams atau Highlights yang adornee atau lampirannya di-stream keluar mendadak berhenti merender. Ini mungkin merupakan perilaku yang dimaksudkan, tetapi Anda harus memverifikasinya.
Peristiwa BasePart.Touched, ProximityPrompts, DragDetectors, dan ClickDetectors tidak beroperasi untuk pemain yang tidak memiliki bagian/model terkait yang di-stream di klien. Jika interaksi harus memungkinkan dari jarak berapa pun, model perlu persisten atau interaksinya memerlukan mekanisme yang berbeda.
Untuk PathfindingService dan penelusuran jalur sisi klien, pencari jalur hanya melihat geometri yang di-stream masuk di klien dan mungkin merutekan melalui rintangan yang ada di server. Lihat di sini untuk strategi.
Kondisi uji yang realistis
Setelah skrip diperbarui, uji permainan secara menyeluruh. Bug streaming sering kali hanya terlihat di tepi area yang di-stream atau selama transisi, jadi hanya menguji dekat spawn atau pada radius target tidak cukup.
Uji dengan Workspace.StreamingTargetRadius disetel ke nilai minimumnya (64). Beberapa bug streaming hanya muncul ketika area yang di-stream kecil.
Mainkan melalui pola traversal penuh permainan, teleportasi antara area yang jauh, dan kunjungi kembali area setelah meninggalkan mereka. Ini adalah situasi yang paling banyak menguji streaming masuk dan streaming keluar.
Gunakan overlay debug streaming untuk memantau pengaturan streaming yang aktif, wilayah yang saat ini dimuat, dan status streaming runtime.
Perhatikan jendela Output dan Developer Console untuk kesalahan, karena banyak dari pola skrip menghasilkan kesalahan daripada perilaku salah diam. Perhatikan khususnya kesalahan dengan bentuk attempt to index nil with ... yang sering menunjukkan adanya panggilan yang hilang pada WaitForChild().
Equip dan aktifkan Tools, tembak senjata, dan picu berbagai interaksi permainan.

