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

一、项目概述

运行效果图

1

2

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4

5

1.1 应用简介

打地鼠是一款经典的休闲益智游戏,考验玩家的反应速度和手眼协调能力。游戏采用清新的绿色草地主题,配合可爱的地鼠形象,为玩家带来轻松愉快的游戏体验。无论是打发碎片时间,还是挑战高分记录,这款游戏都能让玩家沉浸其中。

游戏设有三种地鼠类型:普通地鼠、金色地鼠和炸弹。普通地鼠击中得10分,金色地鼠击中得50分但出现时间短,炸弹击中扣30分需要避开。配合连击系统和三种难度选择,让游戏既有休闲性又有挑战性。60秒限时模式让每一局都紧张刺激,历史记录功能帮助玩家追踪自己的进步。

1.2 核心功能

功能模块功能描述实现方式
游戏主体60秒限时打地鼠Timer定时器
三种地鼠普通/金色/炸弹MoleType枚举
难度选择简单/中等/困难Difficulty枚举
连击系统连续击中奖励combo计数器
历史记录游戏成绩排行GameResult模型
暂停继续游戏状态管理GameState枚举

1.3 地鼠类型概览

地鼠类型英文标识分值出现概率停留时间
普通地鼠normal+1080%1.5秒
金色地鼠golden+5010%0.8秒
炸弹bomb-3010%2.0秒

1.4 难度设置

难度英文标识地鼠出现间隔
简单easy1200毫秒
中等medium800毫秒
困难hard500毫秒

1.5 技术栈

技术领域技术选型版本要求
开发框架Flutter>= 3.0.0
编程语言Dart>= 2.17.0
设计规范Material Design 3-
状态管理setState + Timer-
目标平台鸿蒙OSAPI 21+

1.6 项目结构

lib/
└── main_whack_a_mole.dart
    ├── WhackAMoleApp          # 应用入口
    ├── GameState              # 游戏状态枚举
    ├── Difficulty             # 难度枚举
    ├── MoleType               # 地鼠类型枚举
    ├── Mole                   # 地鼠模型
    ├── GameResult             # 游戏结果模型
    ├── HomePage               # 主页面
    │   ├── _buildStartScreen()  # 开始界面
    │   ├── _buildGameScreen()   # 游戏界面
    │   └── _buildGameField()    # 游戏区域
    └── 动画控制器             # 击中动画效果

二、系统架构

2.1 整体架构图

Data Layer

Game Logic

Presentation Layer

开始界面

游戏界面

结束界面

历史记录

地鼠生成器
_spawnMole

计时器
_countdownTimer

碰撞检测
_hitMole

分数计算
_score

Mole
地鼠数据

GameResult
游戏结果

MoleType
地鼠类型

2.2 类图设计

contains

manages

records

uses

uses

has

WhackAMoleApp

+Widget build()

HomePage

-GameState _gameState

-Difficulty _difficulty

-int _score

-int _timeLeft

-int _combo

-int _maxCombo

-List<Mole?> _moles

-Timer? _gameTimer

-Timer? _moleTimer

-Timer? _countdownTimer

-List<GameResult> _history

+Widget build()

-void _startGame()

-void _pauseGame()

-void _resumeGame()

-void _endGame()

-void _spawnMole()

-void _hitMole()

Mole

+int holeIndex

+MoleType type

+DateTime appearTime

+bool isHit

+int get score

+int get duration

GameResult

+int score

+int hits

+int misses

+int goldenHits

+int bombHits

+int combo

+DateTime date

«enumeration»

GameState

idle

playing

paused

finished

«enumeration»

Difficulty

easy

medium

hard

«enumeration»

MoleType

normal

golden

bomb

2.3 数据流程图

击中地鼠

击中炸弹

错过

游戏开始

启动计时器

地鼠生成循环

玩家点击

加分+连击

扣分+重置连击

增加错过数

更新UI

时间是否结束

保存结果

显示结束界面

2.4 游戏流程

计时器 地鼠生成器 游戏界面 开始界面 用户 计时器 地鼠生成器 游戏界面 开始界面 用户 loop [每隔一定时间] 选择难度 点击开始 切换界面 启动60秒倒计时 启动生成循环 随机位置生成地鼠 点击地鼠 计算得分 时间结束 显示结果弹窗

三、核心模块设计

3.1 数据模型设计

3.1.1 地鼠模型 (Mole)
class Mole {
  final int holeIndex;       // 所在洞口索引(0-8)
  final MoleType type;       // 地鼠类型
  final DateTime appearTime; // 出现时间
  bool isHit;                // 是否被击中
  
