情绪小智:当AI聊天遇上表情动画,打造 "有情绪"的小智

149 阅读5分钟

🎭 情绪小智:当AI聊天遇上表情动画,打造 "有情绪的小智"

screenshot-3.jpg

🚀 项目背景:

没啥背景,就是看到 LOOI 项目挺有趣的,刚好上手弄一个玩儿玩儿。想做的事情其实很多,这只是个开始(希望别只是开始。。。)。其实现在这个并不是我期望的效果。但是拖延症犯了,先丢出来再说吧。

🎨 核心亮点:表情系统

表情驱动的聊天体验

系统会根据AI的回复内容,自动匹配最合适的表情:

// 根据AI回复内容智能更新表情
void updateExpressionFromAIResponse(String content) {
  final expression = FaceExpressionExtension.fromAIResponse(content);
  updateExpression(expression);
}

表情映射逻辑

  • 😊 开心/兴奋 - 当AI回复包含"太好了"、"真棒"等词汇
  • 😮 惊讶 - 当AI回复包含"哇"、"没想到"等词汇
  • 🤔 思考 - 当AI回复包含"让我想想"、"考虑一下"等词汇
  • 😍 喜爱 - 当AI回复包含"喜欢"、"爱"等词汇
  • 😉 眨眼 - 随机触发,增加生动感

实时动画效果

实现了多种动画效果:

1. 自动眨眼动画
void _startAutoBlinking() {
  _stopAutoBlinking();
  _scheduleNextBlink();
}

void _scheduleNextBlink() {
  if (!state.config.autoBlinkEnabled) return;
  
  // 随机间隔2-5秒
  final delay = Duration(milliseconds: 2000 + _random.nextInt(3000));
  
  _blinkTimer = Timer(delay, () {
    if (!isClosed && state.config.autoBlinkEnabled) {
      triggerBlink();
      _scheduleNextBlink();
    }
  });
}
2. 说话动画
void startSpeaking() {
  if (state.isSpeaking) return;
  
  // 启动说话动画定时器
  _speakingTimer = Timer.periodic(const Duration(milliseconds: 100), (timer) {
    if (!state.isSpeaking) {
      timer.cancel();
      return;
    }
    
    // 模拟说话时的嘴部动画
    final animationValue = 0.5 + 0.5 * math.sin(timer.tick * 0.5);
    emit(state.copyWith(speakingAnimationValue: animationValue));
  });
}
3. 呼吸动画
// 动画大小变化,0.8 倍到 1.2 倍之间缩放
_pulseAnimation = Tween<double>(begin: 0.98, end: 1.05).animate(
  CurvedAnimation(parent: _pulseController, curve: Curves.easeInOut),
);

🎯 技术实现:Flutter + 状态管理

架构设计

采用了Bloc/Cubit + Provider的混合状态管理方案(遗留问题,后续统一优化):

class FaceAnimationCubit extends Cubit<FaceAnimationState> {
  Timer? _blinkTimer;
  Timer? _speakingTimer;
  final math.Random _random = math.Random();

  FaceAnimationCubit() : super(const FaceAnimationState()) {
    _startAutoBlinking();
  }

  // 更新表情
  void updateExpression(FaceExpression expression) {
    final newConfig = state.config.copyWith(expression: expression);
    emit(state.copyWith(config: newConfig));
  }
}

音频处理技术栈

为了实现流畅的语音交互,使用了以下音频处理技术:

// 音频处理相关库
opus_flutter: ^3.0.3        // Opus音频编码
opus_dart: ^3.0.1           // Dart Opus实现
flutter_sound: ^9.26.0      // 音频录制和播放
flutter_pcm_player: ^0.0.1  // PCM播放器
audio_streamer: ^4.1.1      // 音频流处理

音频处理流程

  1. 录音 - 使用PCM格式录制用户语音
  2. 编码 - 转换为Opus格式发送给服务器
  3. 解码 - 接收服务器返回的Opus音频流
  4. 播放 - 转换为PCM格式播放

WebSocket实时通信

