Harmony Flutter 跨平台开发实战:鸿蒙与音乐律动艺术、元胞自动机:生命游戏的音频演化逻辑

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net


🧬 一、元胞自动机:简单规则产生复杂行为

📚 1.1 元胞自动机的历史

元胞自动机(Cellular Automaton,简称 CA)是由数学家**约翰·冯·诺依曼(John von Neumann)**在 1940 年代提出的概念,最初用于研究自复制系统。1970 年,**约翰·康威(John Conway)**发明了著名的"生命游戏",使元胞自动机广为人知。

历史里程碑

年份 人物 贡献
1940 斯坦尼斯瓦夫·乌拉姆 提出网格模型
1948 约翰·冯·诺依曼 自复制自动机
1970 约翰·康威 生命游戏
1983 斯蒂芬·沃尔夫拉姆 一维 CA 分类
2002 斯蒂芬·沃尔夫拉姆 《一种新科学》

📐 1.2 生命游戏的规则

康威生命游戏是最著名的二维元胞自动机,规则极其简单却能产生复杂的行为:

基本规则

状态 邻居数量 结果
活细胞 < 2 死亡(孤独)
活细胞 2 或 3 存活
活细胞 > 3 死亡(拥挤)
死细胞 = 3 复活(繁殖)
复制代码
生命游戏示意:

初始状态:        下一代:
□ □ □ □ □       □ □ ■ □ □
□ □ ■ □ □  →    □ □ ■ □ □
□ □ ■ □ □       □ □ ■ □ □
□ □ ■ □ □       □ □ □ □ □
□ □ □ □ □       □ □ □ □ □

■ = 活细胞
□ = 死细胞

🔬 1.3 经典图案与行为

生命游戏中存在许多有趣的图案:

图案类型 名称 特点
🔄 振荡器 闪烁器、蟾蜍 周期性变化
🚀 飞船 滑翔机、轻量飞船 持续移动
🔫 滑翔机枪 持续产生飞船
💥 爆发 R-五格骨牌 快速扩张
🏛️ 静止 方块、蜂巢 稳定不变

滑翔机(Glider)

复制代码
□ ■ □
□ □ ■
■ ■ ■

🎯 1.4 元胞自动机的应用

领域 应用 效果
🧬 生物学 生态系统模拟 种群动态
🌊 物理学 流体动力学 格子气模型
🔐 密码学 伪随机数生成 流密码
🎨 艺术 生成艺术 动态图案
🎵 音乐可视化 音频驱动演化 节奏响应

🔧 二、元胞自动机的 Dart 实现

🧮 2.1 基础网格类

dart 复制代码
import 'dart:math';
import 'dart:typed_data';

/// 元胞状态
enum CellState { dead, alive }

/// 二维网格
class Grid2D {
  final int width;
  final int height;
  final List<int> _cells;
  
  Grid2D(this.width, this.height) : _cells = List<int>.filled(width * height, 0);
  
  /// 获取单元格状态
  int get(int x, int y) {
    if (x < 0 || x >= width || y < 0 || y >= height) return 0;
    return _cells[y * width + x];
  }
  
  /// 设置单元格状态
  void set(int x, int y, int value) {
    if (x >= 0 && x < width && y >= 0 && y < height) {
      _cells[y * width + x] = value;
    }
  }
  
  /// 获取邻居数量
  int countNeighbors(int x, int y) {
    int count = 0;
    for (int dy = -1; dy <= 1; dy++) {
      for (int dx = -1; dx <= 1; dx++) {
        if (dx == 0 && dy == 0) continue;
        count += get(x + dx, y + dy);
      }
    }
    return count;
  }
  
  /// 清空网格
  void clear() {
    _cells.fillRange(0, _cells.length, 0);
  }
  
  /// 随机填充
  void randomize(double density, [Random? random]) {
    random ??= Random();
    for (int i = 0; i < _cells.length; i++) {
      _cells[i] = random!.nextDouble() < density ? 1 : 0;
    }
  }
  
  /// 复制网格
  Grid2D copy() {
    final newGrid = Grid2D(width, height);
    for (int i = 0; i < _cells.length; i++) {
      newGrid._cells[i] = _cells[i];
    }
    return newGrid;
  }
  
  /// 获取活细胞数量
  int get aliveCount => _cells.fold(0, (sum, cell) => sum + cell);
  
  /// 获取密度
  double get density => aliveCount / _cells.length;
}

/// 生命游戏规则
class LifeRules {
  final Set<int> birthConditions;
  final Set<int> surviveConditions;
  
  const LifeRules({
    required this.birthConditions,
    required this.surviveConditions,
  });
  
  /// 标准康威生命游戏规则
  static const LifeRules conway = LifeRules(
    birthConditions: {3},
    surviveConditions: {2, 3},
  );
  
