《HarmonyOS 7 ArkGraphics 3D 空间设计开发实战》08:动画、生命周期与SpaceRoom工程化收尾【鸿蒙心迹】

最后不堆新功能,把整个工程收回来。

前言

七篇做下来,SpaceRoom 已经有:场景、模型、坐标、材质、灯光、相机交互、拾取选择、性能优化。一个能看能点的 3D 空间设计器差不多齐了。

最后一篇不加新功能,做工程收尾:补动画、理生命周期、把模块拆清楚。很多 Demo 看着能跑,真要做成工程就发现到处都是耦合:Scene 和 UI 绑死了,模型加载和页面生命周期搅在一起,退出页面以后资源还在内存里。

这一篇就把这些问题收拾干净,让 SpaceRoom 从一个 Demo 变成一个结构清楚的工程。


一、先补动画:柜门、抽屉、灯光渐变

先加几个简单动画,让场景活起来:

typescript 复制代码
import { scene } from '@kit.ArkGraphics3DKit';

export class AnimationController {
  private animations: Map<string, scene.Animation> = new Map();

  /**
   * 播放柜门开启动画
   */
  playDoorOpen(doorNode: scene.Node): void {
    // 从0度转到90度,500ms
    const anim = doorNode.createComponent(scene.NodeComponentType.ANIMATION) as scene.Animation;
    anim.duration = 500;
    anim.from = {rotation: {x: 0, y: 0, z: 0}};
    anim.to = {rotation: {x: 0, y: 90, z: 0}};
    anim.easing = 'easeOut';
    anim.play();

    this.animations.set('door', anim);
    console.info('AnimationController: door opened');
  }

  /**
   * 播放灯光渐变
   */
  playLightFade(lightNode: scene.Node, targetIntensity: number): void {
    const light = lightNode.getComponent(scene.NodeComponentType.LIGHT) as scene.Light;
    const anim = lightNode.createComponent(scene.NodeComponentType.ANIMATION) as scene.Animation;
    anim.duration = 800;
    anim.from = {intensity: light.intensity};
    anim.to = {intensity: targetIntensity};
    anim.easing = 'easeInOut';
    anim.play();
  }

  /**
   * 停止所有动画
   */
  stopAll(): void {
    this.animations.forEach(anim => {
      anim.stop();
    });
    this.animations.clear();
  }
}

动画本身不复杂,关键是动画的生命周期:页面退到后台的时候要暂停,回到前台的时候继续;页面销毁的时候要停掉所有动画。


二、生命周期:页面退了以后,谁来清理

这是工程里最容易出问题的地方。3D 对象的生命周期和 ArkUI 页面的生命周期不一样。

页面走了,Scene 可能还在后台跑动画、跑渲染,白白耗电。

写个统一的入口:

typescript 复制代码
@Entry
@Component
struct SpaceRoomPage {
  private sceneMgr: SceneManager = new SceneManager();
  private cameraCtrl: CameraController | null = null;
  private modelMgr: ModelResourceManager | null = null;
  private lightMgr: LightManager | null = null;
  private pickingMgr: PickingManager | null = null;
  private animCtrl: AnimationController | null = null;
  private perfMonitor: ScenePerformanceMonitor | null = null;

  // 页面出现:初始化所有模块
  onPageShow(): void {
    this.initScene();
    this.animCtrl?.resumeAll();
  }

  // 页面隐藏:暂停动画,不要后台跑
  onPageHide(): void {
    this.animCtrl?.pauseAll();
    this.perfMonitor?.stop();
  }

  // 页面销毁:释放所有资源
  aboutToDisappear(): void {
    // 1. 停动画
    this.animCtrl?.stopAll();

    // 2. 释放模型资源
    this.modelMgr?.releaseAll();

    // 3. 释放场景
    this.sceneMgr.release();

    console.info('SpaceRoomPage: all resources released');
  }

  private initScene(): void {
    // 按顺序初始化
    this.sceneMgr.setupScene();
    this.cameraCtrl = new CameraController(this.sceneMgr.getScene()!);
    this.cameraCtrl.setupCamera();

    this.lightMgr = new LightManager(this.sceneMgr.getScene()!);
    this.lightMgr.setupSceneLights(this.sceneMgr.getScene()!);

    this.modelMgr = new ModelResourceManager(this.sceneMgr.getScene()!);
    this.pickingMgr = new PickingManager(this.sceneMgr.getScene()!);
    this.animCtrl = new AnimationController();
    this.perfMonitor = new ScenePerformanceMonitor();
  }

  build() {
    Stack() {
      scene.SceneView({ scene: this.sceneMgr.getScene() ?? undefined })
        .width('100%')
        .height('100%')
    }
    .width('100%')
    .height('100%')
  }
}

