HarmonyOS 6(API 23)实战:“智灵幻境“——基于悬浮导航与沉浸光感的AI NPC智能体游戏开发
文章目录

每日一句正能量
“没有哪一段经历是白费的,也没有哪一次跌倒是毫无意义的。”
看似走错的路、摔倒的跤,都在暗中塑造你的肌肉记忆和判断力。就像酿酒,坏葡萄也可能变成醋——但醋也有醋的用处。只要活着,你走过的每一步都在构成你。
前言
摘要:2026年,AI NPC智能体正从"脚本驱动"向"自主意识"进化。HarmonyOS 6(API 23)引入的鸿蒙智能体框架(HMAF)与端侧AI推理能力,为游戏开发者提供了构建"有灵魂NPC"的全新可能。本文将实战开发一款名为"智灵幻境"的开放世界角色扮演游戏,展示如何利用HMAF构建具备自主目标、持久记忆和动态对话的AI NPC智能体,通过悬浮导航实现游戏内多角色快速切换,基于沉浸光感打造"情绪即光效"的NPC交互体验,以及结合分布式软总线实现跨设备NPC托管与协作。
一、前言:AI NPC智能体的范式跃迁
传统游戏中的NPC(非玩家角色)长期受限于脚本驱动的行为模式——固定的对话树、预设的行动路径、机械的状态切换。玩家与NPC的互动往往停留在"点击对话→选择选项→获得奖励"的线性流程中,缺乏真正的沉浸感和情感连接。
2026年,随着大语言模型和智能体技术的成熟,AI NPC正经历从L1(模型阶段)到L3(类人智能体阶段)的跃迁。根据行业分析,AI NPC智能体具备四大核心能力:自主行为(独立目标与决策)、动态对话(实时生成对话)、持久记忆(跨会话关系积累)和环境感知(响应游戏世界变化)。
HarmonyOS 6(API 23)的HMAF框架不仅支持LLM模式、工作流模式、A2A模式和OpenClaw模式四种智能体编排方式,更关键的是将端侧AI推理能力与游戏引擎深度集成。配合悬浮导航与沉浸光感特性,游戏开发者可以构建"情绪即光效"的NPC交互系统——NPC的喜怒哀乐不再只是对话框里的文字,而是整个游戏世界的光效变化。
本文核心亮点:
- 情绪感知光效系统:NPC的情绪状态(平静/好奇/友好/愤怒/恐惧/兴奋)实时映射为环境光色与脉冲节奏
- HMAF NPC智能体引擎:基于Agent Framework Kit构建具备自主目标、持久记忆和动态对话的AI NPC
- 悬浮角色导航:底部悬浮页签实现多NPC快速切换与组队管理,支持PC端鼠标悬停预览
- 分布式NPC托管:利用HarmonyOS分布式软总线,实现手机端控制+PC端渲染的跨设备游戏体验
- 端侧AI推理:基于MindSpore Lite实现NPC对话的端侧实时生成,无需联网即可交互

二、核心特性解析与技术选型
2.1 沉浸光感在游戏NPC中的价值
HarmonyOS 6的systemMaterialEffect通过模拟物理光照模型,为UI组件带来细腻的光晕与反射效果。在AI NPC游戏场景中,这种材质效果能够:
- 情绪可视化:NPC的情绪状态直接映射为环境光色,玩家无需查看UI即可感知NPC心情
- 氛围营造:玻璃拟态的半透明层让背景光效柔和过渡,营造"与有灵魂的NPC对话"的沉浸感
- 状态即时反馈:NPC运行时光效脉冲变化,对话中时呼吸光、战斗时脉冲光、死亡时渐隐光
2.2 悬浮导航的游戏适配
与传统应用不同,开放世界RPG游戏需要处理:
- 多角色快速切换:玩家常在主角、队友NPC、召唤物间快速切换
- 信息密度平衡:既要保证导航可见,又不能遮挡游戏画面
- PC端操作优化:支持鼠标悬停预览NPC状态、右键打开交互菜单
HarmonyOS 6的悬浮页签支持**强(85%)、平衡(70%)、弱(55%)**三档透明度自定义,结合PC端的自由窗口能力,可以实现"需要时出现,专注时隐退"的智能导航体验。
2.3 技术架构选型
| 技术模块 | 选用方案 | 说明 |
|---|---|---|
| 智能体框架 | HMAF (HarmonyOS Multi-Agent Framework) | 系统级NPC智能体能力 |
| 端侧AI推理 | MindSpore Lite | NPC对话端侧实时生成 |
| 意图理解 | Intents Kit | 玩家语音/手势意图解析 |
| 分布式能力 | Distributed Service Kit | 跨设备NPC托管 |
| 渲染引擎 | Canvas + 3D Scene | 游戏场景渲染 |
| 状态管理 | AppStorage | 跨组件/跨窗口状态同步 |
| 光效系统 | SystemMaterialEffect + 自定义动画 | 情绪光效实现 |

三、项目实战:"智灵幻境"架构设计
3.1 应用场景与功能规划
面向HarmonyOS PC/手机的开放世界RPG游戏,核心功能包括:
| 功能模块 | 技术实现 | 沉浸光感/HMAF应用 |
|---|---|---|
| 游戏主场景 | Canvas + 3D Scene |
环境光随NPC情绪变化 |
| NPC智能体 | HMAF LLM Mode | 自主对话与决策 |
| 悬浮角色导航 | HdsTabs + systemMaterialEffect |
玻璃拟态角色切换页签 |
| NPC情绪面板 | Form + HdsInput |
选中NPC主题色同步 |
| 对话系统 | MindSpore Lite + HMAF | 端侧实时对话生成 |
| 分布式托管 | Distributed Service Kit | 手机控制+PC渲染 |
| 战斗系统 | HMAF Workflow Mode | 战斗流程编排 |
| 任务系统 | HMAF A2A Mode | 多NPC协作任务 |
3.2 技术架构图