  /// HighLife 规则
  static const LifeRules highLife = LifeRules(
    birthConditions: {3, 6},
    surviveConditions: {2, 3},
  );
  
  /// Day & Night 规则
  static const LifeRules dayNight = LifeRules(
    birthConditions: {3, 6, 7, 8},
    surviveConditions: {3, 4, 6, 7, 8},
  );
  
  /// Seeds 规则(爆炸性增长)
  static const LifeRules seeds = LifeRules(
    birthConditions: {2},
    surviveConditions: {},
  );
  
  /// 判断下一个状态
  bool nextState(bool isAlive, int neighbors) {
    if (isAlive) {
      return surviveConditions.contains(neighbors);
    } else {
      return birthConditions.contains(neighbors);
    }
  }
}

⚡ 2.2 生命游戏引擎

dart 复制代码
/// 生命游戏引擎
class LifeEngine {
  Grid2D grid;
  LifeRules rules;
  int generation;
  
  LifeEngine({
    required int width,
    required int height,
    this.rules = LifeRules.conway,
  }) : grid = Grid2D(width, height),
       generation = 0;
  
  /// 演化一代
  void step() {
    final newGrid = Grid2D(grid.width, grid.height);
    
    for (int y = 0; y < grid.height; y++) {
      for (int x = 0; x < grid.width; x++) {
        final isAlive = grid.get(x, y) == 1;
        final neighbors = grid.countNeighbors(x, y);
        
        if (rules.nextState(isAlive, neighbors)) {
          newGrid.set(x, y, 1);
        }
      }
    }
    
    grid = newGrid;
    generation++;
  }
  
  /// 演化多代
  void stepMultiple(int count) {
    for (int i = 0; i < count; i++) {
      step();
    }
  }
  
  /// 重置
  void reset() {
    grid.clear();
    generation = 0;
  }
  
  /// 随机初始化
  void randomize(double density) {
    grid.randomize(density);
    generation = 0;
  }
  
  /// 添加图案
  void addPattern(int x, int y, List<List<int>> pattern) {
    for (int py = 0; py < pattern.length; py++) {
      for (int px = 0; px < pattern[py].length; px++) {
        grid.set(x + px, y + py, pattern[py][px]);
      }
    }
  }
  
  /// 预设图案
  static const List<List<int>> glider = [
    [0, 1, 0],
    [0, 0, 1],
    [1, 1, 1],
  ];
  
  static const List<List<int>> blinker = [
    [1, 1, 1],
  ];
  
  static const List<List<int>> beacon = [
    [1, 1, 0, 0],
    [1, 0, 0, 0],
    [0, 0, 0, 1],
    [0, 0, 1, 1],
  ];
  
  static const List<List<int>> pulsar = [
    [0,0,1,1,1,0,0,0,1,1,1,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0],
    [1,0,0,0,0,1,0,1,0,0,0,0,1],
    [1,0,0,0,0,1,0,1,0,0,0,0,1],
    [1,0,0,0,0,1,0,1,0,0,0,0,1],
    [0,0,1,1,1,0,0,0,1,1,1,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,1,1,1,0,0,0,1,1,1,0,0],
    [1,0,0,0,0,1,0,1,0,0,0,0,1],
    [1,0,0,0,0,1,0,1,0,0,0,0,1],
    [1,0,0,0,0,1,0,1,0,0,0,0,1],
    [0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,1,1,1,0,0,0,1,1,1,0,0],
  ];
}

🎨 2.3 音频驱动的元胞自动机

dart 复制代码
import 'package:flutter/material.dart';
import 'package:just_audio_ohos/just_audio_ohos.dart';
import 'package:audio_session/audio_session.dart';

/// 音频驱动的生命游戏控制器
class AudioLifeController extends ChangeNotifier {
  final AudioPlayer _player = AudioPlayer();
  late LifeEngine _engine;
  
  bool _isPlaying = false;
  bool _isRunning = false;
  Duration _position = Duration.zero;
  Duration _duration = Duration.zero;
  
  Float32List _audioData = Float32List(128);
  double _energy = 0;
  double _bass = 0;
  double _mid = 0;
  double _treble = 0;
  
  double _time = 0;
  int _gridWidth = 80;
  int _gridHeight = 80;
  double _stepInterval = 0.1;
  double _accumulatedTime = 0;
  
  bool get isPlaying => _isPlaying;
  bool get isRunning => _isRunning;
  Duration get position => _position;
  Duration get duration => _duration;
  LifeEngine get engine => _engine;
  Float32List get audioData => _audioData;
  double get energy => _energy;
  double get bass => _bass;
  double get mid => _mid;
  double get treble => _treble;
  AudioPlayer get player => _player;
  
