Flutter for OpenHarmony 实战:2048游戏完整开发指南

欢迎加入开源鸿蒙跨平台社区:开源鸿蒙跨平台开发者社区

Flutter for OpenHarmony 实战:2048游戏完整开发指南

文章目录

  • [Flutter for OpenHarmony 实战:2048游戏完整开发指南](#Flutter for OpenHarmony 实战:2048游戏完整开发指南)
    • 摘要
    • 一、项目背景与功能概述
      • [1.1 2048游戏介绍](#1.1 2048游戏介绍)
      • [1.2 应用功能规划](#1.2 应用功能规划)
      • [1.3 方块颜色设计](#1.3 方块颜色设计)
    • 二、数据模型设计
      • [2.1 游戏数据结构](#2.1 游戏数据结构)
      • [2.2 游戏初始化](#2.2 游戏初始化)
    • 三、随机方块生成
      • [3.1 添加随机方块](#3.1 添加随机方块)
    • 四、移动与合并算法
      • [4.1 向左移动](#4.1 向左移动)
      • [4.2 向右移动](#4.2 向右移动)
      • [4.3 向上移动](#4.3 向上移动)
      • [4.4 向下移动](#4.4 向下移动)
    • 五、游戏状态管理
      • [5.1 移动处理](#5.1 移动处理)
      • [5.2 检查游戏状态](#5.2 检查游戏状态)
      • [5.3 检查是否还能移动](#5.3 检查是否还能移动)
    • 六、UI界面实现
      • [6.1 网格构建](#6.1 网格构建)
      • [6.2 方块UI](#6.2 方块UI)
      • [6.3 颜色映射](#6.3 颜色映射)
      • [6.4 控制按钮](#6.4 控制按钮)
    • 七、总结

摘要

2048是由Gabriel Cirulla在2014年创造的经典益智游戏,通过滑动屏幕合并相同数字的方块,目标是合成2048这个数字。本文将详细介绍如何使用Flutter for OpenHarmony框架开发一款功能完整的2048游戏。文章涵盖了方块合并算法、随机生成策略、分数计算、游戏状态管理等核心技术点。通过本文学习,读者将掌握Flutter在滑动类游戏开发中的完整流程,了解数组操作和游戏逻辑的实现。


一、项目背景与功能概述

1.1 2048游戏介绍

2048是一款简单但极具挑战性的益智游戏:

  • 目标:合成数字为2048的方块
  • 规则
    1. 滑动时所有方块向该方向移动
    2. 相同数字的方块碰撞时合并
    3. 每次移动后在空位生成2或4
    4. 无法移动时游戏结束

1.2 应用功能规划

功能模块 具体功能
方向控制 上下左右四个方向移动
方块合并 相同数字合并为2倍值
随机生成 90%概率生成2,10%生成4
分数计算 每次合并累加分数
最高分记录 保存历史最高分
胜利判断 合成2048时获胜
失败判断 无法移动时游戏结束
颜色映射 不同数字不同颜色

1.3 方块颜色设计

数字 背景色 文字色
0 灰色300 -
2 灰色100 深灰
4 琥珀100 深灰
8 琥珀200 白色
16 橙色200 白色
32 橙色300 白色
64 橙色400 白色
128 深橙300 白色
256 深橙400 白色
512 红色300 白色
1024 红色400 白色
2048 红色500 白色

二、数据模型设计

2.1 游戏数据结构

dart 复制代码
class _GamePageState extends State<GamePage> {
  // 4×4游戏板
  late List<List<int>> _board;

  // 游戏状态
  int _score = 0;
  int _bestScore = 0;
  bool _gameOver = false;
  bool _gameWon = false;
  final Random _random = Random();
}

2.2 游戏初始化

dart 复制代码
void _initGame() {
  _board = List.generate(4, (_) => List.filled(4, 0));
  _score = 0;
  _gameOver = false;
  _gameWon = false;

  _addRandomTile();
  _addRandomTile();

  setState(() {});
}

三、随机方块生成

3.1 添加随机方块

dart 复制代码
void _addRandomTile() {
  final emptyCells = <Point>[];

  // 收集所有空位
  for (int r = 0; r < 4; r++) {
    for (int c = 0; c < 4; c++) {
      if (_board[r][c] == 0) {
        emptyCells.add(Point(r, c));
      }
    }
  }

  // 随机选择一个空位
  if (emptyCells.isNotEmpty) {
    final randomCell = emptyCells[_random.nextInt(emptyCells.length)];
    _board[randomCell.x.toInt()][randomCell.y.toInt()] =
      _random.nextDouble() < 0.9 ? 2 : 4;
  }
}

关键设计

  • 90%概率生成2,10%概率生成4
  • 只在空位生成
  • 每次移动后生成一个新方块

四、移动与合并算法

4.1 向左移动

dart 复制代码
bool _moveLeft() {
  bool moved = false;

  for (int r = 0; r < 4; r++) {
    // 1. 过滤掉0,收集非零元素
    final row = _board[r].where((n) => n != 0).toList();
    final newRow = <int>[];

    // 2. 合并相同数字
    for (int i = 0; i < row.length; i++) {
      if (i + 1 < row.length && row[i] == row[i + 1]) {
        final merged = row[i] * 2;
        newRow.add(merged);
        _score += merged;
        if (merged == 2048 && !_gameWon) {
          _gameWon = true;
        }
        i++; // 跳过下一个
      } else {
        newRow.add(row[i]);
      }
    }

    // 3. 补齐0
    while (newRow.length < 4) {
      newRow.add(0);
    }

    // 4. 更新棋盘并检测是否有移动
    for (int c = 0; c < 4; c++) {
      if (_board[r][c] != newRow[c]) {
        moved = true;
      }
      _board[r][c] = newRow[c];
    }
  }

  return moved;
}

算法步骤

  1. 过滤掉空白格(0)
  2. 合并相邻的相同数字
  3. 在末尾补齐0
  4. 检测是否有变化

4.2 向右移动

dart 复制代码
bool _moveRight() {
  bool moved = false;

  for (int r = 0; r < 4; r++) {
    final row = _board[r].where((n) => n != 0).toList();
    final newRow = <int>[];

    // 从右向左合并
    for (int i = row.length - 1; i >= 0; i--) {
      if (i - 1 >= 0 && row[i] == row[i - 1]) {
        final merged = row[i] * 2;
        newRow.insert(0, merged);
        _score += merged;
        if (merged == 2048 && !_gameWon) {
          _gameWon = true;
        }
        i--; // 跳过前一个
      } else {
        newRow.insert(0, row[i]);
      }
    }

    // 在前面补齐0
    while (newRow.length < 4) {
      newRow.insert(0, 0);
    }

    for (int c = 0; c < 4; c++) {
      if (_board[r][c] != newRow[c]) {
        moved = true;
      }
      _board[r][c] = newRow[c];
    }
  }

  return moved;
}

4.3 向上移动

dart 复制代码
bool _moveUp() {
  bool moved = false;

  for (int c = 0; c < 4; c++) {
    // 收集列的非零元素
    final col = <int>[];
    for (int r = 0; r < 4; r++) {
      if (_board[r][c] != 0) {
        col.add(_board[r][c]);
      }
    }

    final newCol = <int>[];

    // 从上向下合并
    for (int i = 0; i < col.length; i++) {
      if (i + 1 < col.length && col[i] == col[i + 1]) {
        final merged = col[i] * 2;
        newCol.add(merged);
        _score += merged;
        if (merged == 2048 && !_gameWon) {
          _gameWon = true;
        }
        i++; // 跳过下一个
      } else {
        newCol.add(col[i]);
      }
    }

    while (newCol.length < 4) {
      newCol.add(0);
    }

    for (int r = 0; r < 4; r++) {
      if (_board[r][c] != newCol[r]) {
        moved = true;
      }
      _board[r][c] = newCol[r];
    }
  }

  return moved;
}

4.4 向下移动

dart 复制代码
bool _moveDown() {
  bool moved = false;

  for (int c = 0; c < 4; c++) {
    final col = <int>[];
    for (int r = 0; r < 4; r++) {
      if (_board[r][c] != 0) {
        col.add(_board[r][c]);
      }
    }

    final newCol = <int>[];

    // 从下向上合并
    for (int i = col.length - 1; i >= 0; i--) {
      if (i - 1 >= 0 && col[i] == col[i - 1]) {
        final merged = col[i] * 2;
        newCol.insert(0, merged);
        _score += merged;
        if (merged == 2048 && !_gameWon) {
          _gameWon = true;
        }
        i--; // 跳过前一个
      } else {
        newCol.insert(0, col[i]);
      }
    }

    while (newCol.length < 4) {
      newCol.insert(0, 0);
    }

    for (int r = 0; r < 4; r++) {
      if (_board[r][c] != newCol[r]) {
        moved = true;
      }
      _board[r][c] = newCol[r];
    }
  }

  return moved;
}

五、游戏状态管理

5.1 移动处理

dart 复制代码
void _handleMove(String direction) {
  if (_gameOver) return;

  bool moved = false;

  switch (direction) {
    case 'left':
      moved = _moveLeft();
      break;
    case 'right':
      moved = _moveRight();
      break;
    case 'up':
      moved = _moveUp();
      break;
    case 'down':
      moved = _moveDown();
      break;
  }

  if (moved) {
    _addRandomTile();

    if (_score > _bestScore) {
      _bestScore = _score;
    }

    _checkGameState();

    setState(() {});
  }
}

5.2 检查游戏状态

dart 复制代码
void _checkGameState() {
  // 检查是否胜利
  if (_gameWon) {
    _showWinDialog();
    return;
  }

  // 检查是否游戏结束
  if (!_canMove()) {
    _gameOver = true;
    _showGameOverDialog();
  }
}

5.3 检查是否还能移动

dart 复制代码
bool _canMove() {
  // 检查是否有空格
  for (int r = 0; r < 4; r++) {
    for (int c = 0; c < 4; c++) {
      if (_board[r][c] == 0) return true;
    }
  }

  // 检查是否有相邻相同的数字
  for (int r = 0; r < 4; r++) {
    for (int c = 0; c < 4; c++) {
      final current = _board[r][c];
      if (c + 1 < 4 && _board[r][c + 1] == current) return true;
      if (r + 1 < 4 && _board[r + 1][c] == current) return true;
    }
  }

  return false;
}

检测条件

  1. 有空格:可以移动
  2. 有相邻相同的数字:可以合并
  3. 都不满足:游戏结束

六、UI界面实现

6.1 网格构建

dart 复制代码
Widget _buildBoard() {
  return Container(
    padding: const EdgeInsets.all(8),
    decoration: BoxDecoration(
      color: Colors.brown.shade300,
      borderRadius: BorderRadius.circular(8),
    ),
    child: GridView.builder(
      primary: true,
      padding: EdgeInsets.zero,
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 4,
        crossAxisSpacing: 8,
        mainAxisSpacing: 8,
        childAspectRatio: 1.0,
      ),
      itemCount: 16,
      itemBuilder: (context, index) {
        final row = index ~/ 4;
        final col = index % 4;
        return _buildTile(row, col);
      },
    ),
  );
}

6.2 方块UI

在这里插入图片描述

dart 复制代码
Widget _buildTile(int row, int col) {
  final value = _board[row][col];

  return Container(
    decoration: BoxDecoration(
      color: _getTileColor(value),
      borderRadius: BorderRadius.circular(8),
    ),
    child: Center(
      child: value == 0
        ? null
        : Text(
            '$value',
            style: TextStyle(
              fontSize: value >= 1000 ? 24 : 32,
              fontWeight: FontWeight.bold,
              color: _getTextColor(value),
            ),
          ),
    ),
  );
}

6.3 颜色映射

dart 复制代码
Color _getTileColor(int value) {
  switch (value) {
    case 0: return Colors.grey.shade300;
    case 2: return Colors.grey.shade100;
    case 4: return Colors.amber.shade100;
    case 8: return Colors.amber.shade200;
    case 16: return Colors.orange.shade200;
    case 32: return Colors.orange.shade300;
    case 64: return Colors.orange.shade400;
    case 128: return Colors.deepOrange.shade300;
    case 256: return Colors.deepOrange.shade400;
    case 512: return Colors.red.shade300;
    case 1024: return Colors.red.shade400;
    case 2048: return Colors.red.shade500;
    default: return Colors.purple;
  }
}

Color _getTextColor(int value) {
  if (value <= 4) return Colors.grey.shade800;
  return Colors.white;
}

6.4 控制按钮

dart 复制代码
Container(
  padding: const EdgeInsets.all(16),
  color: Colors.grey.shade200,
  child: Column(
    children: [
      Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            onPressed: () => _handleMove('left'),
            style: ElevatedButton.styleFrom(
              minimumSize: const Size(60, 60),
            ),
            child: const Icon(Icons.arrow_back, size: 30),
          ),
        ],
      ),
      Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            onPressed: () => _handleMove('up'),
            style: ElevatedButton.styleFrom(
              minimumSize: const Size(60, 60),
            ),
            child: const Icon(Icons.arrow_upward, size: 30),
          ),
          const SizedBox(width: 16),
          ElevatedButton(
            onPressed: () => _handleMove('down'),
            style: ElevatedButton.styleFrom(
              minimumSize: const Size(60, 60),
            ),
            child: const Icon(Icons.arrow_downward, size: 30),
          ),
        ],
      ),
      const SizedBox(height: 8),
      ElevatedButton.icon(
        onPressed: () => _initGame(),
        icon: const Icon(Icons.refresh),
        label: const Text('重新开始'),
        style: ElevatedButton.styleFrom(
          backgroundColor: Colors.blue,
          foregroundColor: Colors.white,
        ),
      ),
    ],
  ),
)

七、总结

本文详细介绍了使用Flutter for OpenHarmony开发2048游戏的完整过程,涵盖了以下核心技术点:

  1. 数据模型:4×4游戏板、分数、游戏状态
  2. 随机生成:空位收集、概率控制
  3. 移动算法:四方向移动、数字合并、零填充
  4. 状态检测:胜利判断、失败检测、可移动检测
  5. UI实现:网格构建、方块渲染、颜色映射
  6. 交互设计:方向按钮、重新开始

这个项目展示了Flutter在滑动类游戏开发中的完整流程,特别是数组操作和合并逻辑的实现。


欢迎加入开源鸿蒙跨平台社区 : 开源鸿蒙跨平台开发者社区

相关推荐
dd_6699612 分钟前
游戏上市公司合同系统实施案例(三):从需求分层到全生命周期管理
游戏·信息可视化·数据可视化
追梦的鱼儿1 小时前
Flutter 生命周期详解:Stateless 与 Stateful 完全对比
flutter
tangweiguo030519871 小时前
Flutter 页面生命周期超全总结(附 addPostFrameCallback 详解)
flutter
国医中兴2 小时前
Flutter 三方库 dson 的鸿蒙化适配指南 - 极简的序列化魔法、在鸿蒙端实现反射式 JSON 映射实战
flutter·harmonyos·鸿蒙·openharmony
张老师带你学2 小时前
unity船资源,快艇,帆船,游轮
科技·游戏·unity·游戏引擎·模型
Lesile3 小时前
Flutter回顾#1:动画:
flutter
云边散步3 小时前
godot2D游戏教程系列二(17)
笔记·学习·游戏
大囚长3 小时前
游戏主机神经纹理压缩与AI重建技术的综合应用方案分析
人工智能·游戏
yuanlaile3 小时前
2026年全新版Flutter教程_Dart Flutter入门实战系列视频教程
flutter·flutter教程·dart教程·flutter必备基础
池央3 小时前
在鸿蒙上跑 AI Agent:JiuwenClaw-on-OpenHarmony 完整实战
人工智能·华为·harmonyos