鸿蒙 ArkTS 休闲小游戏开发实战 ——「光点追逐」从零到一


一、引言
在移动应用开发中,游戏是最能考验开发者对框架掌握程度的应用类型之一。一个看似简单的休闲小游戏,背后涉及状态管理、用户交互、动画反馈、计时逻辑、难度曲线设计等多个方面的知识。
鸿蒙 ArkTS 虽然是声明式 UI 框架,但它具备完整的游戏开发能力——通过 @State 管理游戏状态、onClick 处理用户输入、setTimeout / setInterval 控制游戏节奏、shadow 和颜色变化实现视觉反馈。
本文将带领你从零开始,用 ArkTS 构建一个完整的休闲小游戏——「光点追逐」(Light Chase)。游戏规则非常简单:在 30 秒内尽可能多地点击屏幕上出现的发光圆点,得分越高,光点移动越快、体积越小、难度越大。
通过这个项目,你将学到:
- 如何在 ArkTS 中管理游戏状态机(菜单 / 进行中 / 结算)
- 如何使用定时器控制游戏节奏和倒计时
- 如何实现点击反馈和连击系统
- 如何通过状态驱动的难度曲线
- 如何利用
shadow和颜色 HSL 实现视觉特效 - 单文件组织一个完整游戏的代码结构
二、游戏设计概览
2.1 游戏规则
| 项目 | 内容 |
|---|---|
| 游戏名称 | 光点追逐(Light Chase) |
| 游戏类型 | 反应速度测试 / 点击收集 |
| 每局时长 | 30 秒 |
| 核心操作 | 点击屏幕上的发光圆点 |
| 计分方式 | 每点击一次光点 +1 分 |
| 连击系统 | 连续点击 ≥3 次显示连击提示 |
| 难度递增 | 得分越高,光点越小、移动越快 |
| 评级系统 | 40+ 超凡 / 25+ 敏捷 / 15+ 不错 |
2.2 游戏状态机
游戏包含三个明确的状态,通过 gamePhase 变量控制:
┌──────────┐ 点击「开始游戏」 ┌──────────┐
│ menu │ ────────────────▶ │ playing │
│ 主菜单 │ │ 游戏中 │
└──────────┘ └────┬─────┘
▲ │ 30 秒倒计时结束
│ ▼
│ ┌──────────┐
└─────── 返回菜单 ───────│ over │
│ 结算画面 │
└──────────┘
2.3 核心玩法循环
1. 光点出现在随机位置
2. 玩家点击光点 → 得分 +1,连击 +1
3. 光点闪烁白色反馈 → 80ms 后刷新到新位置
4. 若玩家未点击 → 光点在停留时间结束后自动刷新
5. 每次刷新根据当前分数调整:停留时间缩短、光点变小
6. 30 秒倒计时归零 → 进入结算画面
三、完整代码实现
3.1 常量定义与状态声明
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG = 'LightChase';
const GAME_DURATION = 30; // 每局 30 秒
const BASE_INTERVAL = 1200; // 光点初始停留时间(毫秒)
const MIN_INTERVAL = 300; // 光点最快停留时间(毫秒)
核心游戏状态全部使用 @State 装饰器声明,确保 ArkTS 在状态变化时自动重新渲染 UI:
@Entry
@Component
struct LightChaseGame {
@State score: number = 0;
@State timeLeft: number = GAME_DURATION;
@State gamePhase: string = 'menu'; // 'menu' | 'playing' | 'over'
@State dotX: number = 50; // 光点 X 位置(百分比)
@State dotY: number = 50; // 光点 Y 位置(百分比)
@State dotSize: number = 48; // 光点大小
@State dotColor: string = '#ffdd57'; // 光点颜色
@State combo: number = 0; // 连击数
@State bestScore: number = 0;
@State flashText: string = ''; // 浮动提示文字
@State flashAlpha: number = 0;
@State bgHue: number = 220; // 背景色相
private timerId: number = -1;
private moveTimerId: number = -1;
关键设计决策:所有需要触发 UI 更新的变量都使用 @State,而定时器 ID 等不需要触发渲染的变量使用 private 属性。这是 ArkTS 性能优化的基本原则。
3.2 游戏控制方法
开始与重置
private resetGame(): void {
this.score = 0;
this.timeLeft = GAME_DURATION;
this.combo = 0;
this.flashText = '';
this.flashAlpha = 0;
this.dotSize = 48;
this.dotColor = '#ffdd57';
}
private startGame(): void {
this.resetGame();
this.gamePhase = 'playing';
this.spawnDot();
this.startTimer();
}
倒计时系统
private startTimer(): void {
clearInterval(this.timerId);
this.timerId = setInterval(() => {
this.timeLeft = Math.max(0, this.timeLeft - 1);
if (this.timeLeft <= 0) {
this.endGame();
}
}, 1000);
}
使用 setInterval 每秒更新一次剩余时间。当时间归零时调用 endGame() 切换到结算状态。注意在每次开始前 clearInterval 防止定时器叠加。
结束与结算
private endGame(): void {
this.gamePhase = 'over';
clearInterval(this.timerId);
clearTimeout(this.moveTimerId);
if (this.score > this.bestScore) {
this.bestScore = this.score;
}
}
结算时清理所有定时器,避免游戏结束后光点仍在移动或倒计时仍在运行。
3.3 光点刷新逻辑(核心玩法)
private spawnDot(): void {
if (this.gamePhase !== 'playing') return;
// 随机位置(避开边缘 10%)
this.dotX = 10 + Math.random() * 80;
this.dotY = 10 + Math.random() * 70;
// 根据分数增加难度
const difficulty = Math.min(this.score / 20, 1);
this.dotSize = 48 - difficulty * 20; // 48px → 28px
const hue = (this.score * 30) % 360;
this.dotColor = `hsl(${hue}, 100%, 65%)`;
// 移动间隔随分数递减
const interval = Math.max(MIN_INTERVAL, BASE_INTERVAL - this.score * 30);
clearTimeout(this.moveTimerId);
this.moveTimerId = setTimeout(() => {
this.spawnDot(); // 超时未点击,自动换位
}, interval);
}
这是整个游戏最核心的方法,包含了三个重要的游戏设计:
1. 位置随机化:光点出现在屏幕 10%~90% 的水平位置和 10%~80% 的垂直位置,避免过于靠近边缘导致点击困难。
2. 难度曲线:使用 difficulty = Math.min(score / 20, 1) 将分数映射到 0~1 的难度值。当分数达到 20 时难度封顶(光点最小、移动最快)。光点大小从 48px 线性减小到 28px。
3. 颜色循环:光点颜色使用 HSL 色相模式,每得一分色相增加 30 度,形成彩虹色渐变效果。这是一种简单但有效的视觉正反馈。
4. 自动超时:如果在 interval 毫秒内玩家没有点击光点,光点会自动刷新到新位置。这防止了玩家「守株待兔」。
3.4 点击处理
private onDotTap(): void {
if (this.gamePhase !== 'playing') return;
clearTimeout(this.moveTimerId);
this.score++;
this.combo++;
// 连击提示
if (this.combo >= 5) {
this.showFlash(`🔥 ${this.combo} 连击!`);
} else if (this.combo >= 3) {
this.showFlash('✨ +1');
}
// 闪白反馈
this.dotColor = '#ffffff';
setTimeout(() => {
if (this.gamePhase === 'playing') {
this.spawnDot();
}
}, 80);
}
private onMissTap(): void {
if (this.gamePhase !== 'playing') return;
this.combo = 0; // 连击中断
}
点击光点:得分、增加连击、显示浮动提示、闪白反馈、刷新光点。
点击空白区域:连击归零。这个设计让玩家不能「乱点」——必须准确点击到光点才能维持连击。
3.5 浮动提示系统
private showFlash(text: string): void {
this.flashText = text;
this.flashAlpha = 1;
setTimeout(() => {
this.flashAlpha = 0;
}, 600);
}
结合 UI 中的 Text 组件:
Text(this.flashText)
.fontSize(24).fontWeight(FontWeight.Bold).fontColor('#ffdd57')
.opacity(this.flashAlpha)
.position({ x: '50%', y: '30%' })
.translate({ x: '-50%' })
当 flashAlpha 从 1 变为 0 时,文字从完全可见逐渐变为完全透明,产生「浮现→消失」的动画效果。
3.6 背景色动态变化
aboutToAppear(): void {
setInterval(() => {
this.bgHue = (this.bgHue + 0.5) % 360;
}, 100);
}
private getBgColor(): string {
return `hsl(${this.bgHue}, 30%, 8%)`;
}
在组件初始化时启动一个定时器,每 100 毫秒让背景色相增加 0.5 度,形成极其缓慢的色彩渐变。这个效果非常微妙——玩家几乎察觉不到变化,但长时间游玩会感受到氛围的差异。
3.7 build 方法:三态 UI 渲染
build() {
Column() {
if (this.gamePhase === 'menu') {
// ═══ 主菜单 ═══
// 光点装饰、标题、最高分、开始按钮
} else if (this.gamePhase === 'playing') {
// ═══ 游戏进行中 ═══
// 顶部 HUD(得分、连击、倒计时)
// 游戏区域(Stack:背景 + 光点 + 浮动提示)
} else {
// ═══ 结算画面 ═══
// 最终得分、评级、最高分、操作按钮
}
}
.width('100%').height('100%')
.backgroundColor(this.getBgColor())
}
ArkTS 的条件渲染(if / else if / else)非常适合实现游戏的状态机。三个状态互斥,UI 完全独立,代码结构清晰。
3.8 完整页面结构
主菜单界面
if (this.gamePhase === 'menu') {
Column() {
// 装饰光点(带 shadow 光晕)
Text('●').fontSize(64).fontColor('#ffdd57')
.margin({ top: 60 }).shadow({ radius: 30, color: '#80ffdd57' })
Text('光点追逐').fontSize(36).fontWeight(FontWeight.Bold).fontColor('#ffffff')
if (this.bestScore > 0) {
Text(`🏆 最高分: ${this.bestScore}`).fontSize(16).fontColor('#ffdd57')
}
Button('开始游戏').width(200).height(52)
.backgroundColor('#ffdd57').fontColor('#1a1a2e')
.fontSize(18).fontWeight(FontWeight.Bold).borderRadius(26)
.shadow({ radius: 20, color: '#40ffdd57' })
.onClick(() => { this.startGame(); })
}
.width('100%').height('100%')
.justifyContent(FlexAlign.Start)
.alignItems(HorizontalAlign.Center)
}
游戏进行中界面
if (this.gamePhase === 'playing') {
Column() {
// ── 顶部 HUD ──
Row() {
Text(`⭐ ${this.score}`).fontSize(22).fontWeight(FontWeight.Bold).fontColor('#ffffff')
if (this.combo >= 3) {
Text(`🔥 ${this.combo}`).fontSize(16).fontColor('#ff6b6b').margin({ left: 8 })
}
Text(`${this.timeLeft}s`).fontSize(20).fontWeight(FontWeight.Bold)
.fontColor(this.timeLeft <= 5 ? '#ff6b6b' : '#aaa')
.margin({ left: 'auto' })
}
.width('100%').padding({ left: 20, right: 20, top: 12 })
// ── 游戏区域 ──
Stack() {
// 可点击背景(miss 区域)
Column().width('100%').height('100%')
.onClick(() => { this.onMissTap(); })
// 光点(带光晕 shadow)
Text('●').fontSize(this.dotSize).fontColor(this.dotColor)
.shadow({ radius: this.dotSize * 0.6, color: this.dotColor + '80' })
.position({ x: `${this.dotX}%`, y: `${this.dotY}%` })
.onClick(() => { this.onDotTap(); })
// 浮动提示
Text(this.flashText).fontSize(24).fontWeight(FontWeight.Bold)
.fontColor('#ffdd57').opacity(this.flashAlpha)
.position({ x: '50%', y: '30%' }).translate({ x: '-50%' })
}
.layoutWeight(1).width('100%').clip(true)
}
.width('100%').height('100%')
}
Stack 布局:使用 Stack 将背景、光点、浮动提示叠放在同一层。背景的 onClick 处理 miss 点击,光点的 onClick 处理得分点击。两层点击区域互不干扰。
clip 属性:.clip(true) 防止光点超出游戏区域边界。
结算界面
if (this.gamePhase === 'over') {
Column() {
Text('⏱ 时间到!').fontSize(20).fontColor('#888')
Text(`${this.score}`).fontSize(72).fontWeight(FontWeight.Bold)
.fontColor('#ffdd57').shadow({ radius: 30, color: '#40ffdd57' })
// 评级
Text(this.score >= 40 ? '🌟 超凡反应!' :
this.score >= 25 ? '⚡ 反应敏捷' :
this.score >= 15 ? '👍 不错哦' : '💪 再来一次')
.fontSize(16).fontColor('#ffdd57')
// 操作按钮
Button('再来一局').width(200).height(48)
.backgroundColor('#ffdd57').fontColor('#1a1a2e')
.borderRadius(24).onClick(() => { this.startGame(); })
Button('返回菜单').width(200).height(44)
.backgroundColor('#333').fontColor('#aaa')
.borderRadius(22).onClick(() => { this.gamePhase = 'menu'; })
}
.width('100%').height('100%')
.justifyContent(FlexAlign.Start)
.alignItems(HorizontalAlign.Center)
}
四、关键技术点详解
4.1 @State 驱动的游戏循环
与传统游戏开发中的「游戏循环」不同,ArkTS 游戏完全由状态变化驱动:
用户点击 → @State 变化 → UI 自动重渲染 → 用户看到新画面
定时器触发 → @State 变化 → UI 自动重渲染 → 用户看到新画面
这种模式的好处是开发者不需要手动管理渲染更新,只需要关心状态怎么变。缺点是频繁的状态更新可能影响性能——好在休闲小游戏的更新频率远低于 ArkTS 的渲染能力上限。
4.2 定时器的正确管理
游戏中使用了两种定时器:
| 定时器 | 用途 | 间隔 | 清理时机 |
|---|---|---|---|
timerId (setInterval) |
倒计时每秒更新 | 1000ms | 游戏结束、组件销毁 |
moveTimerId (setTimeout) |
光点超时自动刷新 | 动态(300~1200ms) | 点击光点、游戏结束、组件销毁 |
最佳实践:
- 每次设置新定时器前先
clear旧定时器,防止重叠 - 在
aboutToDisappear中清理所有定时器,防止内存泄漏 - 每次 spawnDot 时清理旧的 moveTimer,防止光点「瞬移」
aboutToDisappear(): void {
clearInterval(this.timerId);
clearTimeout(this.moveTimerId);
}
4.3 难度曲线设计
好的休闲游戏需要平滑的难度曲线——太简单会无聊,太难会劝退。本游戏的难度公式:
难度因子 = Math.min(得分 / 20, 1) // 0 ~ 1
光点大小 = 48 - 难度因子 × 20 // 48px ~ 28px
停留时间 = max(300, 1200 - 得分 × 30) // 1200ms ~ 300ms
这意味着:
- 前 10 分:光点较大(>38px),移动较慢(>900ms),适合上手
- 10~20 分:难度快速提升,光点缩小、移动加快
- 20 分以上:难度封顶,考验玩家的极限反应
4.4 视觉反馈系统
休闲游戏需要即时、清晰的视觉反馈来让玩家感知自己的操作效果:
| 反馈类型 | 实现方式 | 效果 |
|---|---|---|
| 点击得分 | 得分数字 +1 | 即时满足感 |
| 连击提示 | 浮动文字浮现 + 🔥 emoji | 激励连续操作 |
| 光点闪白 | .dotColor = '#ffffff' → 80ms 后恢复 |
点击确认感 |
| 颜色渐变 | 色相随得分循环 | 视觉多样性 |
| 背景色移 | 色相缓慢变化 | 沉浸氛围 |
| 倒计时变色 | ≤5 秒时文字变红 | 紧迫感 |
4.5 Stack 布局的双层点击
游戏区域使用 Stack 实现双层点击:
Stack() {
// 底层:空白区域(miss)
Column().width('100%').height('100%')
.onClick(() => { this.onMissTap(); })
// 上层:光点(得分)
Text('●').position({ x: '...%', y: '...%' })
.onClick(() => { this.onDotTap(); })
}
关键原理:Stack 中的子组件按声明顺序从下到上叠放。光点在「上面」,所以点击光点时会触发光点的 onClick 而不会穿透到底层的 onClick。点击空白区域时,光点没有挡住点击事件,底层的 onClick 被触发。
这是 ArkTS 事件冒泡机制的自然体现。
五、build() 方法的 ArkTS 约束与实战技巧
5.1 不能在 build() 中使用声明式语句
ArkTS 对 build() 方法有严格的语法限制——只能使用 UI 组件表达式、if 条件渲染、ForEach 循环渲染。以下代码在 ArkTS 中是不允许的:
// ❌ 禁止:不能在 build() 中使用 let 声明局部变量
build() {
let isDisabled = false; // 编译错误!
if (condition) { isDisabled = true; }
}
// ❌ 禁止:不能在 build() 中使用赋值语句
build() {
this.someFlag = true; // 编译错误!
}
解决方案是使用 三元表达式 或 方法调用:
// ✅ 正确:使用三元表达式内联判断
Button(choice.text)
.backgroundColor(condition ? '#333' : '#2d2d2d')
.enabled(!condition)
// ✅ 正确:将逻辑提取到方法中
Button(choice.text)
.onClick(() => { this.handleChoice(idx); })
5.2 ForEach 的 key 生成器必须唯一
ForEach 的第三个参数是 key 生成器,用于标识列表项的唯一性:
ForEach(
items,
(item, index) => { /* 渲染 UI */ },
(item) => item.text // ← key 生成器
)
key 必须在当前列表中唯一。如果两个选项有相同的 text 值,会导致渲染异常。在「光点追逐」中的选项菜单虽然简单,但在处理动态列表时这是一个常见陷阱。
5.3 条件渲染的层叠顺序
当 build() 方法中有多个 if/else 条件块时,每个条件块内的组件必须具有正确的层叠位置(position)。ArkTS 要求条件渲染块不能「悬空」,必须作为某个容器的子组件:
// ✅ 正确:条件块作为 Column 的子组件
Column() {
if (cond1) { Text('A') }
else if (cond2) { Text('B') }
else { Text('C') }
}
// ❌ 错误:条件块不能作为根级代码
if (cond1) { /* ... */ } // 编译错误!
5.4 .ets 单文件的模块化技巧
虽然整个游戏可以写在一个 .ets 文件中,但为了代码可维护性,可以通过以下方式实现「伪模块化」:
- 子组件抽取:将 UI 片段(如 HUD 面板、结算界面)抽取为独立的
@Component struct - 接口定义:在文件头部定义数据接口,作为组件之间的契约
- 工具函数:将游戏逻辑(如难度计算、评级判定)封装为类或函数
六、游戏数据持久化
6.1 使用 Preferences 存储最高分
为了让玩家每次打开游戏时能看到历史最高分,需要将 bestScore 持久化到本地存储:
import { preferences } from '@kit.ArkData';
// 保存最高分
async function saveHighScore(context: Context, score: number): Promise<void> {
const pref = await preferences.getPreferences(context, 'game_data');
await pref.put('high_score', score);
await pref.flush();
}
// 读取最高分
async function loadHighScore(context: Context): Promise<number> {
const pref = await preferences.getPreferences(context, 'game_data');
return await pref.get('high_score', 0);
}
在 aboutToAppear() 中读取历史最高分,在 endGame() 中写入新纪录。
6.2 持久化最佳实践
- 使用异步 API:
getPreferences和flush都是异步操作,注意使用async/await - 合理时机写入:只在游戏结束时写入,避免每帧写入导致性能问题
- Key 命名规范:使用有意义的 key,如
'high_score'、'total_plays'
七、游戏测试与调试技巧
7.1 使用 Log 输出调试信息
在开发过程中,通过 hilog 输出关键状态信息:
import { hilog } from '@kit.PerformanceAnalysisKit';
// 在关键位置输出日志
hilog.info(0x0000, TAG, 'Game started, score=%{public}d', this.score);
hilog.info(0x0000, TAG, 'Dot spawned at x=%{public}f, y=%{public}f', this.dotX, this.dotY);
hilog.info(0x0000, TAG, 'Game over, final score=%{public}d', this.score);
在 DevEco Studio 的 Logcat 面板中可以通过过滤 TAG 来查看特定日志。
7.2 作弊调试模式
为了方便测试高难度阶段,可以添加一个「调试模式」的入口:
// 调试:直接跳到指定分数
private debugSetScore(s: number): void {
this.score = s;
this.spawnDot(); // 刷新光点以匹配新分数
}
可以给主菜单的标题 Text 添加一个长按事件来激活调试面板:
Text('光点追逐')
.onLongClick(() => {
this.debugSetScore(20); // 长按标题跳到 20 分难度
})
八、扩展思路
8.1 增加音效
当前游戏没有声音。可以在点击光点时通过 AudioPlayer 播放短促的提示音,在游戏结束时播放结算音效。需要先将音频文件放入 resources/rawfile/ 目录:
import { audio } from '@kit.AudioKit';
// 播放点击音效
const player = audio.createAudioPlayer();
await player.play('touch_sound.mp3');
8.2 增加道具系统
- 双倍分道具:出现金色光点,点击后接下来 5 秒内得分 ×2
- 时间暂停:出现蓝色光点,点击后倒计时暂停 3 秒
- 磁铁效果:光点短暂停留在屏幕中央,便于点击
8.3 增加排行榜
使用 @ohos.data.preferences 持久化存储最高分,甚至可以接入网络排行榜:
import { preferences } from '@kit.ArkData';
const pref = await preferences.getPreferences(this.context, 'game_data');
await pref.put('high_score', this.bestScore);
await pref.flush();
8.4 增加主题切换
提供不同的视觉主题,如「深海」(蓝紫色调)、「极光」(青绿色调)、「熔岩」(红橙色调),通过切换 HSL 色相范围实现。
九、ArkTS 游戏开发经验总结
9.1 适合在 ArkTS 中开发的游戏类型
| 游戏类型 | 适合度 | 说明 |
|---|---|---|
| 点击收集 / 反应测试 | ⭐⭐⭐⭐⭐ | 状态简单,交互直接 |
| 记忆翻牌 | ⭐⭐⭐⭐ | 需要状态跟踪,适合 @State |
| 文字冒险 | ⭐⭐⭐⭐⭐ | 纯状态驱动,非常适合 |
| 拼图 / 滑块 | ⭐⭐⭐ | 需要复杂的位置计算 |
| 动作 / 平台跳跃 | ⭐⭐ | 需要高频物理更新,性能受限 |
| 射击 / 弹幕 | ⭐ | 高频重绘,ArkTS 渲染开销较大 |
9.2 性能优化的几个原则
- 减少 @State 变量:每个 @State 变化都会触发重渲染,能用普通属性就不用 @State
- 减少组件层级:过多的嵌套 Column/Row 会增加布局计算开销
- 避免高频 setInterval:如果不需要每秒 60 帧的更新频率,尽量使用更粗的间隔
- 及时清理定时器:
aboutToDisappear是清理定时器的唯一可靠时机
9.3 与 Web 游戏开发的对比
| 维度 | Web (Canvas) | ArkTS |
|---|---|---|
| 渲染方式 | Canvas 像素绘制 | 声明式组件渲染 |
| 游戏循环 | requestAnimationFrame | @State + setInterval |
| 碰撞检测 | 数学计算 | 事件拦截 + 坐标判断 |
| 适用类型 | 任何类型 | 状态驱动型休闲游戏 |
| 开发效率 | 低(需手动绘制) | 高(声明式 UI) |
十、完整代码清单
完整代码见项目文件:entry/src/main/ets/pages/LightChaseGame.ets
游戏已在 HarmonyOS NEXT 6.1.1(API 24)上编译通过,可在模拟器或真机上运行。整个游戏仅 282 行代码,是一个学习 ArkTS 游戏开发的绝佳入门示例。
十一、总结
本文通过一个完整的休闲小游戏「光点追逐」的构建过程,展示了鸿蒙 ArkTS 在游戏开发领域的能力和特点。
核心收获
- 三态游戏状态机:menu → playing → over,通过
@State gamePhase控制 UI 切换 - 定时器管理:倒计时用
setInterval,光点刷新用setTimeout,注意清理 - 难度曲线:
difficulty = Math.min(score / 20, 1)实现平滑难度递增 - 视觉反馈:颜色变化、shadow 光晕、浮动提示、连击系统,多层反馈增强体验
- Stack 双层点击:上层光点 + 底层空白区域,事件互不干扰
- ArkTS 约束:build() 中不能声明变量、不能赋值,用三元表达式替代
为什么选择 ArkTS 做游戏?
很多开发者认为声明式 UI 框架不适合游戏开发,认为游戏需要 Canvas 级别的像素控制。但「光点追逐」证明了:对于状态驱动型的休闲游戏,ArkTS 完全胜任。
传统游戏引擎(如 Unity、Cocos)的学习曲线陡峭,需要掌握复杂的引擎 API 和资产管线。而 ArkTS 做游戏的好处在于:
- 零额外学习成本:如果你已经会用 ArkTS 做应用,那你就已经会做游戏
- 全平台运行:编译一次,在手机、平板、折叠屏上一致运行
- 组件复用:游戏 UI 和业务 UI 使用同一套组件体系,混合开发零摩擦
从游戏到应用的反向启发
通过开发这个小游戏,我们也可以反推出一些 ArkTS 应用开发的普遍原则:
- 状态驱动是 ArkTS 的核心思想——无论是游戏还是业务应用,清晰的状态定义是代码可维护性的基石
- 定时器管理是永恒的话题——无论是游戏中的倒计时还是应用中的轮询,清理定时器都是必须养成的习惯
- 声明式 UI 的渲染性能足够优秀——可以放心地在 ArkTS 中实现实时更新的交互
下一步
「光点追逐」虽然简单,但包含了游戏开发的核心要素:状态管理、用户交互、难度设计、视觉反馈。基于这些基础,你可以轻松扩展出更复杂的游戏:
- 增加多种模式(限时模式、无限模式、精确模式)
- 增加道具和技能系统
- 接入排行榜和成就系统
- 增加粒子特效和过渡动画
- 支持多指同时点击
最关键的是——这个游戏只用了一个 .ets 文件,不到 300 行代码。这表明在鸿蒙 ArkTS 中,开发休闲小游戏的入门门槛非常低。拿起你的 DevEco Studio,开始你的第一个 ArkTS 游戏吧!
更多推荐



所有评论(0)