Skip to content
🌊海洋蓝
🌸樱花粉
🍃森林绿
🔮幻夜紫
🌙暗夜黑

01-概览

前言

2013 年,一款名为 Flappy Bird 的像素风格小游戏横空出世,凭借其极简的操作和极高的难度迅速席卷全球。玩家只需点击屏幕控制小鸟飞行,躲避不断出现的绿色管道,这种"一秒上手,百次不过"的魔性体验让无数玩家欲罢不能。尽管原作早已下架,但它所代表的像素游戏精神和核心玩法设计至今仍然影响着无数开发者。

如今,HarmonyOS 作为华为推出的新一代智能终端操作系统,正以其分布式能力和流畅的用户体验吸引着越来越多的开发者加入生态建设。ArkTS 作为 HarmonyOS 的主力开发语言,在保持 TypeScript 类型安全优势的同时,针对声明式 UI 进行了深度优化,让开发者能够以更简洁的代码构建高性能应用。

本系列文章将带领你从零开始,使用 HarmonyOS ArkTS 和 Canvas API 打造一个完整的像素小鸟游戏。无需复杂的游戏引擎,仅凭原生 Canvas 绘制能力和简洁的物理模拟,就能在鸿蒙设备上跑起一个流畅的 2D 游戏。无论你是刚接触 HarmonyOS 开发的新手,还是希望了解 ArkTS 游戏开发实践的老手,这个项目都将为你提供有价值的参考。

游戏效果展示

在深入代码之前,让我们先通过截图来预览游戏的三种核心状态。

准备状态(Ready)

游戏开始界面

游戏启动后进入准备状态,屏幕中央显示"像素小鸟"标题和"点击屏幕开始游戏"的提示文字。背景是清新的蓝天渐变,点缀着缓慢飘动的像素云朵和旋转的像素太阳。小鸟在屏幕中央上下悬停,翅膀以三帧动画循环扇动,等待玩家的第一次点击。这个状态的设计目标是让玩家在进入游戏前有一个视觉缓冲,同时通过小鸟的悬停动画吸引注意力。

游戏进行中(Playing)

游戏中(小鸟飞行)

游戏中(带管道)

玩家点击屏幕后,游戏切换到进行状态。小鸟获得向上的初速度开始飞行,同时管道从屏幕右侧不断生成并向左移动。玩家需要通过节奏性的点击来控制小鸟的飞行高度,穿过管道之间的间隙。每成功穿过一组管道,屏幕顶部的分数就会增加,并伴随一个缩放动画反馈。云朵继续以不同速度飘动,为画面增添层次感。

游戏结束(Game Over)

游戏结束界面

当小鸟撞到管道或地面时,游戏进入结束状态,屏幕中央弹出分数面板,显示当前得分和历史最高分。玩家点击屏幕即可重置游戏,回到准备状态重新开始挑战。整个状态切换流畅自然,给玩家明确的反馈和再次挑战的动力。

技术栈与架构总览

本项目采用 HarmonyOS ArkTS 原生技术栈,核心架构遵循 MVVM 模式,将 UI 渲染、业务逻辑和数据模型清晰分离。

技术选型

技术点实现方案说明
开发语言ArkTSHarmonyOS 声明式 UI 开发语言,TypeScript 严格子集
状态管理ArkUI State Management V2使用 @ComponentV2@Local@Param 等 V2 装饰器
游戏渲染Canvas API原生 2D 画布绘制,无需引入第三方游戏引擎
游戏循环setInterval16ms 间隔(约 60 FPS)驱动更新与渲染
物理模拟自定义实现重力加速度、速度积分、碰撞检测等基础物理
架构模式MVVMModel-View-ViewModel 三层分离

为什么选择 Canvas 而非纯 ArkUI 组件

HarmonyOS 的 ArkUI 提供了丰富的声明式组件,但对于像素风格的 2D 游戏来说,Canvas 具有不可替代的优势:

  1. 像素级绘制控制:通过 fillRect 等底层 API,可以精确绘制每一个像素块,完美还原复古游戏的视觉风格
  2. 高性能帧动画:游戏循环以 60 FPS 驱动,Canvas 的即时模式渲染避免了声明式 UI 的 diff 开销
  3. 自由的游戏逻辑:不受组件生命周期约束,可以灵活控制渲染顺序和绘制层级
  4. 学习成本低:无需掌握复杂游戏引擎,掌握基础 Canvas API 即可上手

MVVM 架构设计

项目目录结构清晰体现了 MVVM 的分层思想:

entry/src/main/ets/
├── pages/
│   └── Index.ets          # 入口页面(View 层根节点)
├── view/
│   └── GameCanvas.ets     # Canvas 渲染组件(View 层)
├── viewmodel/
│   └── GameEngine.ets     # 游戏引擎(ViewModel 层)
├── model/
│   ├── GameStateModel.ets # 状态枚举与配置(Model 层)
│   ├── BirdModel.ets      # 小鸟数据模型
│   ├── PipeModel.ets      # 管道数据模型
│   └── BoundsModel.ets    # 碰撞矩形模型
└── entryability/
    └── EntryAbility.ets   # 应用 Ability(全屏配置)

Model 层负责纯数据结构的定义,包括 BirdModel(小鸟的位置、速度、动画帧)、PipeModel(管道的位置、间隙、碰撞区域)以及 GameStateModel(游戏状态枚举和全局配置常量)。这些类不包含任何渲染逻辑,仅提供数据更新方法。

