在9月30日更新的 OpenHarmony3.0 LTS 上,规范零碎新增反对了方舟开发框架(ArkUI)、分布式组网和 FA 跨设施迁徙能力等新个性,因而咱们联合了这三种个性应用 ets 开发了一款如下视频所示传炸弹利用。

https://v.qq.com/x/page/v3313...

刷新
关上利用在通过邀请用户进行设施认证后,用户须依据提醒实现相应操作,而后通过分布式流转实现随机传递炸弹给下一位用户的成果。

那么这样一款传炸弹利用如何进行开发呢?

残缺的我的项目构造目录如下:

├─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 新建工程

  1. 编写游戏页面

图2 游戏界面效果图

效果图如上能够分为两局部:
顶部状态提醒栏

1)首先在 @entry 组件入口 build() 中应用 Stack 作为容器,达到图片和文字重叠的成果; 2)接着顺次写入 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)    } }

两头游戏炸弹九宫格区域

1)应用 Grid 网格容器来编写九宫格区域;
2)在 GridItem 中 Stack (容器顺次增加方块背景图片和炸弹图片;
3)在 visibility 属性中用 bombIndex 变量值来决定炸弹显示的地位;
4)通过 onClick 点击事件和 GestureGroup 组合手势退出单击、双击和长按的监听事件;

 Stack() {   Image($r("app.media.background")).objectFit(ImageFit.Contain)   Grid() {     ForEach(this.grid, (item) => {       GridItem() {         Stack() {           Image($r("app.media.squares")).objectFit(ImageFit.Contain)           Image($r("app.media.bomb"))             .width('50%')             .objectFit(ImageFit.Contain)             .visibility(this.bombIndex == item ? Visibility.Visible : Visibility.Hidden)             // 炸弹点击事件             .onClick((event) => {               // 单击               this.judgeGame(RuleType.click)             })             .gesture(             GestureGroup(GestureMode.Exclusive,             LongPressGesture({ repeat: false })               .onAction((event: GestureEvent) => {                 // 长按                 this.judgeGame(RuleType.longPress)               }),             TapGesture({ count: 2 })               .onAction(() => {                 // 双击                 this.judgeGame(RuleType.doubleClick)               })             )         }       }.forceRebuild(false)     }, item => item)   }   .columnsTemplate('1fr 1fr 1fr')   .rowsTemplate('1fr 1fr 1fr')   .columnsGap(10)   .rowsGap(10)   .width('90%')   .height('75%') }.width('80%').height('70%')
  1. 增加弹窗

创立规定游戏弹窗

1)通过 @CustomDialog 装璜器来创立自定义弹窗,应用形式可参考
自定义弹窗文档:
https://gitee.com/openharmony...

2)规定弹窗成果如下,弹窗组成由两个 Text 和两个 Image 竖向排列组成,所以咱们能够在 build()下应用 Column 容器来包裹,组件代码如下;

图3 游戏规则

@CustomDialogstruct 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 = trueprivate controller: CustomDialogController@Consume deviceList: RemoteDevice[]private confirm: () => voidprivate interval = nullbuild() {   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),即可把应用程序流转到另一设施。

本来分布式流转利用流程如下:

2)调用实例的 startDeviceDiscovery(),开始设施发现未信赖设施;
3)设置设施状态监听 on('deviceStateChange',callback),监听设施高低线状态;
4)设置设施状态监听 on('deviceFound',callback),监听设施发现;
5)传入未信赖设施参数,调用实例 authenticateDevice 办法,对设施进行 PIN 码认证;
6)若是已信赖设施,可通过实例的 getTrustedDeviceListSync() 办法来获取设施信息;
7)将设施信息中的 deviceId 传入featureAbility.startAbility 办法,实现流转;
8)流转接管方可通过featureAbility.getWant() 获取到发送方携带的数据;
9)登记设施发现监听 off('deviceFound');
10)登记设施状态监听 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)

  1. 刷新设施列表

在生命周期 aboutToAppear 中,调用刷新设施列表和开始发现设施。

aboutToAppear 定义:函数在创立自定义组件的新实例后,在执行其 build 函数之前执行。aboutToAppear() {  this.remoteDm.refreshRemoteDeviceList() // 刷新设施列表  this.remoteDm.startDeviceDiscovery() // 开始发现设施}
  1. 设施认证
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!" })  })}
  1. 跨设施流转

从 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);    });  });}
  1. 登记监听

在申明周期 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)}
  1. 判断输赢

编写判断逻辑,用于不同的点击事件中调用。

/** * 判断游戏输赢 * @param operation 点击类型 */judgeGame(operation:RuleType) {   this.stopCountDown()   if (operation != this.ruleText) {      this.gameFail()   } else {      prompt.showToast({ message: "finish" })      this.bombIndex = -1      this.startAbilityRandom()   }}
  1. 游戏失败

游戏失败,弹出游戏失败弹框。

gameFail() {  prompt.showToast({    message: 'Game Fail'  })  CommonLog.info('gameFail');  this.gameFailDialog.open()}

四、我的项目下载和导入

我的项目仓库地址:
https://gitee.com/openharmony...

1)git下载

git clone https://gitee.com/openharmony-sig/knowledge_demo_temp.git

2)我的项目导入

关上 DevEco Studio,点击 File->Open->下载门路/FA/Entertainment/BombGame

五、束缚与限度

  1. 设施编译束缚

获取OpenHarmony源码(OpenHarmony 版本须 3.0 LTS 以上):
https://www.openharmony.cn/pa...

装置开发板环境:
https://www.openharmony.cn/pa...
开发板烧录:
https://www.openharmony.cn/pa...

  1. 利用编译束缚

参考利用开发疾速入门:
https://www.openharmony.cn/pa...
集成开发环境(DevEco Studio 3.0.0.601版本以上)下载地址:
https://developer.harmonyos.c...
OpenHarmony SDK 3.0.0.0 以上;