在語聊房項目中,禮物特效播放是一個常見的需求,通常包括動畫、聲音等多種媒體形式。為了處理不同的禮物類型,我們可以采用抽象的設計方法,使得系統易于擴展和維護。
設計架構思路:
抽象禮物特效接口:定義一個通用的禮物特效接口,所有類型的禮物特效都實現這個接口。接口中包括播放、停止、釋放資源等方法。
禮物特效工廠:使用工廠模式根據禮物類型創建對應的特效實例。
特效管理:有一個特效管理器來管理當前正在播放的特效,處理多個禮物同時播放時的排隊、疊加等邏輯。
資源管理:禮物特效可能包含動畫、聲音等資源,需要統一管理資源的加載和釋放。
與UI層解耦:特效播放器應該與UI層解耦,通過事件或回調與UI通信。
一、架構設計
二、核心抽象設計
1. 禮物特效抽象基類
abstract class GiftEffect {final String effectId;final int priority; // 特效優先級,用于處理多個特效同時播放的情況final Duration duration; // 特效持續時間GiftEffect({required this.effectId,required this.priority,required this.duration,});// 初始化特效資源Future<void> initialize();// 播放特效Future<void> play();// 停止特效Future<void> stop();// 釋放資源Future<void> dispose();// 特效狀態變化回調void Function(GiftEffectState state)? onStateChanged;
}// 特效狀態枚舉
enum GiftEffectState {initializing,ready,playing,paused,completed,error,
}
2. 不同禮物類型的特效實現
基礎禮物特效
class BasicGiftEffect extends GiftEffect {final String animationPath;final String? soundPath;AnimationController? _animationController;AudioPlayer? _audioPlayer;BasicGiftEffect({required super.effectId,required super.priority,required super.duration,required this.animationPath,this.soundPath,});@overrideFuture<void> initialize() async {try {// 預加載動畫資源await precacheAnimation(animationPath);// 如果有音效,預加載音效if (soundPath != null) {_audioPlayer = AudioPlayer();await _audioPlayer!.setAsset(soundPath!);}onStateChanged?.call(GiftEffectState.ready);} catch (e) {onStateChanged?.call(GiftEffectState.error);rethrow;}}@overrideFuture<void> play() async {onStateChanged?.call(GiftEffectState.playing);// 播放動畫_animationController = AnimationController(duration: duration,vsync: // 獲取TickerProvider,)..forward();// 播放音效if (_audioPlayer != null) {await _audioPlayer!.play();}// 監聽動畫完成_animationController!.addStatusListener((status) {if (status == AnimationStatus.completed) {onStateChanged?.call(GiftEffectState.completed);}});}@overrideFuture<void> stop() async {_animationController?.stop();await _audioPlayer?.stop();onStateChanged?.call(GiftEffectState.paused);}@overrideFuture<void> dispose() async {_animationController?.dispose();await _audioPlayer?.dispose();onStateChanged?.call(GiftEffectState.completed);}
}
高級禮物特效(如全屏特效)
class FullScreenGiftEffect extends GiftEffect {final String lottieAnimationPath;final String particleEffectPath;final String backgroundMusicPath;final List<String> additionalEffects;FullScreenGiftEffect({required super.effectId,required super.priority,required super.duration,required this.lottieAnimationPath,required this.particleEffectPath,required this.backgroundMusicPath,this.additionalEffects = const [],});@overrideFuture<void> initialize() async {// 使用Isolate預加載復雜資源,避免阻塞UI線程await compute(_preloadFullScreenResources, {'lottie': lottieAnimationPath,'particles': particleEffectPath,'music': backgroundMusicPath,'additional': additionalEffects,});onStateChanged?.call(GiftEffectState.ready);}static void _preloadFullScreenResources(Map<String, dynamic> resources) {// 在Isolate中執行資源預加載// 這里可以加載Lottie動畫、粒子效果、音樂等}@overrideFuture<void> play() async {// 實現全屏特效播放邏輯// 可能涉及多個動畫同步、粒子系統、音樂播放等}// 其他方法實現...
}
3D禮物特效
class ThreeDGiftEffect extends GiftEffect {final String modelPath;final String animationName;final List<LightConfig> lights;final CameraConfig camera;ThreeDGiftEffect({required super.effectId,required super.priority,required super.duration,required this.modelPath,required this.animationName,required this.lights,required this.camera,});@overrideFuture<void> initialize() async {// 使用Isolate加載3D模型和動畫await compute(_load3DModel, modelPath);onStateChanged?.call(GiftEffectState.ready);}static void _load3DModel(String modelPath) {// 在Isolate中加載3D模型}@overrideFuture<void> play() async {// 實現3D特效播放邏輯// 使用Flutter 3D渲染引擎(如Filament或自定義OpenGL渲染)}// 其他方法實現...
}
3. 禮物特效工廠
class GiftEffectFactory {static GiftEffect createEffect(GiftItem gift) {switch (gift.type) {case GiftType.basic:return BasicGiftEffect(effectId: gift.id,priority: gift.priority,duration: gift.duration,animationPath: gift.animationPath,soundPath: gift.soundPath,);case GiftType.fullScreen:return FullScreenGiftEffect(effectId: gift.id,priority: gift.priority,duration: gift.duration,lottieAnimationPath: gift.animationPath,particleEffectPath: gift.particleEffectPath,backgroundMusicPath: gift.backgroundMusicPath,additionalEffects: gift.additionalEffects,);case GiftType.threeD:return ThreeDGiftEffect(effectId: gift.id,priority: gift.priority,duration: gift.duration,modelPath: gift.modelPath,animationName: gift.animationName,lights: gift.lights,camera: gift.camera,);case GiftType.special:// 特殊禮物類型的處理return SpecialGiftEffect(effectId: gift.id,priority: gift.priority,duration: gift.duration,// 特殊參數...);default:throw Exception('Unsupported gift type: ${gift.type}');}}
}
4. 禮物特效管理器
class GiftEffectManager {final List<GiftEffect> _activeEffects = [];final Queue<GiftEffect> _effectQueue = Queue();final int _maxConcurrentEffects;GiftEffectManager({int maxConcurrentEffects = 3}): _maxConcurrentEffects = maxConcurrentEffects;// 添加禮物特效到播放隊列Future<void> addGiftEffect(GiftItem gift) async {final effect = GiftEffectFactory.createEffect(gift);// 初始化特效await effect.initialize();// 監聽狀態變化effect.onStateChanged = (state) {if (state == GiftEffectState.completed || state == GiftEffectState.error) {_removeEffect(effect);_playNextEffect();}};// 根據優先級決定是立即播放還是加入隊列if (_activeEffects.length < _maxConcurrentEffects) {_activeEffects.add(effect);await effect.play();} else {// 查找隊列中是否有更低優先級的特效可以替換final lowestPriorityEffect = _findLowestPriorityEffect();if (lowestPriorityEffect != null && effect.priority > lowestPriorityEffect.priority) {// 暫停低優先級特效,播放高優先級特效await lowestPriorityEffect.stop();_activeEffects.remove(lowestPriorityEffect);_effectQueue.addFirst(lowestPriorityEffect);_activeEffects.add(effect);await effect.play();} else {// 加入隊列等待_effectQueue.add(effect);}}}// 查找當前播放中優先級最低的特效GiftEffect? _findLowestPriorityEffect() {if (_activeEffects.isEmpty) return null;GiftEffect lowest = _activeEffects.first;for (final effect in _activeEffects) {if (effect.priority < lowest.priority) {lowest = effect;}}return lowest;}// 移除特效void _removeEffect(GiftEffect effect) {_activeEffects.remove(effect);effect.dispose();}// 播放下一個隊列中的特效void _playNextEffect() {if (_effectQueue.isNotEmpty && _activeEffects.length < _maxConcurrentEffects) {final nextEffect = _effectQueue.removeFirst();_activeEffects.add(nextEffect);nextEffect.play();}}// 清空所有特效Future<void> clearAllEffects() async {for (final effect in _activeEffects) {await effect.stop();await effect.dispose();}_activeEffects.clear();for (final effect in _effectQueue) {await effect.dispose();}_effectQueue.clear();}
}
5. 資源加載與緩存
class EffectResourceLoader {static final Map<String, dynamic> _resourceCache = {};// 預加載常用禮物資源static Future<void> preloadCommonGiftResources(List<String> giftIds) async {await Future.wait(giftIds.map((id) => _preloadGiftResources(id)));}static Future<void> _preloadGiftResources(String giftId) async {// 獲取禮物配置信息final giftConfig = await GiftConfigService.getConfig(giftId);// 使用Isolate加載資源,避免阻塞UI線程final resources = await compute(_loadResourcesInIsolate, giftConfig);// 緩存資源_resourceCache[giftId] = resources;}static Future<Map<String, dynamic>> _loadResourcesInIsolate(GiftConfig config) async {final resources = <String, dynamic>{};// 加載動畫資源if (config.animationPath != null) {resources['animation'] = await loadAnimation(config.animationPath!);}// 加載音效資源if (config.soundPath != null) {resources['sound'] = await loadAudio(config.soundPath!);}// 加載其他資源...return resources;}// 獲取已緩存的資源static dynamic getCachedResource(String giftId, String resourceType) {final giftResources = _resourceCache[giftId];return giftResources != null ? giftResources[resourceType] : null;}// 清理緩存static void clearCache() {_resourceCache.clear();}
}
三、性能優化策略
1. 資源預加載與緩存
???????
在應用啟動時預加載常用禮物資源
實現LRU緩存策略,自動清理不常用的資源
根據用戶行為預測下一步可能發送的禮物,提前加載
2. Isolate的使用
??????????????
使用Isolate進行資源加載,避免阻塞UI線程
復雜特效的渲染在單獨的Isolate中進行
實現Isolate池管理,復用Isolate實例
3. 內存管理
??????????????
實現特效資源的引用計數,及時釋放不再使用的資源
監控內存使用情況,在內存緊張時自動降低特效質量或跳過次要特效
4. 特效優先級與隊列管理
??????????????
根據禮物價值、發送者身份等因素動態調整特效優先級
實現智能隊列管理,避免低優先級特效阻塞高優先級特效
總結
這個禮物特效播放架構通過抽象工廠模式創建不同類型的特效,使用管理器處理特效的優先級和并發播放,利用Isolate進行資源加載和復雜計算,實現了高性能、可擴展的禮物特效系統。該架構具有以下優點:
良好的擴展性:通過抽象接口,可以輕松添加新的禮物特效類型
高性能:使用Isolate進行資源加載和復雜計算,避免阻塞UI線程
智能調度:基于優先級的特效隊列管理,確保重要特效優先播放
內存友好:實現了資源緩存和內存管理機制,避免內存泄漏
易于維護:分層架構和清晰的職責劃分,使代碼易于理解和維護
這種設計能夠滿足語聊房項目中各種復雜禮物特效的需求,同時保證應用的流暢性和穩定性。