碰一碰,秒更新!游戲近場快傳助力多人聯(lián)機無縫組隊
在多人聯(lián)機游戲場景中,玩家組隊對戰(zhàn)時因游戲版本不一致導(dǎo)致的體驗中斷問題長期存在。當(dāng)游戲更新資源包體積龐大時,未及時更新的玩家需依賴網(wǎng)絡(luò)下載,不僅消耗流量,還因等待時間過長引發(fā)用戶流失。
HarmonyOS SDK 游戲服務(wù)(Game Service Kit)推出的游戲近場快傳服務(wù)支持設(shè)備在彼此靠近的情況下進行游戲數(shù)據(jù)交換,已更新版本的玩家可發(fā)送自身資源包給未更新隊友,助其迅速完成游戲更新,提升玩家體驗。
功能優(yōu)勢
-
無需網(wǎng)絡(luò):近場快傳技術(shù)無需依賴網(wǎng)絡(luò),在無網(wǎng)絡(luò)或網(wǎng)絡(luò)不穩(wěn)定的環(huán)境下,玩家也能輕松分享游戲資源或組隊。玩家只需將兩臺支持近場快傳的設(shè)備靠近,即可自動建立連接并開始傳輸,操作簡單快捷。
-
高速傳輸:游戲近場快傳技術(shù)傳輸速度最高可達100MB/S,能夠顯著提升游戲資源的傳輸效率。
-
跨設(shè)備兼容:游戲近場快傳支持多種設(shè)備平臺,如Phone、PC/2in1、Tablet,實現(xiàn)無縫傳輸。
使用步驟
-
設(shè)備靠近:玩家需要將支持游戲近場快傳的設(shè)備靠近,以確保能夠建立穩(wěn)定的通信鏈路。
-
選擇傳輸內(nèi)容:在游戲內(nèi)或?qū)iT的傳輸應(yīng)用中,玩家需要選擇要傳輸?shù)挠螒蛸Y源或數(shù)據(jù)。
-
開始傳輸:一旦設(shè)備間建立連接,玩家即可啟動傳輸過程,等待資源傳輸完成。
接入步驟
以下示例中使用的API說明詳見接口文檔。
開發(fā)流程