ViewModel 层GameEngine 类承担,它是整个游戏的大脑。GameEngine 管理游戏状态机(Ready / Playing / Game Over),驱动物理更新(重力、跳跃、碰撞检测),维护分数和最高分,并协调所有 Model 对象的更新。ViewModel 不直接操作 Canvas,而是将自身状态暴露给 View 层进行渲染。

View 层Index.ets 页面和 GameCanvas.ets 组件组成。GameCanvas 负责创建 Canvas 上下文、启动游戏循环、处理触摸事件,并在每一帧调用 GameEngine 更新状态后,将游戏世界绘制到画布上。这种分离确保了业务逻辑和渲染逻辑的独立演进。

开发环境搭建

1. 安装 DevEco Studio

首先确保已安装最新版本的 DevEco Studio(推荐 5.0 及以上),这是 HarmonyOS 应用的官方 IDE。安装完成后,通过 IDE 的 SDK Manager 下载对应版本的 HarmonyOS SDK。

2. 创建项目

在 DevEco Studio 中选择"Create Project",选择"Empty Ability"模板,语言选择 ArkTS,API 版本建议选择 API 12 或更高以确保 V2 状态管理装饰器的完整支持。

3. 全屏配置

像素小鸟游戏需要沉浸式全屏体验,隐藏系统状态栏和导航栏。在 EntryAbility.etsonWindowStageCreate 生命周期中进行如下配置:

typescript
import { window } from '@kit.ArkUI';

onWindowStageCreate(windowStage: window.WindowStage): void {
  const mainWindow: window.Window = windowStage.getMainWindowSync();
  mainWindow.setWindowLayoutFullScreen(true);
  mainWindow.setWindowSystemBarEnable([]);

  windowStage.loadContent('pages/Index', (err) => {
    if (err.code) {
      hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
      return;
    }
    hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
  });
}

setWindowLayoutFullScreen(true) 将应用布局扩展至全屏,setWindowSystemBarEnable([]) 则禁用所有系统栏(状态栏、导航栏),让游戏画面占据整个屏幕。

4. 目录结构准备

按照 MVVM 架构创建对应的目录:

bash
mkdir -p entry/src/main/ets/{pages,view,viewmodel,model}

将各层代码文件放置到对应目录中,保持清晰的模块边界。

关键代码速览

入口页面 Index.ets

入口页面极其简洁,仅负责创建 GameEngine 实例并将其传递给 GameCanvas 组件:

typescript
import { GameEngine } from '../viewmodel/GameEngine';
import { GameCanvas } from '../view/GameCanvas';

@Entry
@ComponentV2
struct Index {
  @Local private engine: GameEngine = new GameEngine();

  build() {
    Stack({ alignContent: Alignment.Center }) {
      GameCanvas({ engine: this.engine })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#4FC3F7')
  }

  aboutToAppear(): void {
    this.engine = new GameEngine();
  }
}

这里使用了 @ComponentV2 标记 V2 组件,@Local 装饰的 engine 是组件的本地状态。页面背景色设置为天空蓝 #4FC3F7,与 Canvas 内的天空渐变形成呼应。

游戏循环

游戏循环是驱动整个游戏运转的心脏,位于 GameCanvas.ets 中:

typescript
private startGameLoop(): void {
  this.gameLoopTimer = setInterval(() => {
    const timestamp: number = Date.now();
    this.engine.update(timestamp);
    this.render();
  }, 16);
}

每 16 毫秒(约 60 FPS)执行一次循环:先调用 engine.update(timestamp) 更新游戏逻辑(物理、碰撞、分数),再调用 render() 将更新后的状态绘制到 Canvas 上。这种"先更新后渲染"的顺序确保了画面始终反映最新的游戏状态。

jump 三态处理

GameEngine 中的 jump() 方法是玩家交互的核心入口,它根据当前游戏状态执行不同的逻辑:

typescript
jump(): void {
  if (this.status === GameStatus.PLAYING) {
    this.bird.jump(GameConfig.JUMP_FORCE);
  } else if (this.status === GameStatus.READY) {
    this.startGame();
  } else if (this.status === GameStatus.GAME_OVER) {
    this.reset();
  }
}

这个设计非常巧妙——整个游戏只需要一个点击交互:

  • 准备状态:点击触发 startGame(),切换到进行状态并给小鸟初始跳跃速度
  • 进行状态:点击触发 bird.jump(),赋予小鸟向上的速度对抗重力
  • 结束状态:点击触发 reset(),重置所有状态回到准备界面

三态合一的交互设计极大简化了用户操作,也是原作令人上瘾的重要原因之一。

游戏状态枚举

状态管理使用简单的枚举定义:

typescript
export enum GameStatus {
  READY = 0,
  PLAYING = 1,
  GAME_OVER = 2,
}

配合 GameConfig 中的全局常量(重力、跳跃力度、游戏速度、管道间距等),整个游戏的可调参数集中在一处,便于平衡性调整。

小结与下篇预告

本文介绍了 HarmonyOS 像素小鸟项目的整体概况,包括游戏效果展示、技术栈选型、MVVM 架构设计、开发环境搭建以及关键代码的速览。通过 Canvas API 和 setInterval 游戏循环,我们在不依赖任何第三方游戏引擎的情况下,搭建起了一个可运行的 2D 游戏框架。

在下一篇文章中,我们将深入 GameCanvas.ets 的渲染细节,详细讲解如何使用 Canvas 2D API 绘制像素风格的小鸟、管道、云朵和地面,以及如何实现分数动画和状态切换的 UI 效果。敬请期待!

Released under the MIT License.