CHUAN2 DEV ENGINE
996 正版授权研发中心 · 360 授权合作教学中心 · 抖音传奇直播合作授权 · 快手推广运营商授权
OFFICIAL LICENSED ACADEMY 查验官方授权证书 →
// 威海旷世互娱教学基地 · 技术文章
进阶实战游戏功能996引擎

Lua数字滚动:旧值到新值的缓动翻牌

2026-10-02 02:28 作者:996 技术组 996引擎Lua教程传奇脚本进阶实战游戏功能996引擎

【游戏功能】

上个月一笔充值的到账被投诉了:钱到账的瞬间数字从四位数直接跳成七位数——玩家截图发群里说"到账异常,肯定是补丁"。钱一分没少,跳变的样子太吓人——账实相符的到账也会被当成事故,呈现的粗糙是要还账的。数字滚动就是把跳变改成翻牌:旧值到新值之间加一段缓动,数字像计价器一样滚上去,滚到哪算哪,滚完稳稳站住,站住的数字才算数。这篇把整套写法拆开:配置表、起止记账、缓动插值、上屏输出、定时器、调试按钮,一段一段照抄能跑。

一、效果演示:旧值到新值的缓动翻牌

演示场一块数字面板。点「加一万」:数字从当前值滚向一万——起步快、收尾慢,中途还带一点缩放的呼吸;滚完停在整数位上,稳稳的——落定的那一下还有一声轻响,账目到齐的仪式感就差这一声。点「加一百万」:跨度的滚法更明显——前半段数字翻得飞快,后半段一格一格落定。点「随机跳」:跳到哪算哪,滚动的弧线永远是从旧值滑向新值的一条减速线——弧线不挑起点,跳多少都能接住。演示里试两笔账:跳变上屏和滚动的上屏——钱数一样,吓人的程度天差地别。数字滚动教的是过渡:数值会跳,呈现不能跳——账目上的突变是严谨,屏幕上的突变是惊吓,两回事要分开待。

mermaid
flowchart TD
A[记下旧值和新值] --> B[计时从零到一]
B --> C[缓动曲线折算进度]
C --> D[显示值 = 旧值加差值乘进度]
D --> E{计满了吗}
E -- 否 --> B
E -- 是 --> F[显示值钉死新值]
demo
 fx-numroll

二、底层原理:一次起止记账加一阶缓动插值

模块的机关是一对搭档。起止记账:滚动发起的那一拍把旧值和新值都记下——滚动的原料是两个数和一段时长,账本三行写完。缓动插值:计时的进度过一遍缓动曲线再折算显示值——起步快收尾慢,数字的滚动自带减速的体面。和直接赋值的分野在"滚动的数字有过程":直接赋值是结果没有过程,滚动把变化的过程演给玩家看——到账、结算、计分,凡是金额类的跳变,有过程就不吓人——玩家盯着数字滚完的那一秒,信任感就是这时候攒下的——数字会说话,滚着说比跳着说中听。

缓动为什么选先快后慢不选匀速?匀速的滚动像秒表走字,机械;先快后慢符合"大数来得快、零头慢慢对"的直觉,收尾的那几格慢动作是滚动感的灵魂——前段翻多快都行,最后几格一定要让人看清落定在哪。

三、核心代码:完整模块(上·骨架)

lua
-- @file NumberRoll.lua
-- 数字滚动 —— 旧值到新值的缓动翻牌

local NumberRoll = {}

local CONST = {
    ROLL_LIFE     = 1.0,
    PULSE_SCALE   = 0.06,

    COLOR_NUM     = "#c9d1d9",
    COLOR_PANEL   = "#161b22",

    ZORDER_ROLL   = 150,
    AUTOINC_BASE  = 1152000,
}

local _shown     = 0
local _from      = 0
local _to        = 0
local _rollT     = 0
local _rolling   = false
local _label     = nil
local _scheduler = nil
local _autoInc   = 0

local function GenID(prefix)
    _autoInc = _autoInc + 1
    return string.format("%s_%d", prefix or "NRL",
        CONST.AUTOINC_BASE + _autoInc)
end

local function ShowTip(msg)
    if msg and msg ~= "" then SL:ShowSystemTips(msg) end
end

local function EaseOutCubic(k)
    return 1 - math.pow(1 - k, 3)
end

function NumberRoll.Shown()
    return _shown
end

四、核心代码:完整模块(下·滚动发起与上屏输出)

lua
-- 滚动发起:记旧记新,开滚
function NumberRoll.RollTo(target)
    _from = _shown
    _to = target
    _rollT = 0
    _rolling = true
end

-- 上屏输出:缓动折算的显示值逐拍刷新
if not _scheduler then
    _scheduler = SL:Schedule(function(dt)
        if not _rolling then return end
        _rollT = _rollT + dt
        local k = math.min(1, _rollT / CONST.ROLL_LIFE)
        _shown = math.floor(
            _from + (_to - _from) * EaseOutCubic(k))
        if _label and not GUI:Widget_IsNull(_label) then
            GUI:setText(_label,
                tostring(string.format("%d", _shown)))
        end
        if k >= 1 then
            _rolling = false
            _shown = _to
            ShowTip("滚完——稳稳停在 " .. _to)
        end
    end, 0.02)
