闪烁的灯光 是影响环境氛围的强大力量。例如,一个亮度一致的家居照明可能感觉温暖而亲切,但如果你在同一个家的走廊中添加闪烁的灯光,它可能会变得阴森,暗示前方可能有危险。通过在你的体验中战略性地建模和编写不同的光源,你可以为你的环境叙事增添全新的一层。
与所有3D创作一样,有许多方法可以实现任何特定目标。在本指南中,你可以通过仅使用 Studio 中可用的工具和方法,以及一些基本资产(包括一个 .rbxm 文件用于银行家台灯模型,该模型包含可以自定义的子 Class.MeshParts 以符合你自己体验的美学),快速创建闪烁灯光动画。
在下面创建闪烁灯光的方法中,按照每个部分学习如何创建:
- 一个亮度 NumberSequence 来影响灯光随时间的亮度。
- 一个循环持续时间属性来确定每个闪烁循环需花费的时间。
导入示例灯具
本指南使用可下载的 .rbxm 文件,这是一个高质量、可定制的银行家台灯模型,以演示闪烁灯光的技术。你可以使用该模型来理解基本原理,然后将其应用于你在 Studio 或其他第三方建模软件中创建的模型。
要导入 BankersLamp .rbxm:
在 资源管理器 窗口中,右键单击 Workspace。一个上下文菜单显示。
选择 从文件插入… 文件浏览器显示。
选择 BankersLamp .rbxm,然后单击 打开 按钮。模型在视口中显示。

创建亮度 NumberSequence
NumberSequence 是一个数据类型,表示在实例生命周期内从 0 到 1 的一系列数值。这种数据类型对于创建闪烁的灯光非常有用,因为你可以指定灯光亮度在其生命周期内如何变化,当你在短时间内从全强度切换到无光时,可以实现闪烁效果。
NumberSequence 的 X 轴表示时间,而 Y 轴表示相对亮度。数值序列开始和结束时的每个方块都是一个 关键点,它确定灯光亮度生命线中该点的属性值。当你首次创建 NumberSequence 时,图表是一条直线,灯光在整个生命周期内保持相同的亮度强度,但通过添加和移动关键点,你可以在序列中绘制曲线,并随时间改变灯光的亮度。

虽然台灯默认没有亮度 NumberSequence,但你可以创建一个亮度属性,将其设置为 NumberSequence 类型,然后通过添加具有不同值的关键点进行实验,直到灯光以你设定的频率闪烁。
要创建一个亮度 NumberSequence:
向灯具模型添加一个新的 NumberSequence 属性。
在 资源管理器 窗口中,选择灯具模型。
在 属性 窗口中,导航到 属性 部分并单击 添加属性 按钮。添加属性 对话框显示。
在 添加属性 对话框中,
在 名称 字段中输入 BrightnessCurve。
单击 类型 下拉菜单,然后选择 NumberSequence。
单击 保存 按钮。新属性在 属性 窗口的 属性 部分中显示。

选择新的 BrightnessCurve 属性,然后单击 … 按钮。一个数字序列弹窗显示。

执行以下操作之一:
- 要在某个点更改亮度,单击关键点并向上或向下拖动,或在 值 字段中输入值。
- 要插入新的关键点,单击图表中的任意点。
- 要删除关键点,选择关键点,然后单击 删除 按钮。
- 要添加亮度的随机范围,单击任何关键点并将包络线向上或向下拖动。当时,灯光将生成在粉色包络线之间的随机亮度。
例如,以下图表使灯光在第一次闪烁时达到全亮度,在第二次闪烁时为50%亮度,在第三次闪烁时为75%亮度。

创建循环持续时间
既然你有一个 NumberSequence 来确定灯光的亮度如何在其生命周期内变化,你必须确定闪烁循环的持续时间。换句话说,这个循环持续时间基本上控制 NumberSequence 每次重复的时间(以秒为单位)。
要创建循环持续时间:
向灯具模型添加一个新的循环持续时间属性。
在 资源管理器 窗口中,选择灯具模型。
在 属性 窗口中,导航到 属性 部分并单击 添加属性 按钮。添加属性 对话框显示。
在 添加属性 对话框中,
在 名称 字段中输入 LoopDuration。
单击 类型 下拉菜单,然后选择 Number。
单击 保存 按钮。新属性在 属性 窗口的 属性 部分中显示。

