HarmonyOS鸿蒙Next中单机游戏app如何实现无网联机呢

HarmonyOS鸿蒙Next中单机游戏app如何实现无网联机呢 【问题描述】:做的一个卡牌类单机游戏,我看快游戏是可以通过近场联机实现无网联机的,那游戏app如何实现无网联机呢是否有方案

【问题现象】:单机游戏app如何实现无网联机,具体该怎么实现呢

【版本信息】:开发工具版本:6.1.1、手机系统版本:6.1.0.117、Api语言版本:23

【复现代码】:未涉及

【尝试解决方案】:未涉及

11 回复

尊敬的开发者您好,您可使用WLAN P2P(Wi-Fi直连)技术配合TCP/UDP Socket通信,实现两台设备的局域网无网互联。房主设备创建P2P群组作为微型服务器,客机设备扫描并加入该群组获取分配的局域网IP,随后通过 Socket建立稳定的字节流数据通道,用于卡牌游戏的状态同步。

【实施步骤】

步骤1:权限声明。在项目的 module.json5 文件中申请必要的权限,包括 ohos.permission.GET_WIFI_INFOohos.permission.SET_WIFI_INFO 以及位置权限(用于扫描周边的 P2P 设备)。

步骤2:房主端建立房间。调用 [@ohos](/user/ohos).wifiManagerp2p.createGroup() 建立 P2P 群组。获取自身分配的群主 IP 后,引入 [@ohos](/user/ohos).net.socket 模块,使用 constructTCPSocketServer() 绑定 IP 并监听特定端口。

步骤3:玩家端扫描与连接。调用 getP2pLinkedInfo() 发现房主设备,找到后通过 p2pconnect() 发起连接。连接成功获取房主 IP 后,使用 constructTCPSocket() 建立 TCP 连接,完成双端数据通道搭建。

【参考文档】

