Stories

Detail Return Return

碰一碰,秒更新!遊戲近場快傳助力多人聯機無縫組隊 - Stories Detail

在多人聯機遊戲場景中,玩家組隊對戰時因遊戲版本不一致導致的體驗中斷問題長期存在。當遊戲更新資源包體積龐大時,未及時更新的玩家需依賴網絡下載,不僅消耗流量,還因等待時間過長引發用户流失。

HarmonyOS SDK 遊戲服務(Game Service Kit)推出的遊戲近場快傳服務支持設備在彼此靠近的情況下進行遊戲數據交換,已更新版本的玩家可發送自身資源包給未更新隊友,助其迅速完成遊戲更新,提升玩家體驗。

功能優勢

  • 無需網絡:近場快傳技術無需依賴網絡,在無網絡或網絡不穩定的環境下,玩家也能輕鬆分享遊戲資源或組隊。玩家只需將兩台支持近場快傳的設備靠近,即可自動建立連接並開始傳輸,操作簡單快捷。
  • 高速傳輸:遊戲近場快傳技術傳輸速度最高可達100MB/S,能夠顯著提升遊戲資源的傳輸效率。
  • 跨設備兼容:遊戲近場快傳支持多種設備平台,如Phone、PC/2in1、Tablet,實現無縫傳輸。

使用步驟

  1. 設備靠近:玩家需要將支持遊戲近場快傳的設備靠近,以確保能夠建立穩定的通信鏈路。
  2. 選擇傳輸內容:在遊戲內或專門的傳輸應用中,玩家需要選擇要傳輸的遊戲資源或數據。
  3. 開始傳輸:一旦設備間建立連接,玩家即可啓動傳輸過程,等待資源傳輸完成。

接入步驟

以下示例中使用的API説明詳見接口文檔。

開發流程

導入模塊

導入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';

申請權限

申請ohos.permission.DISTRIBUTED_DATASYNC權限用於設備發現。

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);
}

創建遊戲近場快傳服務並註冊相關回調

導入相關模塊後,需先調用create接口創建遊戲近場快傳服務,然後註冊各回調事件。

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 // 是否展示系統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);
  }
}

// 註冊監聽
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) {

}

接收端接受協同

接收端實現onCollaborate回調,回調中調用acceptCollaboration接口接受協同。

export default class EntryAbility extends UIAbility { 
  // 協同回調
  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;
  }
}

接收端發佈自身遊戲近場快傳服務

接收端調用publishNearbyGame接口發佈自身遊戲近場快傳服務。

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);
}

發送端綁定接收端遊戲近場快傳服務

發送端綁定接收端遊戲近場快傳服務支持如下兩種方式:自動綁定和選擇綁定。

這裏以自動綁定為例,發送端調用autoBindNearbyGame接口自動綁定接收端近場快傳服務。

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);
}

接收端發送自身文件信息

收到建鏈成功回調後,接收端調用sendPackageInfo接口發送自身文件,如版本信息、包信息(包名和擴展數據的字符長度範圍:[0, 2048]、版本號字符長度範圍:[0, 256]、文件列表最多10000條)。

function connectNotifyCallBack(callback: gameNearbyTransfer.ConnectNotification) {
  if (callback.connectState == gameNearbyTransfer.ConnectState.CONNECTED) {
    // 連接成功回調,判斷當前是否為接收端。若當前設備為接收端,請設置為true,否則請設置為false。
    let isReceive = true;
    if (!isReceive) {
      return;
    }
    // 接收端收到連接回調後需要處理,發送資源包信息給發送端
    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);
    }
  }
}

發送端對比後傳輸資源包

發送端收到接收端發送的版本信息後進行對比,調用replyPackageInfoResult上報對比結果,根據對比結果決定是否需要調用transferPackageData接口發送資源包數據。

function receivePackageInfoCallBack(callback: gameNearbyTransfer.PackageInfo) {
  // 比較版本,決定是否需要發送資源包,也可以比較文件hash
  let packageInfoResult: gameNearbyTransfer.PackageInfoResult = {
    packageInfoResultCode: gameNearbyTransfer.PackageInfoResultCode.PACKAGE_AVAILABLE_COMPARED
  };
  try {
    // 上報對比結果
    gameNearbyTransfer.replyPackageInfoResult(packageInfoResult).then(() => {
      let packageData: gameNearbyTransfer.PackageData = {
        name: 'com.huawei.gamenearbydemo',
        version: '1.0.0',
        files: [{
          srcPath: '/data/xxxx/a.zip', // srcPath是需要發送文件的沙箱路徑,詳情請參見應用沙箱目錄。
          destPath: 'xxxx/a.zip'       // destPath是接收文件的自定義路徑,完整的沙箱路徑是fileStoragePath+destPath,詳情請參見應用沙箱目錄。
        }] 
      }
      try {
        // 發送資源包
        gameNearbyTransfer.transferPackageData(packageData).then(() => {
          // 發送成功
        }).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);
      }
    }).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);
  }
}

處理資源包傳輸進度信息

發送端和接收端在傳輸回調中處理傳輸進度信息。

function transferNotifyCallBack(callback: gameNearbyTransfer.TransferNotification) {
  if (callback.transferState == gameNearbyTransfer.TransferState.SEND_PROCESS) {
    // 處理髮送進度,如顯示進度條和速率
  }
  if (callback.transferState == gameNearbyTransfer.TransferState.SEND_FINISH) {
    // 發送完成
  }
  if (callback.transferState == gameNearbyTransfer.TransferState.RECEIVE_PROCESS) {
    // 處理接收進度,如顯示進度條和速率
  }
  if (callback.transferState == gameNearbyTransfer.TransferState.RECEIVE_FINISH) {
    // 接收完成,獲取到資源包存儲的沙箱路徑
    let fileStoragePath = callback.fileStoragePath;
    // 對fileStoragePath下的文件做處理
  }
}

處理已接收資源包後銷燬服務

對已接收數據做好處理或轉移後,調用destroy接口銷燬服務。若服務銷燬後再次使用近場快傳服務,需重新創建遊戲近場快傳服務並註冊相關回調。

public destroy() {
  // 取消回調註冊
  this.unregisterCallback();
  // 銷燬服務
  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);
    // 發送端選擇手動綁定接收端且已訂閲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) {
}

瞭解更多詳情\>\>

訪問遊戲服務聯盟官網

獲取遊戲近場快傳開發指導文檔

user avatar songminzh Avatar waylau521 Avatar
Favorites 2 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.