摘要本文详细介绍如何使用 HarmonyOS ArkTS 开发一款弹弹球Bouncing Ball物理碰撞游戏。涵盖 Circle 绘制、边界碰撞检测、分数累积、颜色循环以及触摸加速等核心技术点帮助开发者快速掌握 ArkTS 声明式 UI 与交互开发技巧。一、项目概述弹弹球是一款经典的休闲物理小游戏核心玩法是小球在屏幕内自由弹跳用户点击小球使其加速并获得分数。在 HarmonyOS ArkTS 框架下我们利用Canvas与Circle 组件的协同工作配合onTouch 事件实现流畅的物理模拟与交互反馈。本项目的技术亮点包括使用 ArkTS 声明式语法构建 UI 层Circle 组件实现圆形小球的绘制与动画矩形边界碰撞检测算法基于状态变量驱动的颜色循环触摸事件onTouch触发加速逻辑分数增量累积与实时显示二、项目架构与目录结构entry/src/main/ets/ ├── pages/ │ ├── index.ets // 主页面游戏画布与UI │ ├── BouncingBall.ets // 弹弹球逻辑组件 │ └── GameEngine.ets // 物理引擎碰撞检测、运动计算 └── common/ └── Constants.ets // 常量定义颜色、速度、半径等整个应用采用分层架构表现层index.ets 负责界面布局与状态展示逻辑层BouncingBall.ets 封装小球对象的状态与行为引擎层GameEngine.ets 实现物理碰撞检测与运动计算配置层Constants.ets 统一管理游戏参数三、核心技术分析3.1 Circle 组件与自定义绘制ArkTS 提供了丰富的组件库其中Circle是绘制圆形的声明式组件。在弹弹球游戏中我们使用 Circle 组件来渲染小球// BouncingBall.ets Component export struct BouncingBall { Prop ballX: number 200 Prop ballY: number 300 Prop ballRadius: number 25 Prop ballColor: Color Color.Red build() { Circle({ width: this.ballRadius * 2, height: this.ballRadius * 2 }) .fill(this.ballColor) .position({ x: this.ballX, y: this.ballY }) .width(this.ballRadius * 2) .height(this.ballRadius * 2) } }关键点Circle组件的宽高决定其大小传入直径ballRadius * 2position属性控制小球在父容器中的绝对位置Prop装饰器使得小球属性可以由父组件驱动更新3.2 边界碰撞检测算法碰撞检测是物理游戏的核心。我们的场景是一个矩形画布小球在其中运动碰到四边则反弹。// GameEngine.ets export class GameEngine { // 检测并处理边界碰撞 static checkBoundaryCollision( x: number, y: number, radius: number, vx: number, vy: number, canvasWidth: number, canvasHeight: number ): { x: number, y: number, vx: number, vy: number } { let newVx vx let newVy vy let newX x let newY y // 左右边界检测 if (x - radius 0) { newX radius newVx -vx } else if (x radius canvasWidth) { newX canvasWidth - radius newVx -vx } // 上下边界检测 if (y - radius 0) { newY radius newVy -vy } else if (y radius canvasHeight) { newY canvasHeight - radius newVy -vy } return { x: newX, y: newY, vx: newVx, vy: newVy } } }算法要点边界碰撞条件反弹处理左边界x - radius 0x radius, vx -vx右边界x radius canvasWidthx canvasWidth - radius, vx -vx上边界y - radius 0y radius, vy -vy下边界y radius canvasHeighty canvasHeight - radius, vy -vy碰撞检测的关键在于将圆形简化为质点判断质点位置与边界距离是否小于半径。当检测到碰撞时不仅反转速度方向还需修正位置防止小球卡在边界外。3.3 触摸加速功能用户点击小球时小球获得瞬时加速度。我们通过onTouch事件监听触摸位置判断是否与小球区域重合// index.ets — 主页面触摸事件 Entry Component struct GamePage { State ballX: number 200 State ballY: number 400 State ballVx: number 3 State ballVy: number -4 State score: number 0 State ballColor: Color Color.Red private readonly CANVAS_WIDTH: number 360 private readonly CANVAS_HEIGHT: number 780 private readonly RADIUS: number 30 private readonly ACCELERATION: number 2.5 build() { Stack() { // 分数显示 Text(得分: ${this.score}) .fontSize(24) .fontWeight(FontWeight.Bold) .position({ x: 20, y: 40 }) // 弹弹球 Circle({ width: this.RADIUS * 2, height: this.RADIUS * 2 }) .fill(this.ballColor) .position({ x: this.ballX, y: this.ballY }) .onTouch((event: TouchEvent) { if (event.type TouchType.Down) { // 判断触摸点是否在小球区域内 const touchX event.touches[0].x const touchY event.touches[0].y const dx touchX - (this.ballX this.RADIUS) const dy touchY - (this.ballY this.RADIUS) const distance Math.sqrt(dx * dx dy * dy) if (distance this.RADIUS) { // 触摸小球加速并计分 this.ballVx * this.ACCELERATION this.ballVy * this.ACCELERATION this.score this.changeColor() } } }) } .width(100%) .height(100%) .backgroundColor(#F5F5F5) } }onTouch 事件处理流程用户触摸屏幕触发onTouch回调判断事件类型为TouchType.Down手指按下获取触摸点坐标event.touches[0].x / y计算触摸点与圆心的距离若距离 ≤ 半径判定为点击到小球执行加速速度乘以系数、加分、换色3.4 颜色循环机制为了让游戏更具视觉吸引力每次点击小球时切换颜色。我们定义一个颜色数组并循环索引private readonly colors: Color[] [ Color.Red, Color.Blue, Color.Green, Color.Orange, Color.Pink, Color.Purple, Color.Yellow, Color.Grey ] private colorIndex: number 0 changeColor(): void { this.colorIndex (this.colorIndex 1) % this.colors.length this.ballColor this.colors[this.colorIndex] }这种循环取模的方式简洁高效无需额外的状态判断。也可以扩展为 HSL 颜色渐变实现更平滑的过渡效果。3.5 帧动画驱动使用setInterval定时更新小球位置实现连续动画aboutToAppear(): void { this.timerId setInterval(() { this.updateBallPosition() }, 16) // 约 60 FPS } updateBallPosition(): void { let newX this.ballX this.ballVx let newY this.ballY this.ballVy // 碰撞检测 const result GameEngine.checkBoundaryCollision( newX, newY, this.RADIUS, this.ballVx, this.ballVy, this.CANVAS_WIDTH, this.CANVAS_HEIGHT ) this.ballX result.x this.ballY result.y this.ballVx result.vx this.ballVy result.vy }帧率说明16ms间隔约等于 60 FPS这是大多数游戏的标准刷新率。如果感觉性能开销大可以调整为 30ms约 33 FPS。四、完整代码整合将以上各部分整合为一个完整可运行的主页面// index.ets — 完整游戏页面 import { GameEngine } from ./GameEngine Entry Component struct BouncingBallGame { State ballX: number 180 State ballY: number 400 State ballVx: number 4 State ballVy: number -5 State score: number 0 State ballColor: Color Color.Red private readonly CANVAS_W: number 360 private readonly CANVAS_H: number 780 private readonly R: number 28 private readonly COLORS: Color[] [ Color.Red, Color.Blue, Color.Green, Color.Orange, Color.Pink, Color.Purple, Color.Yellow, Color.Grey ] private colorIdx: number 0 private timerId: number -1 aboutToAppear(): void { this.timerId setInterval(() { let nx this.ballX this.ballVx let ny this.ballY this.ballVy const ret GameEngine.checkBoundaryCollision( nx, ny, this.R, this.ballVx, this.ballVy, this.CANVAS_W, this.CANVAS_H ) this.ballX ret.x this.ballY ret.y this.ballVx ret.vx this.ballVy ret.vy }, 16) } aboutToDisappear(): void { clearInterval(this.timerId) } changeColor(): void { this.colorIdx (this.colorIdx 1) % this.COLORS.length this.ballColor this.COLORS[this.colorIdx] } build() { Stack() { // 背景 Rect() .width(100%) .height(100%) .fill(#1a1a2e) // 分数 Text( ${this.score}) .fontSize(28) .fontColor(Color.White) .fontWeight(FontWeight.Bold) .position({ x: 20, y: 50 }) // 小球 Circle({ width: this.R * 2, height: this.R * 2 }) .fill(this.ballColor) .position({ x: this.ballX, y: this.ballY }) .onTouch((ev: TouchEvent) { if (ev.type TouchType.Down) { const tx ev.touches[0].x const ty ev.touches[0].y const dx tx - (this.ballX this.R) const dy ty - (this.ballY this.R) if (Math.sqrt(dx * dx dy * dy) this.R) { this.ballVx * 2.0 this.ballVy * 2.0 this.score this.changeColor() } } }) // 操作提示 Text(点击小球加速得分) .fontSize(16) .fontColor(#888888) .position({ x: 90, y: 700 }) } .width(100%) .height(100%) } }五、HarmonyOS 特性深度解析5.1 声明式 UI 与状态驱动ArkTS 采用声明式编程范式开发者只需描述 UI 应处于的状态框架自动处理渲染更新。在弹弹球中State标记的变量ballX, ballY, score, ballColor发生变化时框架自动重绘相关组件无需手动操作 DOM 或调用 invalidate 方法数据流单向状态 → UI避免双向绑定带来的调试困难5.2 Stack 布局的妙用Stack是一个层叠布局容器子组件按声明顺序从下到上堆叠。在游戏中非常适合底层放背景矩形中间层放分数文本顶层放小球 Circle这种布局方式使得 z-order 管理变得直观无需像传统游戏引擎那样手动处理绘制顺序。5.3 生命周期管理aboutToAppear()页面即将显示时调用适合初始化计时器aboutToDisappear()页面即将销毁时调用必须清理计时器防止内存泄漏六、UI/UX 设计建议6.1 视觉风格深色背景 亮色小球深色背景#1a1a2e减少视觉疲劳亮色小球形成对比分数动画得分时显示 “1” 浮动文字增强反馈拖尾效果在小球后方绘制半透明轨迹增加运动感6.2 交互反馈触感反馈点击小球时配合短震动如调用vibrator.vibrate(50)速度显示在画布角落显示当前速度值让用户感知加速效果碰撞音效小球碰到边界时播放简单的「砰」声6.3 优化体验初始速度随机化每次启动游戏时小球的速度方向随机增加可玩性难度递增随着分数增加小球的基础速度逐渐提高暂停机制点击暂停按钮冻结物理模拟七、最佳实践总结7.1 性能优化避免频繁创建对象在updateBallPosition中复用计算结果对象合理使用状态变量只将对 UI 有影响的属性标记为State内部计算变量保持为普通属性定时器清理务必在aboutToDisappear中清理setInterval7.2 代码组织将物理引擎、小球逻辑、页面布局拆分到不同文件中使用Prop和State明确组件间数据关系常量集中管理避免硬编码7.3 兼容性注意Circle 组件的fill属性支持Color枚举和字符串色值onTouch事件在模拟器中与真机行为一致但多点触摸支持需额外处理不同设备的屏幕尺寸差异建议使用breakpoint断点系统适配八、扩展与演进弹弹球游戏虽然简单但可以扩展为更完整的物理引擎重力系统增加竖直方向恒定加速度多球碰撞实现球与球之间的弹性碰撞障碍物在画布中随机生成矩形障碍物粒子特效点击小球时产生粒子爆炸效果九、结语通过弹弹球游戏的开发我们深入实践了 HarmonyOS ArkTS 框架的核心能力声明式组件、状态驱动、触摸事件与动画循环。麻雀虽小五脏俱全这个小项目涵盖了游戏开发中最基础的物理模拟与交互模式是学习 ArkTS 游戏开发的理想起点。希望本文能帮助开发者快速上手 ArkTS 游戏开发在实际项目中灵活运用 Circle、Stack、onTouch 等关键技术。下一期我们将探索表单验证的实现技巧敬请期待