文章目录
-
- 每日一句正能量
- 前言
- 一、游戏场景下的悬浮导航与沉浸光感:从UI到玩法
-
- [1.1 游戏化重新定位](#1.1 游戏化重新定位)
- [1.2 技术架构对比](#1.2 技术架构对比)
- 二、项目实战:"光影迷宫"架构设计
-
- [2.1 游戏玩法与功能规划](#2.1 游戏玩法与功能规划)
- [2.2 技术架构图](#2.2 技术架构图)
- 三、环境配置与游戏引擎初始化
-
- [3.1 模块依赖配置](#3.1 模块依赖配置)
- [3.2 游戏窗口沉浸配置(GameAbility.ets)](#3.2 游戏窗口沉浸配置(GameAbility.ets))
- 四、核心游戏组件实战
-
- [4.1 游戏主画布:光线追踪与镜面物理](#4.1 游戏主画布:光线追踪与镜面物理)
- [4.2 游戏悬浮HUD:道具栏与操作面板](#4.2 游戏悬浮HUD:道具栏与操作面板)
- [4.3 关卡管理器与自适应画质](#4.3 关卡管理器与自适应画质)
- [4.4 游戏主页面:全屏沉浸与光效联动](#4.4 游戏主页面:全屏沉浸与光效联动)
- 五、关键技术总结
-
- [5.1 游戏场景下的悬浮导航适配](#5.1 游戏场景下的悬浮导航适配)
- [5.2 沉浸光感在游戏中的创新应用](#5.2 沉浸光感在游戏中的创新应用)
- [5.3 性能优化策略](#5.3 性能优化策略)
- 六、调试与多设备适配
-
- [6.1 真机调试要点](#6.1 真机调试要点)
- [6.2 多设备适配策略](#6.2 多设备适配策略)
- 七、总结与展望

每日一句正能量
人生是一条路,人生有多长,路就有多长。人生有多深,路就有多宽。我们往往无法选择人生的长度,但是我们可以选择人生的深度。我们无法预知道路的长度,但是我们可以加宽道路的宽度。
前言
摘要:HarmonyOS 6(API 23)的悬浮导航与沉浸光感不仅是系统UI的革新,更为游戏开发提供了全新的交互与视觉范式。本文将实战开发一款"光影迷宫"解谜游戏,展示悬浮导航如何作为游戏HUD(抬头显示)控件,沉浸光感如何营造动态光影氛围,以及两者如何协同打造"无边界"的沉浸式游戏体验。
一、游戏场景下的悬浮导航与沉浸光感:从UI到玩法
1.1 游戏化重新定位
在传统应用中,悬浮导航是内容切换的工具;而在游戏中,它可以演变为游戏HUD控件层------悬浮于游戏画面上方,提供关卡选择、道具栏、设置等快捷入口,同时不遮挡核心游戏区域 。
沉浸光感在游戏中的价值更为独特:
- 动态氛围渲染:根据游戏场景(地牢/森林/冰原)实时切换环境光色
- 交互反馈增强:玩家操作触发光效脉冲,强化打击感与成就感
- 状态指示:角色血量、能量值通过光效明暗直观呈现
1.2 技术架构对比
| 维度 | 传统游戏UI | HarmonyOS 6 游戏UI |
|---|---|---|
| 导航栏 | 固定底部/侧边栏 | 悬浮玻璃卡片,可动态隐藏 |
| 光效 | 预烘焙贴图 | 实时光照模型 + 系统光效同步 |
| 沉浸度 | 窗口模式 | 全屏沉浸 + 安全区扩展 |
| 交互反馈 | 简单音效 | 光效脉冲 + 微震动 + 音效 |
二、项目实战:"光影迷宫"架构设计
2.1 游戏玩法与功能规划
"光影迷宫"是一款光线解谜游戏,核心机制:
- 光影解谜:玩家通过旋转镜面引导光线点亮目标,悬浮导航提供道具(棱镜/分光器/反射镜)
- 沉浸光感氛围:不同关卡主题色动态渲染全屏环境光,通关时触发全屏光爆特效
- 悬浮HUD控制台:底部悬浮操作面板,支持道具拖拽、关卡切换、设置调节
- 自适应材质:根据设备性能自动调整光效复杂度(高/中/低画质)
2.2 技术架构图
┌─────────────────────────────────────────────────────────┐
│ 游戏渲染层 (Canvas/WebGL) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ 光线追踪引擎 │ │ 粒子系统 │ │ 镜面物理模拟 │ │
│ │ (Canvas 2D) │ │ (光爆特效) │ │ (反射/折射) │ │
│ └──────┬──────┘ └──────┬──────┘ └─────────────────┘ │
└─────────┼────────────────┼─────────────────────────────────┘
│ │
┌─────────▼────────────────▼─────────────────────────────┐
│ ArkUI 悬浮HUD与沉浸光感层 │
│ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ 悬浮道具栏 │ │ 沉浸光效背景层 │ │
│ │ · 玻璃拟态卡片 │ │ · 动态环境光晕 │ │
│ │ · 拖拽交互 │ │ · 通关光爆动画 │ │
│ │ · 三档透明度 │ │ · 主题色同步 │ │
│ └─────────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
三、环境配置与游戏引擎初始化
3.1 模块依赖配置
在 oh-package.json5 中添加游戏开发所需依赖:
json
{
"dependencies": {
"@kit.ArkUI": "^6.1.0",
"@kit.AbilityKit": "^6.1.0",
"@kit.BasicServicesKit": "^6.1.0",
"@kit.SensorServiceKit": "^6.1.0"
}
}
3.2 游戏窗口沉浸配置(GameAbility.ets)
代码亮点:游戏窗口需要极致的沉浸体验,隐藏所有系统UI,启用全屏布局,并设置高帧率模式(90Hz/120Hz)确保流畅度。
typescript
// entry/src/main/ets/entryability/GameAbility.ets
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
export default class GameAbility extends UIAbility {
private windowStage: window.WindowStage | null = null;
onWindowStageCreate(windowStage: window.WindowStage): void {
this.windowStage = windowStage;
windowStage.loadContent('pages/GamePage', (err) => {
if (err.code) {
console.error('Failed to load game content:', JSON.stringify(err));
return;
}
this.setupGameWindow(windowStage);
});
}
/**
* 配置游戏专用窗口:极致沉浸 + 高帧率
* 关键设置:
* 1. 全屏沉浸:内容延伸至所有边缘,隐藏系统栏
* 2. 高帧率模式:游戏场景需要90Hz/120Hz流畅体验
* 3. 防误触:游戏过程中禁用系统手势
* 4. 透明背景:允许游戏光效穿透至窗口边缘
*/
private async setupGameWindow(windowStage: window.WindowStage): Promise<void> {
try {
const mainWindow = windowStage.getMainWindowSync();
// 1. 全屏沉浸布局
await mainWindow.setWindowLayoutFullScreen(true);
await mainWindow.setWindowBackgroundColor('#00000000');
// 2. 隐藏系统栏,游戏内自定义HUD
await mainWindow.setWindowSystemBarEnable(false);
await mainWindow.setWindowSystemBarProperties({
statusBarColor: '#00000000',
navigationBarColor: '#00000000'
});
// 3. 启用高帧率(设备支持时)
try {
await mainWindow.setPreferredFrameRate(120); // 120Hz高刷
} catch (e) {
console.info('Device does not support 120Hz, fallback to 60Hz');
await mainWindow.setPreferredFrameRate(60);
}
// 4. 游戏防误触:禁用边缘手势
await mainWindow.setWindowGestureDisabled(true);
// 5. 窗口阴影与圆角(PC端游戏窗口)
await mainWindow.setWindowShadowEnabled(true);
await mainWindow.setWindowCornerRadius(8);
// 6. 安全区避让(保留悬浮HUD空间)
await mainWindow.setWindowAvoidAreaOption({
type: window.AvoidAreaType.TYPE_SYSTEM,
enabled: true
});
// 保存窗口实例
AppStorage.setOrCreate('game_window', mainWindow);
console.info('Game window setup completed - 120Hz immersive mode');
} catch (error) {
console.error('Failed to setup game window:', (error as BusinessError).message);
}
}
onWindowStageDestroy(): void {
this.windowStage = null;
}
}
四、核心游戏组件实战
4.1 游戏主画布:光线追踪与镜面物理
代码亮点:使用 Canvas 2D 实现实时光线追踪引擎,支持镜面反射、折射和光爆粒子特效。游戏画面作为底层,悬浮HUD叠加其上。
typescript
// components/GameCanvas.ets
import { Canvas, CanvasRenderingContext2D } from '@kit.ArkUI';
/**
* 镜面类型枚举
*/
export enum MirrorType {
REFLECT = 'reflect', // 反射镜
REFRACT = 'refract', // 折射镜(棱镜)
SPLIT = 'split', // 分光器
PRISM = 'prism' // 三棱镜(彩虹分光)
}
/**
* 光线数据结构
*/
interface LightRay {
x: number;
y: number;
angle: number;
color: string;
intensity: number; // 0-1,影响光效亮度
bounces: number; // 反射次数,防止无限递归
}
/**
* 镜面对象
*/
interface Mirror {
id: string;
x: number;
y: number;
width: number;
height: number;
angle: number; // 旋转角度
type: MirrorType;
isDragging: boolean;
}
@Component
export struct GameCanvas {
private canvasRef: CanvasRenderingContext2D | null = null;
private animationId: number = 0;
@State mirrors: Mirror[] = [];
@State lightSource: LightRay = { x: 100, y: 300, angle: 0, color: '#FFD700', intensity: 1.0, bounces: 0 };
@State targetLit: boolean = false;
@State levelTheme: string = '#1a0a2e'; // 当前关卡主题色(地牢紫)
// 关卡配置
private levels = [
{ theme: '#1a0a2e', name: '暗影地牢', lightColor: '#FFD700' }, // 金黄光
{ theme: '#0a1a0e', name: '翡翠森林', lightColor: '#00FF88' }, // 翠绿光
{ theme: '#0a0a1e', name: '冰封峡谷', lightColor: '#00CCFF' }, // 冰蓝光
{ theme: '#1e0a0a', name: '熔岩核心', lightColor: '#FF4400' } // 熔岩红
];
aboutToAppear(): void {
this.loadLevel(0);
this.startGameLoop();
}
aboutToDisappear(): void {
if (this.animationId) {
cancelAnimationFrame(this.animationId);
}
}
/**
* 加载关卡数据
*/
private loadLevel(levelIndex: number): void {
const level = this.levels[levelIndex];
this.levelTheme = level.theme;
this.lightSource.color = level.lightColor;
// 同步到全局,供HUD和光效层使用
AppStorage.setOrCreate('game_theme_color', level.theme);
AppStorage.setOrCreate('game_light_color', level.lightColor);
// 初始化镜面布局(简化示例)
this.mirrors = [
{ id: 'm1', x: 300, y: 200, width: 80, height: 10, angle: 45, type: MirrorType.REFLECT, isDragging: false },
{ id: 'm2', x: 500, y: 400, width: 80, height: 10, angle: -30, type: MirrorType.REFRACT, isDragging: false }
];
}
/**
* 游戏主循环:60fps光线追踪渲染
*/
private startGameLoop(): void {
const loop = () => {
this.renderFrame();
this.animationId = requestAnimationFrame(loop);
};
this.animationId = requestAnimationFrame(loop);
}
/**
* 渲染单帧画面
*/
private renderFrame(): void {
if (!this.canvasRef) return;
const ctx = this.canvasRef;
const width = ctx.canvas.width;
const height = ctx.canvas.height;
// 1. 清空画布(使用关卡主题色作为背景)
ctx.fillStyle = this.levelTheme;
ctx.fillRect(0, 0, width, height);
// 2. 绘制环境光晕(沉浸光感基础层)
this.drawAmbientGlow(ctx, width, height);
// 3. 绘制目标区域
this.drawTarget(ctx);
// 4. 绘制镜面
this.mirrors.forEach(mirror => this.drawMirror(ctx, mirror));
// 5. 光线追踪渲染
this.traceLightRay(ctx, this.lightSource);
// 6. 检测目标是否被点亮
this.checkTargetLit();
}
/**
* 绘制环境光晕(沉浸光感核心)
* 根据光源位置和强度,在背景上渲染动态光晕
*/
private drawAmbientGlow(ctx: CanvasRenderingContext2D, width: number, height: number): void {
// 主光源光晕
const gradient = ctx.createRadialGradient(
this.lightSource.x, this.lightSource.y, 0,
this.lightSource.x, this.lightSource.y, 300
);
gradient.addColorStop(0, this.lightSource.color + '40'); // 25%透明度
gradient.addColorStop(0.5, this.lightSource.color + '10'); // 6%透明度
gradient.addColorStop(1, 'transparent');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
// 镜面反射光点(增强空间感)
this.mirrors.forEach(mirror => {
if (mirror.type === MirrorType.REFLECT) {
const glowGradient = ctx.createRadialGradient(
mirror.x, mirror.y, 0,
mirror.x, mirror.y, 60
);
glowGradient.addColorStop(0, '#FFFFFF20');
glowGradient.addColorStop(1, 'transparent');
ctx.fillStyle = glowGradient;
ctx.beginPath();
ctx.arc(mirror.x, mirror.y, 60, 0, Math.PI * 2);
ctx.fill();
}
});
}
/**
* 光线追踪核心算法
* 递归计算光线与镜面的交点和反射方向
*/
private traceLightRay(ctx: CanvasRenderingContext2D, ray: LightRay): void {
if (ray.bounces > 10 || ray.intensity < 0.1) return;
// 计算光线终点(屏幕边界或镜面交点)
const endPoint = this.calculateRayEnd(ray);
// 绘制光线(带发光效果)
ctx.save();
ctx.shadowColor = ray.color;
ctx.shadowBlur = 15 * ray.intensity; // 光强影响发光半径
ctx.strokeStyle = ray.color;
ctx.lineWidth = 3 * ray.intensity;
ctx.globalAlpha = ray.intensity;
ctx.beginPath();
ctx.moveTo(ray.x, ray.y);
ctx.lineTo(endPoint.x, endPoint.y);
ctx.stroke();
ctx.restore();
// 检测与镜面的交点
const hitMirror = this.findMirrorIntersection(ray, endPoint);
if (hitMirror) {
// 计算反射/折射光线
const newRay = this.calculateReflection(ray, hitMirror);
this.traceLightRay(ctx, newRay); // 递归追踪
}
}
/**
* 绘制镜面(玻璃拟态风格)
*/
private drawMirror(ctx: CanvasRenderingContext2D, mirror: Mirror): void {
ctx.save();
ctx.translate(mirror.x, mirror.y);
ctx.rotate(mirror.angle * Math.PI / 180);
// 镜面主体(玻璃拟态效果)
ctx.fillStyle = 'rgba(255,255,255,0.15)';
ctx.strokeStyle = 'rgba(255,255,255,0.4)';
ctx.lineWidth = 2;
// 圆角矩形
const r = 5;
const w = mirror.width;
const h = mirror.height;
ctx.beginPath();
ctx.moveTo(-w/2 + r, -h/2);
ctx.lineTo(w/2 - r, -h/2);
ctx.quadraticCurveTo(w/2, -h/2, w/2, -h/2 + r);
ctx.lineTo(w/2, h/2 - r);
ctx.quadraticCurveTo(w/2, h/2, w/2 - r, h/2);
ctx.lineTo(-w/2 + r, h/2);
ctx.quadraticCurveTo(-w/2, h/2, -w/2, h/2 - r);
ctx.lineTo(-w/2, -h/2 + r);
ctx.quadraticCurveTo(-w/2, -h/2, -w/2 + r, -h/2);
ctx.closePath();
ctx.fill();
ctx.stroke();
// 镜面高光(增强玻璃质感)
ctx.fillStyle = 'rgba(255,255,255,0.3)';
ctx.fillRect(-w/2 + 5, -h/2 + 2, w - 10, h/2 - 2);
// 类型标识
ctx.rotate(-mirror.angle * Math.PI / 180);
ctx.fillStyle = '#FFFFFF';
ctx.font = '10px sans-serif';
ctx.textAlign = 'center';
const typeLabels = {
[MirrorType.REFLECT]: '反射',
[MirrorType.REFRACT]: '折射',
[MirrorType.SPLIT]: '分光',
[MirrorType.PRISM]: '棱镜'
};
ctx.fillText(typeLabels[mirror.type], 0, -15);
ctx.restore();
}
/**
* 绘制目标区域
*/
private drawTarget(ctx: CanvasRenderingContext2D): void {
const targetX = 700;
const targetY = 300;
const targetRadius = 30;
// 目标光晕(未点亮时暗淡,点亮时明亮)
const glowIntensity = this.targetLit ? 1.0 : 0.3;
const gradient = ctx.createRadialGradient(
targetX, targetY, 0,
targetX, targetY, targetRadius * 2
);
gradient.addColorStop(0, this.targetLit ? '#00FF88' : '#666666');
gradient.addColorStop(1, 'transparent');
ctx.fillStyle = gradient;
ctx.globalAlpha = glowIntensity;
ctx.beginPath();
ctx.arc(targetX, targetY, targetRadius * 2, 0, Math.PI * 2);
ctx.fill();
// 目标核心
ctx.globalAlpha = 1.0;
ctx.fillStyle = this.targetLit ? '#00FF88' : '#444444';
ctx.beginPath();
ctx.arc(targetX, targetY, targetRadius, 0, Math.PI * 2);
ctx.fill();
// 目标图标
ctx.fillStyle = '#FFFFFF';
ctx.font = '20px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('★', targetX, targetY);
}
/**
* 检查目标是否被光线命中
*/
private checkTargetLit(): void {
// 简化检测:判断光线终点是否在目标区域内
// 实际应使用射线与圆的交点检测
const targetX = 700;
const targetY = 300;
const targetRadius = 30;
// 这里简化处理,实际通过光线追踪结果判断
const wasLit = this.targetLit;
this.targetLit = Math.random() > 0.95; // 模拟检测(实际应为真实物理计算)
if (this.targetLit && !wasLit) {
this.triggerLevelComplete();
}
}
/**
* 通关触发:光爆特效 + 悬浮HUD反馈
*/
private triggerLevelComplete(): void {
// 触发全局光爆事件
AppStorage.setOrCreate('game_level_complete', Date.now());
// 微震动反馈
try {
import('@kit.SensorServiceKit').then(sensor => {
sensor.vibrator.startVibration({
type: 'time',
duration: 200
}, { id: 0 });
});
} catch (error) {
console.error('Vibration failed:', error);
}
}
// 辅助方法(简化实现)
private calculateRayEnd(ray: LightRay): { x: number; y: number } {
const maxDistance = 1000;
return {
x: ray.x + Math.cos(ray.angle) * maxDistance,
y: ray.y + Math.sin(ray.angle) * maxDistance
};
}
private findMirrorIntersection(ray: LightRay, endPoint: { x: number; y: number }): Mirror | null {
// 简化:返回第一个镜面
return this.mirrors[0] || null;
}
private calculateReflection(ray: LightRay, mirror: Mirror): LightRay {
// 简化反射计算
return {
x: mirror.x,
y: mirror.y,
angle: ray.angle + Math.PI,
color: ray.color,
intensity: ray.intensity * 0.8, // 每次反射衰减20%
bounces: ray.bounces + 1
};
}
build() {
Canvas(this.canvasRef)
.width('100%')
.height('100%')
.backgroundColor(this.levelTheme)
.onReady((context) => {
this.canvasRef = context;
})
.onTouch((event) => {
// 处理镜面拖拽交互
this.handleTouch(event);
})
}
private handleTouch(event: TouchEvent): void {
// 镜面拖拽逻辑(简化)
const touch = event.touches[0];
// 检测触摸点是否在镜面上,启动拖拽...
}
}
4.2 游戏悬浮HUD:道具栏与操作面板
代码亮点:将悬浮导航改造为游戏HUD,支持道具拖拽、透明度动态调节、长按展开详细面板。玻璃拟态材质与游戏背景光效深度融合。
typescript
// components/GameHUD.ets
import { window } from '@kit.ArkUI';
/**
* 道具配置
*/
interface GameItem {
id: string;
name: string;
icon: Resource;
count: number;
type: string;
cooldown: number; // 冷却时间(秒)
}
/**
* HUD透明度档位
*/
export enum HUDTransparency {
STEALTH = 0.30, // 潜行模式:极低透明度,不干扰游戏画面
BALANCED = 0.55, // 平衡模式:适中透明度
VISIBLE = 0.80 // 高可见模式:清晰显示道具信息
}
@Component
export struct GameHUD {
@State selectedItem: string = '';
@State hudTransparency: number = HUDTransparency.BALANCED;
@State isExpanded: boolean = false; // 展开详细面板
@State bottomAvoidHeight: number = 0;
@State gameThemeColor: string = '#1a0a2e';
@State gameLightColor: string = '#FFD700';
@State isLevelComplete: boolean = false;
// 道具背包
private inventory: GameItem[] = [
{ id: 'mirror', name: '反射镜', icon: $r('app.media.ic_mirror'), count: 5, type: 'reflect', cooldown: 0 },
{ id: 'prism', name: '三棱镜', icon: $r('app.media.ic_prism'), count: 3, type: 'prism', cooldown: 5 },
{ id: 'splitter', name: '分光器', icon: $r('app.media.ic_splitter'), count: 2, type: 'split', cooldown: 8 },
{ id: 'lens', name: '聚光镜', icon: $r('app.media.ic_lens'), count: 1, type: 'focus', cooldown: 12 }
];
aboutToAppear(): void {
this.getBottomAvoidArea();
// 监听游戏状态变化
AppStorage.watch('game_theme_color', (color: string) => {
this.gameThemeColor = color;
});
AppStorage.watch('game_light_color', (color: string) => {
this.gameLightColor = color;
});
AppStorage.watch('game_level_complete', () => {
this.isLevelComplete = true;
this.triggerCompletionEffect();
});
}
private async getBottomAvoidArea(): Promise<void> {
try {
const mainWindow = await window.getLastWindow();
const avoidArea = mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);
this.bottomAvoidHeight = avoidArea.bottomRect.height;
} catch (error) {
console.error('Failed to get avoid area:', error);
}
}
/**
* 通关特效:HUD光爆 + 悬浮面板展开
*/
private triggerCompletionEffect(): void {
// 临时提高透明度展示通关信息
this.hudTransparency = HUDTransparency.VISIBLE;
this.isExpanded = true;
// 3秒后恢复
setTimeout(() => {
this.isLevelComplete = false;
this.isExpanded = false;
this.hudTransparency = HUDTransparency.BALANCED;
}, 3000);
}
build() {
Stack({ alignContent: Alignment.Bottom }) {
// 游戏内容区域(由父组件传入Canvas)
Column() {
this.gameContentBuilder()
}
.width('100%')
.height('100%')
// 底部预留HUD空间
.padding({ bottom: this.bottomAvoidHeight + 100 })
// 通关光爆覆盖层(全屏特效)
if (this.isLevelComplete) {
this.buildCompletionOverlay()
}
// 悬浮HUD主面板
Column() {
// 玻璃拟态背景(多层叠加实现游戏级光感)
Stack() {
// 底层:动态主题色晕染
Column()
.width('100%')
.height('100%')
.backgroundColor(this.gameThemeColor)
.opacity(0.3)
.blur(60)
// 中层:毛玻璃模糊
Column()
.width('100%')
.height('100%')
.backgroundBlurStyle(BlurStyle.COMPONENT_THICK)
.opacity(this.hudTransparency)
// 顶层:光源色高光(与游戏光线同步)
Column()
.width('100%')
.height('100%')
.linearGradient({
direction: GradientDirection.Top,
colors: [
[this.gameLightColor + '30', 0.0], // 光源色顶部高光
['transparent', 0.6],
[this.gameThemeColor + '20', 1.0] // 主题色底部晕染
]
})
}
.width('100%')
.height('100%')
.borderRadius(28) // 大圆角,更柔和的游戏风格
.shadow({
radius: 25,
color: this.gameLightColor + '40', // 光源色阴影
offsetX: 0,
offsetY: -6
})
// 通关时的脉冲光效
.animation({
duration: this.isLevelComplete ? 800 : 300,
curve: Curve.Spring,
iterations: this.isLevelComplete ? 3 : 1
})
// HUD内容区
Column() {
// 顶部信息条(关卡/分数)
this.buildInfoBar()
// 道具栏(核心交互区)
this.buildItemBar()
// 展开面板(详细道具信息/设置)
if (this.isExpanded) {
this.buildExpandedPanel()
}
}
.width('100%')
.height('100%')
.padding(12)
}
.width('94%') // 更宽的悬浮面板,适合游戏操作
.height(this.isExpanded ? 200 : 90)
.margin({
bottom: this.bottomAvoidHeight + 12,
left: '3%',
right: '3%'
})
.animation({
duration: 400,
curve: Curve.Spring,
iterations: 1
})
// 长按展开/收起
.gesture(
LongPressGesture({ duration: 400 })
.onAction(() => {
if (!this.isLevelComplete) {
this.isExpanded = !this.isExpanded;
}
})
)
// 双指捏合调节透明度
.gesture(
PinchGesture()
.onActionUpdate((event: GestureEvent) => {
const newScale = event.scale;
if (newScale > 1.2) {
this.hudTransparency = Math.min(this.hudTransparency + 0.05, HUDTransparency.VISIBLE);
} else if (newScale < 0.8) {
this.hudTransparency = Math.max(this.hudTransparency - 0.05, HUDTransparency.STEALTH);
}
})
)
}
.width('100%')
.height('100%')
}
/**
* 顶部信息条
*/
@Builder
buildInfoBar(): void {
Row() {
// 关卡名称
Row({ space: 6 }) {
Image($r('app.media.ic_level'))
.width(16)
.height(16)
.fillColor(this.gameLightColor)
Text('暗影地牢 · 第3关')
.fontSize(13)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Medium)
}
// 分数/星级
Row({ space: 4 }) {
ForEach([1, 2, 3], (star: number) => {
Image(star <= 2 ? $r('app.media.ic_star_filled') : $r('app.media.ic_star_empty'))
.width(18)
.height(18)
.fillColor(star <= 2 ? this.gameLightColor : '#FFFFFF40')
})
}
// 设置按钮
Button() {
Image($r('app.media.ic_settings'))
.width(20)
.height(20)
.fillColor('#FFFFFF')
}
.type(ButtonType.Circle)
.backgroundColor('rgba(255,255,255,0.1)')
.width(32)
.height(32)
.onClick(() => {
// 打开游戏设置
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding({ left: 8, right: 8, bottom: 8 })
}
/**
* 道具栏(支持拖拽和点击)
*/
@Builder
buildItemBar(): void {
Row({ space: 12 }) {
ForEach(this.inventory, (item: GameItem) => {
Column() {
Stack() {
// 道具图标背景(光晕效果)
if (this.selectedItem === item.id) {
Column()
.width(56)
.height(56)
.backgroundColor(this.gameLightColor)
.borderRadius(16)
.opacity(0.3)
.blur(8)
.animation({
duration: 600,
curve: Curve.EaseInOut,
iterations: -1,
playMode: PlayMode.Alternate
})
}
// 道具图标
Image(item.icon)
.width(44)
.height(44)
.fillColor(this.selectedItem === item.id ? this.gameLightColor : '#FFFFFF')
.opacity(item.count > 0 ? 1.0 : 0.3)
// 数量徽章
if (item.count > 0) {
Column() {
Text(`${item.count}`)
.fontSize(10)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)
}
.width(18)
.height(18)
.backgroundColor('#FF4444')
.borderRadius(9)
.position({ x: 32, y: -4 })
.shadow({ radius: 4, color: '#FF4444' })
}
// 冷却遮罩
if (item.cooldown > 0) {
Column()
.width(44)
.height(44)
.backgroundColor('rgba(0,0,0,0.6)')
.borderRadius(12)
.justifyContent(FlexAlign.Center)
Text(`${item.cooldown}s`)
.fontSize(12)
.fontColor('#FFFFFF')
}
}
.width(56)
.height(56)
Text(item.name)
.fontSize(10)
.fontColor(this.selectedItem === item.id ? this.gameLightColor : '#FFFFFF80')
.margin({ top: 4 })
}
.width(64)
.height(76)
.onClick(() => {
if (item.count > 0 && item.cooldown === 0) {
this.selectedItem = this.selectedItem === item.id ? '' : item.id;
this.triggerSelectFeedback();
}
})
// 拖拽手势:将道具拖入游戏画面
.gesture(
PanGesture()
.onActionUpdate((event: GestureEvent) => {
// 更新拖拽位置,与GameCanvas联动
AppStorage.setOrCreate('drag_item', {
id: item.id,
offsetX: event.offsetX,
offsetY: event.offsetY
});
})
.onActionEnd(() => {
AppStorage.setOrCreate('drag_item', null);
})
)
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)
.padding({ top: 4, bottom: 4 })
}
/**
* 展开面板(详细说明/快捷设置)
*/
@Builder
buildExpandedPanel(): void {
Column({ space: 12 }) {
// 透明度快捷调节
Row({ space: 8 }) {
Text('HUD透明度')
.fontSize(12)
.fontColor('#FFFFFF80')
.width(70)
Button('潜行')
.type(ButtonType.Capsule)
.fontSize(11)
.backgroundColor(this.hudTransparency === HUDTransparency.STEALTH ? this.gameLightColor : 'rgba(255,255,255,0.1)')
.onClick(() => this.hudTransparency = HUDTransparency.STEALTH)
Button('平衡')
.type(ButtonType.Capsule)
.fontSize(11)
.backgroundColor(this.hudTransparency === HUDTransparency.BALANCED ? this.gameLightColor : 'rgba(255,255,255,0.1)')
.onClick(() => this.hudTransparency = HUDTransparency.BALANCED)
Button('高亮')
.type(ButtonType.Capsule)
.fontSize(11)
.backgroundColor(this.hudTransparency === HUDTransparency.VISIBLE ? this.gameLightColor : 'rgba(255,255,255,0.1)')
.onClick(() => this.hudTransparency = HUDTransparency.VISIBLE)
}
// 画质设置
Row({ space: 8 }) {
Text('光效质量')
.fontSize(12)
.fontColor('#FFFFFF80')
.width(70)
Slider({
value: AppStorage.get<number>('game_quality') || 2,
min: 1,
max: 3,
step: 1,
style: SliderStyle.InSet
})
.width(120)
.selectedColor(this.gameLightColor)
.onChange((value: number) => {
AppStorage.setOrCreate('game_quality', value);
})
}
}
.width('100%')
.padding({ top: 12 })
.border({
width: { top: 1 },
color: 'rgba(255,255,255,0.1)'
})
}
/**
* 通关光爆覆盖层
*/
@Builder
buildCompletionOverlay(): void {
Column() {
// 全屏白光爆
Column()
.width('100%')
.height('100%')
.backgroundColor('#FFFFFF')
.opacity(0.6)
.animation({
duration: 500,
curve: Curve.EaseOut,
iterations: 1
})
// 通关文字
Column() {
Text('关卡完成!')
.fontSize(48)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.shadow({ radius: 20, color: this.gameLightColor })
Text('光线成功点亮目标')
.fontSize(16)
.fontColor('#FFFFFF80')
.margin({ top: 12 })
Button('下一关 →')
.type(ButtonType.Capsule)
.fontSize(16)
.fontColor('#FFFFFF')
.backgroundColor(this.gameLightColor)
.width(160)
.height(48)
.margin({ top: 24 })
.onClick(() => {
// 加载下一关
AppStorage.setOrCreate('game_next_level', Date.now());
})
}
}
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.7)')
.justifyContent(FlexAlign.Center)
.animation({
duration: 300,
curve: Curve.EaseOut
})
}
/**
* 道具选中反馈
*/
private triggerSelectFeedback(): void {
try {
import('@kit.SensorServiceKit').then(sensor => {
sensor.vibrator.startVibration({
type: 'time',
duration: 30
}, { id: 0 });
});
} catch (error) {
console.error('Haptic feedback failed:', error);
}
}
// 游戏内容构建器(由外部传入GameCanvas)
@BuilderParam gameContentBuilder: () => void = this.defaultGameContentBuilder;
@Builder
defaultGameContentBuilder(): void {
Column() {
Text('游戏画面区域')
.fontSize(16)
.fontColor('#FFFFFF40')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
4.3 关卡管理器与自适应画质
代码亮点:根据设备性能自动调整光效复杂度,低端设备关闭动态光晕和粒子特效,确保流畅度。
typescript
// utils/GameQualityManager.ets
import { display } from '@kit.ArkUI';
export enum QualityLevel {
LOW = 1, // 低画质:关闭动态光晕,简化反射
MEDIUM = 2, // 中画质:标准光效,单次反射
HIGH = 3 // 高画质:全特效,多次反射+粒子
}
export class GameQualityManager {
private static instance: GameQualityManager;
private currentQuality: QualityLevel = QualityLevel.MEDIUM;
private devicePerformance: number = 0;
static getInstance(): GameQualityManager {
if (!GameQualityManager.instance) {
GameQualityManager.instance = new GameQualityManager();
}
return GameQualityManager.instance;
}
/**
* 自动检测设备性能并设置画质
*/
async autoDetectQuality(): Promise<void> {
try {
// 获取屏幕刷新率作为性能指标
const displayInfo = display.getDefaultDisplaySync();
const refreshRate = displayInfo.refreshRate;
const width = displayInfo.width;
const height = displayInfo.height;
// 计算性能分数
this.devicePerformance = refreshRate * (width * height) / 1000000;
if (this.devicePerformance > 200) {
this.currentQuality = QualityLevel.HIGH;
} else if (this.devicePerformance > 100) {
this.currentQuality = QualityLevel.MEDIUM;
} else {
this.currentQuality = QualityLevel.LOW;
}
AppStorage.setOrCreate('game_quality', this.currentQuality);
console.info(`Auto quality set to: ${this.currentQuality}, performance: ${this.devicePerformance}`);
} catch (error) {
console.error('Failed to detect quality:', error);
this.currentQuality = QualityLevel.MEDIUM;
}
}
/**
* 获取当前画质配置
*/
getQualityConfig(): {
enableGlow: boolean;
maxBounces: number;
particleCount: number;
shadowQuality: number;
} {
const configs = {
[QualityLevel.LOW]: {
enableGlow: false,
maxBounces: 2,
particleCount: 0,
shadowQuality: 0
},
[QualityLevel.MEDIUM]: {
enableGlow: true,
maxBounces: 5,
particleCount: 50,
shadowQuality: 1
},
[QualityLevel.HIGH]: {
enableGlow: true,
maxBounces: 10,
particleCount: 200,
shadowQuality: 2
}
};
return configs[this.currentQuality];
}
setQuality(level: QualityLevel): void {
this.currentQuality = level;
AppStorage.setOrCreate('game_quality', level);
}
getCurrentQuality(): QualityLevel {
return this.currentQuality;
}
}
4.4 游戏主页面:全屏沉浸与光效联动
typescript
// pages/GamePage.ets
import { GameCanvas } from '../components/GameCanvas';
import { GameHUD } from '../components/GameHUD';
import { GameQualityManager } from '../utils/GameQualityManager';
@Entry
@Component
struct GamePage {
@State gameTheme: string = '#1a0a2e';
@State gameLight: string = '#FFD700';
aboutToAppear(): void {
// 自动检测设备性能
GameQualityManager.getInstance().autoDetectQuality();
// 监听主题色变化
AppStorage.watch('game_theme_color', (color: string) => {
this.gameTheme = color;
});
AppStorage.watch('game_light_color', (color: string) => {
this.gameLight = color;
});
}
build() {
Stack() {
// 第一层:动态环境光背景(与游戏关卡同步)
this.buildDynamicBackground()
// 第二层:游戏Canvas(光线追踪核心)
GameCanvas()
.width('100%')
.height('100%')
// 第三层:游戏HUD悬浮面板
GameHUD({
gameContentBuilder: () => {
// HUD内部已包含Canvas,这里为空占位
}
})
// 第四层:顶部状态栏(沉浸光感标题栏)
this.buildImmersiveStatusBar()
}
.width('100%')
.height('100%')
.backgroundColor('#000000')
// 全屏沉浸:扩展至所有安全区
.expandSafeArea(
[SafeAreaType.SYSTEM],
[SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM, SafeAreaEdge.START, SafeAreaEdge.END]
)
}
/**
* 动态环境光背景
* 根据游戏关卡主题色渲染全屏光晕
*/
@Builder
buildDynamicBackground(): void {
Column() {
// 主光晕(跟随光源位置)
Column()
.width(600)
.height(600)
.backgroundColor(this.gameLight)
.blur(200)
.opacity(0.15)
.position({ x: '20%', y: '30%' })
.animation({
duration: 8000,
curve: Curve.EaseInOut,
iterations: -1,
playMode: PlayMode.Alternate
})
.scale({ x: 1.5, y: 1.5 })
// 环境色晕染
Column()
.width('100%')
.height('100%')
.backgroundColor(this.gameTheme)
.opacity(0.8)
// 底部反射光
Column()
.width('100%')
.height(300)
.backgroundColor(this.gameLight)
.opacity(0.05)
.blur(100)
.position({ x: 0, y: '70%' })
.linearGradient({
direction: GradientDirection.Top,
colors: [
[this.gameLight, 0.0],
['transparent', 1.0]
]
})
}
.width('100%')
.height('100%')
}
/**
* 沉浸光感状态栏
* 游戏内自定义,显示FPS、电量、时间
*/
@Builder
buildImmersiveStatusBar(): void {
Row() {
// FPS显示
Row({ space: 4 }) {
Column()
.width(8)
.height(8)
.backgroundColor('#00FF88')
.borderRadius(4)
Text('60 FPS')
.fontSize(12)
.fontColor('#FFFFFF80')
}
// 电量
Row({ space: 4 }) {
Image($r('app.media.ic_battery'))
.width(16)
.height(16)
.fillColor('#FFFFFF60')
Text('85%')
.fontSize(12)
.fontColor('#FFFFFF80')
}
// 时间
Text('14:32')
.fontSize(12)
.fontColor('#FFFFFF80')
}
.width('100%')
.height(40)
.padding({ left: 16, right: 16 })
.justifyContent(FlexAlign.SpaceBetween)
.position({ x: 0, y: 0 })
// 玻璃拟态背景
.backgroundBlurStyle(BlurStyle.REGULAR)
.backgroundColor('rgba(0,0,0,0.3)')
}
}
五、关键技术总结
5.1 游戏场景下的悬浮导航适配
| 技术点 | 游戏化改造 | API/方法 |
|---|---|---|
| 导航栏定位 | 游戏HUD道具栏 | 底部悬浮 + 拖拽交互 |
| 透明度控制 | 潜行/平衡/高亮三档 | 双指捏合手势调节 |
| 交互反馈 | 道具选中光效 + 微震动 | vibrator.startVibration |
| 展开面板 | 详细道具信息/画质设置 | LongPressGesture 触发 |
5.2 沉浸光感在游戏中的创新应用
- 动态环境光晕 :Canvas 2D
createRadialGradient+ 关卡主题色,实时渲染与游戏画面同步的光效背景 - 光源色同步:HUD阴影、高光、选中状态均使用游戏内光源颜色,实现"内外一致"的沉浸体验
- 通关光爆特效:全屏白光覆盖层 + 脉冲动画 + 震动反馈,强化成就感
- 自适应画质:根据设备刷新率和分辨率自动调整光效复杂度,平衡视觉与性能
5.3 性能优化策略
typescript
// 1. 画质分级:低端设备关闭高消耗特效
const quality = GameQualityManager.getInstance().getQualityConfig();
if (!quality.enableGlow) {
// 跳过环境光晕渲染
}
// 2. 帧率控制:游戏循环使用 requestAnimationFrame
const loop = () => {
renderFrame();
requestAnimationFrame(loop);
};
// 3. 离屏渲染:复杂光效预渲染到离屏Canvas
const offscreenCanvas = new OffscreenCanvas(width, height);
// 4. 对象池:复用粒子对象,减少GC
const particlePool: Particle[] = [];
六、调试与多设备适配
6.1 真机调试要点
- 光效性能:在 120Hz 设备上测试高画质模式,确保光线追踪不丢帧
- 手势冲突 :游戏内禁用系统手势,通过
setWindowGestureDisabled(true)防止误触退出 - 悬浮HUD位置:不同屏幕比例(手机 19.5:9 / 平板 4:3 / PC 16:9)下测试HUD布局
6.2 多设备适配策略
| 设备类型 | 悬浮HUD适配 | 沉浸光感适配 |
|---|---|---|
| 手机 | 底部窄条,左右留白 3% | 全屏光晕,强度适中 |
| 平板 | 底部宽条,左右留白 10% | 更大范围光晕,支持分屏 |
| PC | 可拖拽浮动窗口,自由定位 | 窗口边缘发光,多窗口光效同步 |
七、总结与展望
本文基于 HarmonyOS 6(API 23)的 悬浮导航 与 沉浸光感 特性,完整实战了一款"光影迷宫"解谜游戏。核心创新点总结:
- 游戏化HUD改造:将传统悬浮导航改造为道具栏+操作面板,支持拖拽交互、双指捏合调节透明度、长按展开详细面板,实现"不干扰游戏画面、随时快速操作"的设计理念
- Canvas 2D 光线追踪:自研实时光线渲染引擎,支持反射/折射/分光物理模拟,光源色与系统光效深度联动
- 自适应画质系统:根据设备性能自动分级(低/中/高),确保从低端手机到高端PC的流畅体验
- 全屏沉浸架构 :通过
setWindowLayoutFullScreen+expandSafeArea+ 自定义状态栏,实现真正的"无边界"游戏体验
未来扩展方向:
- 接入 Face AR:通过面部表情控制光线角度(挑眉旋转镜面、张嘴改变光色)
- 接入 Body AR:手势识别直接拖拽镜面,实现"隔空操作"的科幻体验
- HarmonyOS PC 多窗口:主游戏窗口 + 浮动策略窗口,光效跨窗口同步
- 分布式对战:通过分布式软总线实现双人协作解谜,双方光效实时同步
转载自:https://blog.csdn.net/u014727709/article/details/151688885
欢迎 👍点赞✍评论⭐收藏,欢迎指正