  /// 初始化
  Future<void> initialize() async {
    final session = await AudioSession.instance;
    await session.configure(const AudioSessionConfiguration.music());
    
    _engine = LifeEngine(width: _gridWidth, height: _gridHeight);
    _engine.randomize(0.3);
    
    _player.playerStateStream.listen((state) {
      _isPlaying = state.playing;
      notifyListeners();
    });
    
    _player.positionStream.listen((position) {
      _position = position;
      notifyListeners();
    });
    
    _player.durationStream.listen((duration) {
      _duration = duration ?? Duration.zero;
      notifyListeners();
    });
  }
  
  /// 加载网络音频
  Future<void> loadAudio(String url) async {
    try {
      await _player.setUrl(url);
    } catch (e) {
      debugPrint('加载音频失败: $e');
    }
  }
  
  /// 更新
  void update(double dt) {
    _time += dt;
    
    // 更新音频数据
    _updateAudioData();
    
    // 计算音频特征
    _calculateAudioFeatures();
    
    // 更新演化速度
    _updateStepInterval();
    
    // 演化逻辑
    _accumulatedTime += dt;
    if (_isRunning && _accumulatedTime >= _stepInterval) {
      _engine.step();
      _accumulatedTime = 0;
      
      // 音频驱动的细胞注入
      _injectCells();
    }
    
    notifyListeners();
  }
  
  void _updateAudioData() {
    final random = Random();
    
    for (int i = 0; i < 128; i++) {
      if (_isPlaying) {
        final freq = (i / 128) * 8 + 1;
        final wave1 = sin(_time * freq) * 0.4;
        final wave2 = sin(_time * freq * 1.5 + pi / 3) * 0.3;
        final noise = (random.nextDouble() - 0.5) * 0.15;
        final bassBoost = i < 32 ? 0.3 : 0;
        
        _audioData[i] = _audioData[i] * 0.85 + 
            (wave1 + wave2 + noise + bassBoost) * 0.15;
      } else {
        _audioData[i] *= 0.95;
      }
    }
  }
  
  void _calculateAudioFeatures() {
    double totalEnergy = 0;
    double bassEnergy = 0;
    double midEnergy = 0;
    double trebleEnergy = 0;
    
    for (int i = 0; i < 128; i++) {
      final value = _audioData[i].abs();
      totalEnergy += value;
      
      if (i < 32) {
        bassEnergy += value;
      } else if (i < 96) {
        midEnergy += value;
      } else {
        trebleEnergy += value;
      }
    }
    
    _energy = totalEnergy / 128;
    _bass = bassEnergy / 32;
    _mid = midEnergy / 64;
    _treble = trebleEnergy / 32;
  }
  
  void _updateStepInterval() {
    // 能量越高,演化越快
    _stepInterval = 0.2 - _energy * 0.15;
    _stepInterval = _stepInterval.clamp(0.02, 0.2);
  }
  
  void _injectCells() {
    final random = Random();
    
    // 低音脉冲注入细胞
    if (_bass > 0.5) {
      final count = (_bass * 20).toInt();
      for (int i = 0; i < count; i++) {
        final x = random.nextInt(_gridWidth);
        final y = random.nextInt(_gridHeight);
        _engine.grid.set(x, y, 1);
      }
    }
    
    // 高频注入滑翔机
    if (_treble > 0.6 && random.nextDouble() < 0.3) {
      final x = random.nextInt(_gridWidth - 3);
      final y = random.nextInt(_gridHeight - 3);
      _engine.addPattern(x, y, LifeEngine.glider);
    }
  }
  
  /// 开始/暂停演化
  void toggleRunning() {
    _isRunning = !_isRunning;
    notifyListeners();
  }
  
  /// 单步演化
  void stepOnce() {
    _engine.step();
    notifyListeners();
  }
  
  /// 重置
  void reset() {
    _engine.reset();
    _engine.randomize(0.3);
    notifyListeners();
  }
  
  /// 切换单元格
  void toggleCell(int x, int y) {
    final current = _engine.grid.get(x, y);
    _engine.grid.set(x, y, current == 0 ? 1 : 0);
    notifyListeners();
  }
  
  /// 设置规则
  void setRules(LifeRules rules) {
    _engine.rules = rules;
    notifyListeners();
  }
  
  /// 播放/暂停音频
  Future<void> togglePlay() async {
    if (_isPlaying) {
      await _player.pause();
    } else {
      await _player.play();
    }
  }
  
  /// 跳转
  Future<void> seek(Duration position) async {
    await _player.seek(position);
  }
  
  @override
  void dispose() {
    _player.dispose();
    super.dispose();
  }
}

📦 三、完整示例代码

