角色控制器库

*此内容使用人工智能(Beta)翻译,可能包含错误。若要查看英文页面,请点按 此处。

角色控制器库 (CCL) 是一个模块化框架,通过属性和 Luau 脚本构建角色移动和行为。该架构用灵活、可扩展的角色机制系统替代了僵化的 Humanoid 状态机。

启用 CCL

CCL 是通过 Studio 的 头像设置 窗口 可选 的。要启用它:

  1. 从 头像 选项卡中,打开 头像设置。

    在 Studio 工具栏中指示的头像设置
  2. 在窗口左侧选择 移动 选项卡,在 能力 部分,选择 角色控制器库。

    头像设置窗口中的角色控制器库切换
  3. 所有标准能力,如 奔跑、跳跃 和 攀爬 默认启用。要在运行时禁用其中任何一个,请取消选中相关框。

能力

能力评估角色可以做什么,例如跑步、跳跃、攀爬和游泳的能力。CCL 能力动态确定角色可以做什么以及如何响应玩家输入,而不是依赖于固定的引擎定义角色状态,如 Enum.HumanoidStateType 中的状态。

从结构上讲,能力是一个自包含的 Luau 表,主要指定以下内容:

表字段目的
Name能力的唯一标识;条件 和 冲突 通过名称引用它。注册两个具有相同 Name 的能力会抛出错误。对于一些相似的能力,给每个能力一个不同的 Name,并在 Labels 中共享一个公共 标签。ModuleScript 名称决定唯一配置键。
Labels, TimedLabels共享 64 位掩码中的命名位,作为能力之间的协调总线;见 标签。
StartsWhen, RunsWhile定义何时启动能力和何时保持其运行的条件;见 条件。
Blocks, Stops, Suspends, ExclusiveGroup如何处理不能同时处于活动状态的能力之间的 冲突。
Input触发能力的输入。Input.Mode 控制输入如何启动和停止能力;RunsWhile 仅包含世界条件,而不包含输入本身;见 输入。
Config, State每个能力注册的默认配置值和复制状态。回调从 abilityCtx.Config 读取配置,并通过 abilityCtx.State 读取或写入复制状态。
OnSetup, OnStart, OnStop, OnUpdate, OnTeardown生命周期回调,能力的实际行为在此处编写;见 回调。

标签

标签是共享 64 位掩码中的命名位,作为能力之间的协调总线。基本上:

  • 活动能力 广播 其 Labels 和 TimedLabels 到世界掩码。
  • 能力 条件 (StartsWhen, RunsWhile) 测试 世界掩码并做出反应。
  • 能力 冲突 定义在激活时被阻止、停止或暂停的 其他 能力。

在以下设置中,当 Running 处于活动状态时,"CanFallDown" 标签被广播到世界掩码。具有其 条件 的 FallingDown 能力 StartsWhen = All( "CanFallDown", "Stunned" ) 自动成为候选,但 "Stunned" 也必须在 FallingDown 发生之前广播到世界掩码。

Running Ability
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" }, -- 能力处于活动状态时广播的标签
StartsWhen = Sensor.Ground,
RunsWhile = Sensor.Ground,
}
FallingDown Ability
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" ), -- 启动能力所需的标签
Blocks = { Ability.Running }
}

标签也可以使用 TimedLabels 字典以 定时 方式广播或消耗。

键描述
TimedLabels.OnStart包含标签(键)和相关持续时间的字典。当能力启动时,标签会被广播,并在其持续时间结束时自动过期。例如,OnStart = { Dashing = 1 } 在能力启动时广播 Dashing 标签 1 秒。
TimedLabels.OnStop包含标签(键)和相关持续时间的字典。当能力停止时,标签会被广播,并在其持续时间结束时自动过期。例如,OnStop = { DashCooldown = 2 } 在能力停止时广播 DashCooldown 标签 2 秒。
TimedLabels.Consumes在能力激活时要移除(消耗)的标签列表。例如,如果一款格斗游戏允许玩家在阻挡对手攻击后进行反击,CounterAttack 能力可能包含 StartsWhen = "AfterBlock" 和 TimedLabels = { Consumes = { "AfterBlock" } } 以防止 CounterAttack 能力的双重触发。
Timed Labels
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 },
},
}

条件

条件是一个或多个 标签、传感器 或输入引用,用于 StartsWhen 和 RunsWhile。条件在运行时编译为位掩码操作,评估是整数数学 — 没有表遍历和字符串比较。