  int get score;             // 得分(根据类型)
  int get duration;          // 停留时间(毫秒)
}
3.1.2 游戏结果模型 (GameResult)
class GameResult {
  final int score;           // 总得分
  final int hits;            // 击中次数
  final int misses;          // 错过次数
  final int goldenHits;      // 金色地鼠击中数
  final int bombHits;        // 炸弹击中数
  final int combo;           // 最大连击
  final DateTime date;       // 游戏日期
}
3.1.3 游戏状态枚举 (GameState)

应用启动

开始游戏

暂停

继续

时间结束

返回首页

再来一局

idle

playing

paused

finished

3.2 游戏逻辑设计

3.2.1 地鼠生成算法

生成地鼠

获取空闲洞口

有空闲洞口?

等待下次生成

随机选择洞口

随机选择地鼠类型

创建Mole对象

设置消失定时器

更新UI显示

3.2.2 地鼠类型概率
80% 10% 10% 地鼠类型出现概率 普通地鼠 金色地鼠 炸弹
3.2.3 得分计算规则
情况分数变化连击变化
击中普通地鼠+10+1
击中金色地鼠+50+1
击中炸弹-30重置为0
错过普通地鼠0重置为0
连击达到5的倍数+连击数×5-

3.3 页面结构设计

3.3.1 开始界面

开始界面

游戏图标

游戏标题

难度选择

开始按钮

历史记录入口

简单

中等

困难

3.3.2 游戏界面

游戏界面

顶部状态栏

游戏区域

底部控制栏

分数显示

倒计时

连击数

3x3洞口网格

地鼠显示

暂停/继续

结束游戏

3.4 状态管理

3.4.1 核心状态变量
class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
  GameState _gameState = GameState.idle;      // 游戏状态
  Difficulty _difficulty = Difficulty.medium; // 难度
  int _score = 0;                              // 当前分数
  int _timeLeft = 60;                          // 剩余时间
  int _combo = 0;                              // 当前连击
  int _maxCombo = 0;                           // 最大连击
  int _hits = 0;                               // 击中数
  int _misses = 0;                             // 错过数
  int _goldenHits = 0;                         // 金色击中数
  int _bombHits = 0;                           // 炸弹击中数
  final List<Mole?> _moles = List.filled(9, null); // 9个洞口
  final List<GameResult> _history = [];        // 历史记录
}
3.4.2 计时器管理
void _startGame() {
  // 初始化状态
  _score = 0;
  _timeLeft = 60;
  _combo = 0;
  
  // 启动倒计时
  _countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
    _timeLeft--;
    if (_timeLeft <= 0) _endGame();
  });
  
  // 启动地鼠生成
  _moleTimer = Timer.periodic(Duration(milliseconds: _getSpawnInterval()), (timer) {
    _spawnMole();
  });
}

四、UI设计规范

4.1 配色方案

游戏采用草地主题配色:

颜色类型色值用途
主色Brown (Material)按钮、标题
草地绿Green.shade300-600背景渐变
泥土棕Brown.shade300-600洞口、地鼠
金色Amber金色地鼠、星星
炸弹灰Grey.shade700炸弹

4.2 字体规范

元素字号字重颜色
游戏标题48pxBold#FFFFFF
分数数字24pxBold#FFFFFF
倒计时24pxBold#FFFFFF
连击数20pxBold#FFFFFF
按钮文字16-20pxBold#FFFFFF

4.3 组件规范

4.3.1 开始界面
┌─────────────────────────────────────────────────┐
│                                                 │
│                   🐹                            │
│                                                 │
│                 打地鼠                          │
│           点击地鼠得分,小心炸弹!              │
│                                                 │
│         ┌─────────────────────────┐            │
│         │    选择难度              │            │
│         │  简单   中等   困难      │            │
│         └─────────────────────────┘            │
│                                                 │
│         ┌─────────────────────────┐            │
│         │       开始游戏          │            │
│         └─────────────────────────┘            │
│                                                 │
│               历史记录                         │
│                                                 │
└─────────────────────────────────────────────────┘
4.3.2 游戏界面
┌─────────────────────────────────────────────────┐
│  ⭐ 320              ⏱ 45s             🔥 x5   │
├─────────────────────────────────────────────────┤
│                                                 │
│   ┌───┐    ┌───┐    ┌───┐                     │
│   │ 🐹 │    │   │    │ ⭐ │                     │
│   └───┘    └───┘    └───┘                     │
│                                                 │
│   ┌───┐    ┌───┐    ┌───┐                     │
│   │   │    │ 💣 │    │   │                     │
│   └───┘    └───┘    └───┘                     │
│                                                 │
│   ┌───┐    ┌───┐    ┌───┐                     │
│   │ 🐹 │    │   │    │ 🐹 │                     │
│   └───┘    └───┘    └───┘                     │
│                                                 │
├─────────────────────────────────────────────────┤
│         ⏸ 暂停          ⏹ 结束                 │
└─────────────────────────────────────────────────┘
4.3.3 地鼠类型
┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│             │  │             │  │             │
│     🐹      │  │     ⭐      │  │     💣      │
│             │  │    +50      │  │    -30      │
│   普通+10   │  │   金色+50   │  │   炸弹-30   │
└─────────────┘  └─────────────┘  └─────────────┘

