La Biblioteca del Controlador de Personajes (CCL) es un marco modular para construir el movimiento y comportamientos de personajes a través de atributos y scripts de Luau. Esta arquitectura reemplaza las máquinas de estado rígidas de Humanoid con un sistema flexible y extensible para la mecánica de personajes.
Habilitar CCL
El CCL es opcional a través de la ventana de Configuración de Avatar de Studio. Para habilitarlo:
Habilita la beta del CCL a través de Archivo ⟩ Funciones Beta ⟩ Biblioteca de Controladores de Personajes de Avatar.
Desde la pestaña Avatar, abre Configuración de Avatar.

Selecciona la pestaña Movimiento en el lado izquierdo de la ventana y, en la sección Habilidades, selecciona Biblioteca de Controladores de Personajes.

Todas las habilidades estándar como Correr, Saltar y Escalar están habilitadas por defecto. Para deshabilitar cualquiera de ellas en tiempo de ejecución, desmarca la casilla asociada.
Habilidades
Las habilidades evalúan lo que un personaje puede hacer, como la capacidad de correr, saltar, escalar y nadar. En lugar de depender de un conjunto fijo de estados de personaje definidos por el motor, como los de Enum.HumanoidStateType, las habilidades de CCL determinan dinámicamente lo que un personaje puede hacer y cómo debe responder a la entrada del jugador.
Estructuralmente, una habilidad es una tabla de Luau autocontenida que especifica principalmente lo siguiente:
| Campos de la Tabla | Propósito |
|---|---|
| Name | La identidad única de la habilidad; condiciones y conflictos la referencian por nombre. Registrar dos habilidades con el mismo Name genera un error. Para algunas habilidades similares, dale a cada una un Name diferente y comparte una etiqueta común en Labels. El nombre del ModuleScript determina la clave de configuración única. |
| Labels, TimedLabels | Bits nombrados en una máscara compartida de 64 bits que actúa como el bus de coordinación entre habilidades; ver etiquetas. |
| StartsWhen, RunsWhile | Condiciones que definen cuándo iniciar la habilidad y cuándo mantenerla activa, respectivamente; ver condiciones. |
| Blocks, Stops, Suspends, ExclusiveGroup | Cómo manejar conflictos entre habilidades que no pueden estar activas al mismo tiempo. |
| Input | La entrada que activa la habilidad. Input.Mode controla cómo la entrada inicia y detiene la habilidad; RunsWhile solo contiene condiciones del mundo, nunca la entrada misma; ver entradas. |
| Config, State | Valores de configuración predeterminados y estado replicado para cada registro de habilidad. Los callbacks leen la configuración de abilityCtx.Config y leen o escriben el estado replicado a través de abilityCtx.State. |
| OnSetup, OnStart, OnStop, OnUpdate, OnTeardown | Callbacks del ciclo de vida donde se scriptan el comportamiento real de la habilidad; ver callbacks. |
Etiquetas
Una etiqueta es un bit nombrado en una máscara compartida de 64 bits que actúa como el bus de coordinación entre habilidades. Esencialmente:
- Una habilidad activa transmite sus Labels y TimedLabels a la máscara del mundo.
- Los conflictos de la habilidad definen qué otras habilidades están bloqueadas, detenidas o suspendidas al activarse.
En la siguiente configuración, la etiqueta "CanFallDown" se transmite a la máscara del mundo cuando Running está activa. La habilidad FallingDown con su condición de StartsWhen = All( "CanFallDown", "Stunned" ) se convierte automáticamente en una candidata, pero "Stunned" también debe ser transmitida a la máscara del mundo antes de que ocurra FallingDown.
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" }, -- Etiquetas transmitidas cuando la habilidad está activa
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" ), -- Etiquetas necesarias para que la habilidad comience
Blocks = { Ability.Running }
}Las etiquetas también pueden ser transmitidas o consumidas de manera temporal utilizando el diccionario TimedLabels.
| Clave | Descripción |
|---|---|
| TimedLabels.OnStart | Diccionario que contiene etiquetas (claves) y duraciones asociadas. La(s) etiqueta(s) se transmiten cuando la habilidad comienza y expiran automáticamente cuando su duración termina. Por ejemplo, OnStart = { Dashing = 1 } transmite la etiqueta Dashing durante 1 segundo cuando la habilidad comienza. |
| TimedLabels.OnStop | Diccionario que contiene etiquetas (claves) y duraciones asociadas. La(s) etiqueta(s) se transmiten cuando la habilidad se detiene y expiran automáticamente cuando su duración termina. Por ejemplo, OnStop = { DashCooldown = 2 } transmite la etiqueta DashCooldown durante 2 segundos cuando la habilidad se detiene. |
| TimedLabels.Consumes | Lista de etiquetas para eliminar (consumir) cuando la habilidad se activa. Por ejemplo, si un juego de lucha permite a los jugadores contraatacar después de bloquear un ataque del oponente, la habilidad CounterAttack puede contener tanto StartsWhen = "AfterBlock" como TimedLabels = { Consumes = { "AfterBlock" } } para evitar la activación doble de la habilidad 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 },
},
}Condiciones
Una condición es una o más etiquetas, sensores o una referencia de entrada, utilizadas por StartsWhen y RunsWhile. Las condiciones se compilan en operaciones de bitmask en tiempo de ejecución y la evaluación es matemáticas enteras — sin recorridos de tablas y sin comparaciones de cadenas.
| Objetivo | Sintaxis | Ejemplo |
|---|---|---|
| Una condición requerida. | StartsWhen = Sensor.Ground | |
| Logica AND para cuando todas las etiquetas existen en la máscara del mundo y todos los sensores están activos. | All() | StartsWhen = All( "CanFallDown", "Stunned" ) |
| Logica OR para cuando cualquiera de las etiquetas existe en la máscara del mundo o cualquiera de los sensores está activo. | Any() | StartsWhen = Any( "WallClimbing", "Climbing" ) |
| Negación de tal manera que las etiquetas no pueden existir en la máscara del mundo y los sensores no pueden estar activos. | Not() | RunsWhile = Not("Stunned") |
La evaluación condicional se puede combinar para una lógica más compleja, como encadenar All() más Not() para indicar que un sensor debe estar activo mientras que una etiqueta debe no existir:
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") ),
}Conflictos
Algunas habilidades no pueden estar activas cuando otra habilidad lo está; por ejemplo, los personajes no pueden saltar mientras nadan, y no pueden correr mientras caen. El motor resuelve estos conflictos de manera declarativa dentro de la definición de una habilidad:
| Clave de Conflicto | Propósito |
|---|---|
| Blocks | Mientras la habilidad propietaria esté activa, las otras habilidades listadas no pueden comenzar. Por ejemplo, una habilidad ScopeAim podría contener Blocks = { Ability.Running, Ability.Jumping } para evitar que los personajes corran o salten mientras apuntan cuidadosamente a través de la mira de su arma. |
| Stops | Cuando la habilidad propietaria comienza, las otras habilidades listadas se detienen forzosamente y deben volver a activarse. Por ejemplo, una habilidad Hover podría contener Stops = { Ability.Running } para detener inmediatamente el movimiento de carrera de un personaje cuando comienza a flotar. |
| Suspends | Cuando la habilidad propietaria comienza, las otras habilidades listadas se pausan y luego se reanudan automáticamente cuando la habilidad propietaria se detiene. Por ejemplo, una habilidad de sprint personalizada podría contener Suspends = { Ability.Running } para que correr 🄐 se pause al iniciar el sprint, 🄑 se bloquee durante el sprint, y 🄒 se reanude al detener el sprint. |
Otra clave de conflicto única es ExclusiveGroup que coloca múltiples habilidades en un grupo, cada una con un valor de Priority. Solo una habilidad por grupo puede estar activa y la de mayor prioridad gana. Sin embargo, si un retador declara Stops apuntando al nombre/etiqueta del poseedor, gana independientemente de la prioridad.
En la siguiente configuración, tres habilidades (Sprinting, Crouching, Stagger) se añaden a un grupo exclusivo Locomotion. Sprinting tiene la mayor prioridad (200) por lo que gana sobre Crouching (100) y las dos nunca se ejecutan al mismo tiempo. Sin embargo, Stagger detiene forzosamente el sprint (Stops = { Ability.Sprinting }), por lo que puede interrumpir y superar a Sprinting a pesar de que su prioridad (150) es más baja.
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 },
}
-- Una habilidad de menor prioridad puede anular una habilidad de mayor prioridad al detenerla
local Stagger: AvatarAbilities.AbilityDefinition = {
Name = "Stagger",
ExclusiveGroup = { Name = "Locomotion", Priority = 150 },
Stops = { Ability.Sprinting },
}Entradas
La definición de Input de una habilidad especifica la entrada utilizada para intentar activar la habilidad. Toma pares clave-valor que configuran el comportamiento de entrada, el slot de acción y los íconos de botón táctil opcionales.
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 es un nombre lógico, no una clave. La CCL genera un sensor de entrada y siempre lo ANDa en StartsWhen.
Mode define cómo esta entrada inicia y detiene la habilidad. RunsWhile nunca contiene la entrada misma; solo contiene condiciones del mundo, como estar en el suelo o en el agua, por lo que Mode es la única fuente de inicio y detención impulsada por la entrada:
Modo Inicia Detiene Desde la Entrada Press Al presionar. Nunca. La habilidad se termina a sí misma a través de un conflicto, su propio RunsWhile, o llamando a Stop(). Hold Al presionar. Al soltar. Hold con Repeats Al presionar. El soltar termina el re-disparo, no el ciclo ya en vuelo. Toggle Al activar. Al desactivar. Press es adecuado para acciones discretas como dash, ataque y lanzamiento. Hold y Toggle son adecuados para acciones sostenidas como sprint, apuntar, bloquear, agacharse y levitar. Hold con Repeats es adecuado para acciones que se detienen solas y se re-disparan mientras se mantienen, como saltos repetidos.
Repeats (booleano, solo Hold) hace que la habilidad se termine a sí misma en cada ciclo y se re-dispare mientras la entrada se mantenga, en lugar de ejecutarse continuamente durante toda la duración de la presión.
OutlivesInputWhile (solo Hold/Toggle) es una condición bajo la cual la habilidad sigue funcionando más allá de la liberación o desactivación, deteniéndose una vez que la condición se despeja. Úsalo cuando una habilidad deba durar más que su entrada bajo una condición del mundo específica.
ActionSlot define un slot de acción que está asociado con una lista de InputActions y InputBindings dentro del Sistema de Acción de Entrada.
Varios vínculos de entrada de ranura de acción están predefinidos por Roblox y, en el futuro, el Administrador de Acciones de Entrada te permitirá reconfigurar los vínculos de entrada predeterminados para las ranuras de acción según desees. Establecer ActionSlot en 0 elegirá la siguiente ranura vacía disponible. En dispositivos móviles, las ranuras 1-7 se llenan con botones en la pantalla (ver diagrama a continuación).
Ranura Teclado y Ratón Controlador Táctil Asignación Predeterminada 1 Space ButtonA ① Saltar 2 LeftShift ButtonL1 ② Correr 3 LeftControl ButtonB ③ Agacharse 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 y CustomIconInvalid especifican IDs de activos de Roblox para el botón táctil cuando la habilidad está inactiva, activa o no disponible, respectivamente.
RunsWhile solo contiene condiciones del mundo. El compilador compone el efecto de la entrada en la continuación real de la habilidad detrás de escena basado en Mode, por lo que poner la entrada misma en StartsWhen o RunsWhile genera un error (no referencies Rule.Input allí). Las habilidades Hold y Toggle ya se detienen al soltar o desactivar por defecto, por lo que no necesitas duplicar eso en 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),
}Para hacer que una habilidad Hold o Toggle dure más que su entrada bajo una condición del mundo, establece OutlivesInputWhile en lugar de agregar la entrada a RunsWhile. La siguiente habilidad Crouching mantiene la posición agachada después de que el jugador libera la entrada de agacharse, siempre que algo permanezca por encima:
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,
}Sensores
Un sensor es un valor nombrado sobre el mundo que el motor lee por ti. Normalmente leerás sensores en lugar de escribirlos. Para conveniencia, varios sensores están pre-registrados:
| Sensor | Descripción |
|---|---|
| Sensor.Ground | De pie sobre una superficie |
| Sensor.IsMoving | Se está aplicando entrada de movimiento |
| Sensor.MoveInput | El vector de movimiento en sí |
| Sensor.Ceiling | Algo está directamente encima |
| Sensor.Climb | Una superficie escalable está al alcance |
| Sensor.Water / Sensor.WaterSurface | En el agua / en la superficie |
| Sensor.Sit | Sentado |
| Sensor.Tipped | Se ha caído |
| Sensor.Tool | Sosteniendo un Tool |
| Sensor.LookDirectionInput | La dirección de mirada ordenada |
| Sensor.RotateToLookDirectionInput | Si el personaje debe rotar hacia la dirección de mirada ordenada |
Callbacks
Las funciones de callback de habilidad te permiten scriptar comportamientos específicos:
Aunque registras habilidades personalizadas en el servidor, sus callbacks se ejecutan tanto en la simulación del cliente predicha como en la simulación del servidor autoritativa. Mantén el comportamiento de los callbacks determinista para que ambas simulaciones produzcan el mismo resultado.
| Callback | Se Ejecuta | Casos de Uso |
|---|---|---|
| OnSetup(managerCtx, abilityCtx) | Una vez, cuando la habilidad es registrada. | Cachear referencias, inicializar estado, etc. |
| OnStart(managerCtx, abilityCtx, hadLabel) | Cada vez que se activa la habilidad. | Aplicar un efecto como un impulso. La función hadLabel() informa si una etiqueta específica estaba presente cuando comenzó la activación, antes de la resolución de conflictos. |
| OnUpdate(managerCtx, abilityCtx) | Cada cuadro activo. | Trabajo continuo como temporizadores o fuerzas por cuadro. |
| OnStop(managerCtx, abilityCtx) | Cada desactivación, voluntaria o forzada. | Deshacer lo que hizo OnStart(). |
| OnTeardown(managerCtx, abilityCtx) | Al eliminar la habilidad. | Desconectar conexiones, destruir instancias, etc. |
El primer parámetro de cada función de callback, managerCtx, es un objeto ManagerContext con propiedades compartidas de personaje y administrador, incluyendo:
- managerCtx.AbilityOwner — El Model del personaje tal que managerCtx.AbilityOwner.PrimaryPart es la parte raíz.
- managerCtx.AbilityManager — Una vista reducida del administrador para que una habilidad pueda agregar, eliminar y consultar habilidades desde dentro de sus propios callbacks.
- managerCtx.BodyParts — Las partes del cuerpo del personaje, con ayudantes para activar y desactivar la colisión por extremidad.
- managerCtx.ControllerManager — El ControllerManager del personaje para el control de física. Puede ser nil cuando ninguna habilidad registrada requiere física.
- managerCtx.RootCFrame — El CFrame de la parte raíz, capturado una vez al inicio del cuadro para que los callbacks no tengan que buscarlo cada uno por su cuenta.
- managerCtx.RootLookVector — Dirección en la que está mirando la parte raíz del personaje.
- managerCtx.RootUpVectorY — El componente Y del vector hacia arriba de la parte raíz.
- managerCtx.TaskSynchronize() — Sincroniza un callback antes del acceso al DataModel cuando el soporte de callbacks paralelos está habilitado. Actualmente, OnUpdate no se ejecuta en un contexto paralelo, por lo que esta función no tiene efecto. Se planea soporte completo de Luau paralelo para una futura actualización.
El segundo parámetro, abilityCtx, es un objeto AbilityContext con tablas gestionadas por el motor para el registro actual de la habilidad:
- abilityCtx.Config — Valores de configuración de solo lectura para este registro de habilidad.
- abilityCtx.State — Estado mutable que se replica a través del DataModel. La Autoridad del Servidor restaura estos valores durante la reversión y la re-simulación.
- abilityCtx.Local — Estado mutable temporal que no se replica ni participa en la reversión.
Almacena datos personalizados de callback en abilityCtx.State o abilityCtx.Local. Es un error escribir campos personalizados directamente en abilityCtx.