将新的 LoopDuration 属性设置为 1。这告诉 NumberSequence 在一秒后重复。
脚本化灯光闪烁
现在你已经拥有控制灯具亮度生命周期内所有元素的工具,是时候创建一个 Script 来使所有内容一起工作,并使灯光闪烁。
要脚本化灯光闪烁:
- 在 资源管理器 窗口中,将鼠标悬停在灯具模型上,然后单击 ⊕ 按钮。一个上下文菜单显示。
- 从菜单中插入一个 脚本。
- 在新脚本中,输入以下内容:
local RunService = game:GetService("RunService")
-- 获取模型上设置的属性值。
local brightnessCurve = script.Parent:GetAttribute("BrightnessCurve")
local loopDuration = script.Parent:GetAttribute("LoopDuration")
-- 存储将要更改的模型实例的引用。
local light = script.Parent.lamp_hood.SpotLight
local bulb = script.Parent.lightbulb
local beam = script.Parent.lamp_hood.Beam
-- 存储将要更改的属性的原始值。
local origLightBrightness = light.Brightness
local origBeamBrightness = beam.Brightness
local origBulbColor = bulb.Color
-- 在特定时间(nsTime)获取 NumberSequence (ns) 的值
function evaluateNumberSequence(ns: NumberSequence, nsTime: number)
-- 如果我们在0或1,返回第一个或最后一个关键点的值。
if nsTime == 0 then
return ns.Keypoints[1].Value
end
if nsTime == 1 then
return ns.Keypoints[#ns.Keypoints].Value
end
-- 否则,逐对检查每一对关键点。
for i = 1, #ns.Keypoints - 1 do
-- 获取当前和下一个关键点。
local currKp = ns.Keypoints[i]
local nextKp = ns.Keypoints[i + 1]
-- 如果 nsTime 在关键点的时间之间,
if nsTime >= currKp.Time and nsTime < nextKp.Time then
-- 计算 nsTime 在关键点时间之间所处的位置,并称其为 alpha。
local alpha = (nsTime - currKp.Time) / (nextKp.Time - currKp.Time)
-- 使用 alpha 返回 nsTime 对应的值。
return currKp.Value + (nextKp.Value - currKp.Value) * alpha
end
end
end
RunService.Heartbeat:Connect(function()
-- 计算 NumberSequence 的时间(在 0 和 1 之间)。
local t = time() / loopDuration
local numberSequenceTime = t - (t // 1)
-- 获取该时间的 NumberSequence 的值。
local brightnessValue = evaluateNumberSequence(brightnessCurve, numberSequenceTime)
-- 根据该值调整亮度和颜色属性。
light.Brightness = origLightBrightness * brightnessValue
beam.Brightness = origBeamBrightness * brightnessValue
bulb.Color = Color3.new(
origBulbColor.r * brightnessValue,
origBulbColor.g * brightnessValue,
origBulbColor.b * brightnessValue
)
end)
当你 测试你的体验 时,Heartbeat 事件连接在每帧执行以下内容:
- 计算在 brightnessCurve NumberSequence 内的时间(numberSequenceTime)。
- 时间在 0 和 1 之间,分别表示 NumberSequence 的开始和结束。
- 计算在时间 numberSequenceTime 的 brightnessCurve NumberSequence 的值(brightnessValue)。
- evaluateNumberSequence() 计算与任何 NumberSequence 的时间相关联的值。
- 该值用作应用于随时间变化的属性的相对亮度值。
- 通过将 brightnessValue 乘以灯光的亮度、光束的亮度和灯泡的颜色来改变灯具的属性。
这些属性随时间的变化导致了下面的闪烁效果。