【OpenHarmony/HarmonyOS】自定义对战大厅设计:1v1、3v3、队伍槽位与邀请流程
【OpenHarmony/HarmonyOS】自定义对战大厅设计1v1、3v3、队伍槽位与邀请流程多人大厅的难点不在于把六个头像摆成两列而在于“谁有权修改什么”。房主选择模式和地图附近设备收到邀请访客接受后进入房间槽位可能是等待、AI 或关闭最后所有设备必须基于同一份配置同时进入游戏。本篇基于 OpenHarmony/HarmonyOS 项目中的自定义房间原型拆解 ArkUI 大厅的数据结构、P2P 信令和路由参数并明确当前模拟设备、UDP 原型与 3v3 未闭环的边界。一、页面有两个主要阶段CustomTeamPage没有拆成两个路由页而是使用showLobby在设置视图和大厅视图之间切换StateselectedMode:1v1|3v31v1;StatemapSize:small|medium|largemedium;StatemapSizeValue: number 1;StateshowLobby: boolean false;build(): void { Stack() { GalaxyBackground();if(!this.showLobby) {this.SetupView(); }else{this.LobbyView(); } } }阶段用户任务核心状态Setup选择 1v1/3v3、地图小中大、创建房间selectedMode、mapSizeLobby发现设备、邀请、安排槽位、开始游戏玩家数组、slotConfig、P2P 回调这种页面内切换能保留设置数据也避免房间创建前增加一层路由。但showLobby只是布尔值无法表达 Joining、Waiting、Starting、Failed 等网络阶段网络流程复杂后需要显式状态。二、队伍与设备的当前模型页面使用轻量PlayerModelclassPlayerModel{name:string;isHost:boolean;deviceId:string;constructor(name:string, isHost:booleanfalse) {this.name name;this.isHost isHost; } }三个列表分别表达本队、对方和附近设备StateteamAPlayers: PlayerModel[] [ new PlayerModel(我 (本机), true) ];StateteamBPlayers: PlayerModel[] [];StatenearbyDevices: PlayerModel[] [];这个模型适合 UI 原型但不足以支撑真实多人状态。name不是稳定身份deviceId只对发现列表有意义加入队伍后的newPlayer没有保存 peer IP 或 ID断线时难以定位应移除哪个槽位。生产模型至少还需要typePlayerConnectioninvited|joining|connected|disconnected;interfaceLobbyPlayer {playerId:string;displayName:string; deviceId?:string; peerAddress?:string;team:A|B;slotIndex:number;role:host|guest|ai;connection:PlayerConnection;ready:boolean; }这是演进方案。当前代码尚未建立完整玩家身份与准备状态。三、模式与地图选择如何驱动 UI模式按钮接收1v1 | 3v3选中时更新颜色并修改状态。地图使用 0、1、2 的分段控件同时维护字符串值。.onClick(() { animateTo({ duration:200}, () {this.mapSizeValue value;if(value 0)this.mapSize small;elseif(value 1)this.mapSize medium;elsethis.mapSize large; }); })1v1 时每队显示 1 个槽位3v3 时显示 3 个this.TeamSlots(this.selectedMode 1v1?1:3,this.teamAPlayers,#00E5FF);模式状态确实改变了大厅布局但它还没有限制加入人数、验证队伍完整性或改变网络同步协议。所以“UI 可选 3v3”和“支持六人真实对战”是两回事。四、创建房间后才开始发现设备 点击创建房间会清空附近设备并开始扫描createRoom(): void { animateTo({duration: 300 }, () { this.showLobby true; this.nearbyDevices [];P2PConnectionManager.getInstance().startDiscovery(); }); }把扫描放在进入大厅之后能避免用户还在选择配置时持续消耗无线与定时器资源。页面离开时调用stopDiscovery()清除广播 interval 并停止分布式发现。页面显示时先初始化单例 Manager再绑定四类回调发现设备、收到邀请、Peer 连接、房主开始游戏。这形成了一套事件驱动 UIManager 不依赖 ArkUI页面将网络事件转换为列表、Dialog、Toast 与路由。五、设备发现来自两条通道P2PConnectionManager同时尝试分布式设备管理和 UDP 广播flowchartTDA[startDiscovery]--B[Distributed Device Discovery]A-- C[UDP 每 2 秒广播 DISCOVER]A-- D[2 秒后注入模拟设备]B-- E[onDeviceFound]C -- F[收到对端 DISCOVER]F -- E D -- E E -- G[页面去重并加入 nearbyDevices]分布式设备在线事件被转换为统一DiscoveredDeviceUDP 收到DISCOVER后生成lan_ip作为设备 ID。页面按deviceId去重manager.onDeviceFound(device: DiscoveredDevice) {constexists this.nearbyDevices.find(itemitem.deviceId device.deviceId);if(exists)return;constplayer newPlayerModel(device.deviceName); player.deviceId device.deviceId;this.nearbyDevices.push(player); };同一物理设备可能同时通过分布式发现和 UDP 出现两个通道的 ID 不同所以按 deviceId 仍可能显示两次。需要统一身份关联或给通道结果标注来源并合并。六、附近列表中包含明确的模拟设备startDiscovery()两秒后总会调用setTimeout((){ this.mockDeviceFound(dev_001,Mate 60 Pro (模拟)); },2000);邀请dev_*时也不会真正发送网络包而是使用模拟 IP并在一秒后伪造 ACCEPTif(deviceId.startsWith(dev_)) { const targetIp 192.168.1.100;setTimeout((){ this.handleSignal({ type:ACCEPT, name:Mock Player, ip: targetIp }, targetIp); },1000);return; }这对单机演示 UI 很有价值但文章、截图和产品说明必须标注“模拟”。否则读者会以为两台真机发现和握手已经完成。七、邀请握手是怎样完成的真实 LAN 设备 ID 形如lan_ip。房主点击连接后发送{type:INVITE,name:我 (本机)}客端收到后弹出 AlertDialog。接受时发送ACCEPTacceptInvite(from: P2PInviteInfo):void{ manager.acceptInvite(from.ip,我 (本机));this.showLobby true;this.teamAPlayers [newPlayerModel(from.name,true) ];this.teamBPlayers [newPlayerModel(我 (本机),false) ]; }房主收到 ACCEPT 后把 peer 写入peersMap并触发onPeerConnected。页面当前只在 Team B 为空时加入一个玩家manager.onPeerConnected (peer: P2PPeerInfo) {if(this.teamBPlayers.length0) { this.teamBPlayers.push(newPlayerModel(peer.name,false)); }// 当前源码明确保留 3v3 待处理注释};因此握手链路主要覆盖 1v1。第二、第三名玩家、断线重连、队伍切换和房主迁移都没有闭环。八、槽位状态等待、AI、关闭空槽位点击时在三个值间循环StateslotConfig:Mapstring,string newMap();toggleSlotState(index:number,_count:number,isTeamA:boolean):void{constteam isTeamA ?A:B;constkey ${team}_${index};letcurrent this.slotConfig.get(key) ||open;if(current open) current ai;elseif(current ai) current closed;elsecurrent open;this.slotConfig.set(key, current); }值UI 含义GameEngine 当前消费open等待真人加入没有完整真人槽位绑定ai电脑补位Engine 会按 A_/B_ key 生成 AIclosed关闭该槽位不生成 AI页面还保留teamASlotStates、teamBSlotStates两个数组和getSlotState()但实际显示主要读取slotConfigMap。这是原型迭代留下的重复模型应选一个权威来源。Map 原地set()后使用this.mapSizeValue this.mapSizeValue尝试强制刷新不够可靠。更合适的是克隆 Map 后重新赋值。九、房主与访客权限目前没有真正隔离 ⚠️客端接受邀请后进入同一个 LobbyView。源码注释已经提出“Guest shouldnt change settings”但没有实现房主权限判断。开始按钮、槽位按钮和返回设置仍可能对访客可见。多人大厅至少要区分操作房主访客选择模式/地图允许只读修改 AI/关闭槽位允许不允许邀请附近设备允许通常不允许切换自己队伍/准备可按规则允许开始游戏允许且需校验不允许离开房间解散或迁移房主退出自身权限不能只隐藏按钮网络信令接收端也必须验证 sender 是否为房主否则访客可以自行构造 START_GAME。十、START_GAME 配置如何广播房主把 Map 转为普通对象并字符串化constconfigObject:Recordstring,string {};this.slotConfig.forEach((value, key) { configObject[key] value; }); manager.broadcastGameStart({mapSize:this.mapSize,teamMode:this.selectedMode,slotConfig:JSON.stringify(configObject) });Manager 为每个已连接 peer 发送 UDP JSON{type:START_GAME,config: {mapSize:medium,teamMode:3v3,slotConfig:{\A_1\:\ai\}} }客端收到后回调页面并pushUrl到 Index。房主也执行同样路由。这个协议表达了“开始意图”但没有房间 ID、配置版本、随机种子、开始时间和确认 ACK。十一、UDP 信令的可靠性与安全边界当前原型使用固定 8888 端口和 JSON 明文。UDP 不保证送达、顺序或唯一性START_GAME 丢包时可能房主已进入、客端仍留在大厅。重复包则可能重复导航。生产设计至少需要messageId去重roomId隔离同网段不同房间senderId验证消息来源sequence判断新旧配置ACK retry关键命令确认protocolVersion兼容升级身份验证或会话签名阻止任意局域网设备伪造信令长度与字段白名单避免恶意大包和非法 JSON。当前handleSignal(START_GAME)会直接把发送者加入 peers 并触发回调没有确认其此前是否通过邀请成为房主。它适合可信局域网原型不适合作为公网或正式竞技协议。十二、开局参数当前没有在 Index 形成闭环CustomTeamPage 会发送并路由gameModemultiplayer、地图和槽位配置但 Index 的onPageShow()中消费多人参数的代码被注释标注为离线版本移除。GameEngine 确实能解析多人配置并为A_*、B_*的ai槽生成不同队伍 AI但页面路由到startGame(multiplayer, ..., config)的接线当前没有执行。因此准确结论是大厅 UI、设备发现接口、邀请信令和开局配置格式已经存在模拟设备可以演示 1v1 加入效果3v3 真人槽位尚未处理路由接收端当前不会自动启动完整多人局UDP 状态同步属于原型不能宣称正式联机已完成。十三、页面离开时的清理还不完整aboutToDisappear()调用stopDiscovery()能清除广播 interval 和设备发现监听。但页面赋给单例 Manager 的以下回调仍保留onDeviceFoundonReceiveInviteonGameStartonPeerConnected如果页面实例离开后单例收到 UDP 消息回调仍可能修改旧页面状态或执行路由。应在离开时仅当回调仍属于当前页面时置空或由 Manager 返回 unsubscribe 函数。UDP socket 本身也没有在页面离开时关闭因为它属于全局单例。是否保持监听取决于产品如果后台也要接收邀请应由 Ability 生命周期管理如果只在大厅有效应提供disposeSocket()。十四、推荐的大厅状态机stateDiagram-v2 [*] -- Configuring Configuring--Discovering: 房主创建房间 Configuring--Joining: 访客接受邀请 Discovering--Lobby: 扫描启动 Joining--Lobby: 握手确认 Lobby--Starting: 房主点击开始 Starting--Lobby: 有设备未确认/发送失败 Starting--InGame: 所有设备确认同一配置 Lobby--Closed: 房主解散 Lobby--Left: 访客离开主状态之外再保存LobbySnapshot房间 ID、版本、房主、玩家、槽位、地图和模式。每次变更由房主增加 revision 并广播完整快照客端只接受更新版本能比零散地修改数组更容易恢复一致。十五、测试矩阵 场景预期重复发现同一 deviceId附近列表只出现一次分布式与 LAN 发现同设备通过稳定身份合并模拟设备出现UI 明确标“模拟”1v1 接受邀请房主和访客占据正确队伍第二名 peer 加入 1v1拒绝或进入观察不静默丢失3v3 三名访客加入每人有稳定 slotIndex访客点击开始UI 和协议端都拒绝START_GAME 丢包重试并等待 ACKSTART_GAME 重复只导航一次非房主伪造 START_GAME消息被拒绝页面离开后收到消息不再修改旧组件非法 slotConfig JSON保留大厅并提示配置错误Index 参数消费关闭测试明确标记端到端未完成十六、总结 ✨项目已经搭建了一条有价值的自定义大厅原型ArkUI 中选择模式与地图进入大厅后发现设备UDP 发送邀请和接受信令槽位可切换等待、AI、关闭房主广播统一开局配置客端根据回调导航。但“原型可演示”和“多人功能完成”必须严格区分。当前附近列表主动注入模拟设备Peer 加入只完整处理 1v13v3 真人槽位留有待处理注释访客权限没有隔离UDP 没有 ACK、身份验证和去重Index 的开局参数消费也处于注释状态。下一步应先建立权威 LobbySnapshot、房主权限、稳定玩家身份和可靠信令再连接战斗会话。只有配置、网络、页面和引擎四端都消费同一份状态大厅才真正成为多人对战的入口。推荐标签OpenHarmonyHarmonyOSArkTSArkUI局域网UDP多人游戏状态同步P2P

相关新闻

2026北京GEO解决方案服务流程详解:企业配合资料与避坑指南

2026北京GEO解决方案服务流程详解:企业配合资料与避坑指南

随着豆包、DeepSeek等生成式搜索引擎成为用户获取信息的重要渠道,北京企业对品牌在AI回答中曝光度与引用率的关注度持续提升。不少企业在咨询GEO解决方案服务流程相关问题时,既担心遇到流程不规范的机构踩坑,也不清楚自己需要提前准备哪些配合…

2026/10/21 9:03:50 阅读更多 →
【2027最新】基于SpringBoot+Vue的兴顺物流管理系统管理系统源码+MyBatis+MySQL

【2027最新】基于SpringBoot+Vue的兴顺物流管理系统管理系统源码+MyBatis+MySQL

💡实话实说:C有自己的项目库存,不需要找别人拿货再加价。博主介绍:🎓 江南大学计算机科学与技术专业在读研究生 | CSDN博客专家 | Java技术爱好者 在校期间积极参与实验室项目研发,现为CSDN特邀作者、掘金优…

2026/10/21 2:28:50 阅读更多 →
AzurLaneAutoScript终极指南:7×24小时全自动碧蓝航线舰队管理

AzurLaneAutoScript终极指南:7×24小时全自动碧蓝航线舰队管理

AzurLaneAutoScript终极指南:724小时全自动碧蓝航线舰队管理 【免费下载链接】AzurLaneAutoScript Azur Lane bot (CN/EN/JP/TW) 碧蓝航线脚本 | 无缝委托科研,全自动大世界 项目地址: https://gitcode.com/gh_mirrors/az/AzurLaneAutoScript 你是…

2026/10/21 10:45:49 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/22 10:18:38 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/22 11:01:53 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/22 9:44:47 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/22 10:42:00 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/22 10:40:08 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/22 10:56:37 阅读更多 →

日新闻

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/22 1:47:45 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/22 21:33:42 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/22 5:12:39 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/22 22:56:44 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/22 22:56:12 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/22 19:06:46 阅读更多 →