[WLAN P2P 开发指南(@ohos.wifiManager)](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/p2p-development-guide#简介)

[@ohos.net.socket (Socket连接)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-socket)

【备选方案】

方案A:星闪通信(NearLink)。(优点:短距通信技术,时延极低、连接最稳定,且跳过了繁琐的 IP 和 Socket 握手过程;缺点:依赖硬件,仅限搭载星闪芯片的最新特定机型支持,设备普及率有限。)

方案B:传统蓝牙直连(Bluetooth SPP)。(优点:兼容性最广,全平台设备支持,无需申请位置权限也能在部分场景下完成发现与连接;缺点:带宽较窄,传输距离较短且抗干扰能力弱于 WLAN直连。)

更多关于HarmonyOS鸿蒙Next中单机游戏app如何实现无网联机呢的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


使用蓝牙通信就可以~ 只要两台或多台设备能互相连接,发送数据不是难事~~

概述

蓝牙(Bluetooth)是一种无线通信技术,被广泛应用于各种电子设备之间的短距离连接与数据传输。而低功耗蓝牙(Bluetooth Low Energy,简称 BLE)是一种能够在低功耗条件下进行通信的蓝牙技术,支持发起BLE扫描、发送BLE广播报文以及基于通用属性协议(Generic Attribute Profile,GATT)的连接与数据传输。与传统蓝牙相比,BLE功耗更低,适用于需要长时间运行的低功耗设备,例如智能手表、健康监测设备、智能家居等。

本文针对BLE开发,以心跳监控仪场景为例,主要介绍蓝牙扫描管理、蓝牙连接状态管理和蓝牙设备特征值同步三个场景,并分别从服务端和客户端描述其相关实现。

实现原理

实现低功耗蓝牙通信的主要步骤如下:

  1. 广播与扫描阶段:服务端创建GattServer实例,定义服务特征并配置广播参数,验证蓝牙状态后启动广播,使自身可被发现;客户端确保蓝牙开启,设置扫描过滤条件,注册设备发现回调并启动扫描,收集可连接设备。
  2. 连接管理阶段:服务端调用on(‘connectionStateChange’)订阅GATT profile协议的连接状态变化事件,管理连接生命周期;客户端基于扫描到的服务端广播地址创建GattClientDevice实例,调用on(‘BLEConnectionStateChange’)订阅GATT profile协议的连接状态变化事件,发起连接请求,成功后获取服务与特征,启用特征值通知功能,为数据传输做准备。
  3. 数据传输阶段:服务端订阅描述符写请求事件,准备数据(如心率值),通过特征通知机制向已连接客户端发送数据;客户端监听特征值变化事件,接收并解析数据内容,根据业务需求更新用户界面或执行相应逻辑。
  4. 断开连接阶段:服务端在连接状态回调中处理断开事件,可以停止广播并清理资源;客户端主动断开连接,关闭GATT客户端,注销监听器,重置连接状态,可选择将设备ID持久化以便后续自动重连。

cke_404.png

背景知识:

1、快游戏近场联机与原生 App 区别

  • 快游戏内置专属近场联机封装 SDK,底层屏蔽设备发现、蓝牙 / Wi-Fi 自组网、会话管理,一行 API 即可开启无网对战,仅适配 RPK 快游戏,不能直接移植到原生 ArkTS 游戏 App
  • 原生鸿蒙卡牌 App 无现成封装,需要自主搭建近场通信底层链路,API23(HarmonyOS 6.1)提供 4 套成熟无网通信底座,全部支持飞行模式、无蜂窝 / Wi-Fi 外网环境

2、无网联机底层技术对比(适配卡牌轻量数据同步)

方案 底层依托 组网条件 延迟 / 带宽 适配卡牌游戏优缺点
分布式软总线(推荐首选) DSoftBus、蓝牙 / Wi-Fi P2P 自动切换 两台设备登录同一华为账号,无需热点、无外网 低延迟、稳定 自动发现、碰一碰快速配对、官方原生 API、上架无审核风险;限制同华为账号
GameService 游戏近场快传 Game Service Kit 无需同账号,仅两台设备同时打开游戏 高速,适合批量卡牌同步 游戏专项能力,封装完善;仅 API20 + 支持,需集成游戏 SDK
蓝牙 BLE Socket 直连 蓝牙低功耗 手动配对蓝牙,无任何账号限制 带宽偏低,仅传操作指令 跨鸿蒙 / 安卓兼容,双人极简;多人组网差、配对繁琐
Wi-Fi P2P 直连 无线点对点 双方开启 Wi-Fi,无需路由器热点 带宽最高 多人联机友好;耗电高、部分机型兼容性一般

问题解决:

方案一:分布式软总线(原生 App 最优,同华为账号,碰一碰联机)

伪代码:

//1、初始化设备管理器,扫描附近同账号游戏设备

import deviceManager from '@ohos.distributedHardware.deviceManager';
import distributedSession from '@ohos.distributedHardware.session';

// 全局设备管理实例
let dmIns: deviceManager.DeviceManager | null = null;
const APP_BUNDLE = "com.xxx.cardgame";
// 存储扫描到的联机设备
let remoteDeviceList: deviceManager.DeviceInfo[] = [];

// 初始化+启动扫描
async function startScanDevice() {
  dmIns = await deviceManager.createDeviceManager(APP_BUNDLE);
  // 监听扫描到新设备
  dmIns.on("deviceFound", (dev: deviceManager.DeviceInfo) => {
    // 过滤仅运行本游戏的设备(能力标识匹配)
    if(dev.capabilitySet?.has("card_game_online")){
      remoteDeviceList.push(dev);
    }
  })
  // 启动主动扫描,高频搜索近场设备
  dmIns.startDeviceDiscovery({
    publishId: 10001,
    mode: "ACTIVE",
    capability: "card_game_online"
  })
}

//2、选择设备,建立 P2P 分布式通信 Session(无网数据通道)

let gameSession: distributedSession.Session | null = null;
// 发起连接,建立对战会话
async function connectGameDevice(targetDevId: string) {
  const sessionOpt: distributedSession.SessionOption = {
    sessionName: "card_game_p2p",
    deviceId: targetDevId,
    sessionType: distributedSession.SessionType.TYPE_P2P
  }
  // 创建可靠传输通道,卡牌指令不丢包
  gameSession = await distributedSession.createSession(sessionOpt);
  // 监听远端发来的卡牌操作指令
  gameSession.on("message", (msg: ArrayBuffer) => {
    const cmdStr = buffer.from(msg).toString("utf-8");
    const gameCmd = JSON.parse(cmdStr);
    handleCardGameCmd(gameCmd); // 本地执行出牌/回合逻辑
  })
}

// 3、封装卡牌指令发送工具(本地操作同步至对手)
import buffer from '@ohos.buffer';
// 发送出牌、摸牌等操作
function sendCardCommand(cmdObj: Record<string, any>) {
  if(!gameSession) return;
  const buf = buffer.from(JSON.stringify(cmdObj));
  gameSession.sendMessage(buf.buffer);
}
// 示例:玩家打出卡牌
sendCardCommand({
  type: "play_card",
  cardId: 1001,
  playerIndex: 0
})


//生命周期释放(退出房间销毁会话、停止扫描)
function closeGameLink() {
  if(gameSession){
    gameSession.close();
    gameSession = null;
  }
  if(dmIns){
    dmIns.stopDeviceDiscovery();
    dmIns.off("deviceFound");
  }
}

碰一碰 NFC 快速组队优化(提升用户体验)

  • 新增 NFC 权限 ohos.permission.NFC_TAG;
  • 两台设备 NFC 区域触碰,自动交换设备 ID,跳过手动扫描列表选择,直接建立对战会话;
  • 飞行模式下仅需开启蓝牙 / NFC 即可触发配对,完全不需要网络。

方案二:GameService 近场快传(无需同华为账号,游戏专用)

适合不想强制用户登录同一华为账号的场景,API23 完整兼容:

  1. 集成 Game Service Kit 游戏 SDK,声明系统能力 SystemCapability.GameService.GameNearby;
  2. 调用 gameNearbyTransfer.discoveryNearbyGame() 扫描附近打开同款游戏的设备;
  3. 绑定近场通道后建立消息回调,传输卡牌操作指令;
  4. 优势:跨账号、专门为游戏设计;劣势:需要引入额外游戏 SDK 包。

备选兜底方案:蓝牙 BLE 直连(无华为账号限制)

  1. 开启蓝牙扫描,搜索对方蓝牙设备,手动配对;
  2. 建立 BLE GATT 读写通道,JSON 指令分包传输;
  3. 适合双人极简联机,多人同时对战性能较差;
  4. 缺点:配对步骤繁琐,用户操作成本高,延迟高于分布式软总线。

可以做,建议别把“单机游戏”改成重服务器架构,而是在本地游戏逻辑外加一层近场通信。卡牌类更适合同步“操作指令”,例如抽牌、出牌、结束回合、随机种子和递增 seq,不要同步整个画面或完整状态。

我这边建议先用 BLE GATT 做最小闭环:A 机创建房间,作为 GattServer,添加自定义 service/characteristic 后 startAdvertising;B 机 startBLEScan 按 serviceUuid 找房间,createGattClientDevice 后 connect;连接成功后 getServices,B 机用 writeCharacteristicValue 发送操作,A 机用 notifyCharacteristicChanged 回推确认或回合同步。

伪代码可以按这个粒度封装:

import { ble } from '@kit.ConnectivityKit';

type Move = { roomId: string; seq: number; op: 'draw' | 'play' | 'end'; cardId?: string; seed: number };
const SERVICE_UUID = '00001888-0000-1000-8000-00805f9b34fb';
const CHAR_UUID = '00002a88-0000-1000-8000-00805f9b34fb';

function encodeMove(move: Move): ArrayBuffer {
return new Uint8Array([...JSON.stringify(move)].map(c => c.charCodeAt(0))).buffer;
}

function syncMove(gattClient: ble.GattClientDevice, move: Move) {
const characteristic: ble.BLECharacteristic = {
serviceUuid: SERVICE_UUID,
characteristicUuid: CHAR_UUID,
characteristicValue: encodeMove(move),
descriptors: []
};
gattClient.writeCharacteristicValue(characteristic, ble.GattWriteType.WRITE, err => {
if (err) { console.error('sync move failed: ' + JSON.stringify(err)); }
});
}

落地时再加 3 个保护:声明并动态申请 ohos.permission.ACCESS_BLUETOOTH;连接后立即 stopBLEScan/stopAdvertising,避免耗电;所有操作带 seq 和 ack,断线重连后从最后确认的 seq 补发。这样两台手机像交换“出牌小纸条”,游戏仍各自在本地跑,通信层只负责把动作送到对方。

参考:

无网联机主要依赖局域网/近场通信技术 蓝牙、星闪、NFC 等

这个需求可以实现,思路和“快游戏近场联机”类似,本质不是联网,而是利用设备之间的近场通信能力。

卡牌类游戏一般有几种方案:

方案1:蓝牙/WiFi P2P(比较常用)

手机A创建房间 → 蓝牙/WiFi发现设备 → 手机B加入 → 建立Socket通信 → 同步游戏数据

比如:

玩家A出牌 → 发送操作数据 → 玩家B同步状态

这里传的不是整个游戏画面,而是:

卡牌ID、玩家操作、随机种子、状态变化

这种方式延迟低,适合游戏。

方案2:NearLink(星闪)

鸿蒙设备可以考虑 NearLink。

流程:

设备发现 → 建立连接 → 数据传输

优势:

  • 延迟更低
  • 功耗更低
  • 连接体验更好

适合:

双人对战、实时同步。

方案3:局域网联机

如果两台设备连接同一个热点:

手机A启动服务 → 手机B连接IP → TCP/UDP通信

实现简单。

卡牌游戏一般不建议同步整个场景数据。

推荐:

本地运行游戏逻辑 + 同步操作

比如:

玩家A:

抽牌 → 发消息

玩家B:

收到 → 执行同样逻辑

这样数据量小,也不容易不同步。

简单理解:

单机游戏 → 增加一个通信层 → 变成局域网/近场联机

不需要服务器。

希望能帮到你~~~

近场联机是用的鸿蒙设备分布式能力。
联机游戏可以参考《联机对战》,开发比较简单。

p2p把,

蓝牙或者wifi p2p,

在HarmonyOS NEXT中,单机游戏实现无网联机可基于鸿蒙分布式软总线能力,通过Wi-Fi Direct、蓝牙或近场通信建立设备间P2P通道。利用@ohos.wifi.direct@ohos.bluetooth接口实现发现、配对与数据传输,或使用分布式数据管理服务同步游戏状态。无需外部网络,设备自动组网,延迟低,适合局域网对战场景。

HarmonyOS 中单机游戏 App 实现无网联机,可基于以下三种技术:

  • WLAN 直连(Wi-Fi Direct):调用 @ohos.wifi.direct 接口,设备间直接建立 P2P 连接,随后通过 Socket(TCP/UDP)传输游戏数据,无需外部网络。
  • 蓝牙:使用 @ohos.bluetooth 建立 RFCOMM 通道,适合数据量小、低功耗的卡牌游戏,但延迟相对较高。
  • 分布式软总线:这是鸿蒙特色能力,通过 @ohos.distributedDeviceManager 自动发现附近设备,借助软总线建立会话,配合 @ohos.rpc 实现跨设备方法调用。该方案与快游戏的近场联机本质一致,且支持多设备组网,推荐用于实时性要求稍高的场景。

实现时通常一台设备作为服务端创建通道,其余设备作为客户端发现并连接,建立后通过消息同步或 RPC 交换游戏状态即可。API 23 已支持上述接口,可直接在开发工具中使用。

回到顶部