導(dǎo)入模塊
導(dǎo)入Game Service Kit及公共模塊。
import { abilityAccessCtrl, AbilityConstant, UIAbility, common } from "@kit.AbilityKit";
import { hilog } from '@kit.PerformanceAnalysisKit';
import { gameNearbyTransfer } from '@kit.GameServiceKit';
import { BusinessError } from '@kit.BasicServicesKit';
申請權(quán)限
申請ohos.permission.DISTRIBUTED_DATASYNC權(quán)限用于設(shè)備發(fā)現(xiàn)。
let atManager = abilityAccessCtrl.createAtManager();
let uiAbilityContext = this.getUIContext()?.getHostContext() as common.UIAbilityContext;
try {
atManager.requestPermissionsFromUser(uiAbilityContext, ['ohos.permission.DISTRIBUTED_DATASYNC']).then((data) => {
hilog.info(0x0000, 'nearby', '%{public}s', 'data: ' + JSON.stringify(data));
}).catch((err: object) => {
hilog.error(0x0000, 'nearby', '%{public}s', 'err: ' + JSON.stringify(err));
})
} catch (err) {
hilog.error(0x0000, '[nearby]', '%{public}s', 'error' + (err as Error).message);
}
創(chuàng)建游戲近場快傳服務(wù)并注冊相關(guān)回調(diào)
導(dǎo)入相關(guān)模塊后,需先調(diào)用create接口創(chuàng)建游戲近場快傳服務(wù),然后注冊各回調(diào)事件。
public create() {
let uiAbilityContext = this.getUIContext()?.getHostContext() as common.UIAbilityContext;
let initParam: gameNearbyTransfer.CreateParameters = {
abilityName: uiAbilityContext.abilityInfo.name,
context: uiAbilityContext,
moduleName: uiAbilityContext.abilityInfo.moduleName,
needShowSystemUI: false // 是否展示系統(tǒng)UI,true為展示,false為不展示,默認為false
};
try {
gameNearbyTransfer.create(initParam).then((createResult) => {
hilog.info(0x0000, '[nearby]', '%{public}s', 'create success' + createResult.localDeviceName);
this.registerCallback();
}).catch((err: BusinessError) => {
hilog.error(0x0000, '[nearby]', '%{public}s', 'create error' + (err as Error).message);
})
} catch (err) {
hilog.error(0x0000, '[nearby]', '%{public}s', 'error' + (err as Error).message);
}
}
// 注冊監(jiān)聽
public registerCallback() {
try {
gameNearbyTransfer.on('connectNotify', connectNotifyCallBack);
gameNearbyTransfer.on('receivePackageInfo', receivePackageInfoCallBack);
gameNearbyTransfer.on('transferNotify', transferNotifyCallBack);
gameNearbyTransfer.on('error', errorCallBack);
} catch (err) {
hilog.error(0x0000, '[nearby]', '%{public}s', 'error' + (err as Error).message);
}
}
function connectNotifyCallBack(callback: gameNearbyTransfer.ConnectNotification) {
}
function receivePackageInfoCallBack(callback: gameNearbyTransfer.PackageInfo) {
}
function transferNotifyCallBack(callback: gameNearbyTransfer.TransferNotification) {
}
function errorCallBack(callback: gameNearbyTransfer.ReturnResult) {
}
接收端接受協(xié)同
接收端實現(xiàn)onCollaborate回調(diào),回調(diào)中調(diào)用acceptCollaboration接口接受協(xié)同。
export default class EntryAbility extends UIAbility {
// 協(xié)同回調(diào)
onCollaborate(wantParam: Record<string, Object>): AbilityConstant.CollaborateResult {
try {
gameNearbyTransfer.acceptCollaboration(wantParam);
} catch (err) {
hilog.error(0x0000, '[nearby]', '%{public}s', 'error' + (err as Error).message);
}
hilog.info(0x0000, '[nearby]', '%{public}s', 'onCollaborate: accept collaborate');
return AbilityConstant.CollaborateResult.ACCEPT;
}
}
接收端發(fā)布自身游戲近場快傳服務(wù)
接收端調(diào)用publishNearbyGame接口發(fā)布自身游戲近場快傳服務(wù)。
try {
gameNearbyTransfer.publishNearbyGame().then(() => {
hilog.info(0x0000, '[nearby]', '%{public}s', 'publishNearbyGame success');
}).catch((err: BusinessError) => {
hilog.error(0x0000, '[nearby]', '%{public}s', 'publishNearbyGame error');
})
} catch (err) {
hilog.error(0x0000, '[nearby]', '%{public}s', 'error' + (err as Error).message);
}
發(fā)送端綁定接收端游戲近場快傳服務(wù)
發(fā)送端綁定接收端游戲近場快傳服務(wù)支持如下兩種方式:自動綁定和選擇綁定。
這里以自動綁定為例,發(fā)送端調(diào)用autoBindNearbyGame接口自動綁定接收端近場快傳服務(wù)。
try {
gameNearbyTransfer.autoBindNearbyGame().then(() => {
hilog.info(0x0000, '[nearby]', '%{public}s', 'autoBindNearbyGame success');
}).catch((err: BusinessError) => {
hilog.error(0x0000, '[nearby]', '%{public}s', 'autoBindNearbyGame error');
})
} catch (err) {
hilog.error(0x0000, '[nearby]', '%{public}s', 'error' + (err as Error).message);
}
接收端發(fā)送自身文件信息
收到建鏈成功回調(diào)后,接收端調(diào)用sendPackageInfo接口發(fā)送自身文件,如版本信息、包信息(包名和擴展數(shù)據(jù)的字符長度范圍:[0, 2048]、版本號字符長度范圍:[0, 256]、文件列表最多10000條)。
function connectNotifyCallBack(callback: gameNearbyTransfer.ConnectNotification) {
if (callback.connectState == gameNearbyTransfer.ConnectState.CONNECTED) {
// 連接成功回調(diào),判斷當(dāng)前是否為接收端。若當(dāng)前設(shè)備為接收端,請設(shè)置為true,否則請設(shè)置為false。
let isReceive = true;
if (!isReceive) {
return;
}
// 接收端收到連接回調(diào)后需要處理,發(fā)送資源包信息給發(fā)送端
let packageInfo: gameNearbyTransfer.PackageInfo = {
name: 'com.huawei.xxxx',
files: [],
version: '1.1.0',
extraData: 'extraData'
};
let fileInfo: gameNearbyTransfer.FileInfo = {
path: "/xxx/xxxx/files/data.zip", // 使用沙箱路徑
hash: 'fileHash' // 可選
};
packageInfo.files?.push(fileInfo);
try {
gameNearbyTransfer.sendPackageInfo(packageInfo).then(() => {
hilog.info(0x0000, '[nearby]', '%{public}s', 'sendPackageInfo success');
}).catch((err: BusinessError) => {
hilog.error(0x0000, '[nearby]', '%{public}s', 'sendPackageInfo error');
});
} catch (err) {
hilog.error(0x0000, '[nearby]', '%{public}s', 'error' + (err as Error).message);
}
}
}
發(fā)送端對比后傳輸資源包
發(fā)送端收到接收端發(fā)送的版本信息后進行對比,調(diào)用replyPackageInfoResult上報對比結(jié)果,根據(jù)對比結(jié)果決定是否需要調(diào)用transferPackageData接口發(fā)送資源包數(shù)據(jù)。
function receivePackageInfoCallBack(callback: gameNearbyTransfer.PackageInfo) {
// 比較版本,決定是否需要發(fā)送資源包,也可以比較文件hash
let packageInfoResult: gameNearbyTransfer.PackageInfoResult = {
packageInfoResultCode: gameNearbyTransfer.PackageInfoResultCode.PACKAGE_AVAILABLE_COMPARED
};
try {
// 上報對比結(jié)果
gameNearbyTransfer.replyPackageInfoResult(packageInfoResult).then(() => {
let packageData: gameNearbyTransfer.PackageData = {
name: 'com.huawei.gamenearbydemo',
version: '1.0.0',
files: [{
srcPath: '/data/xxxx/a.zip', // srcPath是需要發(fā)送文件的沙箱路徑,詳情請參見應(yīng)用沙箱目錄。
destPath: 'xxxx/a.zip' // destPath是接收文件的自定義路徑,完整的沙箱路徑是fileStoragePath+destPath,詳情請參見應(yīng)用沙箱目錄。
}]
}
try {
// 發(fā)送資源包
gameNearbyTransfer.transferPackageData(packageData).then(() => {
// 發(fā)送成功
}).catch((err: BusinessError) => {
// 發(fā)送異常
hilog.error(0x0000, '[nearby]', '%{public}s', 'error' + (err as Error).message);
});
} catch (err) {
hilog.error(0x0000, '[nearby]', '%{public}s', 'error' + (err as Error).message);
}
}).catch((err: BusinessError) => {
// 上報異常
hilog.error(0x0000, '[nearby]', '%{public}s', 'error' + (err as Error).message);
});
} catch (err) {
hilog.error(0x0000, '[nearby]', '%{public}s', 'error' + (err as Error).message);
}
}
處理資源包傳輸進度信息
發(fā)送端和接收端在傳輸回調(diào)中處理傳輸進度信息。
function transferNotifyCallBack(callback: gameNearbyTransfer.TransferNotification) {
if (callback.transferState == gameNearbyTransfer.TransferState.SEND_PROCESS) {
// 處理發(fā)送進度,如顯示進度條和速率
}
if (callback.transferState == gameNearbyTransfer.TransferState.SEND_FINISH) {
// 發(fā)送完成
}
if (callback.transferState == gameNearbyTransfer.TransferState.RECEIVE_PROCESS) {
// 處理接收進度,如顯示進度條和速率
}
if (callback.transferState == gameNearbyTransfer.TransferState.RECEIVE_FINISH) {
// 接收完成,獲取到資源包存儲的沙箱路徑
let fileStoragePath = callback.fileStoragePath;
// 對fileStoragePath下的文件做處理
}
}
處理已接收資源包后銷毀服務(wù)
對已接收數(shù)據(jù)做好處理或轉(zhuǎn)移后,調(diào)用destroy接口銷毀服務(wù)。若服務(wù)銷毀后再次使用近場快傳服務(wù),需重新創(chuàng)建游戲近場快傳服務(wù)并注冊相關(guān)回調(diào)。
public destroy() {
// 取消回調(diào)注冊
this.unregisterCallback();
// 銷毀服務(wù)
try {
gameNearbyTransfer.destroy().then(() => {
hilog.info(0x0000, '[nearby]', '%{public}s', 'destroy success');
}).catch((err: Error) => {
hilog.error(0x0000, '[nearby]', '%{public}s', 'destroy error' + (err as Error).message);
});
} catch (err) {
hilog.error(0x0000, '[nearby]', '%{public}s', 'error' + (err as Error).message);
}
}
public unregisterCallback() {
try {
gameNearbyTransfer.off('connectNotify', connectNotifyCallBack);
gameNearbyTransfer.off('receivePackageInfo', receivePackageInfoCallBack);
gameNearbyTransfer.off('transferNotify', transferNotifyCallBack);
gameNearbyTransfer.off('error', errorCallBack);
// 發(fā)送端選擇手動綁定接收端且已訂閱discovery事件
gameNearbyTransfer.off('discovery', discoveryCallBack);
} catch (err) {
hilog.error(0x0000, '[nearby]', '%{public}s', 'error' + (err as Error).message);
}
}
function connectNotifyCallBack(callback: gameNearbyTransfer.ConnectNotification) {
}
function receivePackageInfoCallBack(callback: gameNearbyTransfer.PackageInfo) {
}
function transferNotifyCallBack(callback: gameNearbyTransfer.TransferNotification) {
}
function errorCallBack(callback: gameNearbyTransfer.ReturnResult) {
}
function discoveryCallBack(callback: gameNearbyTransfer.DiscoveryResult) {
}
了解更多詳情>>
浙公網(wǎng)安備 33010602011771號