以下是完整的元胞自动机音乐可视化示例代码:

dart 复制代码
import 'package:flutter/material.dart';
import 'package:just_audio_ohos/just_audio_ohos.dart';
import 'package:audio_session/audio_session.dart';
import 'dart:math';
import 'dart:typed_data';

void main() {
  runApp(const LifeGameApp());
}

class LifeGameApp extends StatelessWidget {
  const LifeGameApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '元胞自动机',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.green, brightness: Brightness.dark),
        useMaterial3: true,
      ),
      home: const LifeHomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class LifeHomePage extends StatelessWidget {
  const LifeHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('🧬 元胞自动机'), backgroundColor: Theme.of(context).colorScheme.inversePrimary),
      body: ListView(padding: const EdgeInsets.all(16), children: [
        _buildCard(context, title: '基础生命游戏', description: '康威生命游戏', icon: Icons.grid_on, color: Colors.green,
            onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const BasicLifeDemo()))),
        _buildCard(context, title: '规则变体', description: '不同规则效果', icon: Icons.settings, color: Colors.teal,
            onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const RulesDemo()))),
        _buildCard(context, title: '图案库', description: '经典图案展示', icon: Icons.category, color: Colors.cyan,
            onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const PatternsDemo()))),
        _buildCard(context, title: '音乐生命', description: '音频驱动演化', icon: Icons.music_note, color: Colors.orange,
            onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const MusicLifeDemo()))),
        _buildCard(context, title: '交互模式', description: '触摸绘制细胞', icon: Icons.touch_app, color: Colors.purple,
            onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const InteractiveLifeDemo()))),
      ]),
    );
  }

  Widget _buildCard(BuildContext context, {required String title, required String description, required IconData icon, 
      required Color color, required VoidCallback onTap}) {
    return Card(
      margin: const EdgeInsets.only(bottom: 12),
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: InkWell(
        onTap: onTap,
        borderRadius: BorderRadius.circular(16),
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Row(children: [
            Container(width: 56, height: 56, decoration: BoxDecoration(color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12)),
                child: Icon(icon, color: color, size: 28)),
            const SizedBox(width: 16),
            Expanded(child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
              Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
              const SizedBox(height: 4),
              Text(description, style: TextStyle(color: Colors.grey[600], fontSize: 14)),
            ])),
            Icon(Icons.chevron_right, color: Colors.grey[400]),
          ]),
        ),
      ),
    );
  }
}

/// 网格类
class Grid {
  final int width, height;
  final List<int> cells;
  Grid(this.width, this.height) : cells = List<int>.filled(width * height, 0);
  
  int get(int x, int y) => (x < 0 || x >= width || y < 0 || y >= height) ? 0 : cells[y * width + x];
  void set(int x, int y, int v) { if (x >= 0 && x < width && y >= 0 && y < height) cells[y * width + x] = v; }
  
  int countNeighbors(int x, int y) {
    int c = 0;
    for (int dy = -1; dy <= 1; dy++) for (int dx = -1; dx <= 1; dx++) 
      if (dx != 0 || dy != 0) c += get(x + dx, y + dy);
    return c;
  }
  
  void randomize(double density) {
    final r = Random();
    for (int i = 0; i < cells.length; i++) cells[i] = r.nextDouble() < density ? 1 : 0;
  }
  
  void clear() => cells.fillRange(0, cells.length, 0);
}

/// 基础生命游戏演示
class BasicLifeDemo extends StatefulWidget {
  const BasicLifeDemo({super.key});
  @override
  State<BasicLifeDemo> createState() => _BasicLifeDemoState();
}

class _BasicLifeDemoState extends State<BasicLifeDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Grid _grid;
  bool _running = false;
  int _generation = 0;
  final int _size = 60;

  @override
  void initState() {
    super.initState();
    _grid = Grid(_size, _size);
    _grid.randomize(0.3);
    _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 100))..repeat();
    _controller.addListener(_update);
  }
  
  void _update() {
    if (_running) {
      _step();
      setState(() {});
    }
  }
  
  void _step() {
    final newGrid = Grid(_size, _size);
    for (int y = 0; y < _size; y++) {
      for (int x = 0; x < _size; x++) {
        final alive = _grid.get(x, y) == 1;
        final n = _grid.countNeighbors(x, y);
        newGrid.set(x, y, (alive && (n == 2 || n == 3)) || (!alive && n == 3) ? 1 : 0);
      }
    }
    _grid = newGrid;
    _generation++;
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('基础生命游戏')),
      body: Column(children: [
        Expanded(child: CustomPaint(painter: LifePainter(_grid), size: Size.infinite)),
        _buildControls(),
      ]),
    );
  }

  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(16),
      color: Colors.black12,
      child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
        Text('代数: $_generation', style: const TextStyle(color: Colors.white)),
        IconButton(icon: Icon(_running ? Icons.pause : Icons.play_arrow, color: Colors.green),
            onPressed: () => setState(() => _running = !_running)),
        IconButton(icon: const Icon(Icons.skip_next, color: Colors.white),
            onPressed: () { _step(); setState(() {}); }),
        IconButton(icon: const Icon(Icons.refresh, color: Colors.white),
            onPressed: () { _grid.randomize(0.3); _generation = 0; setState(() {}); }),
      ]),
    );
  }
}

