Tanaman adalah pengalaman referensi di mana pemain menanam dan menyiram benih, sehingga mereka dapat kemudian memanen dan menjual tanaman yang dihasilkan.

Proyek fokus pada kasus penggunaan umum yang mungkin Anda temui saat mengembangkan pengalaman di Roblox.Di mana berlaku, Anda akan menemukan catatan tentang trade-off, kompromi, dan alasan pilihan implementasi yang berbeda, sehingga Anda dapat membuat keputusan terbaik untuk pengalaman Anda sendiri.
Dapatkan file
- Navigasikan ke halaman pengalaman Tanaman.
- Klik tombol ⋯ dan Edit di Studio .
Gunakan kasus
Tanaman mencakup kasus penggunaan berikut:
- Data sesi dan persistensi data pemain
- Manajemen tampilan UI
- Jaringan klien-server
- Pengalaman Pengguna Pertama Kali (FTUE)
- Pembelian mata uang keras dan lembut
Selain itu, proyek ini memecahkan set masalah yang lebih sempit yang dapat diterapkan pada banyak pengalaman, termasuk:
- Kustomisasi dari area di tempat yang terkait dengan pemain
- Mengelola kecepatan gerakan karakter pemain
- Membuat objek yang mengikuti karakter di sekitar
- Mendeteksi bagian dunia mana seorang karakter berada
Perhatikan bahwa ada beberapa kasus penggunaan dalam pengalaman ini yang terlalu kecil, terlalu niche, atau tidak menunjukkan solusi untuk tantangan desain yang menarik; ini tidak dicakup.
struktur proyek
Keputusan pertama saat membuat pengalaman adalah memutuskan cara mengstrukturkan proyek , yang terutama termasuk di mana menempatkan instans khusus di model data dan cara mengatur dan mengatur titik masuk untuk kode klien dan server.
Model data
Tabel berikut menjelaskan layanan kontainer mana yang ditempatkan di dalam instansi model data.
Layanan | Jenis instance |
---|---|
Workspace | Berisi model statis yang mewakili dunia 3D, khususnya bagian dunia yang tidak milik pemain mana pun.Anda tidak perlu secara dinamis membuat, memodifikasi, atau menghancurkan instans ini saat runtime, sehingga dapat diterima untuk meninggalkannya di sini.: Ada juga kosong Folder , ke model pertanian pemain yang akan ditambahkan saat runtime. |
Lighting | Efek atmosfer dan pencahayaan. |
ReplicatedFirst | Berisi subset terkecil dari instans yang diperlukan untuk menampilkan layar pemuatan dan menginisialisasi game.Semakin banyak instansi yang ditempatkan di ReplicatedFirst , semakin lama menunggu mereka untuk mereplikasi sebelum kode di ReplicatedFirst dapat dijalankan.:
|
ReplicatedStorage | Berfungsi sebagai wadah penyimpanan untuk semua instansi yang membutuhkan akses di klien dan server.:
|
ServerScriptService | Berisi sebuah Script yang berfungsi sebagai titik masuk untuk semua kode sisi server dalam proyek. |
ServerStorage | Berfungsi sebagai wadah penyimpanan untuk semua instansi yang tidak perlu disalin ke klien.:
|
SoundService | Berisi objek Sound yang digunakan untuk efek suara dalam game.Di bawah SoundService , objek ini Sound tidak memiliki posisi dan tidak disimulasikan di ruang 3D. |
Titik masuk
Sebagian besar proyek mengatur kode di dalam reusable ModuleScripts yang dapat diimpor di seluruh basis kode. ModuleScripts adalah dapat digunakan kembali tetapi tidak dijalankan sendiri; mereka perlu diimpor oleh Script atau LocalScript .Banyak proyek Roblox akan memiliki banyak dan objek, masing-masing terkait dengan perilaku atau sistem tertentu dalam game, menciptakan banyak titik masuk.
Untuk mikrogame Tanaman, pendekatan yang berbeda diterapkan melalui satu LocalScript yang merupakan titik masuk untuk semua kode klien, dan satu Script yang merupakan titik masuk untuk semua kode server.Pendekatan yang benar untuk proyek Anda tergantung pada persyaratan Anda, tetapi satu titik masuk memberikan kontrol lebih besar atas urutan di mana sistem dieksekusi.
Daftar berikut menggambarkan trade-off dari kedua pendekatan:
- Satu Script dan satu LocalScript mencakup kode server dan klien masing-masing.
- Kontrol lebih besar atas urutan di mana sistem yang berbeda dimulai karena semua kode diinisialisasi dari satu skrip.
- Dapat melewati objek dengan referensi antar sistem.
Arsitektur sistem tingkat tinggi
Sistem tingkat atas dalam proyek terperinci di bawah ini.Beberapa sistem ini jauh lebih kompleks daripada yang lain, dan dalam banyak kasus fungsionalitas mereka d抽象化 di seluruh hierarki Kelaslain.

Masing-masing sistem ini adalah "singleton," dalam arti bahwa ini adalah kelas yang tidak dapat diinstanisasi yang diinisialisasi oleh klien atau server yang relevan start skrip.Anda dapat membaca lebih lanjut tentang pola tunggal nanti di panduan ini.
Pelayan
Sistem berikut terkait dengan server.
Sistem | Deskripsi |
---|---|
Jaringan |
|
Server Data Pemain |
|
Pasar |
|
Manajer Grup Tabrakan |
|
Server Manajer Peternak |
|
Wadah Objek Pemain |
|
Pemain Tag |
|
Server Manajer Ftue |
|
Pembangkit Karakter |
|
Pelanggan
Sistem berikut terkait dengan klien.
Sistem | Deskripsi |
---|---|
Jaringan |
|
Klien Data Pemain |
|
Klien Pasar |
|
Manajer Lompatan Berjalan Lokal |
|
Klien Manajer Peternak |
|
Pengaturan UI |
|
Klien Manajer Ftue |
|
CharacterSprint |
|
Komunikasi klien-server
Sebagian besar pengalaman Roblox melibatkan beberapa elemen komunikasi antara klien dan server.Ini dapat termasuk permintaan klien ke server melakukan tindakan tertentu dan server menyalin pembaruan ke klien.
Dalam proyek ini, komunikasi klien-server dipertahankan sebagai umum mungkin dengan membatasi penggunaan RemoteEvent dan RemoteFunction objek untuk mengurangi jumlah aturan khusus yang perlu dilacak.Proyek ini menggunakan metode berikut, dalam urutan preferensi:
Replikasi melalui sistem data pemain
Sistem data pemain memungkinkan data untuk dikaitkan dengan pemain yang bertahan di antara sesi penyimpanan .Sistem ini menyediakan replikasi dari klien ke server dan serangkaian API yang dapat digunakan untuk menanyakan data dan berlangganan perubahan, sehingga ideal untuk mereplikasi perubahan ke status pemain dari server ke klien.
Sebagai contoh, daripada menembakkan khusus UpdateCoins``Class.RemoteEvent untuk memberi tahu klien berapa banyak koin yang dimilikinya, Anda dapat memanggil berikut dan membiarkan klien berlangganan melalui peristiwa PlayerDataClient.updated.
PlayerDataServer:setValue(player, "coins", 5)
Tentu saja, ini hanya berguna untuk replikasi server ke klien dan untuk nilai yang ingin Anda pertahankan di antara sesi, tetapi ini berlaku untuk jumlah kasus mengejutkan di proyek, termasuk:
- Tahap FTUE saat ini
- inventarispemain
- Jumlah koin yang dimiliki pemain
- Kondisi pertanian pemain
Replikasi melalui atribut
Dalam situasi di mana server perlu menyalin nilai khusus ke klien yang khusus untuk Instance tertentu, Anda dapat menggunakan atribut .Roblox secara otomatis menyalin nilai atribut, sehingga Anda tidak perlu memelihara rute kode apa pun untuk menyalin status yang terkait dengan objek.Keuntungan lain adalah replicasi ini terjadi bersamaan dengan instansi itu sendiri.
Ini sangat berguna untuk instans yang dibuat saat menjalankan, karena atribut yang ditetapkan pada instans baru sebelum diberikan ke model data akan direplikasi secara atomik dengan instans itu sendiri.Ini mengelakkan kebutuhan untuk menulis kode untuk "menunggu" data ekstra dipulihkan melalui RemoteEvent atau StringValue .
Anda juga dapat langsung membaca atribut dari model data, dari klien atau server, dengan metode GetAttribute(), dan berlangganan perubahan dengan metode GetAttributeChangedSignal().Dalam proyek Tanaman, pendekatan ini digunakan untuk, di antara hal-hal lain, menyalin status saat ini dari tanaman ke klien.
Replikasi melalui tag
CollectionService memungkinkan Anda menerapkan tag string ke Instance . Ini berguna untuk mengkategorikan instans dan menyalin kategorisasi itu ke klien.
Sebagai contoh, tag CanPlant diterapkan pada server untuk menunjukkan kepada klien bahwa pot tertentu dapat menerima tanaman.
Pesan langsung melalui modul jaringan
Untuk situasi di mana tidak ada pilihan sebelumnya yang berlaku, Anda dapat menggunakan panggilan jaringan khusus melalui modul Jaringan .Ini adalah satu-satunya opsi dalam proyek yang memungkinkan komunikasi antara klien dan server dan karena itu paling berguna untuk mengirim permintaan klien dan menerima respons server.
Tanaman menggunakan panggilan jaringan langsung untuk berbagai permintaan klien, termasuk:
- Menyiram tanaman
- Menanam benih
- Membeli item
Kelemahan dengan pendekatan ini adalah bahwa setiap pesan individu memerlukan beberapa konfigurasi khusus yang dapat meningkatkan kompleksitas proyek, meskipun ini telah dihindari di mana pun mungkin, terutama untuk komunikasi server ke klien.
Kelas dan tunggalton
Kelas di proyek Tanaman dapat dibuat dan dihancurkan.Syntax kelasnya terinspirasi oleh pendekatan idiomatik Lua untuk pemrograman berorientasi objek dengan beberapa perubahan untuk mengaktifkan dukungan pemeriksaan tipe ketat.
Instaniasi
Banyak kelas dalam proyek terkait dengan satu atau lebih Instances .Objek dari kelas tertentu dibuat menggunakan metode new() , konsisten dengan cara instans dibuat di Roblox menggunakan Instance.new() .
Pola ini umumnya digunakan untuk objek di mana kelas memiliki representasi fisik di model data, dan kelas memperluas fungsionalitasnya.Contoh yang baik adalah BeamBetween yang membuat objek Beam di antara dua objek yang diberikan Attachment dan menjaga lampiran tersebut berorientasi ke atas sehingga balok selalu menghadap ke atas.Instansi ini bisa diklon dari versi prefabrikasi di ReplicatedStorage atau diserahkan ke new() sebagai argumen dan disimpan di dalam objek di bawah self .
Instansi yang sesuai
Seperti disebutkan di atas, banyak kelas dalam proyek ini memiliki representasi model data, instansi yang sesuai dengan kelas dan dimanipulasi olehnya.
Daripada membuat instansi ini saat objek kelas diinstansiasikan, kode umumnya memilih untuk Clone() membuat versi prefabrikasi dari Instance yang disimpan di bawah ReplicatedStorage atau ServerStorage .Meskipun akan mungkin untuk meng serialisasi properti instans ini dan membuatnya dari awal di fungsi kelas new(), melakukannya akan membuat pengeditan objek sangat rumit dan membuatnya lebih sulit bagi pembaca untuk memparalelkannya.Selain itu, mengkloning instansi umumnya merupakan operasi yang lebih cepat daripada membuat instansi baru dan menyesuaikan propertinya saat runtime.
Komposisi
Meskipun warisan dimungkinkan di Luau menggunakan metatabel, proyek memilih untuk sebaliknya memungkinkan kelas untuk saling memperpanjang melalui komposisi .Saat menggabungkan kelas melalui komposisi, objek "anak" diinstansi dalam metode new() kelas dan dimasukkan sebagai anggota di bawah self .
Untuk contoh ini dalam action, lihat kelas CloseButton yang membungkus kelas Button.
Pembersihan
Sama seperti bagaimana sebuah Instance dapat dihancurkan dengan metode Destroy(), kelas yang dapat diinstansiakan juga dapat dihancurkan.Metode penghancur untuk kelas proyek adalah dengan huruf kecil untuk konsistensi antara metode kode basis, serta untuk membedakan antara kelas proyek dan instansi Roblox.
Peran metode destroy() adalah untuk menghancurkan setiap instansi yang dibuat oleh objek, memutus koneksi apa pun, dan memanggil destroy() pada setiap objek anak.Ini sangat penting untuk koneksi karena instans dengan koneksi aktif tidak dibersihkan oleh pengumpul sampah Luau, bahkan jika tidak ada referensi ke instans atau koneksi ke instans yang tersisa.
Tunggalons
Singletons, seperti namanya menunjukkan, adalah kelas yang hanya satu objek bisa pernah ada.Mereka adalah sebanding dengan layanan Roblox Services .Daripada menyimpan referensi ke objek tunggal dan membagikannya di kode Luau, Tanaman memanfaatkan kenyataan bahwa memerlukan ModuleScript menyimpan nilai yang dikembalikannya.Ini berarti bahwa memerlukan singleton yang sama ModuleScript dari berbagai tempat secara konsisten memberikan objek yang sama yang dikembalikan.Satu-satunya pengecualian untuk aturan ini adalah jika lingkungan yang berbeda (klien atau server) mengakses ModuleScript.
Singletons dibedakan dari kelas tidak dapat diinstan oleh kenyataan bahwa mereka tidak memiliki metode new() .Sebaliknya, objek bersama dengan metodenya dan statusnya dikembalikan langsung melalui ModuleScript .Karena singletons tidak diinstansiakan, syntax self tidak digunakan dan metode dipanggil dengan dot ( . ) daripada kolon ( : ).
Inferensi tipe ketat
Luau mendukung pengetikan bertahap yang berarti Anda bebas menambahkan definisi jenis opsional ke beberapa atau semua kode Anda.Dalam proyek ini, strict pemeriksaan ketik digunakan untuk setiap skrip.Ini adalah opsi paling tidak menguntungkan untuk alat analisis skrip Roblox dan karena itu paling mungkin untuk menangkap kesalahan jenis sebelum runtime.
Syntax kelas ditulis
Pendekatan yang ditetapkan untuk membuat kelas di Lua adalah terdokumentasi dengan baik , namun tidak cocok untuk pengetikan Luau yang kuat.Di Luau, pendekatan termudah untuk mendapatkan jenis kelas adalah metode typeof():
type ClassType = typeof(Class.new())
Ini berfungsi tetapi tidak terlalu berguna ketika kelas Anda diinisialisasi dengan nilai yang hanya ada saat runtime, misalnya Player objek.Selain itu, asumsi yang dibuat dalam sintaks kelas Lua idiomatik adalah bahwa menyatakan metode pada kelas self akan selalu menjadi instansi dari kelas itu; ini bukan asumsi yang bisa dilakukan oleh mesin inferensi tipe.
Untuk mendukung inferensi tipe ketat, proyek Tanaman menggunakan solusi yang berbeda dari frasa kelas Lua idiomatik dalam beberapa cara, beberapa di antaranya mungkin terasa tidak intuitif:
- Definisi self diulang, baik dalam deklarasi jenis maupun dalam konstruktor.Ini memperkenalkan beban pemeliharaan, tetapi peringatan akan ditandai jika dua definisi jatuh keluar dari sinkronisasi satu sama lain.
- Metode kelas dideklarasikan dengan dot, jadi self dapat dideklarasikan secara eksplisit sebagai tipe ClassType.Metode masih dapat dipanggil dengan kolon seperti yang diharapkan.
--!ketat
local MyClass = {}
MyClass.__index = MyClass
export type ClassType = typeof(setmetatable(
{} :: {
property: number,
},
MyClass
))
function MyClass.new(property: number): ClassType
local self = {
property = property,
}
setmetatable(self, MyClass)
return self
end
function MyClass.addOne(self: ClassType)
self.property += 1
end
return MyClass
Lemparkan jenis setelah penjaga logis
Pada saat penulisan, jenis nilai tidak dibatasi setelah pernyataan kondisi penjaga.Sebagai contoh, setelah penjaga di bawah ini, jenis optionalParameter tidak dibatasi menjadi number.
--!ketat
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
print(optionalParameter + 1)
end
Untuk mengatasi ini, variabel baru dibuat setelah penjaga ini dengan jenisnya secara eksplisit dilemparkan.
--!ketat
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
local parameter = optionalParameter :: number
print(parameter + 1)
end
Melintasi hierarki DataModel
Dalam beberapa kasus, kode basis perlu melintasi hierarki model data dari pohon objek yang dibuat saat runtime.Ini menyajikan tantangan menarik untuk pemeriksaan ketik.Pada saat penulisan, tidak mungkin untuk mendefinisikan hierarki model data umum sebagai jenis.Sebagai hasilnya, ada kasus di mana satu-satunya jenis informasi yang tersedia untuk struktur model data adalah jenis kejadiantingkat atas.
Satu pendekatan untuk tantangan ini adalah untuk melempar ke any dan kemudian disempurnakan. Misalnya:
local function enableVendor(vendor: Model)
local zonePart: BasePart = (vendor :: any).ZonePart
end
Masalah dengan pendekatan ini adalah bahwa hal itu mempengaruhi kelayakan baca.Sebagai gantinya, proyek menggunakan modul umum yang disebut getInstance untuk melintasi hierarki model data yang dilemparkan ke any secara internal.
local function enableVendor(vendor: Model)
local zonePart: BasePart = getInstance(vendor, "ZonePart")
end
Saat pemahaman mesin jenis tentang model data berkembang, kemungkinan pola seperti ini tidak lagi diperlukan.
Antarmuka pengguna
Tanaman termasuk berbagai antarmuka pengguna 2D rumit dan sederhana.Ini termasuk item tampilan kepala non-interaktif (HUD) seperti counter koin dan menu interaktif kompleks seperti belanja.
Pendekatan UI
Anda dapat secara longgar membandingkan Roblox UI dengan DOM HTML, karena ini adalah hierarki objek yang menjelaskan apa yang harus dilihat pengguna.Pendekatan untuk membuat dan memperbarui UI Roblox dibagi secara luas menjadi penting dan deklaratif praktik.
Pendekatan | Keuntungan dan kerugian |
---|---|
Perintah | Dalam pendekatan imperatif, UI diperlakukan seperti hierarki instans lain di Roblox.Struktur UI dibuat sebelum runtime di Studio dan ditambahkan ke model data, biasanya langsung di StarterGui .Kemudian, saat menjalankan kode, kode memanipulasi bagian spesifik dari UI untuk mencerminkan status yang diperlukan oleh pencipta.: Pendekatan ini datang dengan beberapa keuntungan.Anda dapat membuat UI dari awal di Studio dan menyimpannya di model data.Ini adalah pengalaman editing sederhana dan visual yang dapat mempercepat kreasiUI.Karena kode UI yang mendesak hanya berurusan dengan apa yang perlu diubah, itu juga membuat perubahan UI sederhana mudah dilaksanakan.: Kelemahan yang signifikan adalah bahwa, karena pendekatan UI yang penting memerlukan status untuk diterapkan secara manual dalam bentuk transformasi, representasi kompleks negara bisa menjadi sangat sulit untuk ditemukan dan diperbaiki.Biasanya kesalahan muncul saat mengembangkan kode UI yang penting, terutama ketika negara dan UI menjadi tidak sinkron karena banyak pembaruan berinteraksi dalam urutan yang tidak terduga.: Tantangan lain dengan pendekatan yang mendesak adalah lebih sulit untuk memecah UI menjadi komponen yang berarti yang dapat diumumkan sekali dan digunakan kembali.Karena seluruh pohon UI dideklarasikan saat diedit, pola umum dapat diulang di beberapa bagian model data. |
Deklaratif | Dalam pendekatan deklaratif, keadaan UI yang diinginkan dideklarasikan secara eksplisit, dan implementasi efisien dari keadaan ini dikeluarkan oleh perpustakaan seperti Roact atau Fusion.: Keuntungan dari pendekatan ini adalah implementasi negara menjadi sederhana dan Anda hanya perlu menjelaskan apa yang Anda ingin UI terlihat seperti.Ini membuat mengidentifikasi dan memecahkan bug menjadi lebih mudah secara signifikan.: Kelemahan kunci adalah harus menyatakan seluruh pohon UI dalam kode.Perpustakaan seperti Roact dan Fusion memiliki syntax untuk membuat ini lebih mudah, tetapi masih merupakan proses yang memakan waktu dan pengalaman editing yang kurang intuitif saat menyusun UI. |
Tanaman menggunakan pendekatan imperatif di bawah gagasan bahwa menunjukkan transformasi secara langsung memberikan pandangan yang lebih efektif tentang cara UI dibuat dan dimanipulasi di Roblox.Ini tidak akan mungkin dengan pendekatan deklaratif.Beberapa struktur dan logika UI yang diulang juga dikeluarkan ke dalam komponen yang dapat digunakan kembali untuk menghindari kesalahan umum dalam desain UI imperatif
Arsitektur tingkat tinggi

Tingkat dan komponen
Di Tanaman , semua struktur UI adalah entah Layer atau Component .
- Layer didefinisikan sebagai grup pengelompokan tingkat atas yang mengemas struktur UI pra-fabrikasi di ReplicatedStorage .Lapisan dapat berisi beberapa komponen, atau dapat melapisi logiknya sendiri sepenuhnya.Contoh lapisan adalah menu inventaris atau indikator jumlah koin di tampilan kepala.
- Component adalah elemen UI yang dapat digunakan kembali.Ketika objek komponen baru diinstansiasikan, ia mengkloning template pra-fabrikasi dari ReplicatedStorage .Komponen mungkin sendiri berisi komponen lain.Contoh komponen adalah kelas tombol umum atau konsep daftar item.
Lihat penanganan
Masalah manajemen UI umum adalah penanganan pandangan.Proyek ini memiliki berbagai menu dan item HUD, beberapa di antaranya mendengarkan input pengguna, dan manajemen yang hati-hati diperlukan ketika mereka terlihat atau diaktifkan.
Tanaman mendekati masalah ini dengan sistem UIHandler yang mengelola kapan lapisan UI harus atau tidak harus terlihat.Semua lapisan UI dalam permainan dikategorikan sebagai HUD atau Menu dan visibilitasnya dikelola oleh aturan berikut:
- Status yang diaktifkan dari Menu dan HUD lapisan dapat diaktifkan ulang.
- Layar yang diaktifkan HUD hanya ditampilkan jika tidak ada lapisan Menu yang diaktifkan.
- Lapisan yang diaktifkan Menu disimpan dalam tumpukan, dan hanya satu lapisan Menu yang terlihat pada satu waktu.Ketika lapisan Menu diaktifkan, itu dimasukkan ke depan tumpukan dan ditampilkan.Ketika lapisan Menu terhapus, itu dihapus dari tumpukan dan lapisan berikutnya yang diaktifkan Menu ditampilkan di antrian.
Pendekatan ini intuitif karena memungkinkan menu untuk di navigasikan dengan sejarah.Jika satu menu dibuka dari menu lain, menutup menu baru akan menunjukkan menu lama lagi.
Layar UI tunggal mendaftarkan diri dengan UIHandler dan diberi sinyal yang menyala saat visibilitasnya harus berubah.
Bacaan lebih lanjut
Dari pandangan lengkap ini tentang proyek Tanaman, Anda mungkin ingin menjelajahi panduan berikut yang pergi lebih jauh dalam kedalaman konsep dan topik terkait.
- Model Klien-Server — Ringkasan tentang model klien-server di Roblox.
- Peristiwa Remote dan Panggilan Balasan — Semua tentang peristiwa jaringan remote dan panggilan balasan untuk komunikasi di antara batas klien-server.
- UI — Detail tentang objek antarmuka pengguna dan desain di Roblox.