class XiaozhiService {
  // 处理WebSocket消息
  void _handleTextMessage(String message) {
    try {
      final Map<String, dynamic> jsonData = json.decode(message);
      final String type = jsonData['type'] ?? '';

      switch (type) {
        case 'tts':
          // TTS消息处理
          final String text = jsonData['text'] ?? '';
          if (text.isNotEmpty) {
            _dispatchEvent(
              XiaozhiServiceEvent(XiaozhiServiceEventType.textMessage, text),
            );
          }
          break;
        case 'emotion':
          // 处理表情消息
          final String emotion = jsonData['emotion'] ?? '';
          if (emotion.isNotEmpty) {
            _dispatchEvent(
              XiaozhiServiceEvent(
                XiaozhiServiceEventType.textMessage,
                '表情: $emotion',
              ),
            );
          }
          break;
      }
    } catch (e) {
      print('$TAG: 解析消息失败: $e');
    }
  }
}

🎪 用户体验:沉浸式动画聊天

全屏沉浸体验

设计了横屏全屏的动画聊天界面,让用户完全沉浸在对话中:

// 强制横屏
SystemChrome.setPreferredOrientations([
  DeviceOrientation.landscapeLeft,
  DeviceOrientation.landscapeRight,
]);

// 设置全屏模式,隐藏状态栏和导航栏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);

交互设计

  • 点击面部 - 循环切换表情
  • 长按面部 - 显示/隐藏日志区域
  • 点击打断 - 中断对话内容
  • 点击挂断 - 退出全屏对话

🔧 开发挑战与解决方案

挑战1:表情与内容的智能匹配

问题:如何让 AI 的表情变化更自然、更符合对话内容?

解决方案:直接让 AI 驱动动画模型生成动画,不要使用固定模式。

🎨 动画效果展示

表情动画库

预定义了多种表情状态:

enum FaceExpression {
  neutral,    // 中性
  happy,      // 开心
  surprised,  // 惊讶
  excited,    // 兴奋
  love,       // 喜爱
  winking,    // 眨眼
  speaking,   // 说话
  thinking,   // 思考
}

动画配置

class AnimationConfig {
  final FaceExpression expression;
  final Color eyeColor;
  final Color mouthColor;
  final FaceSize faceSize;
  final bool autoBlinkEnabled;
  final bool isSpeaking;
}

🚀 未来规划

  • 优化动画过渡效果
  • 增加更多表情类型
  • 添加手势控制
  • 支持自定义表情
  • 集成更多AI平台
  • 支持3D面部动画
  • 添加情感分析
  • 实现多语言支持

🤝 参与讨论

这个项目还有很多可以改进的地方,欢迎开发者们参与讨论:

  • 表情算法优化 - 如何让表情匹配更智能?
  • 动画性能提升 - 有什么更好的动画实现方案?
  • 用户体验改进 - 你觉得哪些交互可以优化?
  • 技术架构讨论 - 对当前的技术选型有什么建议?

项目地址GitHub - emotionzhi

技术栈:Flutter + Dart + Opus + WebSocket + Bloc/Cubit

支持平台:Android、iOS


让AI聊天变得有温度,让技术更有趣! 🎭✨


📝 开发者笔记

项目结构

lib/
├── cubits/          # 状态管理 - FaceAnimationCubit
├── models/          # 数据模型 - AnimationConfig, Conversation
├── providers/       # 状态提供者 - ConfigProvider, ConversationProvider
├── screens/         # 界面页面 - AnimationCallScreen, HomeScreen
├── services/        # 业务服务 - XiaozhiService, WebSocketManager
├── utils/           # 工具类 - AudioUtil, DeviceUtil
└── widgets/         # 自定义组件 - RobotFaceWidget

关键依赖

dependencies:
  flutter_bloc: ^8.1.3      # 状态管理
  opus_flutter: ^3.0.3      # 音频编码
  flutter_sound: ^9.26.0    # 音频处理
  web_socket_channel: ^2.4.0 # WebSocket通信
  freezed: ^2.4.6           # 代码生成

开发环境

  • Flutter 3.7.0+
  • Dart 3.0+
  • Android SDK 21+
  • iOS 11.0+

⭕️ 小结及后续

  1. 表情动画:需要丰富表情效果,这个是重点。现在也有一些想法了,现验证下再说。
  2. 表情更新:目前临时方案是关键字匹配逻辑,后续会让 AI 根据对话内容自动生成情绪模型,然后模型驱动表情动画。
  3. 语音效果:目前是语音对话,感觉没啥意思,后续考虑减少说话频率,或者不说话。所以这块没打算精进了。

如果你觉得这个项目有趣,欢迎Star和Fork!也欢迎在Issues中提出你的想法和建议。 🚀