class LifePainter extends CustomPainter {
  final Grid grid;
  LifePainter(this.grid);
  
  @override
  void paint(Canvas canvas, Size size) {
    final cellW = size.width / grid.width;
    final cellH = size.height / grid.height;
    
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a0a15));
    
    for (int y = 0; y < grid.height; y++) {
      for (int x = 0; x < grid.width; x++) {
        if (grid.get(x, y) == 1) {
          final hue = (x + y) * 2 % 360;
          canvas.drawRect(
            Rect.fromLTWH(x * cellW, y * cellH, cellW - 1, cellH - 1),
            Paint()..color = HSVColor.fromAHSV(1, hue.toDouble(), 0.7, 1).toColor(),
          );
        }
      }
    }
  }
  
  @override
  bool shouldRepaint(covariant LifePainter old) => true;
}

/// 规则变体演示
class RulesDemo extends StatefulWidget {
  const RulesDemo({super.key});
  @override
  State<RulesDemo> createState() => _RulesDemoState();
}

class _RulesDemoState extends State<RulesDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Grid _grid;
  bool _running = false;
  String _ruleName = 'Conway';
  final int _size = 50;
  
  final Map<String, Set<int>> _birthRules = {
    'Conway': {3},
    'HighLife': {3, 6},
    'Day&Night': {3, 6, 7, 8},
    'Seeds': {2},
  };
  
  final Map<String, Set<int>> _surviveRules = {
    'Conway': {2, 3},
    'HighLife': {2, 3},
    'Day&Night': {3, 4, 6, 7, 8},
    'Seeds': {},
  };

  @override
  void initState() {
    super.initState();
    _grid = Grid(_size, _size);
    _grid.randomize(0.3);
    _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 100))..repeat();
    _controller.addListener(_update);
  }
  
  void _update() {
    if (_running) {
      _step();
      setState(() {});
    }
  }
  
  void _step() {
    final birth = _birthRules[_ruleName]!;
    final survive = _surviveRules[_ruleName]!;
    final newGrid = Grid(_size, _size);
    
    for (int y = 0; y < _size; y++) {
      for (int x = 0; x < _size; x++) {
        final alive = _grid.get(x, y) == 1;
        final n = _grid.countNeighbors(x, y);
        newGrid.set(x, y, (alive && survive.contains(n)) || (!alive && birth.contains(n)) ? 1 : 0);
      }
    }
    _grid = newGrid;
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('规则变体')),
      body: Column(children: [
        Expanded(child: CustomPaint(painter: LifePainter(_grid), size: Size.infinite)),
        _buildControls(),
      ]),
    );
  }

  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(16),
      color: Colors.black12,
      child: Column(children: [
        Wrap(spacing: 8, children: _birthRules.keys.map((name) => 
          ChoiceChip(label: Text(name), selected: _ruleName == name,
              onSelected: (_) => setState(() { _ruleName = name; _grid.randomize(0.3); }))).toList()),
        const SizedBox(height: 8),
        Row(mainAxisAlignment: MainAxisAlignment.center, children: [
          IconButton(icon: Icon(_running ? Icons.pause : Icons.play_arrow, color: Colors.green),
              onPressed: () => setState(() => _running = !_running)),
          IconButton(icon: const Icon(Icons.refresh, color: Colors.white),
              onPressed: () { _grid.randomize(0.3); setState(() {}); }),
        ]),
      ]),
    );
  }
}

/// 图案库演示
class PatternsDemo extends StatefulWidget {
  const PatternsDemo({super.key});
  @override
  State<PatternsDemo> createState() => _PatternsDemoState();
}