4.4 动画效果

4.4.1 击中动画
_hitAnimationController = AnimationController(
  duration: const Duration(milliseconds: 200),
  vsync: this,
);
_hitAnimation = Tween<double>(begin: 1.0, end: 0.5).animate(
  CurvedAnimation(parent: _hitAnimationController, curve: Curves.easeOut),
);
4.4.2 动画时序图
1970-01-01 1970-01-01 1970-01-01 1970-01-01 1970-01-01 1970-01-01 1970-01-01 1970-01-01 1970-01-01 1970-01-01 1970-01-01 1970-01-01 1970-01-01 1970-01-01 缩小动画 消失效果 动画效果 击中动画时序

五、核心功能实现

5.1 地鼠生成

void _spawnMole() {
  if (_gameState != GameState.playing) return;

  // 获取空闲洞口
  final availableHoles = <int>[];
  for (int i = 0; i < _moles.length; i++) {
    if (_moles[i] == null) {
      availableHoles.add(i);
    }
  }

  if (availableHoles.isEmpty) return;

  // 随机选择洞口和类型
  final holeIndex = availableHoles[_random.nextInt(availableHoles.length)];
  final moleType = _getRandomMoleType();

  setState(() {
    _moles[holeIndex] = Mole(
      holeIndex: holeIndex,
      type: moleType,
      appearTime: DateTime.now(),
    );
  });

  // 设置消失定时器
  Future.delayed(Duration(milliseconds: _moles[holeIndex]!.duration), () {
    if (_moles[holeIndex] != null && !_moles[holeIndex]!.isHit) {
      setState(() {
        if (_moles[holeIndex]!.type != MoleType.bomb) {
          _misses++;
          _combo = 0;
        }
        _moles[holeIndex] = null;
      });
    }
  });
}

5.2 击中判定

void _hitMole(int index) {
  if (_gameState != GameState.playing) return;
  if (_moles[index] == null || _moles[index]!.isHit) return;

  final mole = _moles[index]!;
  _hitAnimationController.forward(from: 0);

  setState(() {
    mole.isHit = true;
    _score += mole.score;
    _hits++;

    if (mole.type == MoleType.golden) {
      _goldenHits++;
    } else if (mole.type == MoleType.bomb) {
      _bombHits++;
      _combo = 0;
    } else {
      _combo++;
      if (_combo > _maxCombo) {
        _maxCombo = _combo;
      }
      // 连击奖励
      if (_combo > 0 && _combo % 5 == 0) {
        _score += _combo * 5;
      }
    }
  });

  // 延迟移除
  Future.delayed(const Duration(milliseconds: 200), () {
    setState(() {
      _moles[index] = null;
    });
  });
}

5.3 难度控制

int _getSpawnInterval() {
  switch (_difficulty) {
    case Difficulty.easy:
      return 1200;    // 简单:1.2秒
    case Difficulty.medium:
      return 800;     // 中等:0.8秒
    case Difficulty.hard:
      return 500;     // 困难:0.5秒
  }
}

5.4 游戏结束处理

void _endGame() {
  _gameTimer?.cancel();
  _moleTimer?.cancel();
  _countdownTimer?.cancel();

  final result = GameResult(
    score: _score,
    hits: _hits,
    misses: _misses,
    goldenHits: _goldenHits,
    bombHits: _bombHits,
    combo: _maxCombo,
    date: DateTime.now(),
  );

  setState(() {
    _history.insert(0, result);
    _gameState = GameState.finished;
  });

  _showResultDialog();
}

六、游戏设计知识

6.1 游戏平衡性设计

6.1.1 得分平衡

普通地鼠

+10分
80%概率

金色地鼠

+50分
10%概率
快速消失

炸弹

-30分
10%概率
需要躲避

期望得分计算

