
鸿蒙5桌面电子宠物小组件开发指南 原创
鸿蒙5桌面电子宠物小组件开发指南
一、项目概述
本文基于HarmonyOS 5的原子化服务和动画引擎,开发一款桌面电子宠物小组件,借鉴《鸿蒙跨端U同步》中游戏多设备同步的技术原理,实现宠物状态的多设备同步和互动。该电子宠物能够展示丰富动画效果、响应多设备交互,并通过分布式能力实现状态同步。
二、系统架构
±--------------------+ ±--------------------+ ±--------------------+
桌面小组件 <-----> 原子化服务 <-----> 分布式数据总线
(Widget) (Atomic Service) (Distributed Bus)
±---------±---------+ ±---------±---------+ ±---------±---------+
±---------v----------+ ±---------v----------+ ±---------v----------+
动画渲染引擎 宠物状态管理 多设备同步服务
(Animation Engine) (State Manager) (Sync Service)
±--------------------+ ±--------------------+ ±--------------------+
三、核心代码实现
宠物数据模型
// src/main/ets/model/PetModel.ts
export enum PetType {
CAT = ‘cat’,
DOG = ‘dog’,
RABBIT = ‘rabbit’,
DRAGON = ‘dragon’
export enum PetAnimation {
IDLE = ‘idle’,
EATING = ‘eating’,
PLAYING = ‘playing’,
SLEEPING = ‘sleeping’,
WALKING = ‘walking’,
HAPPY = ‘happy’,
SAD = ‘sad’
export class VirtualPet {
petId: string;
name: string;
type: PetType;
level: number;
happiness: number;
hunger: number;
energy: number;
experience: number;
lastFed: number;
lastPlayed: number;
isSleeping: boolean;
currentAnimation: PetAnimation;
position: { x: number, y: number };
deviceId: string;
constructor(name: string, type: PetType = PetType.CAT) {
this.petId = ‘pet_’ + Math.random().toString(36).substring(2, 11);
this.name = name;
this.type = type;
this.level = 1;
this.happiness = 70;
this.hunger = 50;
this.energy = 80;
this.experience = 0;
this.lastFed = Date.now();
this.lastPlayed = Date.now();
this.isSleeping = false;
this.currentAnimation = PetAnimation.IDLE;
this.position = { x: 0.5, y: 0.7 };
this.deviceId = ‘’;
feed(): void {
this.hunger = Math.min(100, this.hunger + 30);
this.happiness = Math.min(100, this.happiness + 10);
this.lastFed = Date.now();
this.currentAnimation = PetAnimation.EATING;
play(): void {
this.happiness = Math.min(100, this.happiness + 20);
this.energy = Math.max(0, this.energy - 15);
this.experience += 10;
this.lastPlayed = Date.now();
this.currentAnimation = PetAnimation.PLAYING;
sleep(): void {
this.isSleeping = true;
this.energy = Math.min(100, this.energy + 5);
this.currentAnimation = PetAnimation.SLEEPING;
wakeUp(): void {
this.isSleeping = false;
this.currentAnimation = PetAnimation.IDLE;
updateStatus(): void {
const now = Date.now();
const hoursSinceFed = (now - this.lastFed) / (1000 60 60);
const hoursSincePlayed = (now - this.lastPlayed) / (1000 60 60);
if (!this.isSleeping) {
this.hunger = Math.max(0, this.hunger - hoursSinceFed * 5);
this.happiness = Math.max(0, this.happiness - hoursSincePlayed * 3);
this.energy = Math.max(0, this.energy - hoursSincePlayed * 2);
else {
this.energy = Math.min(100, this.energy + hoursSincePlayed * 8);
if (this.experience >= this.level * 100) {
this.level++;
this.experience = 0;
this.currentAnimation = PetAnimation.HAPPY;
// 自动恢复空闲状态
if (this.currentAnimation !== PetAnimation.IDLE &&
this.currentAnimation !== PetAnimation.SLEEPING &&
now - Math.max(this.lastFed, this.lastPlayed) > 3000) {
this.currentAnimation = PetAnimation.IDLE;
}
toJson(): string {
return JSON.stringify({
petId: this.petId,
name: this.name,
type: this.type,
level: this.level,
happiness: this.happiness,
hunger: this.hunger,
energy: this.energy,
experience: this.experience,
lastFed: this.lastFed,
lastPlayed: this.lastPlayed,
isSleeping: this.isSleeping,
currentAnimation: this.currentAnimation,
position: this.position,
deviceId: this.deviceId
});
static fromJson(jsonStr: string): VirtualPet {
const json = JSON.parse(jsonStr);
const pet = new VirtualPet(json.name, json.type);
pet.petId = json.petId;
pet.level = json.level;
pet.happiness = json.happiness;
pet.hunger = json.hunger;
pet.energy = json.energy;
pet.experience = json.experience;
pet.lastFed = json.lastFed;
pet.lastPlayed = json.lastPlayed;
pet.isSleeping = json.isSleeping;
pet.currentAnimation = json.currentAnimation;
pet.position = json.position;
pet.deviceId = json.deviceId;
return pet;
}
分布式同步服务
// src/main/ets/service/DistributedSyncService.ts
import { distributedData } from ‘@ohos.data.distributedData’;
import { BusinessError } from ‘@ohos.base’;
import { VirtualPet } from ‘…/model/PetModel’;
import { deviceManager } from ‘@ohos.distributedDeviceManager’;
export class DistributedSyncService {
private static instance: DistributedSyncService;
private kvManager: distributedData.KVManager;
private kvStore: distributedData.KVStore;
private readonly STORE_ID = ‘pet_store’;
private readonly PET_KEY_PREFIX = ‘pet_’;
private subscribers: ((data: VirtualPet) => void)[] = [];
private constructor() {
this.initDistributedData();
public static getInstance(): DistributedSyncService {
if (!DistributedSyncService.instance) {
DistributedSyncService.instance = new DistributedSyncService();
return DistributedSyncService.instance;
private initDistributedData(): void {
const config: distributedData.KVManagerConfig = {
bundleName: 'com.example.virtualpet',
userInfo: {
userId: '0',
userType: distributedData.UserType.SAME_USER_ID
};
distributedData.createKVManager(config, (err: BusinessError, manager: distributedData.KVManager) => {
if (err) {
console.error(Failed to create KVManager. Code: {err.code}, message: {err.message});
return;
this.kvManager = manager;
const options: distributedData.Options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true,
kvStoreType: distributedData.KVStoreType.SINGLE_VERSION,
securityLevel: distributedData.SecurityLevel.S1
};
this.kvManager.getKVStore(this.STORE_ID, options, (err: BusinessError, store: distributedData.KVStore) => {
if (err) {
console.error(Failed to get KVStore. Code: {err.code}, message: {err.message});
return;
this.kvStore = store;
this.registerDataListener();
});
});
private registerDataListener(): void {
this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL, (data: distributedData.ChangeData) => {
if (data.key.startsWith(this.PET_KEY_PREFIX)) {
const pet = VirtualPet.fromJson(data.value.value as string);
this.notifySubscribers(pet);
});
public subscribe(callback: (data: VirtualPet) => void): void {
this.subscribers.push(callback);
public unsubscribe(callback: (data: VirtualPet) => void): void {
this.subscribers = this.subscribers.filter(sub => sub !== callback);
private notifySubscribers(data: VirtualPet): void {
this.subscribers.forEach(callback => callback(data));
public syncPet(pet: VirtualPet): void {
deviceManager.getLocalDeviceInfo((err: BusinessError, info) => {
if (err) {
console.error(Failed to get device info. Code: {err.code}, message: {err.message});
return;
pet.deviceId = info.deviceId;
const key = this.PET_KEY_PREFIX + pet.petId;
this.kvStore.put(key, pet.toJson(), (err: BusinessError) => {
if (err) {
console.error(Failed to sync pet. Code: {err.code}, message: {err.message});
});
});
public async getPet(petId: string): Promise<VirtualPet | null> {
return new Promise((resolve) => {
const key = this.PET_KEY_PREFIX + petId;
this.kvStore.get(key, (err: BusinessError, value: distributedData.Value) => {
if (err) {
console.error(Failed to get pet. Code: {err.code}, message: {err.message});
resolve(null);
return;
resolve(VirtualPet.fromJson(value.value as string));
});
});
}
动画引擎实现
// src/main/ets/engine/AnimationEngine.ts
import { VirtualPet, PetAnimation, PetType } from ‘…/model/PetModel’;
import { LottieAnimation } from ‘@ohos/lottie’;
import { BusinessError } from ‘@ohos.base’;
export class AnimationEngine {
private animations: Map<string, LottieAnimation> = new Map();
private currentPet: VirtualPet | null = null;
private animationContainer: HTMLElement | null = null;
constructor(container: HTMLElement) {
this.animationContainer = container;
this.loadAnimations();
private async loadAnimations(): Promise<void> {
const animationTypes = Object.values(PetAnimation);
const petTypes = Object.values(PetType);
for (const petType of petTypes) {
for (const animType of animationTypes) {
const animId = {petType}_{animType};
try {
const anim = new LottieAnimation({
container: this.animationContainer,
renderer: 'svg',
loop: true,
autoplay: false,
path: resources/animations/{petType}/{animType}.json
});
this.animations.set(animId, anim);
catch (e) {
console.error(Failed to load animation {animId}. Code: {e.code}, message: ${e.message});
}
}
public setPet(pet: VirtualPet): void {
this.currentPet = pet;
this.playAnimation(pet.currentAnimation);
public playAnimation(animType: PetAnimation): void {
if (!this.currentPet) return;
const animId = {this.currentPet.type}_{animType};
const animation = this.animations.get(animId);
if (animation) {
// 停止所有动画
this.animations.forEach(anim => anim.stop());
// 播放新动画
animation.play();
this.currentPet.currentAnimation = animType;
else {
console.error(Animation not found: ${animId});
}
public updatePosition(x: number, y: number): void {
if (!this.currentPet) return;
this.currentPet.position = { x, y };
if (this.animationContainer) {
this.animationContainer.style.left = ${x * 100}%;
this.animationContainer.style.top = ${y * 100}%;
}
原子化服务实现
// src/main/ets/service/AtomicService.ts
import { VirtualPet } from ‘…/model/PetModel’;
import { DistributedSyncService } from ‘./DistributedSyncService’;
import { featureAbility } from ‘@ohos.ability.featureAbility’;
export class PetAtomicService {
private static instance: PetAtomicService;
private syncService = DistributedSyncService.getInstance();
private currentPet: VirtualPet | null = null;
private constructor() {
this.initService();
public static getInstance(): PetAtomicService {
if (!PetAtomicService.instance) {
PetAtomicService.instance = new PetAtomicService();
return PetAtomicService.instance;
private initService(): void {
// 注册原子化服务
featureAbility.registerAbilityLifecycleCallback({
onStart: (want) => {
console.log('PetAtomicService started');
},
onStop: () => {
console.log('PetAtomicService stopped');
});
// 加载或创建宠物
this.loadOrCreatePet();
private async loadOrCreatePet(): Promise<void> {
// 尝试从分布式数据加载已有宠物
const petId = localStorage.getItem('current_pet_id');
if (petId) {
this.currentPet = await this.syncService.getPet(petId);
// 如果没有找到宠物,创建新宠物
if (!this.currentPet) {
this.currentPet = new VirtualPet('小可爱', PetType.CAT);
localStorage.setItem('current_pet_id', this.currentPet.petId);
// 同步宠物状态
this.syncService.syncPet(this.currentPet);
public getCurrentPet(): VirtualPet | null {
return this.currentPet;
public feedPet(): void {
if (!this.currentPet) return;
this.currentPet.feed();
this.syncService.syncPet(this.currentPet);
public playWithPet(): void {
if (!this.currentPet) return;
this.currentPet.play();
this.syncService.syncPet(this.currentPet);
public putPetToSleep(): void {
if (!this.currentPet) return;
this.currentPet.sleep();
this.syncService.syncPet(this.currentPet);
public wakeUpPet(): void {
if (!this.currentPet) return;
this.currentPet.wakeUp();
this.syncService.syncPet(this.currentPet);
public updatePetStatus(): void {
if (!this.currentPet) return;
this.currentPet.updateStatus();
this.syncService.syncPet(this.currentPet);
}
桌面小组件实现
// src/main/ets/widget/PetWidget.ets
import { VirtualPet, PetAnimation } from ‘…/model/PetModel’;
import { PetAtomicService } from ‘…/service/AtomicService’;
import { AnimationEngine } from ‘…/engine/AnimationEngine’;
@Component
export struct PetWidget {
@State pet: VirtualPet | null = null;
@State touchX: number = 0;
@State touchY: number = 0;
private atomicService = PetAtomicService.getInstance();
private animationEngine: AnimationEngine | null = null;
aboutToAppear(): void {
this.pet = this.atomicService.getCurrentPet();
this.animationEngine = new AnimationEngine(this.$element(‘animationContainer’));
if (this.pet) {
this.animationEngine.setPet(this.pet);
this.animationEngine.updatePosition(this.pet.position.x, this.pet.position.y);
// 每10秒更新一次状态
setInterval(() => {
this.atomicService.updatePetStatus();
this.pet = this.atomicService.getCurrentPet();
}, 10000);
build() {
Stack() {
// 宠物动画容器
Div() {
// Lottie动画将通过AnimationEngine动态加载
.id(‘animationContainer’)
.width(100)
.height(100)
.position({ x: this.touchX, y: this.touchY })
.onTouch((event: TouchEvent) => {
// 拖动宠物
if (event.type === TouchType.Move) {
this.touchX = event.touches[0].x / this.$width();
this.touchY = event.touches[0].y / this.$height();
if (this.pet && this.animationEngine) {
this.pet.position = { x: this.touchX, y: this.touchY };
this.animationEngine.updatePosition(this.touchX, this.touchY);
this.atomicService.syncPet(this.pet);
}
})
.onClick(() => {
// 点击互动
this.atomicService.playWithPet();
this.pet = this.atomicService.getCurrentPet();
if (this.pet && this.animationEngine) {
this.animationEngine.playAnimation(this.pet.currentAnimation);
})
// 控制按钮
Row() {
Button('喂食')
.width(60)
.height(30)
.onClick(() => {
this.atomicService.feedPet();
this.pet = this.atomicService.getCurrentPet();
if (this.pet && this.animationEngine) {
this.animationEngine.playAnimation(this.pet.currentAnimation);
})
Button(this.pet?.isSleeping ? '唤醒' : '睡觉')
.width(60)
.height(30)
.margin({ left: 10 })
.onClick(() => {
if (this.pet?.isSleeping) {
this.atomicService.wakeUpPet();
else {
this.atomicService.putPetToSleep();
this.pet = this.atomicService.getCurrentPet();
if (this.pet && this.animationEngine) {
this.animationEngine.playAnimation(this.pet.currentAnimation);
})
.position({ x: ‘50%’, y: ‘90%’ })
.justifyContent(FlexAlign.Center)
.width(‘100%’)
.height('100%')
}
四、与游戏同步技术的结合点
状态同步机制:借鉴游戏中玩家状态同步技术,实现宠物状态的多设备同步
实时交互响应:类似游戏中的实时互动,实现多设备对宠物的同步操作
数据压缩传输:采用游戏中的高效数据压缩算法,减少状态同步数据量
设备角色分配:主设备作为状态权威,类似游戏中的主机角色
冲突解决策略:采用类似游戏的时间戳优先策略解决状态冲突
五、关键特性实现
动画状态管理:
// 平滑过渡动画状态
private transitionAnimation(newAnim: PetAnimation): void {
if (this.currentAnimation === newAnim) return;
// 播放过渡动画
const transitionAnim = this.getTransitionAnimation(this.currentAnimation, newAnim);
if (transitionAnim) {
transitionAnim.playOnce(() => {
this.playAnimation(newAnim);
});
else {
this.playAnimation(newAnim);
}
多设备交互:
// 处理来自其他设备的交互
private handleRemoteInteraction(deviceId: string, action: string): void {
if (deviceId === this.currentDeviceId) return;
switch (action) {
case 'feed':
this.pet.onFedRemotely();
break;
case 'play':
this.pet.onPlayedRemotely();
break;
this.updatePetDisplay();
离线状态处理:
// 恢复离线期间的状态变化
private recoverOfflineChanges(): void {
const offlineTime = Date.now() - this.lastSyncTime;
if (offlineTime > 60000) { // 离线超过1分钟
const hours = offlineTime / (1000 60 60);
this.pet.simulateOfflineChanges(hours);
}
自适应动画质量:
// 根据设备性能调整动画质量
private adjustAnimationQuality(): void {
const perfLevel = device.getPerformanceLevel();
this.animationQuality = perfLevel === ‘high’ ? ‘high’ : ‘low’;
this.loadAnimations(this.animationQuality);
六、性能优化策略
动画资源按需加载:
// 只加载当前需要的动画资源
private loadRequiredAnimations(petType: PetType): void {
this.unloadAllAnimations();
const requiredAnims = [
PetAnimation.IDLE,
PetAnimation.EATING,
PetAnimation.PLAYING,
PetAnimation.SLEEPING
];
requiredAnims.forEach(anim => this.loadAnimation(petType, anim));
状态更新批处理:
// 批量处理状态更新
private batchUpdates(): void {
if (this.updateBatchTimer) return;
this.updateBatchTimer = setTimeout(() => {
this.syncService.syncPet(this.currentPet);
this.updateBatchTimer = null;
}, 500); // 每500ms批量同步一次
内存敏感模式:
// 低内存设备启用简化模式
private checkMemoryStatus(): void {
const memoryInfo = device.getMemoryInfo();
if (memoryInfo.level === ‘low’) {
this.enableLowMemoryMode();
}
后台运行优化:
// 后台运行时减少更新频率
private handleAppStateChange(state: string): void {
if (state === ‘background’) {
this.updateInterval = 30000; // 30秒
else {
this.updateInterval = 10000; // 10秒
}
七、项目扩展方向
宠物换装系统:支持自定义宠物外观
社交互动功能:宠物之间的互动玩法
AR宠物展示:通过AR技术将宠物投射到现实环境
智能语音交互:支持语音指令控制宠物
健康监测集成:结合健康数据影响宠物状态
八、总结
本桌面电子宠物小组件实现了以下核心功能:
基于原子化服务的轻量级宠物模拟
丰富的Lottie动画效果展示
多设备状态同步与互动
响应式用户交互体验
自适应不同设备性能
通过借鉴游戏中的多设备同步技术,我们构建了一个生动有趣的桌面宠物应用。该项目展示了HarmonyOS分布式能力在互动娱乐领域的创新应用,为开发者提供了实现多设备协同互动应用的参考方案。