目标语法示例
一个必需的条件。StartsWhen = Sensor.Ground
AND 逻辑,当 所有 标签存在于世界掩码中且 所有 传感器处于活动状态时。All()StartsWhen = All( "CanFallDown", "Stunned" )
OR 逻辑,当 任何 标签存在于世界掩码中或 任何 传感器处于活动状态时。Any()StartsWhen = Any( "WallClimbing", "Climbing" )
否定,使得标签 不能 存在于世界掩码中且传感器 不能 处于活动状态。Not()RunsWhile = Not("Stunned")

条件评估可以组合以实现更复杂的逻辑,例如 All() 链接加上 Not() 表示传感器必须处于活动状态,而标签必须不存在:

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

冲突

某些能力在另一种能力处于活动状态时无法激活;例如,角色在游泳时无法跳跃,在下落时无法奔跑。引擎在能力定义内部声明性地解决这些冲突:

冲突键目的
Blocks当拥有的能力处于活动状态时,列出的其他能力无法启动。例如,ScopeAim 能力可能包含 Blocks = { Ability.Running, Ability.Jumping } 以防止角色在仔细瞄准武器的瞄准镜时奔跑或跳跃。
Stops当拥有的能力启动时,列出的其他能力 强制停止 并必须重新触发。例如,Hover 能力可能包含 Stops = { Ability.Running } 以在角色开始悬停时立即停止其奔跑动作。
Suspends当拥有的能力启动时,列出的其他能力 暂停,然后在拥有的能力停止时 自动恢复。例如,自定义冲刺能力可能包含 Suspends = { Ability.Running },以便在冲刺开始时暂停奔跑 🄐,在冲刺中被阻止 🄑,并在冲刺停止时恢复 🄒。

另一个独特的冲突键是 ExclusiveGroup,它将多个能力放入一个组,每个组都有一个 Priority 值。每组中只能有一个能力处于活动状态,优先级高的胜出。然而,如果挑战者声明 Stops 目标为持有者的名称/标签,则无论优先级如何,它都将胜出。

在以下设置中,三个能力(Sprinting、Crouching、Stagger)被添加到一个 Locomotion 独占组中。Sprinting 具有最高优先级(200),因此它胜过 Crouching(100),这两个能力不会同时运行。然而,Stagger 强制停止冲刺 (Stops = { Ability.Sprinting }),因此它可以中断并超越 Sprinting,即使它的优先级(150)较低。

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 },
}
-- 低优先级能力可以通过停止高优先级能力来覆盖它
local Stagger: AvatarAbilities.AbilityDefinition = {
Name = "Stagger",
ExclusiveGroup = { Name = "Locomotion", Priority = 150 },
Stops = { Ability.Sprinting },
}

输入

能力的 Input 定义指定用于尝试激活能力的输入。它采用键值对来配置输入行为、动作槽和可选的触摸按钮图标。

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 是一个逻辑名称,而不是一个键。CCL 生成一个输入传感器,并始终将其与 StartsWhen 进行 AND 操作。

  • Mode 定义此输入如何启动和停止能力。RunsWhile 从不包含输入本身;它仅包含世界条件,例如在地面上或在水中,因此 Mode 是输入驱动的启动和停止的唯一来源:

    模式启动从输入停止
    Press按下时。从不。能力通过冲突、其自身的 RunsWhile 或调用 Stop() 自行结束。
    Hold按下时。释放时。
    Hold 与 Repeats按下时。释放结束重新触发,而不是已经在飞行中的周期。
    Toggle切换开启时。切换关闭时。

    Press 适合于离散动作,如冲刺、攻击和投掷。Hold 和 Toggle 适合于持续动作,如冲刺、瞄准、阻挡、蹲下和悬浮。Hold 与 Repeats 适合于在按住时自我停止并重新触发的动作,例如重复跳跃。

  • Repeats(布尔值,仅适用于 Hold)使能力在每个周期自我终止,并在输入保持按住时重新触发,而不是在整个保持期间持续运行。

  • OutlivesInputWhile(仅适用于 Hold/Toggle)是一个 条件,在该条件下能力在释放或切换关闭后继续运行,直到条件清除。使用它当能力在特定世界条件下应持续超过其输入时。

  • ActionSlot 定义一个 动作槽,与 输入动作系统 中的一系列 InputActions 和 InputBindings 相关联。

    几个动作槽输入绑定是由Roblox预定义的,未来的 输入动作管理器 将允许您根据需要重新配置动作槽的默认输入绑定。将 ActionSlot 设置为 0 将选择下一个可用的空槽。在移动设备上,槽 1-7 会填充到屏幕上的按钮(见下图)。

    槽键盘和鼠标游戏手柄触摸默认分配
    1SpaceButtonA①跳跃
    2LeftShiftButtonL1②冲刺
    3LeftControlButtonB③蹲下
    4RButtonX④
    5MouseLeftButtonButtonR2⑤
    6QButtonY⑥
    7XButtonR1⑦
    8CButtonL2
    9FDPadLeft
    10GDPadRight
    11VDPadDown
  • CustomIcon、CustomIconActive 和 CustomIconInvalid 分别指定能力处于闲置、活动或不可用状态时的触摸按钮的 Roblox 资产 ID。

