卷 III · 感CH 12深度 12/24

Juice:加特效之前先想清楚

「Juice it or lose it」—— 这个说法流行了十几年,害处大概和好处一样多。它是对的:一点点动效能让一个死板的原型活过来。但它被理解成了「特效越多越好」,于是无数个人项目走上同一条路:在核心玩法还没立住的时候,花两周做特效,做完发现还是不好玩,但现在它是一个花哨的不好玩。

倒 U 曲线可读性Juice 预算优先级它掩盖了什么

先看那条曲线

下面这个 demo 会自动持续攻击。把滑块从 0 一路推到 200%,同时注意两件事:

  1. 右上角那个统计条里的「可读性」数字
  2. 那个蓝色的小方块(画面上有提示)—— 试着一直用眼睛跟住它

0% 的时候:干巴巴,但你能清楚看到一切。

50–80% 的时候:有力、清晰、爽 —— 这是甜点。

150% 以上的时候:你已经跟丢那个蓝方块了。

◆ 中心事实

Juice 是一条倒 U 曲线,不是一条上升直线。

超过某个点之后,你继续加东西,玩家获得的信息反而在减少 —— 因为新加的东西在遮挡旧的东西。

而在游戏里,玩家读不到的信息 = 他做不出的决策 = 他玩不好 = 他觉得这游戏很随机。

Juice 的预算是「注意力」

理解这一章只需要一个概念:玩家的注意力是一个固定的、很小的池子,而每个视觉元素都在从里面取钱。

屏幕不是无限大的画布,玩家的眼睛也不是。在任何一个瞬间,人能同时追踪的运动物体大概是 4 个左右。超过这个数,剩下的就变成了背景噪音。

◆ 一个可以直接用的分级

把你游戏里所有的视觉元素分成三档,然后严格按这个顺序分配注意力预算

是什么要求
A · 必须看见 会杀死玩家的东西、玩家控制的角色、当前目标 永远不能被遮挡。特效要给它们让路
B · 应该看见 敌人状态、资源变化、可交互物 可以被短暂遮挡,但不能持续
C · 看见了更好 环境动效、装饰粒子、背景 随时可以牺牲

规则很简单:C 档的东西永远不能盖住 A 档的东西。

而新手最常犯的错误恰恰是:给一个 C 档事件(打中了一个小怪)配了 A 档强度的特效(半个屏幕的爆炸)。

实操:给特效定一个「不许进入」的区域

一个很有效的具体做法:

  • 玩家角色周围的一小圈区域,特效要么不生成,要么降低不透明度
  • 屏幕边缘(玩家余光扫描危险的地方)保持干净
  • 血条 / 关键 UI 附近不放粒子

这听起来很琐碎,但它是「华丽」和「混乱」的分界线。

Juice 的优先级:先做哪些

假设你只有两小时做 juice,该做什么?下面这个顺序是按每小时收益排的:

优先级做什么成本为什么排这么前
1 缓动(easing)
所有 UI、所有移动,别用线性
极低 把「机械」变成「有生命」,一个函数的事
2 音效
哪怕是免费素材
贡献一半的手感,不占屏幕空间(第 11 章)
3 命中停顿 三行代码换整个打击感
4 挤压拉伸 让角色有弹性和重量
5 过渡
场景切换、菜单进出别硬切
直接决定「像不像正经游戏」
6 粒子 收益开始递减,且最容易做过头
7 屏幕特效(模糊、色差、扭曲) 收益最低,风险最高

大多数人的实际顺序是:7 → 6 → 然后就没时间了。正好反过来。

第 1 名值得展开:缓动

这是整本书里性价比最高的一个技巧,而且它和游戏类型完全无关。

把任何「从 A 移动到 B」的东西,从线性插值改成有加速度的曲线,画面立刻从「程序」变成「设计」。

# ✗ 线性:机械、廉价
position = position.lerp(target, 0.1)

# ✓ 帧率无关的指数趋近:快速起步、平滑收尾
position = position.lerp(target, 1.0 - exp(-SPEED * delta))

# ✓✓ 或者直接用 Tween,带回弹的缓动函数
var t := create_tween()
t.tween_property(self, "position", target, 0.3)\
 .set_trans(Tween.TRANS_BACK)\
 .set_ease(Tween.EASE_OUT)
✕ 那个所有人都写错的 lerp

第一行 position.lerp(target, 0.1) 是个经典 bug,它依赖帧率

  • 60 帧时:每秒趋近 60 次,每次 10%
  • 30 帧时:每秒趋近 30 次 —— 移动速度直接减半

玩家在低配机器上会觉得摄像机「拖泥带水」,而你在自己机器上完全测不出来。

第二行的 1.0 - exp(-SPEED * delta) 是正确写法:不管帧率多少,一秒之后趋近的比例是一样的。把这个式子记下来,你会用一辈子。

挤压拉伸:两行代码的生命感

起跳时纵向拉长、落地时横向压扁,然后弹回原状。

func on_jump() -> void:
	scale = Vector2(0.76, 1.28)      # 拉长

func on_land(impact: float) -> void:
	scale = Vector2(1.0 + 0.34 * impact, 1.0 - 0.30 * impact)   # 压扁

func _process(delta: float) -> void:
	# 平滑弹回。同样用帧率无关的写法
	scale = scale.lerp(Vector2.ONE, 1.0 - exp(-14.0 * delta))

两个细节让它从「还行」变成「很好」:

  • 缩放的锚点要在脚底,不是中心。否则压扁的时候角色会陷进地里。(在 Godot 里把 Sprite 的 offset 设成向上半个身高,或者用一个额外的 Node2D 作为缩放中心)
  • 形变量要和冲击力挂钩。从高处摔下来压得更扁 —— 这样它就不只是装饰,还携带了信息(第 11 章的第 ② 层职责)。

缓动曲线:哪种用在哪

既然缓动排第一,值得把它讲透一点。不同的曲线传达不同的「物理性格」,选错了会让人说不出哪里怪。

曲线感觉用在
Ease Out
快起步、慢收尾
响应快、有阻尼 绝大多数情况的默认值。UI 出现、摄像机跟随、角色转向
Ease In
慢起步、快收尾
蓄力、加速、坠落 东西离场、下坠、蓄力动作。不要用在「响应玩家操作」上 —— 会显得迟钝
Ease In-Out 平稳、正式 场景过渡、镜头推移。用在小控件上会显得拖沓
Back(回弹) 有弹性、俏皮 按钮按下、道具弹出、卡牌落位。用多了会显得廉价
Elastic(震荡) 夸张、卡通 非常克制地用在庆祝时刻。日常界面用它会让人烦
Linear 机械、无生命 只用在真正匀速的东西上:传送带、进度条、匀速旋转
◆ 一条能解决 80% 情况的默认规则

凡是对玩家操作的响应,用 Ease Out;凡是自动发生的过渡,用 Ease In-Out。

原因:Ease Out 的开头最快,所以玩家会感觉到立刻有反应(第 11 章的「确认」职责),而结尾的减速提供了重量感。两者兼得。

而 Ease In 的开头很慢 —— 玩家按下之后有一小段几乎没动静的时间,那正是「不跟手」的感觉来源。永远不要用 Ease In 响应输入。

时长也要选

曲线之外还有时长。几个经验值:

  • 对操作的即时反馈:60–120 毫秒。超过 150 毫秒开始有延迟感
  • UI 元素进出:150–250 毫秒
  • 场景过渡:300–500 毫秒。超过 800 毫秒玩家会不耐烦(尤其是他要看很多次的那些)
  • 任何玩家会看一百遍的动画:越短越好。第一次觉得酷的过场,第五十次就是障碍物

最后一条特别重要。凡是重复出现的动画,都要能被跳过或者做得极短 —— 这直接关系到第 7 章那条「失败成本要压到 1 秒内」。

Juice 掩盖的那个真问题

这一节是本章存在的真正理由。

Juice 有一个危险的性质:它能让一个平庸的核心玩法在短时间内显得很好。

你加了粒子、震屏、顿帧,然后自己玩了一下 —— 哇,爽多了。于是你觉得游戏变好了。

但玩家的体验曲线是这样的:

前 30 秒  「哇,这个打击感不错」        ← juice 在起作用
第 5 分钟  「嗯……」                     ← juice 的新鲜感耗尽
第 15 分钟 「好像有点无聊」                ← 只剩下核心玩法本身
第 20 分钟  关掉                          ← 核心玩法撑不住
◆ Juice 的作用范围

Juice 影响的是前五分钟,核心玩法决定的是后五小时。

这不是说 juice 不重要 —— 前五分钟极其重要,很多玩家根本活不过前五分钟。但你要清楚它不能做什么:

  • Juice 不能让一个没有深度的动词变得有深度
  • Juice 不能修复一个断掉的会话循环
  • Juice 不能让假选择变成真选择
  • Juice 不能替代手感的 A/B/C 三组(第 9、10 章)

一个诊断方法

怀疑自己在用 juice 掩盖问题?做这个测试:

把所有特效、震动、顿帧、粒子全部关掉,只留最基础的画面和音效,玩十分钟。 还有意思吗?
  • 还有意思 → 你的地基是好的,juice 是纯净的加分。放心加。
  • 变得很无聊 → 你的乐趣可能大部分来自 juice 的新鲜感。停下来,回卷 II 检查动词和循环。

这个测试之所以有效,是因为你自己已经对 juice 免疫了(看过几千遍),但对核心玩法的问题还是有感觉的。

◇ 一点老实话:这个测试你会不想做

因为关掉特效之后的游戏,看起来会很寒酸,而你会本能地觉得「这不公平,成品又不是这样」。

但恰恰是这个反应说明了问题:如果你需要特效才敢看自己的游戏,那你可能一直在回避一个你已经隐约知道的答案。

早点面对它,比在发布后从玩家的评论里读到它便宜得多。

什么时候该做 juice

一个明确的时间点建议:

阶段该做的 juice不该做的
原型期
(验证好不好玩)
只做音效,因为它是最便宜的确认反馈 其他全部不做。你要看的是裸的玩法
成型期
(玩法定了,做内容)
缓动、命中停顿、挤压拉伸、过渡 还不要做粒子和屏幕特效
打磨期
(第 23 章)
粒子、屏幕特效、全套时序调优

为什么原型期几乎不做 juice?因为 juice 会干扰你自己的判断。你需要在最裸的状态下确认「这个动词本身是不是有意思」,而 juice 会给你一个虚假的肯定答案。

本章小结

  • Juice 是倒 U 曲线,不是上升直线。超过甜点之后,加的东西在遮挡信息。
  • 预算是注意力:人同时能追踪约 4 个运动物体。把元素分成 A/B/C 三档,C 永远不能盖住 A
  • 优先级(按每小时收益):缓动 → 音效 → 命中停顿 → 挤压拉伸 → 过渡 → 粒子 → 屏幕特效。多数人的实际顺序正好相反。
  • 记住这个式子:lerp(a, b, 1 - exp(-k * delta))直接写 lerp(a, b, 0.1) 是帧率相关的 bug。
  • Juice 影响前五分钟,核心玩法决定后五小时。它不能修复卷 II 里的任何问题。
  • 诊断:全关掉玩十分钟。变得很无聊 → 回去查动词和循环。
  • 原型期只做音效,其他一律推迟到打磨期。
⇄ 换个类型
  • 卡牌:juice 的甜点比动作游戏,因为玩家需要读牌面文字。过度的粒子会直接让人看不清自己有几费。这类游戏的 juice 预算应该主要花在牌的移动缓动结算的时序编排上,而不是特效。
  • 塔防 / 策略最需要克制的类型。同屏几十个单位,每个都带特效等于什么都看不见。做法:特效强度随缩放级别衰减 —— 镜头拉远时自动简化。
  • 模拟经营:juice 主要花在「变化的可见性」上 —— 作物长大的那一帧要有动画,资源增加要有飘字。这类游戏的 juice 不是为了爽,是为了让缓慢的进展被感知到。
  • 叙事解谜:juice 的甜点非常低。这类游戏靠氛围而不是刺激,一个不合时宜的震屏会毁掉整段情绪。把预算全部投到过渡和音效上。
  • 射击 / 动作:甜点最高的类型,但也最容易过头。硬规则:玩家角色和子弹永远不能被自己的特效遮住。很多手游射击的差评就来自这一条。