E = 0.8 × 10 + 0.1 × 50 + 0.1 × ( − 30 ) = 8 + 5 − 3 = 10 E = 0.8 \times 10 + 0.1 \times 50 + 0.1 \times (-30) = 8 + 5 - 3 = 10 E=0.8×10+0.1×50+0.1×(30)=8+53=10

6.1.2 难度曲线
难度地鼠间隔预期击中率60秒预期得分
简单1.2秒70%~350分
中等0.8秒55%~400分
困难0.5秒40%~450分

6.2 反应时间分析

6.2.1 人类反应时间

~100ms

~100ms

~100ms

视觉刺激

大脑处理

运动指令

手指动作

平均反应时间:约300ms

6.2.2 地鼠停留时间设计
地鼠类型停留时间设计考量
普通1500ms允许1-2次点击尝试
金色800ms需要快速反应
炸弹2000ms有足够时间识别躲避

6.3 连击系统设计

6.3.1 连击奖励机制

连击数

是否为5的倍数?

额外奖励: 连击数×5

无额外奖励

鼓励持续击中

6.3.2 连击中断条件
条件连击变化设计目的
击中普通地鼠+1鼓励持续击中
击中金色地鼠+1高价值奖励
击中炸弹重置为0惩罚误操作
错过地鼠重置为0惩罚反应慢

七、扩展功能规划

7.1 后续版本规划

2024-01-07 2024-01-14 2024-01-21 2024-01-28 2024-02-04 2024-02-11 2024-02-18 2024-02-25 2024-03-03 2024-03-10 2024-03-17 核心游戏逻辑 三种地鼠类型 历史记录功能 音效系统 道具系统 成就系统 多人对战 排行榜 自定义皮肤 V1.0 基础版本 V1.1 增强版本 V1.2 进阶版本 打地鼠游戏开发计划

7.2 功能扩展建议

7.2.1 道具系统
道具效果持续时间
时间冻结暂停倒计时5秒
双倍得分得分翻倍10秒
慢动作地鼠停留时间加倍8秒
护盾免疫炸弹伤害15秒
7.2.2 成就系统
class Achievement {
  final String id;
  final String name;
  final String description;
  final int reward;
  final bool isUnlocked;
}
7.2.3 多人对战
模式说明
同时对战两人同时游戏比分数
轮流挑战轮流挑战高分
合作模式共同击中目标数量

八、注意事项

8.1 开发注意事项

  1. 计时器管理:页面销毁时必须取消所有Timer,避免内存泄漏

  2. 状态同步:地鼠出现和消失需要正确同步UI状态

  3. 触摸响应:确保点击区域足够大,提升用户体验

  4. 动画流畅:击中动画不应影响游戏性能

8.2 游戏体验优化

🎮 游戏体验建议 🎮

  • 提供清晰的视觉反馈
  • 音效增强打击感
  • 合理的难度曲线
  • 适时的奖励激励

8.3 常见问题

问题原因解决方案
地鼠重叠生成逻辑问题检查空闲洞口
计时不准Timer精度使用Stopwatch
点击无响应状态未更新检查游戏状态
内存泄漏Timer未取消dispose中取消

九、运行说明

9.1 环境要求

环境版本要求
Flutter SDK>= 3.0.0
Dart SDK>= 2.17.0
鸿蒙OSAPI 21+

9.2 运行命令

# 查看可用设备
flutter devices

# 运行到鸿蒙设备
flutter run -d 127.0.0.1:5555 lib/main_whack_a_mole.dart

# 运行到Windows
flutter run -d windows -t lib/main_whack_a_mole.dart

# 代码分析
flutter analyze lib/main_whack_a_mole.dart

十、总结

打地鼠游戏通过经典的玩法设计和丰富的游戏元素,为玩家提供了轻松愉快的休闲体验。游戏采用清新的草地主题,配合可爱的地鼠形象,营造出轻松愉快的游戏氛围;代码结构清晰,遵循Flutter最佳实践;游戏逻辑设计合理,难度曲线平滑。

核心玩法涵盖三种地鼠类型、三种难度选择、连击奖励系统和历史记录功能,满足不同玩家的游戏需求。特别值得一提的是金色地鼠和炸弹的设计,金色地鼠高分快速消失考验反应,炸弹扣分需要躲避增加策略性,让游戏既有休闲性又有挑战性。

连击系统是游戏的一大亮点,连续击中地鼠可以获得额外奖励,激励玩家保持专注和快速反应。60秒限时模式让每一局都紧张刺激,历史记录功能帮助玩家追踪自己的进步,不断挑战更高分数。

快速反应,精准打击,挑战高分!


Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