Perpustakaan Pengendali Karakter (CCL) adalah kerangka modular untuk membangun gerakan dan perilaku karakter melalui atribut dan skrip Luau. Arsitektur ini menggantikan mesin status Humanoid yang kaku dengan sistem yang fleksibel dan dapat diperluas untuk mekanika karakter.
Aktifkan CCL
CCL adalah opsional melalui jendela Pengaturan Avatar di Studio. Untuk mengaktifkannya:
Aktifkan beta CCL melalui File ⟩ Fitur Beta ⟩ Perpustakaan Pengontrol Karakter Avatar.
Dari tab Avatar, buka Pengaturan Avatar.

Pilih tab Gerakan di sisi kiri jendela dan, di bagian Kemampuan, pilih Perpustakaan Pengontrol Karakter.

Semua kemampuan standar seperti Berlari, Melompat, dan Memanjat diaktifkan secara default. Untuk menonaktifkan salah satu dari mereka saat runtime, hapus centang pada kotak yang terkait.
Kemampuan
Kemampuan mengevaluasi apa yang dapat dilakukan oleh karakter, seperti kemampuan untuk berlari, melompat, memanjat, dan berenang. Alih-alih bergantung pada seperangkat status karakter yang ditentukan oleh mesin yang tetap seperti yang ada di Enum.HumanoidStateType, kemampuan CCL secara dinamis menentukan apa yang dapat dilakukan karakter dan bagaimana ia harus merespons input pemain.
Secara struktural, sebuah kemampuan adalah tabel Luau yang mandiri yang terutama menentukan hal-hal berikut:
| Bidang Tabel | Tujuan |
|---|---|
| Name | Identitas unik kemampuan; kondisi dan konflik merujuknya dengan nama. Mendaftarkan dua kemampuan dengan Name yang sama akan menghasilkan kesalahan. Untuk beberapa kemampuan yang mirip, berikan masing-masing nama yang berbeda dan bagikan label yang umum di Labels. Nama ModuleScript menentukan kunci konfigurasi yang unik. |
| Labels, TimedLabels | Bit yang dinamai dalam masker 64-bit bersama yang bertindak sebagai bus koordinasi antara kemampuan; lihat label. |
| StartsWhen, RunsWhile | Kondisi yang mendefinisikan kapan untuk memulai kemampuan dan kapan untuk mempertahankannya, masing-masing; lihat kondisi. |
| Blocks, Stops, Suspends, ExclusiveGroup | Bagaimana menangani konflik antara kemampuan yang tidak dapat aktif sekaligus. |
| Input | Input yang memicu kemampuan. Input.Mode mengontrol bagaimana input memulai dan menghentikan kemampuan; RunsWhile hanya memegang kondisi dunia, tidak pernah input itu sendiri; lihat inputs. |
| Config, State | Nilai konfigurasi default dan status yang direplikasi untuk setiap pendaftaran kemampuan. Callback membaca konfigurasi dari abilityCtx.Config dan membaca atau menulis status yang direplikasi melalui abilityCtx.State. |
| OnSetup, OnStart, OnStop, OnUpdate, OnTeardown | Callback siklus hidup di mana perilaku aktual kemampuan diprogram; lihat callback. |
Label
Sebuah label adalah bit yang dinamai dalam masker 64-bit bersama yang bertindak sebagai bus koordinasi antara kemampuan. Pada dasarnya:
- Sebuah kemampuan yang aktif menyiarkan Labels dan TimedLabels ke masker dunia.
- Konflik kemampuan mendefinisikan kemampuan lain mana yang diblokir, dihentikan, atau ditangguhkan saat diaktifkan.
Dalam pengaturan berikut, label "CanFallDown" disiarkan ke masker dunia ketika Running aktif. Kemampuan FallingDown dengan kondisi StartsWhen = All( "CanFallDown", "Stunned" ) secara otomatis menjadi kandidat, tetapi "Stunned" juga harus disiarkan ke masker dunia sebelum FallingDown terjadi.
local AvatarAbilities = require("@rbx/AvatarAbilities")
local Identifiers = AvatarAbilities.Identifiers
local Ability = Identifiers.Ability
local Rule = AvatarAbilities.Rule
local Sensor = Identifiers.Sensor
local All, Any, Not = Rule.All, Rule.Any, Rule.Not
local Running: AvatarAbilities.AbilityDefinition = {
Name = Ability.Running,
Labels = { "CanFallDown" }, -- Labels disiarkan saat kemampuan aktif
StartsWhen = Sensor.Ground,
RunsWhile = Sensor.Ground,
}local AvatarAbilities = require("@rbx/AvatarAbilities")
local Identifiers = AvatarAbilities.Identifiers
local Ability = Identifiers.Ability
local Rule = AvatarAbilities.Rule
local Sensor = Identifiers.Sensor
local All, Any, Not = Rule.All, Rule.Any, Rule.Not
local FallingDown: AvatarAbilities.AbilityDefinition = {
Name = Ability.FallingDown,
StartsWhen = All( "CanFallDown", "Stunned" ), -- Labels yang diperlukan untuk memulai kemampuan
Blocks = { Ability.Running }
}Label juga dapat disiarkan atau dikonsumsi dengan cara berjangka menggunakan kamus TimedLabels.
| Kunci | Deskripsi |
|---|---|
| TimedLabels.OnStart | Kamus yang berisi label (kunci) dan durasi terkait. Label akan disiarkan saat kemampuan dimulai dan secara otomatis kedaluwarsa saat durasinya berakhir. Misalnya, OnStart = { Dashing = 1 } menyiarkan label Dashing selama 1 detik saat kemampuan dimulai. |
| TimedLabels.OnStop | Kamus yang berisi label (kunci) dan durasi terkait. Label akan disiarkan saat kemampuan berhenti dan secara otomatis kedaluwarsa saat durasinya berakhir. Misalnya, OnStop = { DashCooldown = 2 } menyiarkan label DashCooldown selama 2 detik saat kemampuan berhenti. |
| TimedLabels.Consumes | Daftar label yang akan dihapus (dikonsumsi) saat kemampuan diaktifkan. Misalnya, jika sebuah permainan pertarungan memungkinkan pemain untuk melakukan serangan balik setelah memblokir serangan lawan, kemampuan CounterAttack mungkin berisi StartsWhen = "AfterBlock" dan TimedLabels = { Consumes = { "AfterBlock" } } untuk mencegah pemicu ganda dari kemampuan CounterAttack. |
local AvatarAbilities = require("@rbx/AvatarAbilities")
local Identifiers = AvatarAbilities.Identifiers
local Rule = AvatarAbilities.Rule
local Sensor = Identifiers.Sensor
local All, Any, Not = Rule.All, Rule.Any, Rule.Not
local Dash: AvatarAbilities.AbilityDefinition = {
Name = "Dash",
StartsWhen = All( Sensor.Ground, Not("DashCooldown") ),
RunsWhile = "Dashing",
TimedLabels = {
OnStart = { Dashing = 1 },
OnStop = { DashCooldown = 2 },
},
}Kondisi
Sebuah kondisi adalah satu atau lebih label, sensor, atau referensi input, yang digunakan oleh StartsWhen dan RunsWhile. Kondisi dikompilasi menjadi operasi bitmask pada waktu berjalan dan evaluasi adalah matematika integer — tidak ada penelusuran tabel dan tidak ada perbandingan string.
| Tujuan | Sintaksis | Contoh |
|---|---|---|
| Satu kondisi yang diperlukan. | StartsWhen = Sensor.Ground | |
| Logika AND untuk ketika semua label ada di masker dunia dan semua sensor aktif. | All() | StartsWhen = All( "CanFallDown", "Stunned" ) |
| Logika OR untuk ketika salah satu label ada di masker dunia atau salah satu sensor aktif. | Any() | StartsWhen = Any( "WallClimbing", "Climbing" ) |
| Negasi sehingga label tidak dapat ada di masker dunia dan sensor tidak dapat aktif. | Not() | RunsWhile = Not("Stunned") |
Evaluasi kondisional dapat digabungkan untuk logika yang lebih kompleks, seperti penggabungan All() ditambah Not() untuk menunjukkan bahwa sensor harus aktif sementara label harus tidak ada:
local AvatarAbilities = require("@rbx/AvatarAbilities")
local Identifiers = AvatarAbilities.Identifiers
local Rule = AvatarAbilities.Rule
local Sensor = Identifiers.Sensor
local All, Any, Not = Rule.All, Rule.Any, Rule.Not
local Dive: AvatarAbilities.AbilityDefinition = {
Name = "Dive",
StartsWhen = All( Sensor.WaterSurface, Not("Recovering") ),
}Konflik
Beberapa kemampuan tidak dapat aktif ketika kemampuan lain aktif; misalnya, karakter tidak dapat melompat saat berenang, dan mereka tidak dapat berlari saat jatuh. Mesin menyelesaikan konflik ini secara deklaratif di dalam definisi kemampuan:
| Kunci Konflik | Tujuan |
|---|---|
| Blocks | Saat kemampuan pemilik aktif, kemampuan lain yang terdaftar tidak dapat dimulai. Misalnya, kemampuan ScopeAim mungkin berisi Blocks = { Ability.Running, Ability.Jumping } untuk mencegah karakter berlari atau melompat saat dengan hati-hati mengarahkan melalui bidikan senjata mereka. |
| Stops | Ketika kemampuan pemilik dimulai, kemampuan lain yang terdaftar dihentikan secara paksa dan harus diaktifkan kembali. Misalnya, kemampuan Hover mungkin berisi Stops = { Ability.Running } untuk segera menghentikan gerakan berlari karakter saat mereka mulai melayang. |
| Suspends | Ketika kemampuan pemilik dimulai, kemampuan lain yang terdaftar ditangguhkan dan kemudian melanjutkan secara otomatis ketika kemampuan pemilik berhenti. Misalnya, kemampuan sprint kustom mungkin berisi Suspends = { Ability.Running } sehingga berlari 🄐 ditangguhkan saat sprint dimulai, 🄑 diblokir di tengah sprint, dan 🄒 dilanjutkan saat sprint berhenti. |
Kunci konflik unik lainnya adalah ExclusiveGroup yang menempatkan beberapa kemampuan ke dalam satu grup, masing-masing dengan nilai Priority. Hanya satu kemampuan per grup yang dapat aktif dan prioritas yang lebih tinggi menang. Namun, jika penantang menyatakan Stops yang menargetkan nama/label pemegang, itu menang terlepas dari prioritas.
Dalam pengaturan berikut, tiga kemampuan (Sprinting, Crouching, Stagger) ditambahkan ke grup eksklusif Locomotion. Sprinting memiliki prioritas tertinggi (200) sehingga menang atas Crouching (100) dan keduanya tidak pernah berjalan pada saat yang sama. Namun, Stagger secara paksa menghentikan sprinting (Stops = { Ability.Sprinting }), sehingga dapat menginterupsi dan mengalahkan Sprinting meskipun prioritasnya (150) lebih rendah.
local AvatarAbilities = require("@rbx/AvatarAbilities")
local Identifiers = AvatarAbilities.Identifiers
local Ability = Identifiers.Ability
local Rule = AvatarAbilities.Rule
local Sensor = Identifiers.Sensor
local All, Any, Not = Rule.All, Rule.Any, Rule.Not
local Sprinting: AvatarAbilities.AbilityDefinition = {
Name = Ability.Sprinting,
ExclusiveGroup = { Name = "Locomotion", Priority = 200 },
}
local Crouching: AvatarAbilities.AbilityDefinition = {
Name = Ability.Crouching,
ExclusiveGroup = { Name = "Locomotion", Priority = 100 },
}
-- Kemampuan dengan prioritas lebih rendah dapat mengalahkan kemampuan dengan prioritas lebih tinggi dengan menghentikannya
local Stagger: AvatarAbilities.AbilityDefinition = {
Name = "Stagger",
ExclusiveGroup = { Name = "Locomotion", Priority = 150 },
Stops = { Ability.Sprinting },
}Input
Definisi Input dari sebuah kemampuan menentukan input yang digunakan untuk mencoba mengaktifkan kemampuan tersebut. Ini mengambil pasangan kunci-nilai yang mengonfigurasi perilaku input, slot aksi, dan ikon tombol sentuh opsional.
local AvatarAbilities = require("@rbx/AvatarAbilities")
local Identifiers = AvatarAbilities.Identifiers
local Rule = AvatarAbilities.Rule
local Sensor = Identifiers.Sensor
local All, Any, Not = Rule.All, Rule.Any, Rule.Not
local Dash: AvatarAbilities.AbilityDefinition = {
Name = "Dash",
Input = { InputName = "Dash", Mode = "Press", ActionSlot = 5 }
}InputName adalah nama logis, bukan kunci. CCL menghasilkan sensor input dan selalu menggabungkannya ke dalam StartsWhen.
Mode mendefinisikan bagaimana input ini memulai dan menghentikan kemampuan. RunsWhile tidak pernah berisi input itu sendiri; ia hanya memegang kondisi dunia, seperti berada di tanah atau di air, sehingga Mode adalah satu-satunya sumber pemicu yang didorong oleh input:
Mode Memulai Berhenti Dari Input Press Saat ditekan. Tidak pernah. Kemampuan mengakhiri dirinya sendiri melalui konflik, RunsWhile-nya sendiri, atau dengan memanggil Stop(). Hold Saat ditekan. Saat dilepas. Hold dengan Repeats Saat ditekan. Pelepasan mengakhiri pemicu ulang, bukan siklus yang sudah dalam penerbangan. Toggle Saat diaktifkan. Saat dinonaktifkan. Press cocok untuk tindakan diskrit seperti dash, serangan, dan lempar. Hold dan Toggle cocok untuk tindakan berkelanjutan seperti sprint, aim, block, crouch, dan levitate. Hold dengan Repeats cocok untuk tindakan yang berhenti sendiri dan memicu ulang saat ditahan, seperti melompat berulang kali.
Repeats (boolean, Hold hanya) membuat kemampuan mengakhiri setiap siklus dan memicu ulang saat input tetap ditahan, alih-alih berjalan terus menerus selama penahanan.
OutlivesInputWhile (Hold/Toggle hanya) adalah kondisi di mana kemampuan terus berjalan melewati pelepasan atau nonaktif, berhenti setelah kondisi tersebut hilang. Gunakan ini ketika sebuah kemampuan harus bertahan lebih lama dari inputnya di bawah kondisi dunia tertentu.
ActionSlot mendefinisikan slot aksi yang terkait dengan daftar InputActions dan InputBindings dalam Sistem Aksi Input.
Beberapa pengikatan input slot aksi telah ditentukan sebelumnya oleh Roblox dan, di masa depan, Pengelola Aksi Input akan memungkinkan Anda untuk mengonfigurasi ulang pengikatan input default untuk slot aksi sesuai keinginan. Mengatur ActionSlot ke 0 akan memilih slot kosong berikutnya yang tersedia. Di perangkat seluler, slot 1-7 terisi menjadi tombol di layar (lihat diagram di bawah).
Slot Keyboard & Mouse Gamepad Sentuh Penugasan Default 1 Space ButtonA ① Lompat 2 LeftShift ButtonL1 ② Berlari 3 LeftControl ButtonB ③ Merunduk 4 R ButtonX ④ 5 MouseLeftButton ButtonR2 ⑤ 6 Q ButtonY ⑥ 7 X ButtonR1 ⑦ 8 C ButtonL2 9 F DPadLeft 10 G DPadRight 11 V DPadDown 
CustomIcon, CustomIconActive, dan CustomIconInvalid menentukan ID aset Roblox untuk tombol sentuh saat kemampuan tidak aktif, aktif, atau tidak tersedia, masing-masing.
RunsWhile hanya memegang kondisi dunia. Kompiler menyusun efek input ke dalam kelanjutan aktual kemampuan di belakang layar berdasarkan Mode, jadi menempatkan input itu sendiri ke dalam StartsWhen atau RunsWhile akan menghasilkan kesalahan (jangan merujuk Rule.Input di sana). Kemampuan Hold dan Toggle sudah berhenti saat dilepas atau dinonaktifkan secara default, jadi Anda tidak perlu menduplikasi itu di RunsWhile:
local AvatarAbilities = require("@rbx/AvatarAbilities")
local Rule = AvatarAbilities.Rule
local Sensor = AvatarAbilities.Identifiers.Sensor
local All, Any, Not = Rule.All, Rule.Any, Rule.Not
local Glide: AvatarAbilities.AbilityDefinition = {
Name = "Glide",
Input = { InputName = "Glide", Mode = "Hold", ActionSlot = 6 },
StartsWhen = Not(Sensor.Ground),
RunsWhile = Not(Sensor.Ground),
}Untuk membuat kemampuan Hold atau Toggle bertahan lebih lama dari inputnya di bawah kondisi dunia, atur OutlivesInputWhile alih-alih menambahkan input ke RunsWhile. Kemampuan Crouching berikut ini tetap membungkuk setelah pemain melepaskan input membungkuk, selama ada sesuatu yang tetap di atas:
local AvatarAbilities = require("@rbx/AvatarAbilities")
local Identifiers = AvatarAbilities.Identifiers
local Sensor = Identifiers.Sensor
local Crouching: AvatarAbilities.AbilityDefinition = {
Name = "Crouching",
Input = { InputName = "Crouch", Mode = "Hold", OutlivesInputWhile = Sensor.Ceiling, ActionSlot = 3 },
RunsWhile = Sensor.Ground,
}Sensor
Sebuah sensor adalah nilai bernama tentang dunia yang dibaca oleh mesin untuk Anda. Anda biasanya akan membaca sensor daripada menulisnya. Untuk kenyamanan, beberapa sensor telah terdaftar sebelumnya:
| Sensor | Deskripsi |
|---|---|
| Sensor.Ground | Berdiri di atas permukaan |
| Sensor.IsMoving | Input gerakan sedang diterapkan |
| Sensor.MoveInput | Vektor gerakan itu sendiri |
| Sensor.Ceiling | Sesuatu berada tepat di atas |
| Sensor.Climb | Permukaan yang dapat dipanjat berada dalam jangkauan |
| Sensor.Water / Sensor.WaterSurface | Di dalam air / di permukaan |
| Sensor.Sit | Duduk |
| Sensor.Tipped | Terjatuh |
| Sensor.Tool | Memegang Tool |
| Sensor.LookDirectionInput | Arah pandangan yang diperintahkan |
| Sensor.RotateToLookDirectionInput | Apakah karakter harus berputar ke arah pandangan yang diperintahkan |
Callback
Fungsi callback kemampuan memungkinkan Anda untuk memprogram perilaku tertentu:
Meskipun Anda mendaftarkan kemampuan kustom di server, callback mereka berjalan di kedua simulasi klien yang diprediksi dan simulasi server yang berwenang. Jaga perilaku callback tetap deterministik sehingga kedua simulasi menghasilkan hasil yang sama.
| Callback | Menjalankan | Kasus Penggunaan |
|---|---|---|
| OnSetup(managerCtx, abilityCtx) | Sekali, saat kemampuan didaftarkan. | Menyimpan referensi, menginisialisasi status, dll. |
| OnStart(managerCtx, abilityCtx, hadLabel) | Setiap kali kemampuan diaktifkan. | Menerapkan efek seperti impuls. Fungsi hadLabel() melaporkan apakah label tertentu ada saat aktivasi dimulai, sebelum resolusi konflik. |
| OnUpdate(managerCtx, abilityCtx) | Setiap frame aktif. | Pekerjaan berkelanjutan seperti timer atau gaya per-frame. |
| OnStop(managerCtx, abilityCtx) | Setiap deaktivasi, sukarela atau terpaksa. | Membatalkan apa yang dilakukan OnStart(). |
| OnTeardown(managerCtx, abilityCtx) | Saat kemampuan dihapus. | Memutuskan koneksi, menghancurkan instance, dll. |
Parameter pertama dari setiap fungsi callback, managerCtx, adalah objek ManagerContext dengan properti karakter dan manajer yang dibagikan, termasuk:
- managerCtx.AbilityOwner — Karakter Model sehingga managerCtx.AbilityOwner.PrimaryPart adalah bagian akar.
- managerCtx.AbilityManager — Tampilan yang dipangkas dari manajer sehingga kemampuan dapat menambah, menghapus, dan menanyakan kemampuan dari dalam callback-nya sendiri.
- managerCtx.BodyParts — Bagian tubuh karakter, dengan pembantu untuk menghidupkan dan mematikan tabrakan per anggota tubuh.
- managerCtx.ControllerManager — ControllerManager karakter untuk kontrol fisika. Ini bisa nil ketika tidak ada kemampuan terdaftar yang memerlukan fisika.
- managerCtx.RootCFrame — CFrame bagian akar, diambil sekali di awal frame sehingga callback tidak perlu mengambilnya sendiri.
- managerCtx.RootLookVector — Arah yang dihadapi bagian akar karakter.
- managerCtx.RootUpVectorY — Komponen Y dari vektor atas bagian akar.
- managerCtx.TaskSynchronize() — Menyinkronkan callback sebelum akses DataModel ketika dukungan callback paralel diaktifkan. Saat ini, OnUpdate tidak berjalan dalam konteks paralel, jadi fungsi ini tidak berpengaruh. Dukungan Luau Paralel penuh direncanakan untuk pembaruan mendatang.
Parameter kedua, abilityCtx, adalah objek AbilityContext dengan tabel yang dikelola mesin untuk pendaftaran kemampuan saat ini:
- abilityCtx.Config — Nilai konfigurasi hanya-baca untuk pendaftaran kemampuan ini.
- abilityCtx.State — Status yang dapat diubah yang direplikasi melalui DataModel. Otoritas Server mengembalikan nilai ini selama rollback dan resimulasi.
- abilityCtx.Local — Status sementara yang dapat diubah yang tidak direplikasi atau berpartisipasi dalam rollback.
Simpan data callback kustom di abilityCtx.State atau abilityCtx.Local. Menulis bidang kustom langsung ke abilityCtx adalah kesalahan.