class _PatternsDemoState extends State<PatternsDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Grid _grid;
  bool _running = false;
  final int _size = 60;
  String _pattern = 'Glider';
  
  final Map<String, List<List<int>>> _patterns = {
    'Glider': [[0,1,0],[0,0,1],[1,1,1]],
    'Blinker': [[1,1,1]],
    'Beacon': [[1,1,0,0],[1,0,0,0],[0,0,0,1],[0,0,1,1]],
    'Toad': [[0,1,1,1],[1,1,1,0]],
    'Pulsar': [
      [0,0,1,1,1,0,0,0,1,1,1,0,0],
      [0,0,0,0,0,0,0,0,0,0,0,0,0],
      [1,0,0,0,0,1,0,1,0,0,0,0,1],
      [1,0,0,0,0,1,0,1,0,0,0,0,1],
      [1,0,0,0,0,1,0,1,0,0,0,0,1],
      [0,0,1,1,1,0,0,0,1,1,1,0,0],
      [0,0,0,0,0,0,0,0,0,0,0,0,0],
      [0,0,1,1,1,0,0,0,1,1,1,0,0],
      [1,0,0,0,0,1,0,1,0,0,0,0,1],
      [1,0,0,0,0,1,0,1,0,0,0,0,1],
      [1,0,0,0,0,1,0,1,0,0,0,0,1],
      [0,0,0,0,0,0,0,0,0,0,0,0,0],
      [0,0,1,1,1,0,0,0,1,1,1,0,0],
    ],
  };

  @override
  void initState() {
    super.initState();
    _grid = Grid(_size, _size);
    _addPattern();
    _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 150))..repeat();
    _controller.addListener(_update);
  }
  
  void _addPattern() {
    _grid.clear();
    final pattern = _patterns[_pattern]!;
    final x = (_size - pattern[0].length) ~/ 2;
    final y = (_size - pattern.length) ~/ 2;
    for (int py = 0; py < pattern.length; py++) {
      for (int px = 0; px < pattern[py].length; px++) {
        _grid.set(x + px, y + py, pattern[py][px]);
      }
    }
  }
  
  void _update() {
    if (_running) {
      _step();
      setState(() {});
    }
  }
  
  void _step() {
    final newGrid = Grid(_size, _size);
    for (int y = 0; y < _size; y++) {
      for (int x = 0; x < _size; x++) {
        final alive = _grid.get(x, y) == 1;
        final n = _grid.countNeighbors(x, y);
        newGrid.set(x, y, (alive && (n == 2 || n == 3)) || (!alive && n == 3) ? 1 : 0);
      }
    }
    _grid = newGrid;
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('图案库')),
      body: Column(children: [
        Expanded(child: CustomPaint(painter: LifePainter(_grid), size: Size.infinite)),
        _buildControls(),
      ]),
    );
  }

  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(16),
      color: Colors.black12,
      child: Column(children: [
        Wrap(spacing: 8, children: _patterns.keys.map((name) => 
          ChoiceChip(label: Text(name), selected: _pattern == name,
              onSelected: (_) => setState(() { _pattern = name; _addPattern(); }))).toList()),
        const SizedBox(height: 8),
        Row(mainAxisAlignment: MainAxisAlignment.center, children: [
          IconButton(icon: Icon(_running ? Icons.pause : Icons.play_arrow, color: Colors.green),
              onPressed: () => setState(() => _running = !_running)),
          IconButton(icon: const Icon(Icons.refresh, color: Colors.white),
              onPressed: () { _addPattern(); setState(() {}); }),
        ]),
      ]),
    );
  }
}

/// 音乐生命演示
class MusicLifeDemo extends StatefulWidget {
  const MusicLifeDemo({super.key});
  @override
  State<MusicLifeDemo> createState() => _MusicLifeDemoState();
}

class _MusicLifeDemoState extends State<MusicLifeDemo> with TickerProviderStateMixin {
  late AnimationController _animController;
  late AudioPlayer _audioPlayer;
  late Grid _grid;
  Float32List _audioData = Float32List(128);
  bool _isPlaying = false;
  bool _running = true;
  Duration _position = Duration.zero;
  Duration _duration = Duration.zero;
  double _energy = 0, _bass = 0;
  double _time = 0, _stepTime = 0;
  final int _size = 50;
  
  static const String _audioUrl = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3';

  @override
  void initState() {
    super.initState();
    _grid = Grid(_size, _size);
    _grid.randomize(0.25);
    _initAudio();
    _animController = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _animController.addListener(_update);
  }
  
  Future<void> _initAudio() async {
    _audioPlayer = AudioPlayer();
    final session = await AudioSession.instance;
    await session.configure(const AudioSessionConfiguration.music());
    
    _audioPlayer.playerStateStream.listen((s) => setState(() => _isPlaying = s.playing));
    _audioPlayer.positionStream.listen((p) => setState(() => _position = p));
    _audioPlayer.durationStream.listen((d) => setState(() => _duration = d ?? Duration.zero));
    
    try { await _audioPlayer.setUrl(_audioUrl); } catch (e) { debugPrint('加载失败: $e'); }
  }
  