┌─────────────────────────────────────────────────────────────┐
│ 智灵幻境 - AI NPC 智能体游戏 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 沉浸光感层 │ │ 游戏场景层 │ │ 悬浮导航层 │ │
│ │ (Ambient) │ │ (GameScene)│ │ (FloatNav) │ │
│ │ 情绪环境光 │ │ NPC渲染 │ │ 角色切换 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ HMAF NPC引擎 │ │ 端侧AI推理 │ │ 分布式托管 │ │
│ │ (LLM/Work) │ │ (MindSpore) │ │ (Distributed)│ │
│ │ 智能体决策 │ │ 对话生成 │ │ 跨设备协同 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 玩家交互层 (语音/手势/触控) + Intents Kit 意图理解 │
└─────────────────────────────────────────────────────────────┘
四、环境配置与模块依赖
4.1 模块依赖配置
// entry/oh-package.json5
{
"dependencies": {
"@kit.UIDesignKit": "^1.0.0",
"@kit.ArkUI": "^1.0.0",
"@kit.AgentFrameworkKit": "^1.0.0",
"@kit.IntentsKit": "^1.0.0",
"@kit.DistributedServiceKit": "^1.0.0",
"@kit.MindSporeLiteKit": "^1.0.0"
}
}
4.2 权限声明(module.json5)
{
"module": {
"name": "entry",
"type": "entry",
"description": "智灵幻境 - AI NPC智能体游戏",
"mainElement": "GameAbility",
"abilities": [
{
"name": "GameAbility",
"srcEntry": "./ets/gameability/GameAbility.ets",
"description": "游戏主窗口",
"icon": "$media:icon",
"label": "$string:GameAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"windowMode": "fullscreen",
"supportWindowMode": ["fullscreen", "split", "float"]
}
],
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
},
{
"name": "ohos.permission.MICROPHONE"
}
]
}
}
五、核心组件实战
5.1 窗口沉浸配置(GameAbility.ets)
// entry/src/main/ets/gameability/GameAbility.ets
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class GameAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/GamePage', (err) => {
if (err.code) {
console.error('Failed to load content:', JSON.stringify(err));
return;
}
console.info('Succeeded in loading content.');
let mainWindow = windowStage.getMainWindowSync();
this.configureImmersiveGameWindow(mainWindow);
});
}
private configureImmersiveGameWindow(mainWindow: window.Window): void {
// 设置窗口全屏布局,移除系统标题栏
mainWindow.setWindowLayoutFullScreen(true);
// 设置窗口背景为透明,让沉浸光效穿透
mainWindow.setWindowBackgroundColor('#00000000');
// 设置窗口亮度跟随内容
mainWindow.setWindowKeepScreenOn(true);
// 监听窗口焦点变化
mainWindow.on('windowFocusChange', (isFocused: boolean) => {
AppStorage.setOrCreate('game_focused', isFocused);
});
// 初始化分布式能力
this.initDistributedService();
}
private async initDistributedService(): Promise<void> {
// 注册分布式设备发现
let distributedManager = distributedService.createManager();
await distributedManager.init({
deviceType: ['phone', 'tablet', 'pc'],
serviceName: 'zhiling_game_service'
});
// 监听设备上线
distributedManager.on('deviceOnline', (deviceInfo) => {
console.info(`Device online: ${deviceInfo.deviceName}`);
AppStorage.setOrCreate('distributed_device', deviceInfo);
});
}
}
代码亮点:
setWindowLayoutFullScreen(true):移除系统标题栏,实现真正的无边框游戏沉浸体验setWindowBackgroundColor('#00000000'):透明背景让底层光效层完全可见- 分布式设备发现:自动发现同账号下的手机、平板、PC设备,为跨设备NPC托管做准备
5.2 情绪感知光效系统(EmotionLightSystem.ets)
// entry/src/main/ets/systems/EmotionLightSystem.ets
export enum NPCState {
IDLE = 'idle',
PATROL = 'patrol',
INTERACT = 'interact',
COMBAT = 'combat',
FLEE = 'flee',
TRADE = 'trade'
}
export enum Emotion {
CALM = 'calm',
CURIOUS = 'curious',
FRIENDLY = 'friendly',
ANGRY = 'angry',
FEARFUL = 'fearful',
EXCITED = 'excited'
}
export interface LightEffectConfig {
color: string;
intensity: number;
pulseSpeed: number;
blurRadius: number;
ambientColor: string;
}
@Component
export struct EmotionLightSystem {
@State currentEmotion: Emotion = Emotion.CALM;
@State lightIntensity: number = 0.6;
@State pulsePhase: number = 0;
// 情绪-光效映射表
private emotionLightMap: Map<Emotion, LightEffectConfig> = new Map([
[Emotion.CALM, {
color: '#4CC9F0',
intensity: 0.4,
pulseSpeed: 3000,
blurRadius: 100,
ambientColor: 'rgba(76, 201, 240, 0.1)'
}],
[Emotion.CURIOUS, {
color: '#00C9A7',
intensity: 0.5,
pulseSpeed: 2500,
blurRadius: 120,
ambientColor: 'rgba(0, 201, 167, 0.15)'
}],
[Emotion.FRIENDLY, {
color: '#FF9F1C',
intensity: 0.6,
pulseSpeed: 2000,
blurRadius: 150,
ambientColor: 'rgba(255, 159, 28, 0.2)'
}],
[Emotion.ANGRY, {
color: '#FF1744',
intensity: 0.9,
pulseSpeed: 800,
blurRadius: 200,
ambientColor: 'rgba(255, 23, 68, 0.3)'
}],
[Emotion.FEARFUL, {
color: '#7B61FF',
intensity: 0.7,
pulseSpeed: 1500,
blurRadius: 180,
ambientColor: 'rgba(123, 97, 255, 0.25)'
}],
[Emotion.EXCITED, {
color: '#FFD700',
intensity: 0.8,
pulseSpeed: 1000,
blurRadius: 160,
ambientColor: 'rgba(255, 215, 0, 0.25)'
}]
]);
aboutToAppear(): void {
// 监听NPC情绪变化
AppStorage.watch('npc_emotion', (emotion: Emotion) => {
this.currentEmotion = emotion;
this.triggerEmotionTransition(emotion);
});
}
private triggerEmotionTransition(emotion: Emotion): void {
const config = this.emotionLightMap.get(emotion);
if (!config) return;
// 触发全局光效变化
AppStorage.setOrCreate('ambient_color', config.ambientColor);
AppStorage.setOrCreate('ambient_intensity', config.intensity);
// 触发NPC光效变化
AppStorage.setOrCreate('npc_glow_color', config.color);
AppStorage.setOrCreate('npc_glow_intensity', config.intensity);
AppStorage.setOrCreate('npc_pulse_speed', config.pulseSpeed);
}
build() {
Stack() {
// 底层环境光
this.buildAmbientLight()
// 中层NPC光晕
this.buildNPCGlow()
// 顶层脉冲光效
this.buildPulseEffect()
}
.width('100%')
.height('100%')
.pointerEvents(PointerEvents.None)
}
@Builder
buildAmbientLight(): void {
Column()
.width('100%')
.height('100%')
.backgroundColor(this.emotionLightMap.get(this.currentEmotion)?.ambientColor || 'transparent')
.animation({
duration: 1000,
curve: Curve.EaseInOut
})
}
@Builder
buildNPCGlow(): void {
Column()
.width('100%')
.height('100%')
.backgroundColor(this.emotionLightMap.get(this.currentEmotion)?.color || '#FFFFFF')
.blur(this.emotionLightMap.get(this.currentEmotion)?.blurRadius || 100)
.opacity(this.lightIntensity)
.animation({
duration: 800,
curve: Curve.EaseInOut
})
}
@Builder
buildPulseEffect(): void {
Column()
.width('100%')
.height('100%')
.backgroundColor(this.emotionLightMap.get(this.currentEmotion)?.color || '#FFFFFF')
.opacity(this.lightIntensity * 0.3)
.animation({
duration: this.emotionLightMap.get(this.currentEmotion)?.pulseSpeed || 2000,
curve: Curve.EaseInOut,
iterations: -1,
playMode: PlayMode.Alternate
})
.scale({ x: 1.2, y: 1.2 })
}
}
代码亮点:
- 情绪-光效映射表:6种情绪对应6套完整的光效配置(颜色、强度、脉冲速度、模糊半径、环境色)
- 三层光效架构:环境光层 → NPC光晕层 → 脉冲光效层,层次分明
- 平滑过渡动画:情绪切换时1000ms的缓动动画,避免光效突变造成视觉割裂
- 无限脉冲循环:根据情绪类型设置不同的脉冲速度(愤怒800ms快速脉冲、平静3000ms缓慢呼吸)
5.3 AI NPC智能体引擎(AINPCEngine.ets)
// entry/src/main/ets/engine/AINPCEngine.ets
import { hmaf } from '@kit.AgentFrameworkKit';
import { mindSporeLite } from '@kit.MindSporeLiteKit';
export interface NPCProfile {
id: string;
name: string;
role: string;
personality: string;
backstory: string;
skills: string[];
relationships: Map<string, string>;
memory: NPCMemory[];
}
export interface NPCMemory {
timestamp: number;
event: string;
emotion: string;
importance: number;
}
export class AINPCEngine {
private session: hmaf.AgentSession | null = null;
private model: mindSporeLite.Model | null = null;
private npcProfiles: Map<string, NPCProfile> = new Map();
private longTermMemory: Map<string, NPCMemory[]> = new Map();
async initialize(): Promise<void> {
// 创建HMAF智能体会话
this.session = await hmaf.createAgentSession({
mode: hmaf.AgentMode.LLM,
enableDistributed: true,
maxConcurrentAgents: 20
});
// 加载端侧对话模型
this.model = await mindSporeLite.loadModel({
modelPath: 'models/npc_dialogue.mindir',
context: {
target: 'cpu',
precisionMode: 'fp16'
}
});
// 初始化NPC档案
await this.initializeNPCProfiles();
}
private async initializeNPCProfiles(): Promise<void> {
// 商人NPC
this.npcProfiles.set('merchant_01', {
id: 'merchant_01',
name: '老周',
role: '杂货商人',
personality: '精明但热心,喜欢讨价还价,对稀有物品有独特眼光',
backstory: '曾经在王城做过宫廷商人,因得罪权贵流落至此,但积累了丰富的人脉',
skills: ['鉴定', '交易', '情报'],
relationships: new Map([['player', '潜在客户']]),
memory: []
});
// 战士NPC
this.npcProfiles.set('warrior_01', {
id: 'warrior_01',
name: '铁山',
role: '退役老兵',
personality: '豪爽直率,重情重义,对弱者有保护欲',
backstory: '曾是王国禁卫军统领,因一场战役失去左眼,现在以教导年轻人战斗技巧为生',
skills: ['剑术', '战术', '领导力'],
relationships: new Map([['player', '值得培养的年轻人']]),
memory: []
});
// 医者NPC
this.npcProfiles.set('healer_01', {
id: 'healer_01',
name: '青萝',
role: '草药师',
personality: '温柔内敛,对自然充满敬畏,说话轻声细语',
backstory: '自幼跟随祖母学习草药知识,能听懂植物的语言,治愈过无数伤者',
skills: ['治疗', '草药', '自然感知'],
relationships: new Map([['player', '需要照顾的人']]),
memory: []
});
}
// 生成NPC对话
async generateDialogue(
npcId: string,
playerInput: string,
context: GameContext
): Promise<string> {
const profile = this.npcProfiles.get(npcId);
if (!profile) return '...';
// 构建对话提示
const prompt = this.buildDialoguePrompt(profile, playerInput, context);
// 端侧推理生成回复
const response = await this.model?.infer({
input: prompt,
maxLength: 200,
temperature: 0.8
});
// 更新NPC记忆
this.updateNPCMemory(npcId, playerInput, response?.text || '...');
// 分析情绪并触发光效
const emotion = await this.analyzeEmotion(response?.text || '');
AppStorage.setOrCreate('npc_emotion', emotion);
return response?.text || '...';
}
private buildDialoguePrompt(
profile: NPCProfile,
playerInput: string,
context: GameContext
): string {
const memoryContext = this.getRelevantMemory(profile.id);
return `你是${profile.name},一个${profile.role}。
性格:${profile.personality}
背景:${profile.backstory}
技能:${profile.skills.join('、')}
相关记忆:
${memoryContext}
当前场景:${context.scene}
玩家说:${playerInput}
请以${profile.name}的身份和语气回复,保持角色一致性,回复控制在100字以内。`;
}
private getRelevantMemory(npcId: string): string {
const memories = this.longTermMemory.get(npcId) || [];
// 按重要性排序,取最近5条
return memories
.sort((a, b) => b.importance - a.importance)
.slice(0, 5)
.map(m => `- ${m.event} (${m.emotion})`)
.join('\n');
}
private updateNPCMemory(npcId: string, playerInput: string, response: string): void {
const memories = this.longTermMemory.get(npcId) || [];
memories.push({
timestamp: Date.now(),
event: `玩家说:${playerInput},你回复:${response}`,
emotion: 'neutral',
importance: this.calculateImportance(playerInput)
});
// 限制记忆数量
if (memories.length > 100) {
memories.shift();
}
this.longTermMemory.set(npcId, memories);
}
private calculateImportance(input: string): number {
// 简单的重要性计算:关键词匹配
const importantKeywords = ['任务', '秘密', '宝藏', '危险', '承诺'];
let score = 1;
importantKeywords.forEach(keyword => {
if (input.includes(keyword)) score += 2;
});
return Math.min(score, 10);
}
private async analyzeEmotion(text: string): Promise<Emotion> {
// 情绪关键词匹配
if (text.includes('愤怒') || text.includes('可恶') || text.includes('杀')) {
return Emotion.ANGRY;
}
if (text.includes('害怕') || text.includes('危险') || text.includes('逃')) {
return Emotion.FEARFUL;
}
if (text.includes('高兴') || text.includes('太好了') || text.includes('奖励')) {
return Emotion.EXCITED;
}
if (text.includes('朋友') || text.includes('帮助') || text.includes('谢谢')) {
return Emotion.FRIENDLY;
}
if (text.includes('奇怪') || text.includes('好奇') || text.includes('什么')) {
return Emotion.CURIOUS;
}
return Emotion.CALM;
}
}
代码亮点:
- NPC档案系统:每个NPC拥有完整的角色设定(性格、背景、技能、关系网),确保对话一致性
- 长期记忆系统:NPC能够记住与玩家的历史互动,按重要性排序存储最近100条记忆
- 端侧AI推理:基于MindSpore Lite实现对话的端侧实时生成,无需联网即可交互,保护玩家隐私
- 情绪分析引擎:自动分析NPC回复中的情绪关键词,实时触发对应的光效变化
5.4 悬浮角色导航(CharacterFloatNav.ets)
// entry/src/main/ets/components/CharacterFloatNav.ets
import { HdsTabs, HdsTabItem } from '@kit.UIDesignKit';
export interface CharacterInfo {
id: string;
name: string;
avatar: Resource;
hp: number;
maxHp: number;
mp: number;
maxMp: number;
emotion: Emotion;
isActive: boolean;
}
@Component
export struct CharacterFloatNav {
@State characters: CharacterInfo[] = [];
@State selectedIndex: number = 0;
@State transparency: number = 0.70;
@State isExpanded: boolean = false;
@State avoidHeight: number = 0;
// 情绪光色映射
private emotionColors: Map<Emotion, string> = new Map([
[Emotion.CALM, '#4CC9F0'],
[Emotion.CURIOUS, '#00C9A7'],
[Emotion.FRIENDLY, '#FF9F1C'],
[Emotion.ANGRY, '#FF1744'],
[Emotion.FEARFUL, '#7B61FF'],
[Emotion.EXCITED, '#FFD700']
]);
aboutToAppear(): void {
// 初始化角色列表
this.characters = [
{
id: 'player',
name: '主角',
avatar: $r('app.media.avatar_player'),
hp: 100,
maxHp: 100,
mp: 80,
maxMp: 100,
emotion: Emotion.CALM,
isActive: true
},
{
id: 'merchant_01',
name: '老周',
avatar: $r('app.media.avatar_merchant'),
hp: 60,
maxHp: 60,
mp: 40,
maxMp: 50,
emotion: Emotion.FRIENDLY,
isActive: false
},
{
id: 'warrior_01',
name: '铁山',
avatar: $r('app.media.avatar_warrior'),
hp: 150,
maxHp: 150,
mp: 30,
maxMp: 50,
emotion: Emotion.CALM,
isActive: false
},
{
id: 'healer_01',
name: '青萝',
avatar: $r('app.media.avatar_healer'),
hp: 80,
maxHp: 80,
mp: 120,
maxMp: 120,
emotion: Emotion.CURIOUS,
isActive: false
}
];
// 获取底部安全区高度
let windowInstance = window.getLastWindow(getContext(this));
windowInstance.then((win) => {
let avoidArea = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);
this.avoidHeight = avoidArea.bottomRect.height;
});
// 监听NPC情绪变化
AppStorage.watch('npc_emotion', (emotion: Emotion) => {
if (this.selectedIndex > 0) {
this.characters[this.selectedIndex].emotion = emotion;
}
});
}
build() {
Stack({ alignContent: Alignment.Bottom }) {
Column() {
// 角色信息展开面板(悬停时显示)
if (this.isExpanded && this.selectedIndex >= 0) {
this.buildCharacterDetailPanel()
}
// 悬浮角色页签
HdsTabs({
items: this.characters.map((char, index) => ({
icon: char.avatar,
label: char.name,
badge: char.isActive ? 0 : undefined,
customStyle: {
backgroundColor: this.selectedIndex === index
? this.emotionColors.get(char.emotion)
: 'transparent',
borderColor: this.selectedIndex === index
? this.emotionColors.get(char.emotion)
: 'rgba(255,255,255,0.2)'
}
})),
selectedIndex: this.selectedIndex,
onSelect: (index: number) => {
this.selectedIndex = index;
AppStorage.setOrCreate('selected_character', this.characters[index]);
// 触发选中角色的情绪光效
AppStorage.setOrCreate('npc_emotion', this.characters[index].emotion);
},
onHover: (index: number) => {
this.isExpanded = true;
// PC端悬停预览
if (index >= 0) {
AppStorage.setOrCreate('hover_character', this.characters[index]);
}
},
backgroundStyle: {
blurStyle: BlurStyle.REGULAR,
backgroundColor: `rgba(20, 20, 30, ${this.transparency})`,
borderRadius: 24
},
indicatorStyle: {
color: this.emotionColors.get(this.characters[this.selectedIndex]?.emotion) || '#7B61FF',
height: 3,
width: 24,
borderRadius: 2
}
})
.height(64)
.width('96%')
.margin({ bottom: 12 })
}
.width('100%')
.padding({ bottom: this.avoidHeight + 12 })
}
.width('100%')
.height('100%')
.pointerEvents(PointerEvents.BoxNone)
}
@Builder
buildCharacterDetailPanel(): void {
Column({ space: 8 }) {
// 角色头像与名称
Row({ space: 12 }) {
Image(this.characters[this.selectedIndex].avatar)
.width(48)
.height(48)
.borderRadius(24)
.border({
width: 2,
color: this.emotionColors.get(this.characters[this.selectedIndex].emotion) || '#FFFFFF'
})
.shadow({
radius: 10,
color: this.emotionColors.get(this.characters[this.selectedIndex].emotion) || '#FFFFFF'
})
Column({ space: 4 }) {
Text(this.characters[this.selectedIndex].name)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
Text(this.getEmotionLabel(this.characters[this.selectedIndex].emotion))
.fontSize(12)
.fontColor(this.emotionColors.get(this.characters[this.selectedIndex].emotion) || '#FFFFFF')
}
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.padding(12)
// 血条与蓝条
Column({ space: 6 }) {
// HP条
Row({ space: 8 }) {
Text('HP').fontSize(10).fontColor('#FF1744').width(30)
Stack({ alignContent: Alignment.Start }) {
Row()
.width('100%')
.height(8)
.backgroundColor('rgba(255,23,68,0.2)')
.borderRadius(4)
Row()
.width(`${(this.characters[this.selectedIndex].hp / this.characters[this.selectedIndex].maxHp) * 100}%`)
.height(8)
.backgroundColor('#FF1744')
.borderRadius(4)
.animation({ duration: 500, curve: Curve.EaseInOut })
}
.layoutWeight(1)
Text(`${this.characters[this.selectedIndex].hp}/${this.characters[this.selectedIndex].maxHp}`)
.fontSize(10)
.fontColor('rgba(255,255,255,0.6)')
.width(50)
}
.width('100%')
// MP条
Row({ space: 8 }) {
Text('MP').fontSize(10).fontColor('#4CC9F0').width(30)
Stack({ alignContent: Alignment.Start }) {
Row()
.width('100%')
.height(8)
.backgroundColor('rgba(76,201,240,0.2)')
.borderRadius(4)
Row()
.width(`${(this.characters[this.selectedIndex].mp / this.characters[this.selectedIndex].maxMp) * 100}%`)
.height(8)
.backgroundColor('#4CC9F0')
.borderRadius(4)
.animation({ duration: 500, curve: Curve.EaseInOut })
}
.layoutWeight(1)
Text(`${this.characters[this.selectedIndex].mp}/${this.characters[this.selectedIndex].maxMp}`)
.fontSize(10)
.fontColor('rgba(255,255,255,0.6)')
.width(50)
}
.width('100%')
}
.width('100%')
.padding({ left: 12, right: 12 })
}
.width('96%')
.backgroundColor('rgba(20, 20, 30, 0.9)')
.borderRadius(16)
.border({
width: 1,
color: this.emotionColors.get(this.characters[this.selectedIndex].emotion) || 'rgba(255,255,255,0.1)'
})
.margin({ bottom: 8 })
.animation({
duration: 200,
curve: Curve.EaseInOut
})
}
private getEmotionLabel(emotion: Emotion): string {
const labels: Map<Emotion, string> = new Map([
[Emotion.CALM, '平静'],
[Emotion.CURIOUS, '好奇'],
[Emotion.FRIENDLY, '友好'],
[Emotion.ANGRY, '愤怒'],
[Emotion.FEARFUL, '恐惧'],
[Emotion.EXCITED, '兴奋']
]);
return labels.get(emotion) || '未知';
}
}
代码亮点:
- 情绪感知角色页签:每个角色的页签边框和指示器颜色随其当前情绪动态变化
- 悬停详情面板:PC端鼠标悬停时展开角色详细信息(HP/MP/情绪状态),支持实时数据更新
- 玻璃拟态导航:
BlurStyle.REGULAR配合动态透明度,实现悬浮于游戏画面之上的玻璃质感 - 点击穿透:
pointerEvents(BoxNone)确保导航不遮挡游戏画面操作
5.5 NPC对话界面(NPCDialoguePage.ets)
// entry/src/main/ets/pages/NPCDialoguePage.ets
import { AINPCEngine } from '../engine/AINPCEngine';
@Component
struct NPCDialoguePage {
@State npcId: string = '';
@State npcName: string = '';
@State npcAvatar: Resource = $r('app.media.avatar_default');
@State dialogueHistory: DialogueMessage[] = [];
@State currentInput: string = '';
@State isTyping: boolean = false;
@State emotion: Emotion = Emotion.CALM;
private engine: AINPCEngine = new AINPCEngine();
private emotionColors: Map<Emotion, string> = new Map([
[Emotion.CALM, '#4CC9F0'],
[Emotion.CURIOUS, '#00C9A7'],
[Emotion.FRIENDLY, '#FF9F1C'],
[Emotion.ANGRY, '#FF1744'],
[Emotion.FEARFUL, '#7B61FF'],
[Emotion.EXCITED, '#FFD700']
]);
aboutToAppear(): void {
// 获取选中的NPC
let selectedNPC = AppStorage.get('selected_character') as CharacterInfo;
if (selectedNPC) {
this.npcId = selectedNPC.id;
this.npcName = selectedNPC.name;
this.npcAvatar = selectedNPC.avatar;
this.emotion = selectedNPC.emotion;
}
// 初始化引擎
this.engine.initialize();
}
build() {
Stack() {
// 背景光效层
Column()
.width('100%')
.height('100%')
.backgroundColor(this.emotionColors.get(this.emotion) || '#4CC9F0')
.blur(200)
.opacity(0.15)
.animation({
duration: 1000,
curve: Curve.EaseInOut
})
// 对话内容层
Column() {
// NPC信息栏
this.buildNPCHeader()
// 对话历史
List({ space: 12 }) {
ForEach(this.dialogueHistory, (msg: DialogueMessage, index: number) => {
ListItem() {
this.buildMessageBubble(msg)
}
}, (msg: DialogueMessage, index: number) => `${msg.timestamp}-${index}`)
}
.width('100%')
.layoutWeight(1)
.padding(16)
.edgeEffect(EdgeEffect.Spring)
// 输入区域
this.buildInputArea()
}
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
.backgroundColor('rgba(10, 10, 15, 0.95)')
}
@Builder
buildNPCHeader(): void {
Row({ space: 16 }) {
// NPC头像
Stack() {
Image(this.npcAvatar)
.width(56)
.height(56)
.borderRadius(28)
.border({
width: 3,
color: this.emotionColors.get(this.emotion) || '#FFFFFF'
})
// 情绪指示点
Circle()
.width(12)
.height(12)
.fill(this.emotionColors.get(this.emotion) || '#FFFFFF')
.position({ x: 40, y: 40 })
.shadow({
radius: 6,
color: this.emotionColors.get(this.emotion) || '#FFFFFF'
})
.animation({
duration: 1000,
curve: Curve.EaseInOut,
iterations: -1,
playMode: PlayMode.Alternate
})
.scale({ x: 1.3, y: 1.3 })
}
Column({ space: 4 }) {
Text(this.npcName)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
Text(this.getEmotionLabel(this.emotion))
.fontSize(12)
.fontColor(this.emotionColors.get(this.emotion) || '#FFFFFF')
}
.alignItems(HorizontalAlign.Start)
Blank()
// 关闭按钮
Button({ type: ButtonType.Circle }) {
Image($r('app.media.ic_close'))
.width(20)
.height(20)
.fillColor('rgba(255,255,255,0.6)')
}
.width(36)
.height(36)
.backgroundColor('rgba(255,255,255,0.1)')
.onClick(() => {
// 返回游戏场景
AppStorage.setOrCreate('game_state', 'exploring');
})
}
.width('100%')
.height(80)
.padding({ left: 16, right: 16 })
.backgroundColor('rgba(255,255,255,0.03)')
.border({ width: { bottom: 1 }, color: 'rgba(255,255,255,0.1)' })
}
@Builder
buildMessageBubble(msg: DialogueMessage): void {
Row() {
if (msg.sender === 'player') {
Blank()
}
Column({ space: 4 }) {
Text(msg.content)
.fontSize(14)
.fontColor(msg.sender === 'player' ? '#FFFFFF' : 'rgba(255,255,255,0.9)')
.maxWidth('70%')
.padding(12)
.backgroundColor(msg.sender === 'player'
? 'rgba(123, 97, 255, 0.3)'
: 'rgba(255,255,255,0.08)')
.borderRadius(16)
.border({
width: 1,
color: msg.sender === 'player'
? 'rgba(123, 97, 255, 0.5)'
: 'rgba(255,255,255,0.1)'
})
Text(this.formatTime(msg.timestamp))
.fontSize(10)
.fontColor('rgba(255,255,255,0.4)')
}
.alignItems(msg.sender === 'player' ? HorizontalAlign.End : HorizontalAlign.Start)
if (msg.sender === 'npc') {
Blank()
}
}
.width('100%')
}
@Builder
buildInputArea(): void {
Row({ space: 12 }) {
// 语音输入按钮
Button({ type: ButtonType.Circle }) {
Image($r('app.media.ic_mic'))
.width(20)
.height(20)
.fillColor('#FFFFFF')
}
.width(40)
.height(40)
.backgroundColor('rgba(255,255,255,0.1)')
.onClick(() => {
// 触发语音输入
AppStorage.setOrCreate('input_mode', 'voice');
})
// 文本输入框
TextInput({ placeholder: '与NPC对话...', text: $$this.currentInput })
.width('70%')
.height(40)
.backgroundColor('rgba(255,255,255,0.08)')
.fontColor('#FFFFFF')
.placeholderColor('rgba(255,255,255,0.4)')
.borderRadius(20)
.onSubmit(() => {
this.sendMessage();
})
// 发送按钮
Button({ type: ButtonType.Circle }) {
Image($r('app.media.ic_send'))
.width(20)
.height(20)
.fillColor('#FFFFFF')
}
.width(40)
.height(40)
.backgroundColor(this.emotionColors.get(this.emotion) || '#7B61FF')
.onClick(() => {
this.sendMessage();
})
}
.width('100%')
.height(64)
.padding({ left: 16, right: 16 })
.backgroundColor('rgba(255,255,255,0.03)')
.border({ width: { top: 1 }, color: 'rgba(255,255,255,0.1)' })
}
private async sendMessage(): Promise<void> {
if (!this.currentInput.trim()) return;
// 添加玩家消息
this.dialogueHistory.push({
sender: 'player',
content: this.currentInput,
timestamp: Date.now()
});
const input = this.currentInput;
this.currentInput = '';
this.isTyping = true;
// 生成NPC回复
const response = await this.engine.generateDialogue(this.npcId, input, {
scene: '村庄广场',
time: '傍晚',
weather: '晴朗'
});
// 添加NPC回复
this.dialogueHistory.push({
sender: 'npc',
content: response,
timestamp: Date.now()
});
this.isTyping = false;
}
private formatTime(timestamp: number): string {
const date = new Date(timestamp);
return `${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
}
private getEmotionLabel(emotion: Emotion): string {
const labels: Map<Emotion, string> = new Map([
[Emotion.CALM, '平静'],
[Emotion.CURIOUS, '好奇'],
[Emotion.FRIENDLY, '友好'],
[Emotion.ANGRY, '愤怒'],
[Emotion.FEARFUL, '恐惧'],
[Emotion.EXCITED, '兴奋']
]);
return labels.get(emotion) || '未知';
}
}
代码亮点:
- 情绪感知对话背景:整个对话界面的背景光色随NPC当前情绪动态变化
- 情绪指示呼吸灯:NPC头像右下角的情绪指示点持续脉冲呼吸,颜色对应情绪
- 玻璃拟态消息气泡:玩家消息紫色半透明、NPC消息白色半透明,视觉区分清晰
- 语音输入支持:支持语音输入与NPC对话,结合Intents Kit实现语音意图理解
5.6 分布式NPC托管(DistributedNPCHost.ets)
// entry/src/main/ets/distributed/DistributedNPCHost.ets
import { distributedService } from '@kit.DistributedServiceKit';
export class DistributedNPCHost {
private manager: distributedService.Manager | null = null;
private remoteDevices: distributedService.DeviceInfo[] = [];
private hostedNPCs: Map<string, string> = new Map(); // npcId -> deviceId
async initialize(): Promise<void> {
this.manager = distributedService.createManager();
// 初始化分布式服务
await this.manager.init({
deviceType: ['phone', 'tablet', 'pc'],
serviceName: 'zhiling_npc_host',
abilityName: 'NPCDistributedAbility'
});
// 监听设备变化
this.manager.on('deviceStateChange', (deviceInfo, state) => {
if (state === 'online') {
this.remoteDevices.push(deviceInfo);
this.redistributeNPCs();
} else if (state === 'offline') {
this.remoteDevices = this.remoteDevices.filter(d => d.deviceId !== deviceInfo.deviceId);
this.recoverHostedNPCs(deviceInfo.deviceId);
}
});
}
// 智能分配NPC到最优设备
private async redistributeNPCs(): Promise<void> {
const npcList = Array.from(this.hostedNPCs.keys());
for (const npcId of npcList) {
const bestDevice = this.selectBestDevice(npcId);
if (bestDevice && this.hostedNPCs.get(npcId) !== bestDevice.deviceId) {
await this.migrateNPC(npcId, bestDevice.deviceId);
}
}
}
private selectBestDevice(npcId: string): distributedService.DeviceInfo | null {
// 根据NPC类型选择最优设备
// PC端:适合渲染复杂NPC(战斗、动画)
// 手机端:适合轻量NPC(对话、交易)
// 平板端:适合中等复杂度NPC
const npcType = this.getNPCType(npcId);
if (npcType === 'combat' || npcType === 'boss') {
// 优先分配到PC端
return this.remoteDevices.find(d => d.deviceType === 'pc') || null;
} else if (npcType === 'merchant' || npcType === 'healer') {
// 优先分配到手机端
return this.remoteDevices.find(d => d.deviceType === 'phone') || null;
}
// 默认分配到当前设备
return null;
}
private async migrateNPC(npcId: string, targetDeviceId: string): Promise<void> {
// 保存NPC状态
const npcState = await this.saveNPCState(npcId);
// 发送到目标设备
await this.manager?.sendMessage({
targetDeviceId,
message: {
type: 'npc_migrate',
npcId,
state: npcState
}
});
// 更新托管映射
this.hostedNPCs.set(npcId, targetDeviceId);
console.info(`NPC ${npcId} migrated to device ${targetDeviceId}`);
}
private async saveNPCState(npcId: string): Promise<string> {
// 序列化NPC当前状态
const state = {
npcId,
position: AppStorage.get(`npc_${npcId}_position`),
emotion: AppStorage.get(`npc_${npcId}_emotion`),
memory: AppStorage.get(`npc_${npcId}_memory`),
health: AppStorage.get(`npc_${npcId}_health`)
};
return JSON.stringify(state);
}
private async recoverHostedNPCs(deviceId: string): Promise<void> {
// 设备离线时,回收托管的NPC
const affectedNPCs = Array.from(this.hostedNPCs.entries())
.filter(([_, devId]) => devId === deviceId)
.map(([npcId, _]) => npcId);
for (const npcId of affectedNPCs) {
// 恢复到本地设备
this.hostedNPCs.set(npcId, 'local');
console.info(`NPC ${npcId} recovered to local device`);
}
}
private getNPCType(npcId: string): string {
if (npcId.includes('boss')) return 'boss';
if (npcId.includes('warrior')) return 'combat';
if (npcId.includes('merchant')) return 'merchant';
if (npcId.includes('healer')) return 'healer';
return 'generic';
}
}
代码亮点:
- 智能设备选择:根据NPC类型(战斗/商人/医者)自动选择最优设备(PC/手机/平板)
- NPC状态迁移:支持NPC状态的序列化、跨设备传输和反序列化
- 故障自动恢复:设备离线时自动将NPC回收至本地设备,保证游戏连续性
- 负载均衡:根据设备性能和NPC复杂度动态分配,优化整体游戏体验
5.7 主游戏页面集成(GamePage.ets)
// entry/src/main/ets/pages/GamePage.ets
import { EmotionLightSystem } from '../systems/EmotionLightSystem';
import { CharacterFloatNav } from '../components/CharacterFloatNav';
import { AINPCEngine } from '../engine/AINPCEngine';
import { DistributedNPCHost } from '../distributed/DistributedNPCHost';
@Entry
@Component
struct GamePage {
@State gameState: string = 'exploring'; // exploring | dialogue | combat | trading
@State currentEmotion: Emotion = Emotion.CALM;
@State themeColor: string = '#4CC9F0';
@State lightIntensity: number = 0.6;
private npcEngine: AINPCEngine = new AINPCEngine();
private distributedHost: DistributedNPCHost = new DistributedNPCHost();
aboutToAppear(): void {
// 初始化引擎
this.npcEngine.initialize();
this.distributedHost.initialize();
// 监听游戏状态变化
AppStorage.watch('game_state', (state: string) => {
this.gameState = state;
});
// 监听NPC情绪变化
AppStorage.watch('npc_emotion', (emotion: Emotion) => {
this.currentEmotion = emotion;
this.themeColor = this.getEmotionColor(emotion);
});
}
private getEmotionColor(emotion: Emotion): string {
const colors: Map<Emotion, string> = new Map([
[Emotion.CALM, '#4CC9F0'],
[Emotion.CURIOUS, '#00C9A7'],
[Emotion.FRIENDLY, '#FF9F1C'],
[Emotion.ANGRY, '#FF1744'],
[Emotion.FEARFUL, '#7B61FF'],
[Emotion.EXCITED, '#FFD700']
]);
return colors.get(emotion) || '#4CC9F0';
}
build() {
Stack() {
// 第一层:情绪光效背景
EmotionLightSystem()
// 第二层:游戏场景层
this.buildGameScene()
// 第三层:UI层
Column() {
// 顶部状态栏
this.buildStatusBar()
// 中间内容区(根据游戏状态切换)
if (this.gameState === 'dialogue') {
NPCDialoguePage()
} else if (this.gameState === 'combat') {
CombatPage()
} else if (this.gameState === 'trading') {
TradingPage()
}
// 底部悬浮角色导航
CharacterFloatNav()
}
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
.backgroundColor('#0a0a0f')
.expandSafeArea(
[SafeAreaType.SYSTEM],
[SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM, SafeAreaEdge.START, SafeAreaEdge.END]
)
}
@Builder
buildGameScene(): void {
// 游戏场景渲染(Canvas/3D)
Canvas(this.gameContext)
.width('100%')
.height('100%')
.onReady((context) => {
this.renderGameScene(context);
})
.onAreaChange((oldArea, newArea) => {
// 响应区域变化
})
}
@Builder
buildStatusBar(): void {
Row({ space: 16 }) {
// 时间显示
Text(this.getGameTime())
.fontSize(14)
.fontColor('rgba(255,255,255,0.8)')
Blank()
// 当前区域
Text('青石村')
.fontSize(14)
.fontColor('rgba(255,255,255,0.8)')
Blank()
// 网络状态
Row({ space: 4 }) {
Circle()
.width(8)
.height(8)
.fill('#00C853')
Text('在线')
.fontSize(12)
.fontColor('rgba(255,255,255,0.6)')
}
}
.width('100%')
.height(40)
.padding({ left: 16, right: 16 })
.backgroundColor('rgba(0,0,0,0.3)')
.border({ width: { bottom: 1 }, color: 'rgba(255,255,255,0.1)' })
}
private renderGameScene(context: CanvasRenderingContext2D): void {
// 渲染游戏场景
// 包括地形、建筑、NPC、特效等
context.fillStyle = '#1a1a2e';
context.fillRect(0, 0, 1920, 1080);
// 渲染NPC
this.renderNPCs(context);
// 渲染光效
this.renderLightEffects(context);
}
private renderNPCs(context: CanvasRenderingContext2D): void {
// 获取所有NPC位置并渲染
const npcPositions = AppStorage.get('npc_positions') as Map<string, Position>;
if (!npcPositions) return;
npcPositions.forEach((pos, npcId) => {
// 渲染NPC精灵
context.beginPath();
context.arc(pos.x, pos.y, 20, 0, 2 * Math.PI);
context.fillStyle = this.getEmotionColor(
AppStorage.get(`npc_${npcId}_emotion`) as Emotion || Emotion.CALM
);
context.fill();
// 渲染NPC名称
context.fillStyle = '#FFFFFF';
context.font = '12px sans-serif';
context.fillText(npcId, pos.x - 20, pos.y - 25);
});
}
private renderLightEffects(context: CanvasRenderingContext2D): void {
// 渲染环境光效
const gradient = context.createRadialGradient(
960, 540, 0,
960, 540, 500
);
gradient.addColorStop(0, this.themeColor + '20');
gradient.addColorStop(1, 'transparent');
context.fillStyle = gradient;
context.fillRect(0, 0, 1920, 1080);
}
private getGameTime(): string {
const now = new Date();
return `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
}
}
六、NPC状态机与情绪光效联动

6.1 状态机设计
// entry/src/main/ets/states/NPCStateMachine.ets
export class NPCStateMachine {
private currentState: NPCState = NPCState.IDLE;
private transitions: Map<NPCState, NPCState[]> = new Map([
[NPCState.IDLE, [NPCState.PATROL, NPCState.INTERACT]],
[NPCState.PATROL, [NPCState.IDLE, NPCState.INTERACT]],
[NPCState.INTERACT, [NPCState.IDLE, NPCState.COMBAT, NPCState.TRADE]],
[NPCState.COMBAT, [NPCState.IDLE, NPCState.FLEE]],
[NPCState.FLEE, [NPCState.IDLE]],
[NPCState.TRADE, [NPCState.IDLE]]
]);
async transition(newState: NPCState, context: GameContext): Promise<boolean> {
const validTransitions = this.transitions.get(this.currentState) || [];
if (!validTransitions.includes(newState)) {
console.warn(`Invalid transition: ${this.currentState} -> ${newState}`);
return false;
}
// 执行状态退出逻辑
await this.onExitState(this.currentState);
// 更新状态
this.currentState = newState;
// 执行状态进入逻辑
await this.onEnterState(newState, context);
// 触发情绪光效变化
const emotion = this.stateToEmotion(newState);
AppStorage.setOrCreate('npc_emotion', emotion);
return true;
}
private async onEnterState(state: NPCState, context: GameContext): Promise<void> {
switch (state) {
case NPCState.IDLE:
// 播放待机动画
AppStorage.setOrCreate('npc_animation', 'idle');
break;
case NPCState.PATROL:
// 开始巡逻路径
AppStorage.setOrCreate('npc_animation', 'walk');
break;
case NPCState.INTERACT:
// 面向玩家
AppStorage.setOrCreate('npc_animation', 'talk');
break;
case NPCState.COMBAT:
// 进入战斗姿态
AppStorage.setOrCreate('npc_animation', 'combat');
break;
case NPCState.FLEE:
// 逃跑动画
AppStorage.setOrCreate('npc_animation', 'run');
break;
case NPCState.TRADE:
// 打开交易界面
AppStorage.setOrCreate('game_state', 'trading');
break;
}
}
private async onExitState(state: NPCState): Promise<void> {
// 清理状态相关资源
console.info(`Exiting state: ${state}`);
}
private stateToEmotion(state: NPCState): Emotion {
switch (state) {
case NPCState.IDLE: return Emotion.CALM;
case NPCState.PATROL: return Emotion.CURIOUS;
case NPCState.INTERACT: return Emotion.FRIENDLY;
case NPCState.COMBAT: return Emotion.ANGRY;
case NPCState.FLEE: return Emotion.FEARFUL;
case NPCState.TRADE: return Emotion.EXCITED;
default: return Emotion.CALM;
}
}
}
七、关键技术总结
7.1 HMAF游戏智能体开发清单
| 技术点 | API/方法 | 应用场景 |
|---|---|---|
| 智能体会话创建 | hmaf.createAgentSession({ mode: LLM }) |
NPC自主对话 |
| 端侧模型加载 | mindSporeLite.loadModel({ modelPath }) |
实时对话生成 |
| 意图解析 | intents.createIntentEngine({ supportedDomains }) |
玩家语音/手势理解 |
| 状态机管理 | NPCStateMachine |
NPC行为状态切换 |
| 分布式服务 | distributedService.createManager() |
跨设备NPC托管 |
| 情绪光效 | SystemMaterialEffect.IMMERSIVE |
情绪可视化 |
7.2 沉浸光感实现清单
| 技术点 | API/方法 | 应用场景 |
|---|---|---|
| 情绪光效映射 | emotionLightMap |
6种情绪对应6套光效 |
| 三层光效架构 | Ambient + Glow + Pulse |
环境光+光晕+脉冲 |
| 平滑过渡 | animation({ duration: 1000 }) |
情绪切换动画 |
| 无限脉冲 | iterations: -1 |
持续呼吸光效 |
7.3 悬浮导航游戏适配要点
- 角色快速切换:底部悬浮页签支持主角+队友NPC的快速切换
- 情绪可视化:每个角色的页签颜色随其情绪动态变化
- 悬停详情:PC端鼠标悬停展开角色详细信息(HP/MP/情绪)
- 点击穿透:
pointerEvents(BoxNone)确保不遮挡游戏操作
八、调试与性能优化
8.1 真机调试建议
- 光效真机验证:玻璃拟态效果在模拟器上可能显示异常,建议在支持HarmonyOS 6的真机上测试
- 端侧推理测试:验证MindSpore Lite模型在不同设备上的推理性能
- 分布式测试:验证多设备间的NPC状态同步与迁移
8.2 性能优化策略
- 光效降级:低端设备上降低模糊半径和动画帧率
- 模型量化:使用INT8量化减少端侧模型内存占用
- 记忆裁剪:NPC长期记忆超过100条时自动裁剪低重要性记忆
- 分布式负载均衡:根据设备性能动态调整NPC分配策略
九、总结与展望
本文基于HarmonyOS 6(API 23)的悬浮导航与沉浸光感特性,完整实战了一款名为"智灵幻境"的AI NPC智能体游戏。核心创新点总结:
-
情绪感知光效系统:6种NPC情绪(平静/好奇/友好/愤怒/恐惧/兴奋)实时映射为环境光色与脉冲节奏,让NPC的"灵魂"可见可感
-
HMAF NPC智能体引擎:基于Agent Framework Kit构建具备自主目标、持久记忆和动态对话的AI NPC,每个NPC拥有完整的角色设定和长期记忆
-
端侧AI推理:基于MindSpore Lite实现NPC对话的端侧实时生成,无需联网即可交互,保护玩家隐私
-
悬浮角色导航:底部悬浮页签实现多NPC快速切换与组队管理,支持PC端鼠标悬停预览
-
分布式NPC托管:利用HarmonyOS分布式软总线,实现手机端控制+PC端渲染的跨设备游戏体验
未来扩展方向:
- 接入多智能体协作:多个NPC智能体之间通过A2A模式自主协作,形成动态社交网络
- 探索AI生成内容:基于NPC记忆和情绪状态,自动生成个性化任务和剧情
- 结合AR能力:通过摄像头将AI NPC投射到现实世界,实现虚实融合的沉浸体验
- 情感计算升级:通过玩家语音语调分析真实情绪,NPC做出更精准的情感回应
转载自:https://blog.csdn.net/u014727709/article/details/162389721
欢迎 👍点赞✍评论⭐收藏,欢迎指正
更多推荐



所有评论(0)