HarmonyOS新特性-沉浸光感在叠叠消小游戏中的落地实践

前言

前段时间我用 ArkTS + ArkUI 开发了一款消除类小游戏「叠叠消」,核心玩法是叠层图案消除,目标平台为 HarmonyOS NEXT(API 26),涵盖手机、折叠屏和平板。游戏跑起来 60fps 很稳,但 UI 层面总觉得"平"——所有界面元素都是纯色背景,各自为政,没有统一的视觉质感。

恰好 HarmonyOS NEXT 提供了「沉浸光感(Immersive Light Sensing)」这套系统级材质渲染能力,官方描述是"通过模拟真实物理光照模型,在 UI 组件内部产生毛玻璃模糊、按压弹性光效、滑动透明度渐变等效果"。说白了就是系统帮你渲染毛玻璃。

我在三个核心场景做了落地:标题栏、底部操作栏、弹窗体系。本文记录具体的改造过程和踩坑经验,希望对同样在适配沉浸光感的开发者有帮助。

适用读者:有 ArkTS/ArkUI 基础、正在做鸿蒙应用适配或性能优化的开发者。


一、沉浸光感简介

在开始改造之前,先简单梳理一下沉浸光感涉及的 API 和概念。

1.1 涉及的 SDK 包

  • @kit.ArkUI 下的 uiMaterial.ImmersiveMaterial —— 底层材质渲染能力
  • @kit.UIDesignKit 下的 hdsMaterial —— HDS 组件的高级封装

1.2 核心 API

组件的 systemMaterial() 属性,设了就开,不设就关:

// 开启
Button('撤回').systemMaterial(new uiMaterial.ImmersiveMaterial({
  style: uiMaterial.ImmersiveStyle.ULTRA_THIN,
  interactive: true,
  lightEffect: { color: undefined }
}))

// 关闭
Button('撤回').systemMaterial(undefined)

1.3 材质档位

档位 枚举值 说明
MaterialLevel.EXQUISITE 完整效果,含毛玻璃 + 光晕 + 反射
均衡(默认) MaterialLevel.GENTLE 适度效果,平衡性能与视觉
MaterialLevel.SMOOTH 轻量级,仅保留核心视觉特性
系统自适应 MaterialLevel.ADAPTIVE 系统根据设备性能自动选档

实际开发中,大多数场景直接用 ADAPTIVE 最省心。


二、改造一:游戏页标题栏(渐变模糊 + 动态显隐)

2.1 改造前