end

function NumberRoll.Bind(label)
    SafeRemove(label)
    _label = label
end

local function SafeRemove(node)
    if node and not GUI:Widget_IsNull(node) then
        GUI:removeFromParent(node)
    end
end

SL:ScheduleOnce(function()
    NumberRoll.Bind(GUI:GetCashLabel())
    ShowTip("技能已加载: 数字滚动 (调试按钮触发)")
end, 1.0)

function NumberRoll.Unload()
    if _scheduler then SL:Unschedule(_scheduler) end
    SafeRemove(_label)
    _rolling = false
end

return NumberRoll

五、机制问答

问:滚动到一半又来一笔新账怎么办?
答:从当前显示值续滚——旧值改成此刻的显示值,新值照换,滚动不断档也不回跳。

问:旧值和新值一样还滚吗?
答:不滚——零差值的滚动是白耗拍子,起止记账时顺手判一下,相等直接钉死。

问:负数和缩水的账能滚吗?
答:能——缓动插值不挑方向,缩小照样走曲线,扣钱的滚动同样体面。

问:滚动的时长为什么定一秒?
答:一秒是"看得清又不等得烦"的折中——跨度大的账可以按比例放宽,但别超过两秒,等数字滚完的人会烦。

问:缩放的呼吸是给数字加的吗?
答:是——滚动的过程中面板轻轻一缩一放,收尾时归位,呼吸只在滚动期有,站定的数字不喘。

六、调参与实战怎么用

第四笔是续滚的口径:从显示值续滚是连账的规矩——从旧值续滚的版本两笔账会跳变一次,口径定在显示值,连着到账也顺滑。第三笔是时长的档位:一秒的底账随跨度浮动——跨度小的账滚太快看不清过程,跨度大的账滚太久惹人等,按时长封顶加跨度分档是折中。第二笔是缓动的选择:先快后慢的三次曲线是金额的默认——计数类用先慢后快也有张力,但金额先快后慢最像计价器。第一笔是站定的钉死:滚完那一拍显示值必须钉死新值——浮点误差会让最后一位差一,钉死是账面的清白。常见坑三个:滚动到一半的续账没有改起点导致回跳;负数账的取整方向写错少滚一位;缩放呼吸的锚点没设中心向左上放大。

实战里数字滚动是金额类界面的通用件:到账、结算、经验条读数、拍卖出价,全是"旧值滚向新值"同一副骨架。组里的约定是:凡是一千以上的金额跳变一律滚动,小额直接赋值——大账要体面,小账要利索,体面和利索各管一段。这篇的模块照抄能跑,改的就是 CONST 那张表。

写完留一句给做账面系的同学:数字滚动卖的是"变化要有过程"——那一段先快后慢的翻牌和稳稳落定的最后一格,是把吓人的跳变折成体面的过场的一台计价器。

作者履历与出处

本文由 996 技术组基于 996 引擎官方知识库与浮生梦老师课程体系整理。团队长期从事传奇类引擎 Lua 后端逻辑、客户端界面与商业版本交付,内容以官方知识库与真实项目为出处,按版本持续修订。

← 返回文章地图返回研学路径

幂尔框架 · 实战干货 · 接口调用

LATEST ARTICLES

全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →

进阶实战游戏功能

Lua自动炮台:架一个自动索敌开火

【语法算法】 上一版的炮台索敌逻辑有个隐蔽报错:炮台永远打同一个怪——即使那只怪已经死了,索敌的游标也不挪窝。追到底索敌的游…

2026-10-02 04:50 996 技术组
进阶实战游戏功能

Lua元素转换:火伤转冰伤附带减速

【语法算法】 先抛一个坑:打不完的火系怪怎么办?火抗怪火打不动,换冰系技能要切装备要换面板——切完黄花菜都凉了。元素转换的答…

2026-10-02 04:50 996 技术组
进阶实战游戏功能

Lua连锁引燃:一个着火引燃下一个

【语法算法】 上个月的事故复盘会上有个数字被念了三遍:四成——策划写的是"同伴陪疼四成",代码落下去成了"陪疼四十点",两只…

2026-10-02 04:50 996 技术组
进阶实战游戏功能

Lua弹射地垫:踩上去弹飞到空中

【语法算法】 单行代码拆解:弹射初速=-420——弹射的全部动力就这一行的负初速。负号朝上、四百二十是弹射的初速大小——踩上…

2026-10-02 04:50 996 技术组
进阶实战游戏功能

Lua时间减缓:流速减半的局部时差

【语法算法】 上一版的减速类模块全按"乘以零点五"来写,帧率无关的版本照搬了这套写法——结果高帧率机上减速效果好,低帧率机上…

2026-10-02 04:50 996 技术组
进阶实战游戏功能

Lua引力球:丢出去把怪吸过来

【语法算法】 先抛一个坑:怎么把散在四处的怪聚到一起打?逐个拉是笨办法,一个范围技又只能打一片——引力球的答案是一个会动的吸…

2026-10-02 04:50 996 技术组