关键是三个生命周期方法:

  • onPageShow:页面出来,恢复动画和监控
  • onPageHide:页面隐藏,暂停动画,不要后台空跑
  • aboutToDisappear:页面销毁,把所有资源释放干净

很多人只做了 onAppear 初始化,没做 aboutToDisappear 释放,结果就是用户进出几次页面,内存涨好几倍。


三、最后把模块理清楚

回顾一下整个工程的模块划分:

模块 负责什么 不负责什么
SceneManager Scene 生命周期,创建和释放 不管模型、不管灯光
CameraController 相机位置、旋转、缩放 不管手势识别
ModelResourceManager 模型加载、缓存、引用计数 不管模型放哪
LightManager 灯光设置、氛围预设 不管材质
MaterialManager 材质和纹理 不管灯光
PickingManager 点击拾取、选中状态 不管 UI 展示
AnimationController 动画播放和暂停 不管动画内容
PerformanceMonitor 帧率和统计 不管优化策略
SpaceRoomPage 组装所有模块、接 UI 不写具体 3D 逻辑

这个划分的原则是:每个模块只做一件事。以后要加新功能,比如加个截图功能,就新建一个 ScreenshotManager,不要往 Page 里堆。


四、整个系列回顾

八篇走下来,技术线是这样的:

  1. 01 基础场景:Scene、Camera、Light、Node 创建成功 ≠ 能看见
  2. 02 坐标体系:世界坐标和局部坐标,父子节点 Transform 叠加
  3. 03 模型加载:Resource 和 Instance 分开,引用计数管生命周期
  4. 04 材质灯光:质感是材质和灯光配合出来的,不是模型好看就行
  5. 05 相机交互:二维手势映射成三维相机旋转,轨道相机模型
  6. 06 3D 拾取:从屏幕点反推射线,找到命中的物体
  7. 07 性能优化:先测数据再优化,视锥裁剪、共享材质、减少每帧计算
  8. 08 工程收尾:动画、生命周期、模块划分

整个过程里踩的坑,其实都不是 API 不会用,是 3D 开发的思维方式和 ArkUI 不一样:

  • ArkUI 里一个组件就是一个对象,3D 里 Resource 和 Instance 是分开的
  • ArkUI 里位置就是相对于父容器,3D 里要考虑世界坐标和局部坐标
  • ArkUI 里页面销毁就完了,3D 里还有 GPU 资源、纹理、模型要释放

这些概念理清楚了,后面做更复杂的 3D 应用就不会乱。


总结

第八篇是整个系列的收尾:

  1. 动画要管生命周期:后台暂停,销毁停掉
  2. 页面销毁时必须释放所有资源,不然内存越积越多
  3. 模块按职责拆分,每个模块只做一件事
  4. 3D 开发的思维和 ArkUI 不一样,核心是理解"渲染资源"和"业务对象"是分开的

SpaceRoom 到这里就完整了。从一个黑屏的空场景,到一个能转视角、能选家具、有材质有灯光的 3D 房间设计器。后面如果要继续做,可以加模型导入、保存分享、多人协作这些功能,但基础的 3D 开发链路已经跑通了。

相关推荐
威哥爱编程12 小时前
HarmonyOS 7 Agent 开发实战 07:多 Agent 任务分解与反思
agent·harmonyos
威哥爱编程12 小时前
HarmonyOS 7 Agent 开发实战 06:Agent 记忆与上下文管理
agent·harmonyos
HarmonyOS_SDK16 小时前
【HarmonyOS SDK Skills】扫码能力一键构建,系统级高精度扫码秒实现
harmonyos
结局无敌19 小时前
React Native for OpenHarmony 实战:三方库 react-native-modal-dialog 的鸿蒙化适配指南
深度学习·算法·react native·react.js·harmonyos
结局无敌21 小时前
React Native for OpenHarmony 实战:三方库 react-native-paper 的鸿蒙化适配指南
javascript·人工智能·深度学习·react native·react.js·ecmascript·harmonyos
数字新视界21 小时前
机房动环监控提升数据中心安全性
物联网·3d·数字孪生·三维可视化·数据可视化
七月稻草人21 小时前
鸿蒙手机远程控制电脑选哪个?2026 鸿蒙远控软件横评
智能手机·电脑·harmonyos
3D阅阅三维模型轻量化及浏览平台21 小时前
上传 3D 模型自动识别、一键报价,拆解 3D 阅阅报价底层逻辑(机加工 / 钣金)
人工智能·3d·机加工·钣金加工·模具加工
熊猫钓鱼>_>21 小时前
Kotlin Multiplatform for OpenHarmony 实战:kotlinx-coroutines 结构化并发适配
开发语言·kotlin·agent·harmonyos·openharmony·适配·华为云码道
结局无敌1 天前
React Native for OpenHarmony 实战:三方库 react-native-progress-steps 的鸿蒙化适配指南
javascript·人工智能·深度学习·react native·react.js·harmonyos