游戏页(GamePage)标题栏使用 NavDestination,纯色背景(暖紫 #7C3AED),固定不动的标题文字。在游戏画面向上滑动时,标题栏始终占据顶部空间,没有利用价值。

2.2 改造方案

参考官方的 Spatialization-master 参考实现,使用 HDS 组件的 HdsNavDestination + titleBar 属性配置。

// WaterFlowDetailView.ets
import { hdsMaterial, HdsNavigation, HideMode, ScrollEffectType } from '@kit.UIDesignKit';

HdsNavigation() {
  WaterFlowView({
    currentTabIndex: this.currentIndex,
    pageKey: PageKeyEnum.IMMERSIVE_LIGHT,
    handleTabBarAnimation: (yOffset: number) => {
      this.handleTabBarAnimation?.(yOffset);
    }
  })
}
.mode(NavigationMode.Stack)
.hideBackButton(false)
.titleMode(HdsNavigationTitleMode.MINI)
.dynamicHideTitleBar({
  hideTitleArea: true,
  hideStatusBar: true,
  mode: HideMode.SCROLL_UP_TO,
})
.titleBar({
  content: {
    title: { mainTitle: $r('app.string.sample_3_title') },
    backIcon: {
      action: () => { this.handleBackBtnAction(); }
    },
    menu: this.menus,
  },
  style: {
    scrollEffectOpts: {
      enableScrollEffect: true,
      scrollEffectType: ScrollEffectType.GRADIENT_BLUR,
      blurEffectiveEndOffset: LengthMetrics.vp(this.globalInfoModel.statusBarHeight + 56)
    },
    systemMaterialEffect: {
      materialType: hdsMaterial.MaterialType.ADAPTIVE,
      materialLevel: this.customMaterialLevel.level,
    },
  },
  avoidLayoutSafeArea: true,
  enableComponentSafeArea: false,
})
.bindToScrollable([this.scroller])
.ignoreLayoutSafeArea(
  [LayoutSafeAreaType.SYSTEM],
  [LayoutSafeAreaEdge.TOP, LayoutSafeAreaEdge.BOTTOM]
)

2.3 关键属性说明

属性 作用
scrollEffectType GRADIENT_BLUR 标题栏随滑动从透明渐变为毛模糊
materialType ADAPTIVE 自适应材质类型
materialLevel this.customMaterialLevel.level 可动态切换的材质强度
dynamicHideTitleBar.hideTitleArea true 允许标题栏滑动隐藏
dynamicHideTitleBar.hideStatusBar true 允许状态栏联动隐藏
mode SCROLL_UP_TO 上滑到底后才隐藏,不是一滑就藏

2.4 效果描述

页面初始状态:标题栏完全透明,游戏画面铺满全屏。向上滑动时:标题栏逐步浮现毛玻璃效果,不是"闪现"出来。继续上滑到一定位置:标题栏和状态栏整体隐藏,释放更多可视面积。向下滑动时反向过渡,毛玻璃渐隐消失。

2.5 代码量变化

  • 改造前:标题栏配置约 5 行
  • 改造后:配置约 25 行
  • 净增约 20 行,没有改动游戏逻辑代码

三、改造二:底部操作栏(悬浮胶囊 + 左右手自适应)

3.1 改造前

底部操作栏使用 Row 容器 + 深紫色背景(#2D1B69),固定在屏幕底部。视觉效果上是"贴"在屏幕上的一个色块,游戏画面被一条实色条切断。

3.2 改造方案

叠叠消虽然没有多 Tab 切换的需求,但 HDS 的 HdsTabs 悬浮栏模式(barFloatingStyle)恰好提供了所需的「悬浮毛玻璃胶囊」效果,可以直接复用。

// 参考 AdaptiveTabView.ets
HdsTabs({ controller: this.controller }) {
  TabContent() {
    WaterFlowView({
      currentTabIndex: repeatItem.index,
      pageKey: PageKeyEnum.ADAPTIVE_TAB
    })
  }
  .tabBar(repeatItem.item)
}
.barOverlap(true)
.vertical(false)
.barPosition(BarPosition.End)
.barFloatingStyle({
  barBottomMargin: this.globalInfoModel.naviIndicatorHeight > 0
    ? this.globalInfoModel.naviIndicatorHeight
    : $r('sys.float.padding_level8'),
  systemMaterialEffect: {
    materialType: hdsMaterial.MaterialType.IMMERSIVE,
    materialLevel: hdsMaterial.MaterialLevel.ADAPTIVE
  },
  miniBar: {
    miniBarBuilder: () => this.miniBarBuilder(),
    onBarStyleChange: (miniBarStyle: HdsBarStyle) => {
      this.isMiniBar = miniBarStyle === HdsBarStyle.COLLAPSE;
    }
  }
})

3.3 配合智感握姿

adaptToHandedness 属性设为 true 后,底部栏在宽屏设备上会靠向握持手一侧。叠叠消复用了之前智感握姿场景的传感器代码:

// 参考 SmartReachView.ets
import { motion } from '@kit.MultimodalAwarenessKit';

aboutToAppear(): void {
  try {
    if (canIUse('SystemCapability.MultimodalAwareness.Motion')) {
      motion.on('holdingHandChanged', this.handleHoldingHandChange);
    }
  } catch (error) {
    Logger.error(TAG, `Failed on holdingHandChanged. cause: ${error.message}`);
  }
}

handleHoldingHandChange = (status: motion.HoldingHandStatus) => {
  this.getUIContext().animateTo(
    { curve: curves.interpolatingSpring(0, 1, 288, 30) },
    () => {
      // barFloatingStyle 已设 adaptToHandedness,无需手动控制偏移
    }
  );
}

3.4 效果

底部操作栏变成悬浮在游戏画面之上的圆角胶囊,图案堆最下层的图案可以从胶囊底部透出来。adaptToHandedness 在平板和折叠屏展开态下效果明显,手机上屏幕较窄,左右差异不大。


四、改造三:弹窗体系(毛玻璃背景 + 版本兼容)

4.1 改造前

叠叠消的弹窗种类不少:通关结算、失败救场、商城购买、道具确认、设置面板等。使用 CustomDialogController 实现,白底 + 阴影,弹出时游戏画面完全被遮挡。

4.2 工具类封装

弹窗的改造不影响游戏逻辑,核心代码全部封装在 MaterialUtil.ets 中,同时做好 API 版本兼容:

// MaterialUtil.ets
import { uiMaterial } from '@kit.ArkUI';
import { deviceInfo } from '@kit.BasicServicesKit';
import { hdsMaterial } from '@kit.UIDesignKit';

export class MaterialUtil {
  /**
   * 给弹窗/菜单等组件添加沉浸材质
   * 兼容 API 26 以下设备,自动降级
   */
  public static getMaterialOptions<T>(option: T): MaterialOptions<T> | T {
    if (deviceInfo.apiAvailable('26.0.0')
        && hdsMaterial.getSystemMaterialTypes().length > 0) {
      const materialOptions = option as MaterialOptions<T>;
      materialOptions.systemMaterial = new uiMaterial.ImmersiveMaterial({
        style: uiMaterial.ImmersiveStyle.THICK,
        interactive: true,
        lightEffect: { color: undefined },
      });
      return materialOptions;
    }
    return option; // 不支持的设备直接返回原配置
  }

  /**
   * 给按钮等通用组件添加沉浸材质
   */
  public static getMaterialModifier(options: ModifierOptions): CommonModifier {
    if (deviceInfo.apiAvailable('26.0.0')
        && hdsMaterial.getSystemMaterialTypes().length > 0) {
      const materialOptions: uiMaterial.ImmersiveOptions = {
        style: uiMaterial.ImmersiveStyle.ULTRA_THIN,
        interactive: true,
        lightEffect: { color: undefined }
      };
      if (options.materialColor) {
        materialOptions.materialColor = options.materialColor;
      }
      options.modifier.systemMaterial(new uiMaterial.ImmersiveMaterial(materialOptions));
    } else {
      // 低版本降级方案
      if (options.backgroundColor) {
        options.modifier.backgroundColor(options.backgroundColor);
      }
      if (options.backgroundBlurStyle) {
        options.modifier.backgroundBlurStyle(options.backgroundBlurStyle);
      }
    }
    return options.modifier;
  }
}

interface MaterialOptions<T> extends ThisType<T> {
  systemMaterial?: SystemUiMaterial;
}

interface ModifierOptions {
  modifier: CommonModifier;
  style: CustomImmersiveStyle;
  backgroundColor?: ResourceColor;
  materialColor?: ResourceColor;
  backgroundBlurStyle?: BlurStyle;
}

export enum CustomImmersiveStyle {
  ULTRA_THIN = 0,
  THIN = 1,
  REGULAR = 2,
  THICK = 3,
  ULTRA_THICK = 4
}

4.3 弹窗调用

在通关结算弹窗中调用:

// 通关结算弹窗
try {
  const uiContext = this.getUIContext();
  const promptAction = uiContext.getPromptAction();
  const contentNode = new ComponentContent(
    uiContext,
    wrapBuilder(victoryDialogBuilder)
  );
  promptAction.openMenu(
    contentNode,
    { id: 'victoryDialog' },
    MaterialUtil.getMaterialOptions<MenuOptions>({
      mask: false,
      backgroundBlurStyle: BlurStyle.COMPONENT_THICK,
    }) as MenuOptions
  );
} catch (error) {
  Logger.error(TAG, `Show dialog failed: ${error.message}`);
}

4.4 效果

弹窗背景变为毛玻璃材质,背后的游戏画面(图案堆、撒花粒子)隐约可见。弹窗不再是阻断式遮罩,而是浮在游戏上的一层半透明玻璃板,游戏连贯感大幅提升。


五、多设备适配要点

叠叠消需要覆盖手机、折叠屏和平板,沉浸光感在不同设备上的表现和处理要点如下:

5.1 手机(竖屏 360-430vp)

标题栏毛玻璃效果最明显,屏幕窄、内容区滑动时标题栏的渐变过渡容易被注意到。底部悬浮胶囊的圆角在手机上比较协调。

5.2 折叠屏展开态(700-900vp)

折叠态切换时需要监听折叠状态并切换布局,但沉浸材质不需要重新配置:

import { display } from '@kit.ArkUI';

display.on('foldStatusChange', (status: display.FoldStatus) => {
  if (status === display.FoldStatus.EXPANDED) {
    // 切换双列布局(左侧图案堆,右侧收集槽)
    // 沉浸材质不需要重新配置——ADAPTIVE 模式会自动适应新布局
  } else if (status === display.FoldStatus.FOLDED) {
    // 切回单列布局
  }
});

5.3 平板(800-1200vp)

图案放大 30%,毛玻璃的透光效果在大屏上更强烈。底部悬浮栏可以通过 barBottomMargin 适当调大间距,增强悬浮感。


六、性能与降级策略

6.1 帧率影响

在 Mate 70 Pro(麒麟 9010)上的帧率测试数据:

场景 未开启 EXQUISITE 开启 帧率变化
游戏静止(图案堆无操作) 60fps 60fps 无感知
图案点击 + 飞入动画 60fps 60fps 无感知
收集槽消除 + 粒子特效 60fps 58-60fps 偶尔轻微掉帧
通关撒花 + 弹窗弹出 60fps 55-60fps 可感知但可接受

中端设备(Nova 系列)上使用 ADAPTIVE 模式后,系统自动降到了 GENTLE,帧率没有明显波动。

6.2 降级策略实现

function getMaterialLevel(): hdsMaterial.MaterialLevel {
  if (!deviceInfo.apiAvailable('26.0.0')
      || hdsMaterial.getSystemMaterialTypes().length === 0) {
    return hdsMaterial.MaterialLevel.SMOOTH; // 不支持 → 最低档
  }

  // 根据设备性能选择
  if (isHighPerformanceDevice()) {
    return hdsMaterial.MaterialLevel.EXQUISITE;
  }
  return hdsMaterial.MaterialLevel.ADAPTIVE;
}

6.3 降级对照表

设备等级 代表机型 标题栏 底部栏 弹窗 按钮
高性能 Mate 70 Pro / 折叠屏 EXQUISITE EXQUISITE EXQUISITE 完整按压反馈
中性能 Nova 系列 GENTLE GENTLE THICK 仅弹性缩放
低性能 畅享系列 SMOOTH SMOOTH REGULAR 关闭材质
不支持 API < 26 或无能力 降级 backgroundColor + backgroundBlurStyle

七、踩坑记录

7.1 同层渲染场景变透明

在 API 23 及以前版本中,同层渲染(Web 组件内嵌 ArkUI 控件)下设置沉浸光感可能导致控件背景变透明。叠叠消没有 Web 组件所以没触发,但做混合开发的同学需要注意:在 Web 内嵌的 ArkUI 控件上关闭材质。

// 同层渲染场景下关闭材质
component.systemMaterial(undefined);

7.2 HdsTabs 滚动事件处理

HdsTabs 的浮动栏需要通过 WaterFlow 的滚动回调通知 HdsTabsController 执行显示/隐藏动画。需要维护滚动方向状态,避免动画堆积:

handleTabBarAnimation(yOffset: number): void {
  if (this.globalInfoModel.needDynamicHideBar) {
    if (yOffset > 0 && !this.isScrollUp) {
      this.isScrollUp = true;
      this.controller.applyHideAnimation(HdsAnimationMode.SCROLL_ANIMATION);
    } else if (yOffset < 0 && this.isScrollUp) {
      this.isScrollUp = false;
      this.controller.applyShowAnimation(HdsAnimationMode.SCROLL_ANIMATION);
    }
  }
}

7.3 亮色模式下的白色叠层

亮色模式下 EXQUISITE 材质可能覆盖白色叠层,导致按钮文字和背景颜色接近。解决方案:亮色模式自动降级到 GENTLE

7.4 折叠屏切换时的材质闪烁

折叠屏展开/折叠切换时,布局结构变化导致底部栏材质短暂闪烁(约 200ms)。在切换动画执行前预置材质参数可以缓解:

// 折叠状态变化前预置材质
this.controller.setBarFloatingStyle({
  systemMaterialEffect: {
    materialType: hdsMaterial.MaterialType.ADAPTIVE,
    materialLevel: hdsMaterial.MaterialLevel.ADAPTIVE,
  },
});

八、总结

沉浸光感在叠叠消中的落地总结如下:

维度 结论
改动范围 核心改动不到 80 行代码(工具类 50 行 + 页面配置约 30 行)
影响范围 三个改造场景覆盖玩家 90% 的 UI 接触面
逻辑侵入 零——没有改动任何游戏逻辑代码
性能影响 高配设备无感知,中低配 ADAPTIVE 模式自动降级

对我个人来说,最大的感受是:系统级材质渲染和第三方实现的最大区别在于一致性。之前使用 backgroundBlurStyle 手动模拟毛玻璃,不同设备上效果参差不齐,折叠屏切换布局时还要手动重算。现在把材质定义交给系统,不用关心底层渲染实现,也不需要手动适配多设备。

唯一需要上心的是性能分级和降级策略。ADAPTIVE 模式在大多数场景下是最稳妥的选择,只有在高性能设备上可以放开到 EXQUISITE 档位。


参考资料

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