中秋佳节,赏月、品茗、话家常是传统习俗。随着增强现实(AR)技术的发展,我们可以用鸿蒙的开发能力,打造一款 "AR 中秋赏月" 应用 ------ 让虚拟玉兔在现实场景中跳跃,让数字月饼 "飘落" 在桌面,甚至能通过 AR 扫描月亮触发专属节日特效。这里我将将带大家从技术选型到代码实现,完整搭建这款充满节日氛围的鸿蒙 T应用。
文章目录
- 一、应用核心功能与技术栈
-
- [1. 核心功能规划](#1. 核心功能规划)
- [2. 技术栈依赖](#2. 技术栈依赖)
- 二、前置准备:环境配置与资源准备
-
- [1. 开发环境配置](#1. 开发环境配置)
- [2. 中秋 AR 资源准备](#2. 中秋 AR 资源准备)
- [三、核心代码实现:从 AR 场景初始化到互动功能](#三、核心代码实现:从 AR 场景初始化到互动功能)
- [1. AR 场景初始化:连接 AR Engine 与相机](#1. AR 场景初始化:连接 AR Engine 与相机)
-
- [2. 虚拟元素叠加:3D 模型与 AR 场景融合](#2. 虚拟元素叠加:3D 模型与 AR 场景融合)
- [3. 互动与分享功能:截图保存与社交分享](#3. 互动与分享功能:截图保存与社交分享)
- 四、开发注意事项与优化方向
-
- [1. 性能优化](#1. 性能优化)
- [2. 兼容性适配](#2. 兼容性适配)
- [3. 用户体验优化](#3. 用户体验优化)
- 五、总结

一、应用核心功能与技术栈
在开始开发前,我们先明确应用的核心场景与依赖技术,确保开发方向清晰:
1. 核心功能规划
AR 场景识别:通过鸿蒙 AR Engine 识别现实中的 "天空区域",将虚拟月亮模型叠加到真实天空中,实现 "虚实赏月" 效果;
节日互动特效:点击虚拟月亮触发玉兔跳跃、月饼飘落动画,支持手势缩放调整虚拟元素大小;
节日祝福分享:将 AR 赏月画面截图,生成带中秋文案的海报,支持分享到社交平台;
设备兼容性适配:适配鸿蒙手机、平板等不同尺寸设备,保证 AR 场景显示正常。
2. 技术栈依赖
基础框架:HarmonyOS Stage 模型(API 9+),TypeScript 作为开发语言;
AR 能力:依赖鸿蒙 AR Engine(@ohos.arengine),实现环境跟踪、平面识别、虚实叠加;
图形渲染:使用鸿蒙 ArkUI 的 3D 组件()加载 GLB 格式的中秋模型(月亮、玉兔、月饼);
权节日氛围的鸿蒙 TS 应用。
二、前置准备:环境配置与资源准备
在写代码前,我们需要完成两项关键准备工作,避免开发中出现环境或资源问题:
1. 开发环境配置
DevEco Studio 版本:需升级到 4.0 及以上,确保支持 API 9 + 的 AR Engine 能力;
AR Engine 依赖引入:在package.json中添加 AR Engine 依赖,或通过 DevEco Studio 的 "项目设置 - 模块设置" 手动引入:
bash
"dependencies": {
"@ohos.arengine": "^1.0.0",
"@ohos.arkui.3d": "^1.0.0"
}
权限声明:在module.json5中声明必要权限,AR 场景核心依赖相机权限,分享功能依赖存储权限:
"reqPermissions": [
{
"name": "ohos.permission.CAMERA",
"reason": "需要相机采集现实场景,实现AR赏月效果",
"usedScene": { "abilities": ["MainAbility"], "when": "inuse" }
},
{
"name": "ohos.permission.WRITE_MEDIA",
"reason": "需要保存AR赏月截图,用于节日分享",
"usedScene": { "abilities": ["MainAbility"], "when": "inuse" }
}
]
2. 中秋 AR 资源准备
3D 模型:准备 GLB 格式的中秋相关模型,推荐从Sketchfab下载开源模型(需注意版权),如:
低多边形风格月亮模型(带光晕效果);
动态玉兔模型(含跳跃、捣药动画);
圆形月饼模型(带纹理贴图);
特效资源:准备粒子特效配置文件(如月饼飘落的粒子参数),用于增强节日氛围。
三、核心代码实现:从 AR 场景初始化到互动功能
接下来,我们分模块实现应用的核心功能,重点讲解 AR 场景搭建、虚拟元素叠加、互动逻辑三个关键部分。
1. AR 场景初始化:连接 AR Engine 与相机
首先需要初始化 AR Engine,建立相机采集与 AR 跟踪的关联,这是实现虚实融合的基础。我们在MainPage.ets中编写初始化逻辑:
bash
import arengine from '@ohos.arengine';
import camera from '@ohos.multimedia.camera';
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import { BusinessError } from '@ohos.base';
@Entry
@Component
struct ARMidAutumnPage {
// AR Engine核心对象
private arSession: arengine.ARSession | null = null;
// 相机预览组件控制器
private cameraPreviewCtrl: CameraPreviewController = new CameraPreviewController();
// 是否初始化成功
@State isInitSuccess: boolean = false;
build() {
Column() {
// 相机预览(作为AR场景的现实背景)
CameraPreview(controller: this.cameraPreviewCtrl)
.width('100%')
.height('100%')
// 叠加3D虚拟元素(月亮、玉兔等)
.overlay(() => {
if (this.isInitSuccess) {
AR3DElements() // 后续实现3D元素组件
}
})
}
.onPageShow(() => {
this.initARSession(); // 页面显示时初始化AR
})
.onPageHide(() => {
this.destroyARSession(); // 页面隐藏时销毁AR资源
})
}
// 初始化AR Session与相机
private async initARSession() {
try {
// 1. 检查并申请相机权限
const hasCameraPerm = await this.checkAndRequestPermission('ohos.permission.CAMERA');
if (!hasCameraPerm) {
promptAction.showToast({ message: '请授予相机权限以使用AR功能' });
return;
}
// 2. 创建AR Session
this.arSession = await arengine.createARSession();
// 3. 配置AR场景:启用环境跟踪(识别天空、平面)
const config = await this.arSession.getConfig();
config.enableEnvironmentTracking(true); // 开启环境跟踪
await this.arSession.configure(config);
// 4. 绑定相机:将AR Session与相机预览关联
const cameraManager = camera.getCameraManager();
const cameraDevice = await cameraManager.getCameraDevices()[0]; // 获取默认相机
await this.arSession.bindCamera(cameraDevice);
// 5. 启动AR Session
await this.arSession.resume();
this.isInitSuccess = true;
console.log('AR场景初始化成功,可开始AR赏月');
} catch (err) {
console.error(`AR初始化失败:${(err as BusinessError).message}`);
this.isInitSuccess = false;
}
}
// 检查并申请权限
private async checkAndRequestPermission(permName: string): Promise<boolean> {
const atManager = abilityAccessCtrl.createAtManager();
const tokenId = abilityAccessCtrl.getTokenId();
try {
const status = await atManager.checkPermission(tokenId, permName);
if (status === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
return true;
}
// 申请权限
const result = await atManager.requestPermissionsFromUser(tokenId, [permName]);
return result.authResults[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
} catch (err) {
console.error(`权限处理失败:${(err as BusinessError).message}`);
return false;
}
}
// 销毁AR Session,释放资源
private async destroyARSession() {
if (this.arSession) {
await this.arSession.pause();
await this.arSession.destroy();
this.arSession = null;
this.isInitSuccess = false;
}
}
}
2. 虚拟元素叠加:3D 模型与 AR 场景融合
AR 场景初始化成功后,需要将中秋 3D 模型(月亮、玉兔)叠加到现实场景中。这里我们使用 ArkUI 的 3D 组件,结合 AR Engine 的环境跟踪数据,确保虚拟元素 "锚定" 在现实空间中(如将月亮固定在天空区域)。
创建AR3DElements.ets组件,实现 3D 模型加载与位置校准:
bash
import { Ark3D, Model, ModelLoader, Vector3 } from '@ohos.arkui.3d';
import arengine from '@ohos.arengine';
@Component
struct AR3DElements {
// AR Session从父组件传递
@Prop arSession: arengine.ARSession | null;
// 月亮模型加载器
private moonModelLoader: ModelLoader = new ModelLoader();
// 玉兔模型加载器
private rabbitModelLoader: ModelLoader = new ModelLoader();
// 虚拟元素位置(初始设为天空区域)
@State moonPosition: Vector3 = new Vector3(0, 2, -5); // x:水平, y:高度, z:距离
@State rabbitPosition: Vector3 = new Vector3(-1, 1.8, -5);
build() {
Ark3D()
.width('100%')
.height('100%')
.onReady(() => {
this.loadModels(); // ARK3D就绪后加载模型
this.trackSkyArea(); // 跟踪天空区域,校准月亮位置
})
.children([
// 月亮模型
Model(loader: this.moonModelLoader)
.position(this.moonPosition)
.scale(0.5, 0.5, 0.5) // 缩放模型大小
.onClick(() => {
this.triggerMoonEffect(); // 点击月亮触发特效
}),
// 玉兔模型
Model(loader: this.rabbitModelLoader)
.position(this.rabbitPosition)
.scale(0.3, 0.3, 0.3)
.animation('jump', true) // 自动播放跳跃动画
])
}
// 加载3D模型(GLB格式)
private async loadModels() {
try {
// 加载月亮模型(路径需与项目资源目录对应)
await this.moonModelLoader.load('assets/models/moon.glb');
// 加载玉兔模型
await this.rabbitModelLoader.load('assets/models/rabbit.glb');
console.log('中秋3D模型加载成功');
} catch (err) {
console.error(`模型加载失败:${(err as BusinessError).message}`);
}
}
// 跟踪天空区域,校准月亮位置(确保月亮显示在真实天空中)
private async trackSkyArea() {
if (!this.arSession) return;
// 循环获取AR环境跟踪数据
setInterval(async () => {
try {
const frame = await this.arSession.updateFrame(); // 获取AR帧数据
const trackables = await frame.getTrackables(arengine.TrackableType.PLANE); // 获取平面跟踪结果
for (const trackable of trackables) {
const plane = trackable as arengine.ARPlane;
// 判断是否为"天空平面"(AR Engine会识别天空为特殊平面)
if (plane.getPlaneType() === arengine.ARPlane.PlaneType.SKY) {
const centerPose = await plane.getCenterPose(); // 获取天空平面中心姿态
// 根据天空平面位置调整月亮位置,确保虚拟月亮与真实天空对齐
this.moonPosition = new Vector3(
centerPose.getTranslationX(),
centerPose.getTranslationY() + 1, // 向上偏移1单位,避免与天空平面重叠
centerPose.getTranslationZ() - 2 // 向前偏移2单位,让月亮更"靠前"
);
break;
}
}
} catch (err) {
console.error(`天空跟踪失败:${(err as BusinessError).message}`);
}
}, 100); // 每100ms更新一次位置,确保实时对齐
}
// 点击月亮触发特效(玉兔跳跃、月饼飘落)
private triggerMoonEffect() {
// 1. 玉兔跳跃动画加速
this.rabbitModelLoader.setAnimationSpeed('jump', 2); // 动画速度加倍
// 2. 触发月饼飘落粒子特效(此处简化,实际需结合粒子组件实现)
promptAction.showToast({ message: '中秋快乐!月饼来啦~' });
// 3. 1秒后恢复玉兔动画速度
setTimeout(() => {
this.rabbitModelLoader.setAnimationSpeed('jump', 1);
}, 1000);
}
}
3. 互动与分享功能:截图保存与社交分享
最后,我们为应用添加 "截图分享" 功能,让用户能将 AR 赏月画面保存并分享给亲友。在MainPage.ets中添加分享按钮,并实现截图逻辑:
bash
// 在MainPage.ets的build方法中添加分享按钮
Column() {
// 相机预览与AR元素(原有代码)
// ...
// 分享按钮(固定在底部)
Button('保存AR赏月画面')
.width(200)
.height(40)
.backgroundColor('#FF4500')
.textStyle({ color: 'white' })
.margin({ bottom: 30 })
.onClick(() => {
this.captureARScreen(); // 点击触发截图
})
}
// 截图并保存到相册
private async captureARScreen() {
try {
// 1. 检查存储权限
const hasStoragePerm = await this.checkAndRequestPermission('ohos.permission.WRITE_MEDIA');
if (!hasStoragePerm) {
promptAction.showToast({ message: '请授予存储权限以保存截图' });
return;
}
// 2. 对相机预览+AR元素区域截图(使用鸿蒙截图API)
const pixelMap = await capturePixelMap(this.cameraPreviewCtrl); // 简化写法,实际需通过组件ID定位
// 3. 保存截图到相册
const mediaLibrary = media.createMediaLibrary();
const savePath = `Pictures/ARMidAutumn/${new Date().getTime()}.png`;
const saveResult = await mediaLibrary.saveImageToAlbum(pixelMap, savePath);
if (saveResult) {
promptAction.showToast({ message: `截图已保存到:${savePath}` });
// 4. 可选:调用分享API,直接分享截图(需引入分享模块)
// await shareImage(savePath);
} else {
promptAction.showToast({ message: '截图保存失败' });
}
} catch (err) {
console.error(`截图失败:${(err as BusinessError).message}`);
}
}
四、开发注意事项与优化方向
在实际开发中,还需要关注以下问题,确保应用体验流畅、稳定:
1. 性能优化
模型轻量化:中秋 3D 模型需控制面数(建议单个模型面数 < 10000),避免设备卡顿;
AR 帧率控制:通过arSession.setFrameRate(30)将 AR 帧速率设为 30fps,平衡性能与体验;
资源预加载:在应用启动时预加载 3D 模型,避免页面显示后加载导致的延迟。
2. 兼容性适配
设备支持:AR Engine 需设备支持 AR 功能(如具备陀螺仪、加速度计),可通过arSession.isSupported()判断设备兼容性;
屏幕适配:通过@ohos.device.screen获取屏幕尺寸,动态调整 3D 模型大小,避免在小屏设备上显示过大。
3. 用户体验优化
权限引导:当用户拒绝权限时,提供 "前往设置" 的跳转链接(通过abilityAccessCtrl.openPermissionSettings()实现);
加载提示:AR 初始化、模型加载时显示 "正在准备中秋 AR 场景..." 的加载动画,减少用户等待焦虑;
错误处理:当 AR Engine 初始化失败(如设备不支持),显示 "当前设备暂不支持 AR 功能,换个设备试试吧~" 的友好提示。
五、总结
ok,到这里就结束了。我们学习了如何结合鸿蒙 AR Engine 与 ArkUI 3D 能力,实现虚实融合的节日应用。从权限申请、AR 场景搭建,到 3D 模型叠加、互动功能实现,每一步都围绕 "技术赋能节日体验" 的核心,让传统中秋习俗与现代 AR 技术碰撞出有趣的火花。
后续我们还可以扩展更多功能,比如添加 "AR 中秋灯谜"(扫描特定图案触发灯谜)、"亲友 AR 同屏赏月"(通过分布式能力实现多设备同一场景互动)等,让应用更具趣味性和社交属性。最后祝大家中秋快乐,技术之路越走越宽!