  void _update() {
    _time += 0.016;
    
    for (int i = 0; i < 128; i++) {
      if (_isPlaying) {
        final freq = (i / 128) * 8 + 1;
        final wave = sin(_time * freq) * 0.4 + sin(_time * freq * 1.5) * 0.3;
        final bass = i < 32 ? 0.3 : 0;
        _audioData[i] = _audioData[i] * 0.85 + (wave + bass) * 0.15;
      } else {
        _audioData[i] *= 0.95;
      }
    }
    
    double total = 0, bassE = 0;
    for (int i = 0; i < 128; i++) {
      total += _audioData[i].abs();
      if (i < 32) bassE += _audioData[i].abs();
    }
    _energy = total / 128;
    _bass = bassE / 32;
    
    // 演化
    final stepInterval = (0.15 - _energy * 0.1).clamp(0.03, 0.15);
    _stepTime += 0.016;
    
    if (_running && _stepTime >= stepInterval) {
      _step();
      _injectCells();
      _stepTime = 0;
    }
    
    setState(() {});
  }
  
  void _step() {
    final newGrid = Grid(_size, _size);
    for (int y = 0; y < _size; y++) {
      for (int x = 0; x < _size; x++) {
        final alive = _grid.get(x, y) == 1;
        final n = _grid.countNeighbors(x, y);
        newGrid.set(x, y, (alive && (n == 2 || n == 3)) || (!alive && n == 3) ? 1 : 0);
      }
    }
    _grid = newGrid;
  }
  
  void _injectCells() {
    final r = Random();
    if (_bass > 0.5) {
      for (int i = 0; i < (_bass * 15).toInt(); i++) {
        _grid.set(r.nextInt(_size), r.nextInt(_size), 1);
      }
    }
  }

  @override
  void dispose() {
    _animController.dispose();
    _audioPlayer.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('音乐生命')),
      body: Stack(children: [
        CustomPaint(painter: MusicLifePainter(_grid, _energy), size: Size.infinite),
        Positioned(bottom: 30, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(16)),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          const Text('🎵 SoundHelix - Song 1', style: TextStyle(color: Colors.white, fontSize: 14)),
          const SizedBox(height: 12),
          Slider(value: _duration.inMilliseconds > 0 ? _position.inMilliseconds.toDouble().clamp(0, _duration.inMilliseconds.toDouble()) : 0,
              max: _duration.inMilliseconds > 0 ? _duration.inMilliseconds.toDouble() : 1,
              onChanged: (v) => _audioPlayer.seek(Duration(milliseconds: v.toInt()))),
          Row(mainAxisAlignment: MainAxisAlignment.center, children: [
            IconButton(icon: Icon(_running ? Icons.pause : Icons.play_arrow, color: Colors.green),
                onPressed: () => setState(() => _running = !_running)),
            IconButton(icon: Icon(_isPlaying ? Icons.pause : Icons.play_arrow, color: Colors.orange, size: 36),
                onPressed: () => _isPlaying ? _audioPlayer.pause() : _audioPlayer.play()),
            IconButton(icon: const Icon(Icons.refresh, color: Colors.white),
                onPressed: () { _grid.randomize(0.25); setState(() {}); }),
          ]),
        ],
      ),
    );
  }
}

class MusicLifePainter extends CustomPainter {
  final Grid grid;
  final double energy;
  
  MusicLifePainter(this.grid, this.energy);
  
  @override
  void paint(Canvas canvas, Size size) {
    final cellW = size.width / grid.width;
    final cellH = size.height / grid.height;
    
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height),
        Paint()..color = Color.lerp(const Color(0xFF0a0a15), const Color(0xFF100a20), energy)!);
    
    for (int y = 0; y < grid.height; y++) {
      for (int x = 0; x < grid.width; x++) {
        if (grid.get(x, y) == 1) {
          final hue = (x * 3 + y * 5 + energy * 100) % 360;
          final paint = Paint()..color = HSVColor.fromAHSV(0.8 + energy * 0.2, hue, 0.7, 1).toColor();
          
          if (energy > 0.3) paint.maskFilter = MaskFilter.blur(BlurStyle.normal, 1 + energy * 2);
          
          canvas.drawRect(Rect.fromLTWH(x * cellW, y * cellH, cellW - 1, cellH - 1), paint);
        }
      }
    }
  }
  
  @override
  bool shouldRepaint(covariant MusicLifePainter old) => true;
}

/// 交互模式演示
class InteractiveLifeDemo extends StatefulWidget {
  const InteractiveLifeDemo({super.key});
  @override
  State<InteractiveLifeDemo> createState() => _InteractiveLifeDemoState();
}

