第30章 综合案例:HarmonyOs开发图解之 俄罗斯方块游戏
HarmonyOS 学习系统 | 阶段四:专业精通期
学习目标
| 序号 | 能力 |
|---|---|
| 1 | 综合运用线程管理、UI 开发、多模输入等多个知识点 |
| 2 | 掌握游戏主循环的设计与实现 |
| 3 | 理解游戏状态管理和用户交互处理 |
核心图解

内容讲解
30.1 案例技术要点汇总
俄罗斯方块游戏综合运用了阶段三和阶段四的多个知识点:
| 知识点 | 在本案例中的应用 | 所属章节 |
|---|---|---|
| TaskDispatcher | 游戏主循环的定时执行 | 第12章 线程管理 |
| UITaskDispatcher | UI 界面的刷新更新 | 第12章 线程管理 |
| Canvas 绘图 | 绘制游戏网格和方块 | 第10章 JS 开发 UI |
| Component.DrawTask | 自定义绘制组件 | 基础 UI 能力 |
| DirectionalLayout | 游戏界面的整体布局 | 基础 UI 能力 |
| 触摸事件处理 | 按钮点击响应 | 第11章 多模输入 |
生活化类比:游戏开发就像"搭建一个游乐场"
TaskDispatcher 是游乐场的管理员------他控制每个设施(任务)什么时候开放、什么时候关闭。游戏主循环就像摩天轮------不停转动,每转一圈就处理一次游戏状态。UITaskDispatcher 是游乐园的广播系统------所有需要游客看到的变化(分数更新、画面刷新)都通过广播来通知。
30.2 游戏核心逻辑
30.2.1 游戏状态管理
java
// 游戏状态
private boolean isRunning = false;
private int scoreValue = 0;
private int[][] grids = new int[15][10]; // 15行10列的网格
// 网格数据结构
class Grid {
int x; // 列位置(0-9)
int y; // 行位置(0-14)
int color; // 颜色值
}
30.2.2 游戏初始化
java
private void initialize() {
grids = new int[15][10];
createGrids(); // 创建网格数据
drawGrids(); // 绘制网格到界面
}
30.2.3 游戏主循环
游戏主循环是整个游戏的心脏,通过 TaskDispatcher 定时执行,实现方块的自动下落。
java
// 游戏主循环 - 通过 TaskDispatcher 定时执行
class GameTask implements Runnable {
@Override
public void run() {
if (!isRunning) return;
if (couldDown()) {
// 方块可以下移
clearGrids();
updateGridPosition(); // 更新位置
drawGrids(); // 重绘
} else {
// 方块不能下移,固定到网格中
createGrids(); // 生成新方块
if (isGameOver()) {
isRunning = false;
showTip("Game Over! Score: " + scoreValue);
return;
}
drawGrids();
}
// 继续下一帧(500ms 间隔)
getUITaskDispatcher().delayDispatch(this, 500);
}
}
// 启动游戏
private void startGame() {
isRunning = true;
scoreValue = 0;
initialize();
getUITaskDispatcher().delayDispatch(new GameTask(), 500);
}
30.2.4 方块操作
java
// 左移
private void moveLeft() {
if (couldLeft()) {
clearGrids();
updateGridPosition(-1, 0); // x-1
drawGrids();
}
}
// 右移
private void moveRight() {
if (couldRight()) {
clearGrids();
updateGridPosition(1, 0); // x+1
drawGrids();
}
}
// 旋转
private void rotate() {
clearGrids();
shiftGrid(); // 旋转方块形状
if (!isValidPosition()) {
shiftGrid(); // 旋转失败,恢复
}
drawGrids();
}
// 消行检测
private void checkLines() {
for (int row = 14; row >= 0; row--) {
if (isLineFull(row)) {
removeLine(row); // 消除该行
scoreValue += 100; // 加分
row++; // 重新检查该行(上面的行会下移)
}
}
}
30.3 UI 布局设计
xml
<!-- DirectionalLayout: 游戏整体布局 -->
<DirectionalLayout
ohos:width="match_parent"
ohos:height="match_parent"
ohos:orientation="vertical">
<!-- 顶部信息栏:分数、等级 -->
<DirectionalLayout ohos:width="match_parent" ohos:height="50vp">
<Text ohos:id="$+id:score_text" ohos:text="Score: 0"/>
</DirectionalLayout>
<!-- 中间:游戏区域 -->
<Component
ohos:id="$+id:game_canvas"
ohos:width="match_parent"
ohos:height="0vp"
ohos:weight="1"/>
<!-- 底部:控制按钮 -->
<DirectionalLayout ohos:width="match_parent" ohos:height="80vp"
ohos:orientation="horizontal">
<Button ohos:id="$+id:btn_left" ohos:text="←"/>
<Button ohos:id="$+id:btn_rotate" ohos:text="Rotate"/>
<Button ohos:id="$+id:btn_right" ohos:text="→"/>
<Button ohos:id="$+id:btn_down" ohos:text="↓"/>
</DirectionalLayout>
</DirectionalLayout>
30.4 综合练习建议
在完成基础俄罗斯方块后,可以尝试以下扩展方向:
- 添加音效:使用第 16 章的 AudioRenderer 或 SoundPlayer 添加消除音效、背景音乐
- 排行榜:使用第 27 章的偏好数据库保存最高分
- 跨端迁移:使用第 29 章的流转能力,手机迁移到平板继续游戏
- 手势操作:使用第 11 章的多模输入替代按钮,支持滑动手势
- AI 预测:使用第 20/21 章的 AI 能力,实现截图识别其他玩家的方块布局
代码速查卡
| API | 功能 | 示例 |
|---|---|---|
getUITaskDispatcher().delayDispatch(task, ms) |
延迟派发任务(游戏循环) | delayDispatch(gameTask, 500) |
Component.addDrawTask(task) |
添加自定义绘制任务 | canvas.addDrawTask(this::onDraw) |
TouchEvent |
触摸事件处理 | 用于按钮和手势操作 |
preferences.putInt("score", val) |
保存最高分 | 使用偏好数据库 |
continueAbility(deviceId) |
跨端迁移游戏 | 用于流转功能 |
与 Android/iOS 对比
| 特性 | HarmonyOS | Android | iOS |
|---|---|---|---|
| 游戏循环 | UITaskDispatcher.delayDispatch | Handler.postDelayed | Timer / CADisplayLink |
| 自定义绘制 | Component.DrawTask | Canvas / SurfaceView | CAShapeLayer / Core Graphics |
| 布局 | DirectionalLayout | LinearLayout / ConstraintLayout | UIStackView |
| 存储最高分 | Preferences | SharedPreferences | UserDefaults |
⚠️ 踩坑回忆录
游戏越来越卡,最后直接 ANR 了
我在游戏主循环里做了太多事情------每次循环都重新创建大量对象(Grid 数组、颜色数组等),GC 压力巨大,游戏越玩越卡。正确做法是对象复用:提前创建好所有需要的对象,在游戏循环中只更新属性值,不创建新对象。
还有一个坑:delayDispatch 的间隔不能太短。我一开始设成 100ms,结果在低端设备上 UI 根本来不及绘制。设成 500ms 才比较稳定。如果要做更精细的控制,可以根据设备性能动态调整间隔。
必做实操任务
| 序号 | 任务 | 难度 |
|---|---|---|
| 1 | 实现游戏网格的绘制和方块的显示 | 中 |
| 2 | 实现方块的自动下落和手动控制(左/右/下) | 中 |
| 3 | 实现方块旋转和碰撞检测 | 高 |
| 4 | 实现消行检测和分数统计 | 中 |
| 5 | 添加游戏开始/暂停/重新开始功能 | 低 |
学习检查清单
- 能使用 TaskDispatcher 实现游戏主循环
- 能使用 Component.DrawTask 自定义绘制游戏画面
- 能实现方块的下落、移动、旋转逻辑
- 能实现碰撞检测(边界和已固定方块)
- 能实现消行检测和分数统计
- 能使用 DirectionalLayout 搭建游戏界面
- 理解游戏状态管理(运行/暂停/结束)
- 能综合运用多章节知识完成完整项目
进阶方向
- 添加难度递增机制(随等级提升下落速度)
- 实现下一个方块的预览功能
- 添加网络对战功能(使用第 24 章网络管理)
- 实现跨设备的流转对战
- 探索 HarmonyOS 游戏引擎和图形渲染优化
辑 - 能实现碰撞检测(边界和已固定方块)
- 能实现消行检测和分数统计
- 能使用 DirectionalLayout 搭建游戏界面
- 理解游戏状态管理(运行/暂停/结束)
- 能综合运用多章节知识完成完整项目
进阶方向
- 添加难度递增机制(随等级提升下落速度)
- 实现下一个方块的预览功能
- 添加网络对战功能(使用第 24 章网络管理)
- 实现跨设备的流转对战
- 探索 HarmonyOS 游戏引擎和图形渲染优化