RunsWhile 仅包含世界条件。编译器根据 Mode 在幕后将输入的效果组合到能力的实际延续中,因此将输入本身放入 StartsWhen 或 RunsWhile 会抛出错误(不要在此处引用 Rule.Input)。Hold 和 Toggle 能力在释放或切换关闭时默认已经停止,因此您不需要在 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),
}

要使 Hold 或 Toggle 能力在世界条件下超过其输入,请设置 OutlivesInputWhile,而不是将输入添加到 RunsWhile。以下 Crouching 能力在玩家释放蹲下输入后保持蹲下,只要上方仍有物体:

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.Ground站在一个表面上
Sensor.IsMoving正在应用移动输入
Sensor.MoveInput移动向量本身
Sensor.Ceiling上方有物体
Sensor.Climb在范围内有可攀爬的表面
Sensor.Water / Sensor.WaterSurface在水中 / 在水面上
Sensor.Sit坐着
Sensor.Tipped翻倒
Sensor.Tool持有一个 Tool
Sensor.LookDirectionInput命令的视线方向
Sensor.RotateToLookDirectionInput角色是否应该旋转到命令的视线方向

回调

能力 回调函数 让您编写特定行为:

尽管您在服务器上注册自定义能力,但它们的回调在预测的客户端仿真和权威的服务器仿真中都运行。保持回调行为的确定性,以便两个仿真产生相同的结果。

回调运行时机用例
OnSetup(managerCtx, abilityCtx)注册能力时运行一次。缓存引用,初始化状态等。
OnStart(managerCtx, abilityCtx, hadLabel)每次激活能力时运行。应用效果,例如冲击。hadLabel() 函数报告在激活开始时是否存在指定标签,在冲突解决之前。
OnUpdate(managerCtx, abilityCtx)每个活动帧。持续工作,例如计时器或每帧力。
OnStop(managerCtx, abilityCtx)每次停用,无论是自愿还是强制。撤销 OnStart() 所做的事情。
OnTeardown(managerCtx, abilityCtx)在能力移除时运行。断开连接,销毁实例等。

每个回调函数的第一个参数 managerCtx 是一个 ManagerContext 对象,具有共享的角色和管理器属性,包括:

  • managerCtx.AbilityOwner — 角色的 Model,使得 managerCtx.AbilityOwner.PrimaryPart 是根部件。
  • managerCtx.AbilityManager — 管理器的简化视图,以便能力可以在其自己的回调中添加、移除和查询能力。
  • managerCtx.BodyParts — 角色的身体部件,具有每个肢体打开和关闭碰撞的助手。
  • managerCtx.ControllerManager — 角色的 ControllerManager 用于物理控制。当没有注册的能力需要物理时,它可以是 nil。
  • managerCtx.RootCFrame — 根部件的 CFrame,在帧开始时快照一次,以便回调不必各自去获取它。
  • managerCtx.RootLookVector — 角色根部件面朝的方向。
  • managerCtx.RootUpVectorY — 根部件的上向量的 Y 组件。
  • managerCtx.TaskSynchronize() — 在启用并行回调支持时,在访问 DataModel 之前同步回调。目前,OnUpdate 不在并行上下文中运行,因此此函数没有效果。计划在未来的更新中提供完整的并行 Luau 支持。

第二个参数 abilityCtx 是一个 AbilityContext 对象,具有引擎管理的当前能力注册的表:

  • abilityCtx.Config — 此能力注册的只读配置值。
  • abilityCtx.State — 可变状态,通过 DataModel 复制。服务器权限 在回滚和重新仿真期间恢复这些值。
  • abilityCtx.Local — 不复制或参与回滚的可变临时状态。

将自定义回调数据存储在 abilityCtx.State 或 abilityCtx.Local 中。直接写入自定义字段到 abilityCtx 是错误的。

©2026 Roblox Corporation、Roblox、Roblox 标志及 Powering Imagination 是我们在美国及其他国家或地区的注册与未注册商标。