class _InteractiveLifeDemoState extends State<InteractiveLifeDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Grid _grid;
  bool _running = false;
  final int _size = 40;

  @override
  void initState() {
    super.initState();
    _grid = Grid(_size, _size);
    _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 150))..repeat();
    _controller.addListener(_update);
  }
  
  void _update() {
    if (_running) {
      _step();
      setState(() {});
    }
  }
  
  void _step() {
    final newGrid = Grid(_size, _size);
    for (int y = 0; y < _size; y++) {
      for (int x = 0; x < _size; x++) {
        final alive = _grid.get(x, y) == 1;
        final n = _grid.countNeighbors(x, y);
        newGrid.set(x, y, (alive && (n == 2 || n == 3)) || (!alive && n == 3) ? 1 : 0);
      }
    }
    _grid = newGrid;
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('交互模式')),
      body: GestureDetector(
        onPanStart: (details) => _toggleCell(details.localPosition),
        onPanUpdate: (details) => _toggleCell(details.localPosition),
        child: Stack(children: [
          CustomPaint(painter: LifePainter(_grid), size: Size.infinite),
          Positioned(bottom: 20, left: 20, child: Container(
            padding: const EdgeInsets.all(8),
            decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(8)),
            child: const Text('触摸绘制细胞', style: TextStyle(color: Colors.white54, fontSize: 12)),
          )),
        ]),
      ),
      floatingActionButton: Row(mainAxisAlignment: MainAxisAlignment.end, children: [
        FloatingActionButton(heroTag: 'play', backgroundColor: _running ? Colors.orange : Colors.green,
            child: Icon(_running ? Icons.pause : Icons.play_arrow),
            onPressed: () => setState(() => _running = !_running)),
        const SizedBox(width: 10),
        FloatingActionButton(heroTag: 'clear', backgroundColor: Colors.red,
            child: const Icon(Icons.clear),
            onPressed: () { _grid.clear(); setState(() {}); }),
      ]),
    );
  }
  
  void _toggleCell(Offset localPosition) {
    final RenderBox box = context.findRenderObject() as RenderBox;
    final size = box.size;
    final cellW = size.width / _size;
    final cellH = size.height / _size;
    final x = (localPosition.dx / cellW).floor();
    final y = (localPosition.dy / cellH).floor();
    
    if (x >= 0 && x < _size && y >= 0 && y < _size) {
      setState(() => _grid.set(x, y, _grid.get(x, y) == 0 ? 1 : 0));
    }
  }
}

📝 四、总结

本篇文章深入探讨了元胞自动机在音乐可视化中的应用,从康威生命游戏到音频驱动的演化规则,构建了具有"生命感"的动态网格动画效果。

✅ 核心知识点回顾

知识点 说明
🧬 元胞自动机 网格、邻居、规则
🎮 生命游戏 康威规则、经典图案
📐 规则变体 HighLife、Day&Night
🎵 音频驱动 能量控制演化速度
🔊 网络音乐 just_audio_ohos 在线播放

⭐ 最佳实践要点

  • ✅ 使用双缓冲避免读写冲突
  • ✅ 低分辨率网格保证性能
  • ✅ 音频特征注入细胞
  • ✅ 支持交互绘制

🚀 进阶方向

  • 🔮 多状态元胞自动机
  • ✨ 3D 元胞自动机
  • 👆 多点触控绘制
  • ⚡ GPU 并行计算

相关推荐
早點睡3902 小时前
Harmony Flutter 跨平台开发实战:鸿蒙与音乐律动艺术、Lissajous 利萨茹曲线:频率耦合的轨迹艺术
flutter
2601_949593652 小时前
Harmony Flutter 跨平台开发实战:鸿蒙与音乐律动艺术、Mandelbrot 分形生长:自相似性的音频映射
flutter
菜鸟小芯2 小时前
【GLM-5 陪练式创意 UI 实战】第一篇:创意魔法盒 —— 用 AI 生成 “开心” 主题 Flutter UI,搞定深浅色与响应式
人工智能·flutter·ui
Esaka_Forever2 小时前
「为什么不用 Flutter 做游戏」「为什么不用 Cocos 做 App 界面」
flutter·游戏
程序员老刘·2 小时前
Flutter 3.41 更新要点速评:主打优化,避坑AGP 9
flutter·跨平台开发·客户端开发
2501_921930832 小时前
进阶实战 Flutter for OpenHarmony:自定义路由管理系统 - 声明式导航与深层链接实现
flutter
松叶似针2 小时前
Flutter三方库适配OpenHarmony【doc_text】— Android 端 Apache POI 实现分析
flutter
阿林来了2 小时前
Flutter三方库适配OpenHarmony【flutter_web_auth】— Android 端 Chrome Custom Tabs 实现分析
android·chrome·flutter
松叶似针6 小时前
Flutter三方库适配OpenHarmony【secure_application】— 总结回顾与隐私保护技术展望
flutter