标准系统新增支持了方舟开发框架(ArkUI)、分布式组网和 FA 跨设备迁移能力等新特性,因此我们结合了这三种特性使用 ets 开发了一款如下动图所示传炸弹应用。
打开应用在通过邀请用户进行设备认证后,用户须根据提示完成相应操作,然后通过分布式流转实现随机传递炸弹给下一位用户的效果。那么这样一款传炸弹应用如何进行开发呢?
完整的项目结构目录如下:
├─entry │ └─src │ └─main │ │ config.json // 应用配置 │ │ │ ├─ets │ │ └─MainAbility │ │ │ app.ets //ets应用程序主入口 │ │ │ │ │ └─pages │ │ CommonLog.ets // 日志类 │ │ game.ets // 游戏首页 │ │ RemoteDeviceManager.ets // 设备管理类 │ │ │ └─resources // 静态资源目录 │ ├─base │ │ ├─element │ │ │ │ │ ├─graphic │ │ ├─layout │ │ ├─media // 存放媒体资源 │ │ │ │ │ └─profile │ └─rawfile
我们可以分为如下 3 步:编写声明式 UI 界面、添加分布式能力和编写游戏逻辑。
一、编写声明式UI界面
1. 新增工程
在 DevEco Studio 中点击 File -> New Project ->Standard Empty Ability->Next,Language 选择 ETS 语言,最后点击 Finish 即创建成功。
图1 新建工程
2. 编写游戏页面
图2 游戏界面效果图
效果图如上可以分为两部分:
- 顶部状态提示栏
首先在 @entry 组件入口 build() 中使用 Stack 作为容器,达到图片和文字堆叠的效果;
接着依次写入 Image 包裹的两个 Text 组件;
Stack() { Image($r("app.media.title")).objectFit(ImageFit.Contain).height(120) Column() { Text(this.duration.toString() + 'ms').fontColor(Color.White) Text(this.touchText).fontColor(Color.White) } }
- 中间游戏炸弹九宫格区域
使用 Grid 网格容器来编写九宫格区域;
在 GridItem 中 Stack (容器依次添加方块背景图片和炸弹图片;
在 visibility 属性中用 bombIndex 变量值来决定炸弹显示的位置;
通过 onClick 点击事件和 GestureGroup 组合手势加入单击、双击和长按的监听事件;
Stack() { Image($r("app.media.title")).objectFit(ImageFit.Contain).height(120) Column() { Text(this.duration.toString() + 'ms').fontColor(Color.White) Text(this.touchText).fontColor(Color.White) } }
3. 添加弹窗
- 创建规则游戏弹窗
1)通过 @CustomDialog 装饰器来创建自定义弹窗,使用方式可参考:
自定义弹窗文档:https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/arkui-ts/ts-methods-custom-dialog-box.md
2)规则弹窗效果如下,弹窗组成由两个 Text 和两个 Image 竖向排列组成,所以我们可以在 build()下使用 Column 容器来包裹,组件代码如下;
图3 游戏规则
@CustomDialog struct RuleDialog { controller: CustomDialogController confirm: () => void invite: () => void @Consume deviceList: RemoteDevice[] build() { Column() { Text('游戏规则').fontSize(30).margin(20) Text('炸弹会随机出现在9个方块内,需要在规定时间内完成指定操作(点击、双击或长按),即可将炸弹传递给下一个人,小心炸弹可是会越来越快的喔!') .fontSize(24).margin({ bottom: 10 }) Image($r("app.media.btn_start")).objectFit(ImageFit.Contain).height(80).margin(10) .onClick(() => { console.info(TAG + 'Click start game') if (checkTrustedDevice(this.remoteDeviceModel)) { this.controller.close() this.confirm() } }) Image($r("app.media.btn_Invite")).objectFit(ImageFit.Contain).height(80).margin(10) .onClick(() => { this.invite() }) }.width('90%') .margin(20) .backgroundColor(Color.White) } }
3)在 @entry 创建 CustomDialogController 对象并传入弹窗所需参数,后面可通过该对象 open() 和 close() 方法进行打开和关闭弹窗;
@Provide deviceList: RemoteDevice[] = [] private ruleDialog: CustomDialogController = new CustomDialogController({ builder: RuleDialog({ invite: () => this.InvitePlayer(), confirm: () => this.startGame(), deviceList: this.deviceList }), autoCancel: false })
- 创建游戏失败弹窗,并添加动画效果
图4 游戏失败弹窗动画
1)编写弹窗布局:将游戏失败文本、炸弹图片和再来一局按钮图片放置于 Column 容器中;
2)用变量来控制动画起始和结束的位置:用 Flex 容器包裹炸弹图片,并用 @State 装饰变量 toggle,通过变量来动态修改 [Flex]的direction 属性;
@State toggle: boolean = true private controller: CustomDialogController @Consume deviceList: RemoteDevice[] private confirm: () => void private interval = null build() { Column() { Text('游戏失败').fontSize(30).margin(20) Flex({ direction: this.toggle ? FlexDirection.Column : FlexDirection.ColumnReverse, alignItems: ItemAlign.Center }) { Image($r("app.media.bomb")).objectFit(ImageFit.Contain).height(80) }.height(200) Image($r("app.media.btn_restart")).objectFit(ImageFit.Contain).height(120).margin(10) .onClick(() => { this.controller.close() this.confirm() }) } .width('80%') .margin(50) .backgroundColor(Color.White) }
3)设置动画效果:使用 animateTo 显式动画接口炸弹位置切换时添加动画,并且设置定时器定时执行动画;
aboutToAppear() { this.setBombAnimate() } setBombAnimate() { let fun = () => { this.toggle = !this.toggle; } this.interval = setInterval(() => { animateTo({ duration: 1500, curve: Curve.Sharp }, fun) }, 1600) }
二、添加分布式流转
分布式流转需要在同一网络下通过 DeviceManager 组件进行设备间发现和认证,获取到可信设备的 deviceId 调用 FeatureAbility.startAbility(parameter),即可把应用程序流转到另一设备。
原本分布式流转应用流程如下:
- 创建 DeviceManager 实例;
- 调用实例的 startDeviceDiscovery(),开始设备发现未信任设备;
- 设置设备状态监听 on(‘deviceStateChange’,callback),监听设备上下线状态;
- 设置设备状态监听 on(‘deviceFound’,callback),监听设备发现;
- 传入未信任设备参数,调用实例 authenticateDevice 方法,对设备进行 PIN 码认证;
- 若是已信任设备,可通过实例的 getTrustedDeviceListSync() 方法来获取设备信息;
- 将设备信息中的 deviceId 传入featureAbility.startAbility 方法,实现流转;
- 流转接收方可通过featureAbility.getWant() 获取到发送方携带的数据;
- 注销设备发现监听 off(‘deviceFound’);
- 注销设备状态监听 off(‘deviceStateChange’);
项目中将上面设备管理封装至 RemoteDeviceManager,通过 RemoteDeviceManager 的四个方法来动态维护 deviceList 设备信息列表。
图5 分布式流转
项目实现分布式流转只需如下流程:
1. 创建RemoteDeviceManager实例
1)导入 RemoteDeviceManager
import {RemoteDeviceManager} from './RemoteDeviceManager'
2)声明 @Provide 装饰的设备列表变量 deviceList,和创建 RemoteDeviceManager 实例。
@Provide deviceList: RemoteDevice[] = [] private remoteDm: RemoteDeviceManager = new RemoteDeviceManager(this.deviceList)
2. 刷新设备列表
在生命周期 aboutToAppear 中,调用刷新设备列表和开始发现设备。
aboutToAppear 定义:函数在创建自定义组件的新实例后,在执行其 build 函数之前执行。
aboutToAppear() { this.remoteDm.refreshRemoteDeviceList() // 刷新设备列表 this.remoteDm.startDeviceDiscovery() // 开始发现设备 }
3. 设备认证
invitePlayer(remoteDevice:RemoteDevice) { if (remoteDevice.status == RemoteDeviceStatus.ONLINE) { prompt.showToast({ message: "Already invited!" }) return } this.remoteDm.authDevice(remoteDevice).then(() => { prompt.showToast({ message: "Invite success! deviceName=" + remoteDevice.deviceName }) }).catch(() => { prompt.showToast({ message: "Invite fail!" }) }) }
4. 跨设备流转
从 deviceList 中获取设备列表在线的设备 Id,通过 featureAbility.startAbility 进行流转。
async startAbilityRandom() { let deviceId = this.getRandomDeviceId() // 随机获取设备id CommonLog.info('featureAbility.startAbility deviceId=' + deviceId); let bundleName = await getBundleName() let wantValue = { bundleName: bundleName, abilityName: 'com.sample.bombgame.MainAbility', deviceId: deviceId, parameters: { ongoing: true, transferNumber: this.transferNumber + 1 } }; featureAbility.startAbility({ want: wantValue }).then((data) => { CommonLog.info(' featureAbility.startAbility finished, ' + JSON.stringify(data)); featureAbility.terminateSelf((error) => { CommonLog.info('terminateSelf finished, error=' + error); }); }); }
5. 注销监听
在声明周期 aboutToDisappear 进行注销监听。
aboutToDisappear 定义:函数在自定义组件析构消耗之前执行。
aboutToDisappear() { this.remoteDm.stopDeviceDiscovery() // 注销监听 }
三、编写游戏逻辑
1. 开始游戏
startGame() { CommonLog.info('startGame'); this.randomTouchRule() // 随机游戏点击规则 this.setRandomBomb() // 随机生成炸弹位置 this.stopCountDown() // 停止倒计时 if (this.transferNumber < 10) { this.duration = 3000 - this.transferNumber * 100 } else { this.duration = 2000 } const interval: number = 500 // 开始倒计时 this.timer = setInterval(() => { if (this.duration <= interval) { this.duration = 0 clearInterval(this.timer) this.timer = null this.gameFail() } else { this.duration -= interval } }, interval) }
2. 判断输赢
编写判断逻辑,用于不同的点击事件中调用。
/** * 判断游戏输赢 * @param operation 点击类型 */ judgeGame(operation:RuleType) { this.stopCountDown() if (operation != this.ruleText) { this.gameFail() } else { prompt.showToast({ message: "finish" }) this.bombIndex = -1 this.startAbilityRandom() } }
3. 游戏失败
游戏失败,弹出游戏失败弹框。
gameFail() { prompt.showToast({ message: 'Game Fail' }) CommonLog.info('gameFail'); this.gameFailDialog.open() }
四、项目下载和导入
项目仓库地址:
https://gitee.com/openharmony-sig/knowledge_demo_temp/tree/master/FA/Entertainment/BombGame
1)git下载
git clone https://gitee.com/openharmony-sig/knowledge_demo_temp.git
2)项目导入
打开 DevEco Studio,点击 File->Open->下载路径/FA/Entertainment/BombGame
为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05
《鸿蒙开发学习手册》:
如何快速入门:https://qr21.cn/FV7h05
- 基本概念
- 构建第一个ArkTS应用
- ……
开发基础知识:https://qr21.cn/FV7h05
- 应用基础知识
- 配置文件
- 应用数据管理
- 应用安全管理
- 应用隐私保护
- 三方应用调用管控机制
- 资源分类与访问
- 学习ArkTS语言
- ……
基于ArkTS 开发:https://qr21.cn/FV7h05
- Ability开发
- UI开发
- 公共事件与通知
- 窗口管理
- 媒体
- 安全
- 网络与链接
- 电话服务
- 数据管理
- 后台任务(Background Task)管理
- 设备管理
- 设备使用信息统计
- DFX
- 国际化开发
- 折叠屏系列
- ……
鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH
鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH
1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向
- 创建游戏失败弹窗,并添加动画效果
- 创建规则游戏弹窗
- 中间游戏炸弹九宫格区域