简介
demo基于OpenHarmony系统使用ETS语言进行编写,在邀请用户进行设备认证后,用户根据操作提示完成相应操作,然后通过分布式流转实现随机传递***的效果。
工程目录
完整的项目结构目录如下
├─entry\src\main
│ │ config.json
│ ├─ets
│ │ └─MainAbility
│ │ │ app.ets
│ │ └─pages
│ │ CommonLog.ets
│ │ game.ets
│ │ RemoteDeviceManager.ets
│ └─resources
│ ├─base
│ │ ├─element
│ │ ├─graphic
│ │ ├─layout
│ │ ├─media
│ │ └─profile
│ └─rawfile
开发步骤
- 新建OpenHarmony ETS项目
在DevEco Studio中点击File -> New Project ->[Standard]Empty Ability->Next,Language 选择ETS语言,最后点击Finish即创建成功。
- 编写游戏页面
效果图如上可以分为两部分
2.1 顶部状态提示栏
1)首先在@entry组件入口build()中使用Stack作为容器,达到图片和文字堆叠的效果;
2)接着依次写入Image和Column包裹的两个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)
}
}
2.2 中间游戏***九宫格区域
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%')
- 添加弹窗
3.1 创建规则游戏弹窗
1)通过**@CustomDialog**装饰器来创建自定义弹窗,使用方式可参考 自定义弹窗;
2)规则弹窗效果如下,弹窗组成由两个Text和两个Image竖向排列组成,所以我们可以在build()下使用Column容器来包裹,组件代码如下;
@CustomDialog
struct RuleDialog {
controller: CustomDialogController
confirm: () => void
invite: () => void
[url=home.php?mod=space&uid=3273389]@consume[/url] 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
})
3.2 创建游戏失败弹窗,并添加动画效果
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 ,即可把应用程序流转到另一设备。
原本分布式流转应用流程如下:
1)创建DeviceManager实例;
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设备信息列表。
项目实现分布式流转只需如下流程:
4.1 创建RemoteDeviceManager实例
1)导入RemoteDeviceManager
import {RemoteDeviceManager} from './RemoteDeviceManager'
2)声明@Provide装饰的设备列表变量deviceList,和创建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()
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.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 判断输赢
编写判断逻辑,用于不同的点击事件中调用。
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()
}
项目下载和导入
项目仓库地址: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
约束与限制
- 设备编译约束
润和HiSpark Taurus AI Camera(Hi3516d)开发板套件:
润和大禹系列HH-SCDAYU200开发套件:
- 应用编译约束
- 参考 应用开发快速入门
- 集成开发环境:DevEco Studio 3.0.0.601版本,下载地址;
- OpenHarmony SDK 3.0.0.0;