1.介紹
本文將介紹分布式游戲鑒權(quán)應(yīng)用。操作過程為:
- 設(shè)備A點擊“開始游戲”按鈕,開始搜索周邊設(shè)備。
- 設(shè)備A顯示周邊設(shè)備,點擊設(shè)備B并發(fā)起連接請求,遠程拉起設(shè)備B的FA。
- 設(shè)備B收到請求后,選擇是否允許“開啟游戲”。
- 選擇允許,遠程拉起設(shè)備A,并傳遞允許的信息,設(shè)備A解析了信息后自動開始游戲。
- 選擇不允許,遠程拉起設(shè)備A,并傳遞不允許的信息,設(shè)備A回到最初的狀態(tài),并提示申請鑒權(quán)未通過。
效果圖展示:
2.相關(guān)概念
[gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md
]
3.搭建OpenHarmony環(huán)境
完成本篇Codelab我們首先要完成開發(fā)環(huán)境的搭建,本示例以RK3568開發(fā)板為例,參照以下步驟進行:
- [獲取OpenHarmony系統(tǒng)版本]:標(biāo)準(zhǔn)系統(tǒng)解決方案(二進制)。
以3.1版本為例: - 搭建燒錄環(huán)境。
- [完成DevEco Device Tool的安裝]
- [完成RK3568開發(fā)板的燒錄]
- 搭建開發(fā)環(huán)境。
- 開始前請參考[工具準(zhǔn)備] ,完成DevEco Studio的安裝和開發(fā)環(huán)境配置。
- 開發(fā)環(huán)境配置完成后,請參考[使用工程向?qū) 創(chuàng)建工程(模板選擇“Empty Ability”) ,選擇JS或者eTS語言開發(fā)。
- 工程創(chuàng)建完成后,選擇使用[真機進行調(diào)測] 。
4.分布式組網(wǎng)
本章節(jié)以系統(tǒng)自帶的音樂播放器為例(具體以實際的應(yīng)用為準(zhǔn)),介紹如何完成兩臺設(shè)備的分布式組網(wǎng)。
- 硬件準(zhǔn)備:準(zhǔn)備兩臺燒錄相同的版本系統(tǒng)的RK3568開發(fā)板A、B。
- 開發(fā)板A、B連接同一個WiFi網(wǎng)絡(luò)。
打開設(shè)置-->WLAN-->點擊右側(cè)WiFi開關(guān)-->點擊目標(biāo)WiFi并輸入密碼。
將設(shè)備A,B設(shè)置為互相信任的設(shè)備。
- 找到系統(tǒng)應(yīng)用“音樂”。
- 設(shè)備A打開音樂,點擊左下角流轉(zhuǎn)按鈕,彈出列表框,在列表中會展示遠端設(shè)備的id。
- 選擇遠端設(shè)備B的id,另一臺開發(fā)板(設(shè)備B)會彈出驗證的選項框。
- 設(shè)備B點擊允許,設(shè)備B將會彈出隨機PIN碼,將設(shè)備B的PIN碼輸入到設(shè)備A的PIN碼填入框中。
配網(wǎng)完畢。
5.代碼結(jié)構(gòu)解讀
本篇Codelab只對核心代碼進行講解,對于完整代碼,我們會在參考中提供下載方式,整個工程的代碼結(jié)構(gòu)如下:
- common:存放公共資源
- pages:存放頁面
index.js:主頁面 - config.json:配置文件
6.初始化頁面
在本章節(jié)中,您將學(xué)會如何進行頁面初始化。
- 在data下定義需要使用的字段。
data: { // 目標(biāo)設(shè)備Id,用于記錄申請過來的設(shè)備Id targetDeviceId: '', // 是否同意玩游戲 isAgree: false, // 是否顯示開始游戲圖標(biāo) showStart: false },
- 根據(jù)Ability啟動參數(shù)來判斷頁面被拉起的狀態(tài)。
在拉起頁面時候,設(shè)置requestType為分布式拉起頁面的業(yè)務(wù)請求類型(申請鑒權(quán)或者回復(fù)鑒權(quán)結(jié)果),如果沒有requestType參數(shù),則為手動拉起本機Ability。然后通過分析requestType參數(shù)的值來進行不同的業(yè)務(wù)邏輯操作。onInit() { // 獲取Ability啟動參數(shù) featureAbility.getWant().then((want) = > { if (want.parameters !== undefined && want.parameters !== null && want.parameters !== '') { // 如果是請求授權(quán)被拉起Ability(requestType === 0),則記錄申請權(quán)限的設(shè)備id if (want.parameters.requestType === 0) { this.isGame = false; this.targetDeviceId = want.parameters.localDeviceId; } else if (want.parameters.requestType === 1) { // 如果是授權(quán)后被拉起Ability(requestType === 1),則根據(jù)授權(quán)情況判斷是否進行游戲 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: '申請授權(quán)未被允許', duration: 5000 }); } } this.targetDeviceId = want.parameters.localDeviceId; } else { // 如果沒有請求類型字段(requestType),則表明是手動啟動的Ability,此時顯示啟動游戲圖標(biāo) this.showStart = true; } } });
7.顯示鑒權(quán)設(shè)備
在本章節(jié)中,您將學(xué)會如何顯示需要鑒權(quán)的設(shè)備列表。效果圖如下:
在index.js文件中,在data下定義deviceList數(shù)組,用來表示周邊的設(shè)備。代碼如下:
export default { data: { //可授權(quán)的設(shè)備 deviceList: [] } }
在index.hml文件中:
- 定義一個"開始游戲"的button組件,設(shè)置startFA的點擊事件;
- 顯示周邊設(shè)備的對話框dialog,使用list 、list-item實現(xiàn)設(shè)備列表的展示;
- 通過for屬性遍歷deviceList數(shù)組,$item是每一項的實例;
- 給每一項設(shè)置selectDevice點擊事件,參數(shù)為設(shè)備的networkId。
代碼如下:
< 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" >選擇設(shè)備< /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方法,獲得設(shè)備管理器實例并進行獲得同一網(wǎng)段下的所有在線設(shè)備;
// 創(chuàng)建實例 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方法,獲取本設(shè)備信息;
getLocalDeviceInfoSync() { if (dmClass != null) { deviceInfo = dmClass.getLocalDeviceInfoSync(); } else { prompt.showToast({ message: '請先初始化' }); } },
- 將獲取到的同一網(wǎng)段下的所有在線設(shè)備信息放入deviceList數(shù)組中;
- 通過this.$element('showDialog')找到hml文件中dialog組件,調(diào)用show()方法顯示對話框。
- 定義createDeviceManager方法,獲得設(shè)備管理器實例并進行獲得同一網(wǎng)段下的所有在線設(shè)備;
8.鑒權(quán)申請與回應(yīng)
在本章節(jié)中,您將學(xué)會如何從設(shè)備A拉起設(shè)備B的FA,并將設(shè)備A的標(biāo)識信息發(fā)送給設(shè)備B。效果圖如下:
申請鑒權(quán)(同意游戲)
申請鑒權(quán)(拒絕游戲)
- 設(shè)備A點擊開始游戲,顯示可以進行鑒權(quán)申請的設(shè)備列表,并選中設(shè)備申請游戲鑒權(quán)
- 定義startFa方法,用以顯示設(shè)備列表對話框;
startFA() { this.$element('continueAbilityDialog').show(); },
- 定義onRadioChange方法,用以監(jiān)聽選擇的設(shè)備變化;
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方法,用以申請鑒權(quán);
startAbilityContinuation(deviceId, deviceName) { this.$element('continueAbilityDialog').close(); const wantValue = { bundleName: 'com.huawei.cookbook', abilityName: 'com.huawei.gameauthopenh.MainAbility', deviceId: deviceId, // localDeviceId:申請設(shè)備的id,requestType,請求類型:0,申請鑒權(quán) parameters: {'localDeviceId': deviceInfo.deviceId, 'requestType': 0} }; featureAbility.startAbility({ want: wantValue }).then((data) = > { // 銷毀自身Ability featureAbility.terminateSelf(); }); },
- 定義startFa方法,用以顯示設(shè)備列表對話框;
- 設(shè)備B被設(shè)備A分布式拉起,對游戲進行授權(quán)
- index.hml頁面添加div用以顯示授權(quán)選項;
class="div-permit" if="{{!isGame}}" > < text class="text-title" >來自遠程合成設(shè)備小游戲權(quán)限請求,是否允許?< /text > class="div-button" > < text class="text-allow" onclick="responds(true)" >允許< /text > < text class="text-reject" onclick="responds(false)" >不允許< /text >
- 定義responds方法用以反饋鑒權(quán)結(jié)果,并分布式拉起設(shè)備A的Ability;
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(); }); },
- index.hml頁面添加div用以顯示授權(quán)選項;
- 設(shè)備A被分布式拉起并解析鑒權(quán)結(jié)果,并根據(jù)結(jié)果執(zhí)行不同的操作;
在onInit方法中調(diào)用featureAbility.getWant()來獲取啟動信息并根據(jù)啟動信息判斷游戲申請是否被拒絕;onInit() { ... // 獲取Ability啟動參數(shù) featureAbility.getWant().then((want) = > { if (want.parameters !== undefined && want.parameters !== null && want.parameters !== '') { // 如果是請求授權(quán)被拉起Ability(requestType === 0),則記錄申請權(quán)限的設(shè)備id if (want.parameters.requestType === 0) { this.isGame = false; this.targetDeviceId = want.parameters.localDeviceId; } else if (want.parameters.requestType === 1) { // 如果是授權(quán)后被拉起Ability(requestType === 1),則根據(jù)授權(quán)情況判斷是否進行游戲 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: '申請授權(quán)未被允許', duration: 5000 }); } } this.targetDeviceId = want.parameters.localDeviceId; } else { // 如果沒有請求類型字段(requestType),則表明是手動啟動的Ability,此時顯示啟動游戲圖標(biāo) this.showStart = true; } } }); ... },
- index.css文件新增內(nèi)容如下:
.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; }
9.游戲:圖形的運動、碰撞與合成
- [圖形的運動]
- [圖形的碰撞與合成
如下圖所示,按照從左到右的順序,相同的圖形碰撞合成下一個圖形,最終合成OpenHarmony圖形。
效果圖預(yù)覽:
圖形的運動
在index.js的data下定義圖片資源數(shù)組imgArray和顯示在屏幕中圖片的數(shù)組modes。代碼如下:
export default { data: { // 圖片數(shù)組 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'], //在屏幕中出現(xiàn)的數(shù)據(jù) modes: [], } }
modes添加數(shù)據(jù)模型格式為:要顯示的圖形路徑src、圖形的寬度width、圖形的高度height、圖形的等級level(用于區(qū)分不同的圖形),以及圖形的左坐標(biāo)left、頂部坐標(biāo)top和其在x、y方向上的速度。新增一個數(shù)據(jù)模型到數(shù)組中,代碼如下:
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數(shù)組,用Image組件顯示圖形,只需要動態(tài)更改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 >
對應(yīng)的index.css新增內(nèi)容如下:
.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()開啟定時器,反復(fù)執(zhí)行excuteTask()方法,該方法用來計算圖形的運動。圖形的移動主要是將圖形的頂部top和左側(cè)left的坐標(biāo)值,每次遞增各自的x、y方向的速度值。部分代碼如下:
export default { startGame(){ addNewData(); intervalId = setInterval(this.excuteTask, 50); }, excuteTask(){ this.modes.forEach(item = > { item.top += item.speedY; item.left += item.speedX; }); } }
圖形的碰撞與合成
這部分僅介紹核心的思路,具體的實現(xiàn)過程讀者可自行完成,其達到的效果圖如下:
- 兩個圖形若滿足它們的的圓心距離小于它們半徑的總和,則認為它們發(fā)生了碰撞。部分代碼如下:
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; }
- 通過判斷兩個圖形的等級level值是否相等,若相等就可以進行合成。部分代碼如下:
isCompose( productA, productB) { return productA.level == productB.level; }
審核編輯 黃宇
-
分布式
+關(guān)注
關(guān)注
1文章
899瀏覽量
74509 -
鴻蒙
+關(guān)注
關(guān)注
57文章
2352瀏覽量
42859 -
OpenHarmony
+關(guān)注
關(guān)注
25文章
3722瀏覽量
16321 -
鴻蒙OS
+關(guān)注
關(guān)注
0文章
188瀏覽量
4396
發(fā)布評論請先 登錄
相關(guān)推薦
評論