从《原神》公告栏到《王者》英雄池:拆解Scroll Rect在热门手游UI里的高级玩法
在《原神》每日登录时弹出的公告栏里,手指轻轻上滑的阻尼感像推开一扇雕花木门;《王者荣耀》英雄选择界面中,列表随着手指甩动后继续滑行的惯性,仿佛物理世界中的真实物体。这些细腻的交互体验背后,都离不开Unity中那个看似简单的Scroll Rect组件——但真正让它产生"手感魔法的",是开发者对Movement Type、Elasticity、Inertia等参数的毫米级调校。
1. 触觉反馈:Scroll Rect的物理引擎调参术
当《原神》的公告栏滚动到顶部时,那个微妙的回弹效果不是预设动画,而是Elasticity参数与移动端触摸输入事件的化学反应。弹性系数0.3时像橡皮筋,0.1时像弹簧床,这需要配合移动端特有的触摸压力检测:
// 模拟iOS的橡胶带效果 scrollRect.elasticity = Mathf.Lerp(0.05f, 0.3f, Mathf.Clamp01(scrollVelocity / 1000f));移动端专属参数对照表:
| 设备类型 | 推荐Inertia值 | Elasticity阈值 | 触觉反馈方案 |
|---|---|---|---|
| iOS全面屏 | 0.95 | 0.15-0.25 | 配合HapticEngine |
| Android高刷屏 | 0.9 | 0.2-0.3 | 触发100ms微振动 |
| 平板设备 | 0.85 | 0.1-0.15 | 增大滚动衰减系数 |
注意:安卓设备需要额外监听
Input.touches[0].pressure来动态调整弹性系数,压力越大弹性应越小
2. 平台适配:同一套UI的跨端手感统一
《王者荣耀》PC版用鼠标滚轮浏览英雄池时,那种丝滑度并非简单启用Inertia就能实现。实际上需要三套独立配置:
移动端触摸方案:
- Movement Type: Elastic
- Deceleration Rate: 0.135(模拟指腹摩擦)
- 动态灵敏度算法:
def calc_sensitivity(touch_delta): return min(35, max(12, touch_delta * 1.8))
PC端滚轮方案:
- 必须重写ScrollRect的OnScroll方法:
public override void OnScroll(PointerEventData data) { float scale = IsMacPlatform ? -0.03f : 0.03f; scrollRect.velocity += data.scrollDelta * scale; }手柄操控方案:
- 需要添加模拟物理阻尼:
void Update() { if(usingGamepad) { scrollRect.velocity = Vector2.Lerp( scrollRect.velocity, targetVelocity, Time.deltaTime * 5f); } }
3. 性能优化:万人同屏时的滚动列表方案
《原神》活动页面要承载上千玩家留言时,原始Scroll Rect直接崩溃。成熟方案采用"视窗渲染+动态加载":
实现步骤:
计算可见区域四角坐标:
Vector3[] corners = new Vector3[4]; viewport.GetWorldCorners(corners); float minY = corners[0].y; float maxY = corners[2].y;对象池管理方案:
- 活跃节点数 = 屏幕高度/(元素高度+间距)*2 + 缓冲量
- 回收不可见元素时保留1帧延迟避免闪烁
差分更新算法:
def update_visible_items(): new_visible = calculate_visible_range() added = new_visible - current_visible removed = current_visible - new_visible recycle_items(removed) spawn_items(added)
关键指标:在Redmi Note 10上实测,万级数据量下滚动帧率保持58fps+
4. 高级动效:Scroll Rect的延伸创意用法
《崩坏3》角色选择界面那种立体卡片滚动效果,实际上是通过改写ScrollRect的OnDrag事件实现的:
void IDragHandler.OnDrag(PointerEventData eventData) { base.OnDrag(eventData); // 计算当前归一化位置 float normalizedPos = scrollRect.horizontalNormalizedPosition; // 对每个子物体应用3D旋转 foreach(var card in cards) { float offset = Mathf.Abs(card.index - normalizedPos); card.transform.eulerAngles = new Vector3( 0, Mathf.Lerp(0, 45, offset), 0); } }复合动画参数配置表:
| 效果类型 | 关联参数 | 推荐曲线 | 适用场景 |
|---|---|---|---|
| 视差滚动 | scrollRect.normalizedPosition | AnimationCurve.Linear() | 背景图层 |
| 渐隐边缘 | canvasGroup.alpha | EaseOutCubic | 长列表边界提示 |
| 动态缩放 | transform.localScale | EaseInOutBack | 卡片式布局 |
| 材质位移 | material.mainTextureOffset | Sin波函数 | 特殊效果背景 |
在《使命召唤手游》的枪械改装界面中,左右滑动配件列表时会同步触发枪械模型的旋转动画,这种联动的秘密在于监听了ScrollRect的onValueChanged事件:
scrollRect.onValueChanged.AddListener(pos => { weaponModel.transform.rotation = Quaternion.Euler( 0, Mathf.Lerp(-30, 30, pos.x), 0 ); });