基于OpenHarmony系统从零开发传炸弹小游戏
简介
demo基于OpenHarmony系统使用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 (左右移动查看全部内容)
开发步骤
1、新建OpenHarmony ETS项目: 在DevEco Studio中点击File -> New Project ->[Standard]Empty Ability->Next,Language 选择ETS语言,最后点击Finish即创建成功。
2、编写游戏页面
效果图如上可以分为两部分
2.1 顶部状态提示栏
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) } } (左右移动查看全部内容)
2.2 中间游戏炸弹九宫格区域 使用Grid网格容器来编写九宫格区域; 在GridItem中Stack容器依次添加方块背景图片和炸弹图片; 在visibility属性中用bombIndex变量值来决定炸弹显示的位置; 通过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%') (左右移动查看全部内容)
3、添加弹窗 3.1 创建规则游戏弹窗
@CustomDialog struct RuleDialog { controller: CustomDialogController confirm: () => void invite: () => void
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) } } (左右移动查看全部内容)
@Provide deviceList: RemoteDevice[] = [] private ruleDialog: CustomDialogController = new CustomDialogController({ builder: RuleDialog({ invite: () => this.InvitePlayer(), confirm: () => this.startGame(), deviceList: this.deviceList }), autoCancel: false }) (左右移动查看全部内容)
3.2 创建游戏失败弹窗,并添加动画效果
@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) } (左右移动查看全部内容)
aboutToAppear() { this.setBombAnimate() }
setBombAnimate() { let fun = () => { this.toggle = !this.toggle; } this.interval = setInterval(() => { animateTo({ duration: 1500, curve: Curve.Sharp }, fun) }, 1600) } (左右移动查看全部内容)
4、添加分布式流转 分布式流转需要在同一网络下通过 DeviceManager组件 进行设备间发现和认证,获取到可信设备的deviceId调用 featureAbility.startAbility ,即可把应用程序流转到另一设备。
原本分布式流转应用流程如下:
创建DeviceManager实例; 调用实例的startDeviceDiscovery(),开始设备发现未信任设备; 设置设备状态监听on('deviceStateChange',callback),监听设备上下线状态; 设置设备状态监听on('deviceFound',callback),监听设备发现; 传入未信任设备参数,调用实例authenticateDevice方法,对设备进行PIN码认证; 若是已信任设备,可通过实例的getTrustedDeviceListSync()方法来获取设备信息; 将设备信息中的deviceId传入featureAbility.startAbility方法,实现流转; 流转接收方可通过featureAbility.getWant()获取到发送方携带的数据; 注销设备发现监听off('deviceFound'); 注销设备状态监听off('deviceStateChange');
项目中将上面设备管理封装至RemoteDeviceManager,通过RemoteDeviceManager的四个方法来动态维护deviceList设备信息列表。
项目实现分布式流转只需如下流程: 4.1 创建RemoteDeviceManager实例 import {RemoteDeviceManager} from './RemoteDeviceManager' (左右移动查看全部内容)
@Provide deviceList: RemoteDevice[] = [] private remoteDm: RemoteDeviceManager = new RemoteDeviceManager(this.deviceList) (左右移动查看全部内容)
4.2 刷新设备列表 在生命周期aboutToAppear中,调用刷新设备列表和开始发现设备。
aboutToAppear定义:函数在创建自定义组件的新实例后,在执行其build函数之前执行。 aboutToAppear() { this.remoteDm.refreshRemoteDeviceList() // 刷新设备列表 this.remoteDm.startDeviceDiscovery() // 开始发现设备 } (左右移动查看全部内容)
4.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.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); }); }); } (左右移动查看全部内容)
4.5 注销监听 在声明周期aboutToDisappear进行注销监听。
aboutToDisappear定义:函数在自定义组件析构消耗之前执行。 aboutToDisappear() { this.remoteDm.stopDeviceDiscovery() // 注销监听 } (左右移动查看全部内容)
5、编写游戏逻辑 5.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) } (左右移动查看全部内容)
5.2 判断输赢 编写判断逻辑,用于不同的点击事件中调用。 /** * 判断游戏输赢 * @param operation 点击类型 */ judgeGame(operation:RuleType) { this.stopCountDown() if (operation != this.ruleText) { this.gameFail() } else { prompt.showToast({ message: "finish" }) this.bombIndex = -1 this.startAbilityRandom() } } (左右移动查看全部内容)
5.3 游戏失败 游戏失败,弹出游戏失败弹框。 gameFail() { prompt.showToast({ message: 'Game Fail' }) CommonLog.info('gameFail'); this.gameFailDialog.open() } (左右移动查看全部内容)
项目下载和导入
1)git下载 (左右移动查看全部内容)
2)项目导入 打开DevEco Studio,点击File->Open->下载路径/FA/Entertainment/BombGame
约束与限制
1、设备编译约束 润和HiSpark Taurus AI Camera(Hi3516d)开发板套件:
润和大禹系列HH-SCDAYU200开发套件:
2、应用编译约束
|