电子说
本文将介绍分布式游戏鉴权应用。操作过程为:
效果图展示:
[gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md
]
完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:
本章节以系统自带的音乐播放器为例(具体以实际的应用为准),介绍如何完成两台设备的分布式组网。
将设备A,B设置为互相信任的设备。
配网完毕。
本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在参考中提供下载方式,整个工程的代码结构如下:
在本章节中,您将学会如何进行页面初始化。
data: {
// 目标设备Id,用于记录申请过来的设备Id
targetDeviceId: '',
// 是否同意玩游戏
isAgree: false,
// 是否显示开始游戏图标
showStart: false
},
onInit() {
// 获取Ability启动参数
featureAbility.getWant().then((want) = > {
if (want.parameters !== undefined && want.parameters !== null && want.parameters !== '') {
// 如果是请求授权被拉起Ability(requestType === 0),则记录申请权限的设备id
if (want.parameters.requestType === 0) {
this.isGame = false;
this.targetDeviceId = want.parameters.localDeviceId;
} else if (want.parameters.requestType === 1) {
// 如果是授权后被拉起Ability(requestType === 1),则根据授权情况判断是否进行游戏
if (want.parameters.isAgree !== null) {
this.isAgree = want.parameters.isAgree;
if (this.isAgree === true) {
this.isGame = true;
this.isStart = true;
this.startGame();
} else {
this.showStart = true;
prompt.showToast({
message: '申请授权未被允许',
duration: 5000
});
}
}
this.targetDeviceId = want.parameters.localDeviceId;
} else {
// 如果没有请求类型字段(requestType),则表明是手动启动的Ability,此时显示启动游戏图标
this.showStart = true;
}
}
});
在本章节中,您将学会如何显示需要鉴权的设备列表。效果图如下:
在index.js文件中,在data下定义deviceList数组,用来表示周边的设备。代码如下:
export default {
data: {
//可授权的设备
deviceList: []
}
}
在index.hml文件中:
代码如下:
< div class="container" >
< button class="text-button" onclick="startFA" >开始游戏< /button >
< dialog id="continueAbilityDialog" class="dialog-main" oncancel="cancelDialog" >
< div class="dialog-div" >
< text class="dialog_title_text" >选择设备< /text >
< list class="dialog_device_list" divider="true" >
< list-item for="{{ deviceList }}" class="device_list_item" >
< div >
< label class="device_item_title" target="{{ $item.id }}" >{{ $item.name }}< /label >
< input class="device_item_radio" type="radio" checked="{{ $item.id === 'localhost' }}"
id="{{ $item.id }}"
name="radioSample" value="{{ $item.id }}"
onchange="onRadioChange({{ $item.id }})" >< /input >
< /div >
< /list-item >
< /list >
< div class="inner-btn" >
< button class="dialog_cancel_button" type="text" value="取消" onclick="onDismissDialogClicked" >< /button >
< /div >
< /div >
< /dialog >
< /div >
在index.css文件中,定义布局和样式。代码如下:
.container {
flex-direction: column;
justify-content: center;
align-items: center;
}
.text-button{
background-color: #5959f1;
color: #FFFFFF;
text-align: center;
font-size: 16px;
width: 80px;
height: 40px;
border-radius: 8px;
}
.select-device-dialog {
width: 90%;
height: 33%;
}
.select-device-wrapper {
margin: 5%;
width: 90%;
height: 90%;
flex-direction: column;
}
.select-device-title {
width: 100%;
height: 20%;
text-align: left;
font-size: 20px;
}
.select-device-list {
width: 100%;
height: 60%;
text-align: left;
font-size: 15px;
}
.select-device-item {
width: 100%;
height: 33%;
}
.select-device-item-left {
width: 100%;
height: 100%;
text-align: left;
font-size: 16px;
}
.dialog-main {
width: 500px;
}
.dialog-div {
flex-direction: column;
align-items: center;
}
.dialog_title_text {
width: 434px;
height: 80px;
font-size: 32px;
font-weight: 600;
}
.dialog_cancel_button {
width: 100%;
font-size: 32px;
}
在index.js文件中:
// 创建实例
createDeviceManager() {
if (dmClass !== null) {
return;
}
deviceManager.createDeviceManager('com.huawei.cookbook', (err, data) = > {
if (err) {
return;
}
subscribeId = Math.floor(Math.random() * 10000 + 1000);
dmClass = data;
dmClass.on('dmFaCallback', data = > this.log('dmFaCallback on:' + JSON.stringify(data)));
dmClass.on('deviceStateChange', mFilterOption, data = > this.log('deviceStateChange on:' + JSON.stringify(data)));
dmClass.on('deviceFound', data = > this.log('deviceFound on:' + JSON.stringify(data)));
dmClass.on('discoverFail', data = > this.log('discoverFail on:' + JSON.stringify(data)));
dmClass.on('serviceDie', data = > this.log('serviceDie on:' + JSON.stringify(data)));
this.getLocalDeviceInfoSync();
const deviceInfoList = dmClass.getTrustedDeviceListSync();
const list = [];
list[0] = DEVICE_LIST_LOCALHOST;
if (deviceInfoList.length > 0) {
for (let i = 0; i < deviceInfoList.length; i++) {
list[i + 1] = {
name: deviceInfoList[i].deviceName,
id: deviceInfoList[i].deviceId
};
}
}
this.deviceList = list;
});
},
getLocalDeviceInfoSync() {
if (dmClass != null) {
deviceInfo = dmClass.getLocalDeviceInfoSync();
} else {
prompt.showToast({
message: '请先初始化'
});
}
},
在本章节中,您将学会如何从设备A拉起设备B的FA,并将设备A的标识信息发送给设备B。效果图如下:
申请鉴权(同意游戏)
申请鉴权(拒绝游戏)
startFA() {
this.$element('continueAbilityDialog').show();
},
onRadioChange(inputValue, e) {
if (inputValue === e.value) {
if (e.value === 'localhost') {
this.$element('continueAbilityDialog').close();
return;
}
if (this.deviceList.length > 0) {
for (let i = 0; i < this.deviceList.length; i++) {
if (this.deviceList[i].id === e.value) {
this.startAbilityContinuation(this.deviceList[i].id, this.deviceList[i].name);
}
}
}
}
},
startAbilityContinuation(deviceId, deviceName) {
this.$element('continueAbilityDialog').close();
const wantValue = {
bundleName: 'com.huawei.cookbook',
abilityName: 'com.huawei.gameauthopenh.MainAbility',
deviceId: deviceId,
// localDeviceId:申请设备的id,requestType,请求类型:0,申请鉴权
parameters: {'localDeviceId': deviceInfo.deviceId, 'requestType': 0}
};
featureAbility.startAbility({
want: wantValue
}).then((data) = > {
// 销毁自身Ability
featureAbility.terminateSelf();
});
},
class="div-permit" if="{{!isGame}}" >
< text class="text-title" >来自远程合成设备小游戏权限请求,是否允许?< /text >
class="div-button" >
< text class="text-allow" onclick="responds(true)" >允许< /text >
< text class="text-reject" onclick="responds(false)" >不允许< /text >
responds(value) {
const wantValue = {
bundleName: 'com.huawei.cookbook',
abilityName: 'com.huawei.gameauthopenh.MainAbility',
deviceId: this.targetDeviceId,
parameters: {'localDeviceId': deviceInfo.deviceId, 'requestType': 1, 'isAgree': value}
};
featureAbility.startAbility({
want: wantValue
}).then((data) = > {
console.info('featureAbility.startAbility finished, ' + JSON.stringify(data));
featureAbility.terminateSelf();
});
},
onInit() {
...
// 获取Ability启动参数
featureAbility.getWant().then((want) = > {
if (want.parameters !== undefined && want.parameters !== null && want.parameters !== '') {
// 如果是请求授权被拉起Ability(requestType === 0),则记录申请权限的设备id
if (want.parameters.requestType === 0) {
this.isGame = false;
this.targetDeviceId = want.parameters.localDeviceId;
} else if (want.parameters.requestType === 1) {
// 如果是授权后被拉起Ability(requestType === 1),则根据授权情况判断是否进行游戏
if (want.parameters.isAgree !== null) {
this.isAgree = want.parameters.isAgree;
if (this.isAgree === true) {
this.isGame = true;
this.isStart = true;
this.startGame();
} else {
this.showStart = true;
prompt.showToast({
message: '申请授权未被允许',
duration: 5000
});
}
}
this.targetDeviceId = want.parameters.localDeviceId;
} else {
// 如果没有请求类型字段(requestType),则表明是手动启动的Ability,此时显示启动游戏图标
this.showStart = true;
}
}
});
...
},
.div-permit{
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.div-button{
flex-direction: row;
justify-content: center;
align-items: center;
}
.text-title{
color: #222222;
font-size: 22px;
align-items: center;
align-content: center;
margin: 20px;
}
.text-allow{
color: #3E7BDE;
font-size: 18px;
margin-right: 10px;
}
.text-reject{
color: #212121;
font-size: 18px;
margin-left: 10px;
}
如下图所示,按照从左到右的顺序,相同的图形碰撞合成下一个图形,最终合成OpenHarmony图形。
效果图预览:
在index.js的data下定义图片资源数组imgArray和显示在屏幕中图片的数组modes。代码如下:
export default {
data: {
// 图片数组
imgArray: ['common/images/product0.png', 'common/images/product1.png',
'common/images/product2.png', 'common/images/product3.png',
'common/images/product4.png', 'common/images/product5.png',
'common/images/product6.png'],
//在屏幕中出现的数据
modes: [],
}
}
modes添加数据模型格式为:要显示的图形路径src、图形的宽度width、图形的高度height、图形的等级level(用于区分不同的图形),以及图形的左坐标left、顶部坐标top和其在x、y方向上的速度。新增一个数据模型到数组中,代码如下:
addNewData() {
var index = Math.floor(Math.random() * 4);
var src = this.imgArray[index];
var width = 50 + index * 10;
var height = 50 + index * 10;
this.modes.push({
level: index,
width: width,
height: height,
src: src,
top: 0,
left: 120,
speedX: 0,
speedY: 10,
})
}
在index.hml文件中,遍历modes数组,用Image组件显示图形,只需要动态更改class、style、src等属性即可。代码如下:
< div class="div-image" if="{{isStart}}" >
< image for="{{ (index, item) in modes }}"
class="product{{ item.level }}"
style="top : {{ item.top }}; left : {{ item.left }}" src="{{ item.src }} "/ >
< /div >
对应的index.css新增内容如下:
.div-image{
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
width: 100%;
height: 100%;
}
.product0{
width: 50px;
height: 50px;
position: absolute;
}
.product1{
width: 60px;
height: 60px;
position: absolute;
}
.product2{
width: 70px;
height: 70px;
position: absolute;
}
.product3{
width: 80px;
height: 80px;
position: absolute;
}
.product4{
width: 90px;
height: 90px;
position: absolute;
}
.product5{
width: 100px;
height: 100px;
position: absolute;
}
.product6{
width: 110px;
height: 110px;
position: absolute;
}
使用setInterval()开启定时器,反复执行excuteTask()方法,该方法用来计算图形的运动。图形的移动主要是将图形的顶部top和左侧left的坐标值,每次递增各自的x、y方向的速度值。部分代码如下:
export default {
startGame(){
addNewData();
intervalId = setInterval(this.excuteTask, 50);
},
excuteTask(){
this.modes.forEach(item = > {
item.top += item.speedY;
item.left += item.speedX;
});
}
}
这部分仅介绍核心的思路,具体的实现过程读者可自行完成,其达到的效果图如下:
isCollision(run, other) {
var runCenterX = run.left + run.width / 2;
var runCenterY = run.top + run.width / 2;
var otherCenterX = other.left + other.width / 2;
var otherCenterY = other.top + other.width / 2;
var distance = Math.sqrt(Math.abs(runCenterX - otherCenterX) * Math.abs(runCenterX - otherCenterX) +
Math.abs(runCenterY - otherCenterY) * Math.abs(runCenterY - otherCenterY));
if (distance < (run.width + other.width) / 2) { return true;
}
return false;
}
isCompose( productA, productB) {
return productA.level == productB.level;
}
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !